LSL-Editor Help

LSL-Editor Help 1.0

LSL-Editor Help is part of the LSL-Editor project by Alphons van der Heijden and is distilled from www.lslwiki.net but is no part of the Wiki

See also: LSL Wiki Copyright

If you find my work useful, please do support me by making a donation.
By supporting me, I can continue my work on the LSL-Editor.
Let the program using for free and keeping my website up and running.

or

To make a donation to me via Second Life, while logged into the game,
click the Search button, click the People tab, then search for "Alphons Jano".
Then, look at Alphons Jano's profile, and there should be a Pay button visible.

My donating sponsors will be rewarded in a special way.
Thanks

-Alphons van der Heijden.

LSL-Editor and LSL-Editor Help are independent projects and are not affiliated with Linden Lab.
Second Life and LSL are the property of Linden Lab.
LSL-Editor and LSL-Editor Help is not affiliated with LSL Wiki.


HomePage

Welcome to the LSL Wiki, an effort to document the LSL scripting language of Second Life. This wiki is intended as an open documentation resource to help scripters of all skill levels. Anyone can contribute, so feel free to jump in.

Looking for information about Second Life in general? Try Linden Lab's official Second Life Wiki.
Are you new to LSL scripting? Try our LSL Tutorials.

There are other forks of the LSL Wiki in various states of development. See the 'Link to Useful Resources' at Linden Lab's LSL Portal.

Getting Started: LSL Reference: Function Categories:
Whether you're new to scripting or an experienced programmer, these pages should help you learn to use LSL.

Introduction
Search the Wiki
Tutorials
How Do I...
Glossary
Humor

Scripting Mentors
Scripting Groups
Scripting Teachers

Alternate Editors
Errors
Hacks
Known Bugs & Debugging Tips
Annoyances/Gotchas
Lag
Revisions - What's new in LSL?
LSL Style Guide

Script Library
Examples
Protocol Exchange 		Already know how to script, and just need the reference?

Constants
Events
Flow Control:
for, do-while, if-else, jump, return, state, while
Functions (see categories to right)
Operators:
assignment, binary, bitwise, boolean, unary
States
Types:
integer, float, vector, rotation, key, string, list
Variables
  • Agent/Avatar
  • Camera
  • Collision
  • Color
  • Communications
  • Controls
  • Dataserver
  • Detection
  • Dynamics/Movement/Physics
  • Group
  • Inventory
  • Link
  • Land (Ground/Terrain)
  • Light
  • List
  • Math
  • Particle (llParticleSystem)
  • Primitive/Object (llSetPrimitiveParams)
  • Script
  • Sensor
  • Simulator (Environment/World)
  • Sound
  • String (Text/Name)
  • Teleport
  • Texture
  • Time
  • Transformation:
    Rotation/Scaling/Translation
  • Vehicle
  • Video
  • Weather
  • XML-RPC
The LSL Wiki is an independent resource and is not affiliated with Linden Lab. Second Life and LSL are the property of Linden Lab. Portions of this site are copyright © Linden Lab.
By posting here, you agree to allow other users to copy, reproduce and alter your contribution for non-profit use. For additional information on the LSL Wiki's copyright policy, please see LSL Wiki Copyright.


LSL

The Linden Scripting Language (LSL) is used for creating interactive content in Second Life.

LSL is a simple, yet powerful scripting language used to attach behaviors to the objects found in Second Life. It follows the familiar syntax of a C/Java-style language, is event-driven, features states, 3D variable types (vector and quaternion), as well as a variety of built-in functions for manipulating physics and avatar interaction. LSL is also missing some features found in those languages--most notably arrays (but there is a list type) and user-defined constants.

Multiple scripts may also be attached to the same object, allowing a style of small, single-function scripts to evolve, which leads to scripts that perform specific functions (hover, etc.) and allows scripts to be combined to form new behaviors.

Want to learn LSL? Take a look at the crash course, some example scripts, and the Style Guide.

Tutorial | Script | Alternate Editors | LexFile


LSLTutorials

Tutorials

Here are some informative pages geared towards helping you get started with LSL.

Tutorial | Description
LSL101 | If you've never programmed before, this tutorial is for you.
Crash Course | A general overview of programming, targeted at LSL; each chapter builds on previous concepts.
Tutorial | A basic tutorial that covers what a script is, functions, code blocks and style, though it is only one page long.
Vehicle Tutorial | A reformatted and updated version of the original Linden Vehicle Tutorial.
Autonomous Objects | Information, techniques and tips on creating moving objects.
Kan-ed: Using the Linden Script Language | A short tutorial that includes a collection of examples that illustrate basic LSL capabilities in graphics, "physics," communication between users and scripts, and object creation. Examples are chosen to present fairly isolated "building blocks" that may be combined later to create more complex scripts, and to better understand the example scripts and Script Library presented on the Wiki.
Jukebox Tutorial | Step-by-step jukebox music player construction - A short training course in basic scripting to create a music player. Offers basic knowledge of programming in LSL and how everything works.
rotations and quaternions | A useful forum thread with tips and tricks
A New Hampshire Coder in Linden Lab's Court | An article on dev.aol.com discussing software engineering strategies for LSL
Second Life scripting forums | Lots of useful information in the forums. Go, use search, and learn.

Since not everyone finds these useful or accessible there is now a list of scripting teachers available.

Glossary


How Do I...

...get a script to read from another file; IE: a notecard?

...give someone a folder full of inventory items?

...specify which folder I want the inventory items to end up in?
See llGiveInventoryList.

...make a gun?
See llRezObject and ExampleGun.

...find out if land is no-build/no-script, etc?
llGetParcelFlags and llGetRegionFlags

...get the length of a string?
See llStringLength.

...get the arc for a sensor that senses straight ahead?

....and also the arc for a sensor that scans around and above its task (but not underneath it) without changing the task's orientation?
(Specifically, when nobody's around to see a critter, I want to it to sleep to keep from lagging the server without making the critter look wrong from afar.)

....and also the arc for a sensor that scans straight below its task?
See llSensor. For a reduced arc, use a smaller value of radians for the arc parameter. To use the sensor in a different direction than east (forward), rotate the prim in that direction.

...make magical holes in walls that you can walk through?
See llSetPrimitiveParams. You can make holes in box prims by modifying its hollow under the Object tab in the Edit menu.
You can also use llSitTarget to jump through walls.

...make my script communications secure?
See Script security

...create a script that generates a random number?
integer rand(integer max)

{
    float f_max = (float)max;//we do this typecast explicitly (otherwise the compiler just fills it in for us.
    if(max != f_max)//max was an integer that couldn't be made into a float nicely.
        return (integer)(max * llFrand(1.0));//multiply max by a rand float with a 0->1 range.
    return (integer)llFrand(f_max);//the convert was clean so make LSL do the work.
}
This is a function that is called by: rand(50), which would return a random integer between 0 and 49 inclusive.

...make a script that changes the color of an item?
vector color = <1,0,0>; //The color you want the item to change when touched
default
{
    touch_start(integer total_number)
    {
        llSetLinkColor(LINK_SET, color, ALL_SIDES); //Sets the color on all the links
    }
}

...make an invisiprim...
See Invisiprim for an example script - Joker Opus

... get the direction an agent is facing (Not the direction of the head) ...

... have object A tell object B its position and rotation, and have object B replicate the positioning and rotation, aside from a small offset on the local Z axis ...
Get position with llGetPos rotation with llGetRot, cast them to strings, use llSay to communicate them, then in B's listener cast the message back to vector and rotation, adjust it and set it with llSetPos and llSetRot.

... move mutliple linked prims at once without them going out of sync ...

... Make a lift ...

... Modify a script while running ...

... Remove spam comments from the LSL Wiki Mirror 7-7-7? ...

... Make an object move away from avatars, maintaining a minimum distance from all -- similar to a dangling carrot or running away like a scared animal. ...
See Sensors for the general principle, though not a specific method.

...Do the equivalent of an HTML A(nchor) in the object text? I want to make a portion of the text hovering over an object, set with llsetText(), clickable so that I can do something like, show the profile of the owner of the object, when I click his name. etc. ...

HomePage | Tutorial


glossary

Glossary of Terms

This is intended to be a glossary of scripting-specific terms one may come across when learning LSL. For a glossary of general Second Life terms and phrases, see the Second Life Support Wiki's Glossary page.

A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

A
Alpha (Alpha Channel, Transparency)
Argument (Parameter)
Assign (Assignment)
Attachment (Attach)

B
Binary
Bitwise
Block
Boolean
Brace (Curly Braces, Curly Brackets)
Bracket
Bug

C
Call
Character
Client
Code Block
Comma
Comment
Compile
Compiler
Constant
Crash

D
Dataserver
Dialog (Dialog Box)
Do While

E
Editor
Event (Event Handler)
Email (E-mail, Mail)

F
For
Float
Function (Function Call)

G
Glitch (Bug)
Global (Global Coordinate, Global Variable)
Goto
Grid

H
Havok
Heap
HUD (Heads-Up Display)

I
Identifier
IF (IF/ELSE, if-else).
Indent
Integer
Inventory
Instant Message (IM)
Item (Inventory Item)

J
Joint
Jump

K
Key (UUID)

L
Lag
Linden
Line
Link Message
List
Listen (Listener)
Local (Local Coordinate, Local Variable)
LSL (Linden Scripting Language)

M
Math (Maths, Mathematics, Arithmetic)
Memory

N
Notecard

O
Object
Operator

P
Parameter (Argument)
Particle (Particle System)
Prim (Primitive)

Q
Quaternion

R
Return
Rez
Rotation

S
Say
Script
Semicolon
Server
Shout
Sign
Source (Source Code, Code)
Stack
State
String

T
Tab
Type
Typecast (Typecasting)

U
Unary
Underscore
User
User Interface
UUID

V
Value
Variable
Vector
Vehicle

W
While
Whisper
Whitespace
World

X
XML-RPC

Y

Z

Tutorials


Humor

Humor has its place here on the wiki. Even though theyre not directly reference material, we wiki'ans are human *gasp* and do enjoy the occasional side-splitting funny. :-)

Q: Why did the plywood cube cross the road?
A: Because llSetPos stops it from going farther then 10 meters per call! *ducks thrown tomatoes*

Q: Why are the plywood cubes male??
A: Because llSendRemoteData is female!

Q: How many scripters does it take to change a lightbulb?
A: Just one, if the changed event is set.

Fear those who don't have a proper sense of humor. - Ice
Scorn those who have too much of a sense of humor. -EepQuirk
Case in point. - Ice

Somebody just said 'Go rez your mother'. Best insult of 2006 (so far) - Will Moxie

Technical Documentation is like sex. When it's good, its very very good. When it's bad, it's better than nothing. - TiPBaKeR

Best insult of all time (in my opinion) "I scripted your girlfriend..." Misquoted from DraconisNeurocam talking to Abbadon Fujin.

Limericks:

Here I sit broken hearted,
My Scripts compiled but the sim departed,
I spent my time to craft my code,
But poor old SL couldnt take the load,
Oh How I wish I'd never started.....
- Newgate Ludd

There once was a man from Da Boom
His script memory ran out of room
The stack heap collided
The script was divided
And now development can resume
- Ultralite Soleil

contributors | stupid | clever | 31337 | 1337 | l33t | hippo | pfft | boobies | llPizza | llDetectedSex


1337

Shorthand for 31337, although 1337 is much more commonly used.

Wikipedia definition of leet, l337 qualifies as an official varient spelling.

Humor


31337

It means elite. Wow.

Wikipedia definition of leet, 31337 qualifies as an official varient spelling.

Humor


AirSeaAltimeter

Please delete this page it has been moved to LibraryAirSeaAltimeter
To delete this page (if you are the owner) please click http://secondlife.com/badgeo/wakka.php?wakka=AirSeaAltimeter/delete


BarFoo

See FooBar.


Boobies

Boobies are the entire reason why SL is awesome.

However, you cannot script bouncy boobies with LSL, which is the language's ONLY shortcoming. :(

UPDATE: This shortcoming is no more see this forum thread.

We all love to shout

Hooray for boobies!!!

Humor


clever

<a href= http:dvjzw.fizwig.com/sales-uk-viagra.html >best price viagra</a> [url=http:dvjzw.fizwig.com/sales-uk-viagra.html]best price viagra[/url] <a href= http:dvjzw.fizwig.com/gfmgwni6.html >tadalafil generic equivalent of viagra</a> [url=http:dvjzw.fizwig.com/gfmgwni6.html]tadalafil generic equivalent of viagra[/url] <a href= http:dvjzw.fizwig.com/womenviagra.html >blindness viagra</a> [url=http:dvjzw.fizwig.com/womenviagra.html]blindness viagra[/url]


EconResource

UnlimitedChaosePower!
<a href="http:www.anderson.edu/studentblogs/pharmacy/drugs-from-canada.html">drugs from canada</a> [URL=http:www.anderson.edu/studentblogs/pharmacy/drugs-from-canada.html]drugs from canada[/URL] http:www.anderson.edu/studentblogs/pharmacy/drugs-from-canada.html
<a href="http:www.asbury.edu/includes/cache/pharmacy/community-pharmacy.html">community pharmacy</a> [URL=http:www.asbury.edu/includes/cache/pharmacy/community-pharmacy.html]community pharmacy[/URL] http:www.asbury.edu/includes/cache/pharmacy/community-pharmacy.html
<a href="http:www.asbury.edu/includes/cache/ultram/buy-ultram.html">buy ultram</a> [URL=http:www.asbury.edu/includes/cache/ultram/buy-ultram.html]buy ultram[/URL] http:www.asbury.edu/includes/cache/ultram/buy-ultram.html
<a href="http:cdsp.edu/subheads/tramadol/side-affects.html">side affects</a> [URL=http:cdsp.edu/subheads/tramadol/side-affects.html]side affects[/URL] http:cdsp.edu/subheads/tramadol/side-affects.html
<a href="http:www.anderson.edu/studentblogs/ultram/ultram-withdrawal-symptoms.html">ultram withdrawal symptoms</a> [URL=http:www.anderson.edu/studentblogs/ultram/ultram-withdrawal-symptoms.html]ultram withdrawal symptoms[/URL] http:www.anderson.edu/studentblogs/ultram/ultram-withdrawal-symptoms.html
<a href="http:www.anderson.edu/studentblogs/ultram/discount-ultram.html">discount ultram</a> [URL=http:www.anderson.edu/studentblogs/ultram/discount-ultram.html]discount ultram[/URL] http:www.anderson.edu/studentblogs/ultram/discount-ultram.html
<a href="http:www.asbury.edu/includes/cache/ultram/ultram-zoloft.html">ultram zoloft</a> [URL=http:www.asbury.edu/includes/cache/ultram/ultram-zoloft.html]ultram zoloft[/URL] http:www.asbury.edu/includes/cache/ultram/ultram-zoloft.html
<a href="http:www.asbury.edu/includes/cache/tramadol/tramadol-used.html">tramadol used</a> [URL=http:www.asbury.edu/includes/cache/tramadol/tramadol-used.html]tramadol used[/URL] http:www.asbury.edu/includes/cache/tramadol/tramadol-used.html
<a href="http:cdsp.edu/subheads/valium/consultation-online-valium.html">consultation online valium</a> [URL=http:cdsp.edu/subheads/valium/consultation-online-valium.html]consultation online valium[/URL] http:cdsp.edu/subheads/valium/consultation-online-valium.html
<a href="http:cdsp.edu/subheads/ultram/ultram-for-dogs.html">ultram for dogs</a> [URL=http:cdsp.edu/subheads/ultram/ultram-for-dogs.html]ultram for dogs[/URL] http:cdsp.edu/subheads/ultram/ultram-for-dogs.html
<a href="http:www.asbury.edu/includes/cache/tramadol/tramadol-hci.html">tramadol hci</a> [URL=http:www.asbury.edu/includes/cache/tramadol/tramadol-hci.html]tramadol hci[/URL] http:www.asbury.edu/includes/cache/tramadol/tramadol-hci.html
<a href="http:www.anderson.edu/studentblogs/viagra/viagra-online-pharmacy.html">viagra online pharmacy</a> [URL=http:www.anderson.edu/studentblogs/viagra/viagra-online-pharmacy.html]viagra online pharmacy[/URL] http:www.anderson.edu/studentblogs/viagra/viagra-online-pharmacy.html
<a href="http:www.anderson.edu/studentblogs/soma/soma-watson.html">soma watson</a> [URL=http:www.anderson.edu/studentblogs/soma/soma-watson.html]soma watson[/URL] http:www.anderson.edu/studentblogs/soma/soma-watson.html
<a href="http:www.asbury.edu/includes/cache/ultram/ultram-online-pharmacy.html">ultram online pharmacy</a> [URL=http:www.asbury.edu/includes/cache/ultram/ultram-online-pharmacy.html]ultram online pharmacy[/URL] http:www.asbury.edu/includes/cache/ultram/ultram-online-pharmacy.html
<a href="http:www.asbury.edu/includes/cache/pharmacy/drug-store.html">drug store</a> [URL=http:www.asbury.edu/includes/cache/pharmacy/drug-store.html]drug store[/URL] http:www.asbury.edu/includes/cache/pharmacy/drug-store.html
<a href="http:www.asbury.edu/includes/cache/hydrocodone/hydrocodone-5.html">hydrocodone 5</a> [URL=http:www.asbury.edu/includes/cache/hydrocodone/hydrocodone-5.html]hydrocodone 5[/URL] http:www.asbury.edu/includes/cache/hydrocodone/hydrocodone-5.html
<a href="http:www.asbury.edu/includes/cache/pharmacy/pharmacy-technicians.html">pharmacy technicians</a> [URL=http:www.asbury.edu/includes/cache/pharmacy/pharmacy-technicians.html]pharmacy technicians[/URL] http:www.asbury.edu/includes/cache/pharmacy/pharmacy-technicians.html
<a href="http:www.anderson.edu/studentblogs/valium/casino-cheap-free-online-valium.html">casino cheap free online valium</a> [URL=http:www.anderson.edu/studentblogs/valium/casino-cheap-free-online-valium.html]casino cheap free online valium[/URL] http:www.anderson.edu/studentblogs/valium/casino-cheap-free-online-valium.html
<a href="http:cdsp.edu/subheads/pharmacy/pharmacy-direct.html">pharmacy direct</a> [URL=http:cdsp.edu/subheads/pharmacy/pharmacy-direct.html]pharmacy direct[/URL] http:cdsp.edu/subheads/pharmacy/pharmacy-direct.html
<a href="http:www.asbury.edu/includes/cache/viagra/cheap-viagra.html">cheap viagra</a> [URL=http:www.asbury.edu/includes/cache/viagra/cheap-viagra.html]cheap viagra[/URL] http:www.asbury.edu/includes/cache/viagra/cheap-viagra.html

Luck in yo bizzzz!!!


eeped

This term is used to generally describe having a page eepified by either the page owner or another user of this wiki. Signs of eepification include edits that don't make actual sense, edits using exess links, pages not actually related to the wiki's purpose, disobeying the formatting guidelines of the wiki, or otherwise being a shmuck.

Definition

eeped (v.) - The process whereby inconsequential and obvious things are explained for no particular reason, then the right to explain said items is defended using works ending in "ship".

Signs of eepification

  • High blood pressure.
  • blurred vision.
  • Iron taste in one's mouth... er... wait, wrong reference... moment...

  • High blood pressure.
  • Urge to revert a page that has been changed by the same person in the same way as the last reversion.
  • Having to replace links with proper CamelCase versions even when the history shows that CamelCase was originally used.
  • Frequent re-writes to restore an article after someone else practiced Feng shui on it. Which direction is north on a wiki?
  • Page owner breaking a page's readability and then locking it from editing
  • Any page that isn't specifically official policy or administrative in nature being locked from editing by other users who are legitimate contributors.

Humor


GaiusGoodliffe

<a href= http:dvjzw.fizwig.com/buy-viagra.html >is there a female viagra where it available</a> [url=http:dvjzw.fizwig.com/buy-viagra.html]is there a female viagra where it available[/url] <a href= http:dvjzw.fizwig.com/mail-order-viagra-in-uk.html >male enhancement viagra</a> [url=http:dvjzw.fizwig.com/mail-order-viagra-in-uk.html]male enhancement viagra[/url] <a href= http:dvjzw.fizwig.com/men-and-women-s-viagra.html >bbear index php info search viagra viagraviagra wh</a> [url=http:dvjzw.fizwig.com/men-and-women-s-viagra.html]bbear index php info search viagra viagraviagra wh[/url]


l33t

Catherine's preferred way of spelling "1337".

Also the most cute and most stupid "monster" (and pet) in Anarchy Online.

Humor


LOL

OMG LAWL


sound

<a href= http:qwerty.wblog.sk/2007/07/05/gay-fwnzyqsybcc/ >gay men nude</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-fwnzyqsybcc/]gay men nude[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-jtxcmr/ >gay kiss</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-jtxcmr/]gay kiss[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-ussqnph/ >gay nude</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-ussqnph/]gay nude[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-wrnwebrgn/ >gay jockstraps</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-wrnwebrgn/]gay jockstraps[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-klvainscnj/ >gay cock</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-klvainscnj/]gay cock[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-nfdl/ >gay cowboys</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-nfdl/]gay cowboys[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-obyre/ >free gay video</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-obyre/]free gay video[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-iqvvhig/ >gay shorties</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-iqvvhig/]gay shorties[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-hqjneriognr/ >gay sucking</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-hqjneriognr/]gay sucking[/url] <a href= http:qwerty.wblog.sk/2007/07/05/gay-gnbfxkuwwsf/ >free gay movie clips</a> [url=http:qwerty.wblog.sk/2007/07/05/gay-gnbfxkuwwsf/]free gay movie clips[/url]


stupid

People who confuse avatar with agent and/or user and/or client--or any permutation thereof.

There may be other signs of clever stupidity as well.

Stupid people often think they're l33t while clever people are... but often don't state such.

Stupid people also tend to cling to the word, as a way of hiding their own stupidity.

Humor


Variable

A variable is an identifier or name of a place to store information in a script. The process of creating a variable is called "variable declaration", "declaring a variable" or "defining a variable".

A variable always has a type which is either string, integer, float, vector, rotation, key, or list. The type of a variable constrains what type of values can be stored in it.

Variables can be declared wherever code can be run, as well as before the default state (see global variable). This includes, but is not limited to: events and functions. Variables have a scope which is either local or global.

To declare a variable, use this general format:

type name;

The type is one of LSL's types, and the name is any combination of letters (caps or lowercase), numbers, or underscores (_), that does not start with a number.

Variable names that contain the characters: left single quote (`), hash/number sign (#), dollar sign ($), backslash (\), right single quote ('), and question mark (?) at the beginning or end of the variable name will not report an error but these characters will be ignored. Hence the variable called "foo" will be the same as the variable called "foo$", "$foo", "foo?", etc.

Variables can also be given an initial value on the same line as their declaration:
integer bar = 3;
Declares a variable of type integer, with the name bar, and assigns it a value of 3.

To assign a value to a variable, follow this format:

name = value;

Where name is the name of the variable and value is the value assigned to it.

Examples:
integer foo;
Declares a variable of type integer with the name foo.
foo = 2;
Assigns foo the value 2.
string MyLongNamed2284StringThat_USES_underscore;
Declares a variable of type string with the name MyLongNamed2284StringThat_USES_underscore.
MyLongNamed2284StringThat_USES_underscore = "hello!";
Assigns MyLongNamed2284StringThat_USES_underscore the value "hello!"

Q: Okay, so I have a variable. How do I actually do something with the data stored inside it?
A: Simply use the variable name where you'd otherwise place a fixed value. For instance, in the context of "llSay(channel,message);, "channel" and "message" are variable names that would contain the channel to chat on, and the message to say.

Q: Is there a maximum length to a variable's name?
A: Yes, 255 characters. Interestingly, if you define a variable with a longer name, your script will compile succesfully, but attempting to actually do anything with it will cause the compiler to fail with the message, "ERROR: Name not defined within scope". It shouldn't matter anyway; 255 characters is more than sufficient for even the most obsessively descriptive variable name.
A: While you can only really use a variable with up to 255 characters, one may be defined with up to 16,384 characters. If you exceed that limit and attempt to compile the script, your client will freeze. The Lindens do not plan on fixing this any time soon, because it is really esoteric.

Q: Does a long variable name make the script run slower? Should I be naming all my variables "a", "b", and so on to save space?
A: No, variable names only matter for how they'll appear in the editor. From the script's perspective, they might as well be named "a" or "b", but scripts with poorly-named variables are an absolute nightmare to work with. Stick with descriptive names like "last_detected_avatar" -- it's easy to figure out what that does. However, be careful to not be too verbose. Names like "key_detected_avatar_in_state_detection_number_3" are even more of a nightmare to work with than really short names.

Q: Can I use a string as the name of a variable or function?
A: No, there's no way to convert a string to a variable or function name directly. You'd need to use something like this: if (msg == "test") testFunction();

Global Variables | Local Variables


WikiSandbox

This page is for experimenting with WikiFormatting.
Search for:

If the above doesn't work for you, try google
only search the lslwm
Google
WWW LSL Wiki Mirror 7-7-7
Get TextSearch in your web browser! See Mycroft for more.
See the Page Index if you cannot find results using this page.


Scripting Mentors

What follows is a list of people amicable to the concept of helping people with scripting problems, one on one. If you're looking for help, these users may be able to help you. If you are looking for who helped in the creation of this wiki, please see the contributors page.

I am looking for help.
I want to volunteer to help others.
Don't be afraid to ask for help, but do try to read the relevant sections of the wiki beforehand, if you know what they are. If you can't figure it out, don't worry about it, but don't be too discouraged if the person you're asking for help doesn't have time to help you. Scripting mentors tend to be busy and may not be available to help you, even if they're online. To see which scripting mentors are online, use the scanner script at the bottom of the page.

1. When asking for help, it's a good idea to mention this page, as in, "Hello. I got your name from the Scripting Mentors page on the wiki. I have a question about listeners. Do you have time to help me?" This tells the mentor: A) that you're asking for help, B) that you know what you're asking about, and C) where you got their name.

2. If a scripting mentor says they cannot help you, be polite, and ask someone else. Scripting mentors are volunteers who are under no obligation to help you, and who may be very busy. They will be less inclined to help you if you insult or berate them for not helping you.

3. Please remember, just because someone has volunteered to be a scripting mentor, they do not have to write code for you. We may teach you how to fish, but do not expect us to give you a fish. If you ask us how to fish, we will give you our wisdom; but if you ignore our wisdom and ask for a fish, you will end up with neither.

4. This is not a Linden-sponsored or updated group, as the real Mentors are. Nevertheless, they may be helpful for resolving scripting problems and offering advice. They cannot help you with getting SL to run, learning to use SL or resolving disputes. If you need that, contact Live Help. 		Please add yourself if you wish to do so, with a little bit of your area of expertise, the PST times that you are usually on, and whether or not you answer IMs sent while offline. (Don't worry about copying someone else's expertise entry if they've described your skillset accurately.)

(Remember, PST is the time shown in the upper right-hand corner of the SL program.)

If you wish to add your name to this list, please do so in alphabetical order by first name to maintain readability, and add your name and key to the scanner script. Please don't add yourself to this list just because it sounds fun. You do need to have a bit of skill and expertise. This is not the LSL Wiki Mirror 7-7-7 contributors list.

If you know how to script, but don't know how to add your name to the list, see the contribute page to get started.

Attention Main Grid Mentors, A group is available called LSL Wiki Mirror 7-7-7 HELP, feel free to join it if you would like to keep contact with other mentors or display your status as a LSL Wiki Mirror 7-7-7 mentor.

Main Grid Mentors

Name | Times Online (PST) | Expertise | Offline IMs
AyAn4m1 Flan | Random | Distributed systems, complex scripts, interscript communication, physics, limited vehicles. | NO
Bitt Toll | Random | Security (spying, survalence, sensors, cloaking, alarms, etc.) | YES
Catherine Omega | 12:00 noon - 4:00 AM | Communications and designing for efficiency | NO
Christopher Omega | Sporadic, though usually 3:00 PM - 10:00 PM | Jack-of-all-trades, save vehicles (the llSetVehicle* kind) and particles | Urgent Requests Only
Darkside Eldrich | Randomly on, but I check email for IMs regularly | General programming, style guidelines, modular scripts, communications | YES
Deneria Sholokhov | 8 PM - 1AM | Attachments, some vehicles, particles a little, general coding, etc | Sometimes
Earle Swenson | Variable, but usually 3AM - 5AM | General programming, style, optimizations, scanning, user configuration, not vehicles or weapons | YES
Eloise Pasteur | Variable, but often 3 AM - 4PM | Jack-of-all-trades, certainly mistress of none. Not Vehicles or weapons | YES
Escort DeFarge | Variable, but often 3 AM - 3PM | Jack-of-all-trades, including off-world web/rpc (java not php). No weapons. | YES
Folco Boffin | Usually from 2PM - 12AM SLT | No physics or vehicle. Some particle and most everything else. | NO
Fox Diller | Often | Second Life | YES
Gene Poole | Late eve EST/early eve PST | General (most stuff besides vehicles, weapons, or particles) | YES
Jeffrey Gomez | Completely unpredictable; use Find | 3D Programming | YES
Keknehv Psaltery | Basically never, but I respond to IMs | Command Parsing, Particles, most everything else | YES
Kim Seifert | Usually from 5-9PM SLT | Mostly particles | YES
Kithylin Perth | Randomly, usually all day long! ^.^ | Interscript communication and command parsing, object building, poser animation.. Particles are yummy. =^.^= | NO
Kokiri Saarinen | Afternoons and Late night SL time | I'm comfortable with everything except XML and HTTP communications. Everything else is fair game. | YES
Lance Mirabeau | EST Afternoon/evening, erratic. | I do everything but XML-RPC. Vehicles are shakey. (Does weapons.) | YES
Lexx Greatrex | Often. | Everything LSL - Also C#/libsl interfacing, HTTP/RPC interfaces etc. Im a regular SL Mentor. | NO
LordJason Kiesler | Easier to win the lottery. | If I dont know, I want to figure it out. | Urgent Requests Only
Lucca Kitty | Sporatic but often every day at least half the days in a week. | I do a lot of just about everything including both in-world and out of world XML-RPC and llHTTPRequest but I don't do actual weapons (laser tag type weapons are fine, just not actual violent ones) | YES
Marc Eisenberg | I never even know any more. Use FIND. | I don't focus my knowledge in anything specific. I like to know everything. :) | YES
Marius Maeterlinck | Random, but mostly evenings Europe time | Jack of all trades. Relatively new to SL scripting, but I've been hacking all my life and until now always knew how to solve my own problems :-) | YES
Max Case | Use find. | A little from column A, a little from column B. | Sure
MSo Lambert | Usually after 11AM PST | Communications, XML-RPC, physics, animation, vehicles, and a little bit of everything :) | YES
Nonnux White | My time is GMT. I never know when I am ON | I like to know everything like Mark, except No Weapons ;) | YES
Raemus Patel | 11:00 AM - 5:00 PM | As much as i can possible shove into my head. I can do mostly any miscilanious script but i can NOT do vehicles or weapons (But im trying to learn weapons). | YES
Results Tiki | Roughly 3pm-9pm SLT | General programming, interscript communication, complex linking and rezzing, widgets, particles. NOT yet: attachments, vehicles, weapons, physics, XML, HTTP | NO
Rini Rampal | Roughly 2pm-10pm SLT | NOT particles, vehicles, or physics | YES
Rodrick Harrington | Varies | Not much vehicle experience, NO weapons, everything else is game | NO
RyeDin Meiji | Most weekends | Specializing in Widgets and OO accessors | NO
Silicon Plunkett | Random | Physics, Particles, Vector and Rotational math, Animations, Communication, Data storage, Scanners.. eh, everything. | Sometimes
Stephen Psaltery | Work=Master/Sporadic | Math (Angles, Radians, Trigonometry, etc.), Code Efficiency, Artificial Life(Limited) | YES
Sirlor Stonecutter | At least 60% of the day | Communications(Still learning XML-RPC), Gadgetry, weaponry and custom work (save particles</3) | YES
Stone Mason | When I feel like it. | Problem solving.... and bunnies.. yes... bunnies... | YES
Strife Onizuka BlindWanderer | Sporadic | Jack-of-all-trades, Security and Optimizations; not weapons | NO
Trimda Hedges | Variable, but often 5:30pm - 9:00pm EST | Physical movement, general physics, external/remote communications (email and XML RPC), inter-object communications, script resource optimization (CPU and memory) and server side coding (perl, Delphi, C/C++ with MySQL, Oracle, MSSQL, Apache & IIS) Do not ask me to create scripts or debug your scripts for you! I am here to point you in the right direction, not create stuff for you. | NO
Velox Severine | As much as possible. Usually after 4pm PST on weekdays and most times weekends. | XMLRPC to PHP to MySQL, mostly communications and client/server systems, multithreading, some experience in weapons, basic vehicles. A little experience in everything. | YES
Vicero Lambert | As much as possible | Agent/Avatar, Camera, Collision, Color, Communication, Controls, Data server, Detection, Ect... Please do not ask me to do a script for you unless you are willing to pay my hourly rate. I'm here to give advice on what functions work best and cannot afford to do a whole script for you since my income is from scripting. | NO
Yohan Althouse | Varies (8 AM - 9 PM PST) | General Scripting | NO
Wufuzz Carnot | As much as possible (camping instread of going offline). A GMT +7 dweller so will be available at odd hours | General stuff, User interface, XMLRPC to PHP to MySQL, Most of stuff except weapons and vehicles (but will give them a try) | YES
Zarf Vantongerloo | 8:30pm - Midnight (PST) | General programming, data management, communications | YES
ZeroZ Dagger | Less then i want too | Everything that doesn't have rotations and external stuff | KEEP UM COMMING
Snowy Montpark | 01:00am - 11:00 am PST weekends | Multilingual, timers and physics. I haven't tried particle systems yet .. | IF YOU REALLY MUST

Teen Grid Mentors

Name | Times Online (PST) | Expertise | Offline IMs
782 Naumova | 12:00 - 2:00 (PST) European | Weather functions and physical properties, save rotation | NO
Aldar Rayner | Usually 1:30-6:00 or so PST | Mostly everything, but not vehicles(maybe in the future) or HTTP. | Okay.
Aniam Ingmann | Randomly, 2am-3:30am PST, and possibly 12pm-5pm PST. | Listens, Rezzes, Push Object, and some other general topics. | YES
Atte Aderdeen | Randomly between 1pm and 2am (GMT) | No vehicles (yet) or XML-RPC. And please not angle calculations. | YES
Bhavin Poindexter | Mostly on between 2:00 - 8:00 PM (PST) | Mostly multi gadgets, guns, etc. | YES
Corisons Nino | Random times (UK) | Scripting guide giveouts and 101' s in-game | YES
Daniel Benton | Random Times (PST) | Particles, Gadgets, Jetpacks, etc. | YES
Storm Basiat | Random Times (GMT) | Helpful Mentor. | YES
Dolus Naumova | 1:00 - 3:00 (PST) | Jack-of-all-trades | NO
ice Semple | 12:00pm - 7:00pm (PST) | Anything except vehicles or XML-RPC. | YES
Jake Laprade | 3:00pm - 8:00pm (PST) | Anything except vehicles, XML-RPC, or weapons | YES
Jontte Gremlin | 15:00-21:00 (GMT+2). | No servers, XML-RPC or vehicles. I like maths. | YES
Kaira Overdrive | UK. On when you aren't. ( 8 - 10/12 GMT) | Most stuff. | YES
Malarthi Behemoth | Sporadically, in Pacific timezone (GMT - 8) | Fixing bugs | Send to kitten Nerd, or if that's full, 141592 Naumova. Please no offline IMs to Malarthi Behemoth. Thanks!
Tyler Odets | 12:00 - 7:00 (Ugh, school)(PST) | Jack-of-all-trades, save vehicles. Specialty is Multi/Personal Tools | YES
Unique Nerd | Only when I can. | I do everything except XML-RPC and weapons. Oh, and I'll never make those yuk math angle calculations! | YES
Xander Fisher | Sporadically | Anything and everything having to do with LSL, along with some PHP. | ANY TIME!
Dunsel Hodgson | Sometimes. Varies wildly. | All of LSL. Anything. If anyone finds anything I don't know about it, I'll change this entry. | NO

Scripting Mentor Scanner Script

//  Scan for online Scripting Mentors on the main grid.
//  Just drop this script into something
//  by Keknehv Psaltery -- 03/08/06

list mentors = ["AyAn4m1 Flan"      ,"27cd74cb-b591-4913-901c-882325fe63d3",
                "Catherine Omega"   ,"3e1f44f6-5c4e-afc3-d8d0-361eea82ddb6",
                "Christopher Omega" ,"3e8a269d-6669-07df-a09d-c38664cbc89d",
                "Darkside Eldrich"  ,"8f0ffade-de00-43b5-97fc-544bf07cea40",
                "Deneria Sholokhov" ,"95d793ff-7a64-4f6d-a224-e48ab26ee32a",
                "Earle Swenson"     ,"1b7b2686-8fab-46f8-a834-cb9ceb596ce5",
                "Eloise Pasteur"    ,"5b5585b3-8ae6-4cc8-896b-818e6cfa5ea0",
                "Escort DeFarge"    ,"61ca7d87-2316-4c98-b3dc-c7e29e4666b7",
                "Folco Boffin"      ,"f9180a7b-a1a6-4329-a46a-6dd487c0774c",
                "Fox Diller"        ,"6b21aa6e-7c87-45d5-8deb-da9b6079b837",
                "Gene Poole"        ,"8a654241-d2bc-429f-8bd4-9ee59892fa03",
                "Jeffrey Gomez"     ,"529d65f4-fdd9-4b4a-a6ff-08ea62f54936",
                "Keknehv Psaltery"  ,"f1f2d29a-210b-4b00-96d7-ab16c962a40f",
                "Kithylin Perth"    ,"d286dfd7-12b6-403b-8b77-3cc316aa89dc",
                "Kokiri Saarinen"   ,"3acaa17a-f818-49df-b31d-f395d550f14c",
                "Lance Mirabeau"    ,"0b44e869-7618-4623-a261-81f316af8763",
                "LordJason Kiesler" ,"b3e6a03e-5827-4257-aa23-6df63ebd77ff",
                "Lucca Kitty"       ,"e86bf246-a8e7-47cb-9678-ffc6e9c78dd4",
                "Marc Eisenberg"    ,"a44770e5-de4f-4ced-a511-1e3002375e4b",
                "Max Case"          ,"6d869ee0-e19e-4afa-8431-b85705198a0c",
                "MSo Lambert"       ,"f6a5e1aa-2fac-4823-90f9-c242d020239a",
                "Nonnux White"      ,"7499fac7-40b8-4a52-bf89-be1018efdc4c",
                "Raemus Patel"      ,"aea7eb76-7be0-496b-be09-e84f44b94320",
                "Rini Rampal"       ,"fbfcafec-0711-42bb-ac14-e87a4de5f1a1",
                "Rodrick Harrington","34f88fb4-1c15-4341-833c-b2667c7dcd38",
                "RyeDin Meiji"      ,"acef5ef9-6420-46a5-a394-62f675061c20",
                "Stephen Psaltery"  ,"4b091519-3cc1-4ac8-a1d6-f1039c7f74c6",
                "Sirlor Stonecutter","2b17e3bf-d1d5-4835-bb51-d50c9f1f9252",
                "Stone Mason"       ,"e8ac4315-5625-41cb-ae28-444fc3846ab8",
                "Strife Onizuka"    ,"4275d408-da94-4f68-8353-090fb2dfc020",
                "Trimda Hedges"     ,"0cd7918d-dd20-46cc-a168-d5669ca197e8",
                "Velox Severine"    ,"cf2301ee-4b44-4bfe-8f29-8cfe85fd94ef",
                "Vicero Lambert"    ,"744efd47-086e-4d52-bce8-38f6584c3df7",
                "Wufuzz Carnot"     ,"cf807cce-ad6f-42c2-93da-fcbe4db35651",
                "Yohan Althouse"    ,"872a8c40-eed8-496f-9705-6441a7a99070",
                "Zarf Vantongerloo" ,"734300d5-8fbd-465d-9c1a-1948ca8f933e"];

key dataKey;
integer currentMentor;
integer numberMentors;
float startTime;

list onlineMentors;
float percentDone;

scan()
{
        onlineMentors = [];
        currentMentor = 0;
        numberMentors = llGetListLength( mentors ) / 2;
        startTime = llGetTime();
        dataKey = llRequestAgentData( (key)llList2String(mentors,currentMentor*2+1),
                                        DATA_ONLINE );
}

default
{
    on_rez( integer sparam )
    {
        llResetScript();
    }

    state_entry()
    {
        scan();
    }

    touch_start( integer num )
    {
        scan();
    }

    dataserver( key query, string data )
    {
        if ( query == dataKey )
        {
            if ( (integer)data )    //Are they online?
                onlineMentors += [llList2String(mentors,currentMentor*2)];

            ++currentMentor;

            percentDone = (float)currentMentor / numberMentors;

            //This next line is ugly. Beware!

            llSetText( "Scanning for online scripting mentors\n Currently " +
                        (string)currentMentor +"/"+ (string)numberMentors + " -- " +
                        llGetSubString((string)(percentDone*100),0,3) +
                        " percent complete\n" +
                        llGetSubString((string)((llGetTime()-startTime)/(percentDone)*(1-percentDone)),0,3) +
                        " seconds remaining\n Online:\n " + llDumpList2String( onlineMentors , "\n" )
                        , <1.01-percentDone,percentDone,0.0>, percentDone + .4);

            if ( currentMentor < numberMentors )
                dataKey = llRequestAgentData( (key)llList2String(mentors,currentMentor*2+1),
                                            DATA_ONLINE );
            else
            {
                llSetText( "Scan Time: " + llGetSubString((string)((llGetTime()-startTime)),0,3) +
                            " seconds\nScripting Mentors Online:\n" + llDumpList2String( onlineMentors , "\n" )
                            ,<0.2,1.0,0.2>,1 );
                integer nOnline = llGetListLength( onlineMentors );
                if ( nOnline > 1 )
                    llOwnerSay("There are " + (string)nOnline + " scripting mentors online: "+
                                llDumpList2String(
                                    llList2List(onlineMentors,0,nOnline - 2), ", " )
                                + ", and " + llList2String( onlineMentors, nOnline - 1) );
                 else if ( nOnline == 1 )
                    llOwnerSay("Only one scripting mentor is online, "
                                + llList2String(onlineMentors, 0 ));
                 else
                    llOwnerSay("Uh-oh, no scripting mentors are online! Try again later.");
            }
        }
    }
}

For the Teen Grid:
//  Scan for online Scripting Mentors on the teen grid.
//  Just drop this script into something
//  by Keknehv Psaltery -- 03/08/06
// Updated with Teen Grid mentors by Dolus Naumova -- 4/21/06

list mentors = ["782 Naumova"        ,"8b8773b7-2f21-463b-9346-c5d5606c154f",
               "Aldar Rayner"          ,"c0b2233c-0a6a-4c76-bfba-b282a1b8cd2a",
               "Aniam Ingmann"      ,"1abf7e00-3c9e-45a4-b46c-4ea270ab1d08",
                "Atte Aderdeen"        ,"57e83a81-b9e7-4afe-a4bd-f7324c47b2a8",
        "Bhavin Poindexter"    ,"e72939d4-a948-4eb1-aeca-2e87f073cd0d",
               "Corisons Nino"         ,"07872008-dc85-469b-bd99-86693d4e5ef4",
               "Daniel Benton"        ,"81de3fb4-70b1-49ba-80b3-b639a3ac89d8",
               "Dolus Naunova"       ,"4a3e46a6-c9d8-4eac-b95c-6a96bddfc085",
               "Jake Laprade"         ,"c8ac2c98-3807-4a1e-9f85-6492c27a6bc2",
               "Jontte Gremlin"        ,"328d429f-9b42-4002-8d32-935d28bc15a0",
               "Kaira Overdrive"      ,"814081ed-3f16-411b-af21-5897eb9b803e",
               "Malarthi Behemoth"  ,"ec7b741e-8da3-46ba-a45c-698cfe432b4f",
               "Tyler Odets"           ,"69e1cfa3-d85f-4fb9-bab6-3e31305b911d",
               "Unique Nerd"           ,"d8f5efe7-c6fb-44c6-addd-6ec4af4ad78a",
               "ice Semple"             ,"463664d1-ec6b-4efd-a5ba-5db456a302e7",
               "Storm Basiat"          ,"67e34925-dd20-4aee-b27d-807970552373",
               "Vince Bosen"           ,"f24fd603-848a-47d7-afa0-3855225569a1",
               "Xander Fisher",         "2bb46353-ccbf-4b62-90d4-0780f638a2ed",
               "Dunsel Hodgson",         "bc7cb49f-4306-46e0-a1dc-c46aa8bdd7c9"];

key dataKey;
integer currentMentor;
integer numberMentors;
float startTime;

list onlineMentors;
float percentDone;

scan()
{
        onlineMentors = [];
        currentMentor = 0;
        numberMentors = llGetListLength( mentors ) / 2;
        startTime = llGetTime();
        dataKey = llRequestAgentData( (key)llList2String(mentors,currentMentor*2+1),
                                        DATA_ONLINE );
}

default
{
    on_rez( integer sparam )
    {
        llResetScript();
    }

    state_entry()
    {
        scan();
    }

    touch_start( integer num )
    {
        scan();
    }

    dataserver( key query, string data )
    {
        if ( query == dataKey )
        {
            if ( (integer)data )    //Are they online?
                onlineMentors += [llList2String(mentors,currentMentor*2)];

            ++currentMentor;

            percentDone = (float)currentMentor / numberMentors;

            //This next line is ugly. Beware!

            llSetText( "Scanning for online scripting mentors\n Currently " +
                        (string)currentMentor +"/"+ (string)numberMentors + " -- " +
                        llGetSubString((string)(percentDone*100),0,3) +
                        " percent complete\n" +
                        llGetSubString((string)((llGetTime()-startTime)/(percentDone)*(1-percentDone)),0,3) +
                        " seconds remaining\n Online:\n " + llDumpList2String( onlineMentors , "\n" )
                        , <1.01-percentDone,percentDone,0.0>, percentDone + .4);

            if ( currentMentor < numberMentors )
                dataKey = llRequestAgentData( (key)llList2String(mentors,currentMentor*2+1),
                                            DATA_ONLINE );
            else
            {
                llSetText( "Scan Time: " + llGetSubString((string)((llGetTime()-startTime)),0,3) +
                            " seconds\nScripting Mentors Online:\n" + llDumpList2String( onlineMentors , "\n" )
                            ,<0.2,1.0,0.2>,1 );
                integer nOnline = llGetListLength( onlineMentors );
                if ( nOnline > 1 )
                    llOwnerSay("There are " + (string)nOnline + " scripting mentors online: "+
                                llDumpList2String(
                                    llList2List(onlineMentors,0,nOnline - 2), ", " )
                                + ", and " + llList2String( onlineMentors, nOnline - 1) );
                 else if ( nOnline == 1 )
                    llOwnerSay("Only one scripting mentor is online, "
                                + llList2String(onlineMentors, 0 ));
                 else
                    llOwnerSay("Uh-oh, no scripting mentors are online! Try again later.");
            }
        }
    }
}

Q: How do I contact a scripting mentor, and how do I know they're online when I need help?
A: In SL, select "Edit" -> "Find" to open the Find window, then select the "People" tab and type their name into the text box. Their online status will appear next to their name.
Also, you can use the appropriate scanner script, just copy and paste one into an object and it will tell you which scripting mentors are online.

Q: Why wouldn't a scripting mentor want to receive offline IMs?
A: Sometimes, a user will go several days without logging in. During this time, offline IMs can stack up and even overflow. If this happens, the offline user may not even know you wanted help.

Q: Sometimes Find People returns "Unknown" as their online status. What should I do then?
A: Assume they're offline.
A: This has been removed in the newer versions (exact version number needed). Now it just shows "Online" or stays blank.

HomePage | LSL Tutorials | Contributors


ScriptingGroups

Second Life Scripting Groups

Feel free to add your Scripting related group to the list. Also update this page so that it looks better. :-)

Main Grid Scripting Groups

Group Name In alphabetical order | Description
Advanced Scripters of Second Life | Where experianced scripters go to get away from the noise and for novel scripting questions.
Basic Scripting Group | Entry Level Group for the Non-Scripter to learn how to script in LSL & share ideas
Scripters of Second Life | The biggest and best scripting group, chock full of helpful people, always available to answer questions!
LSLWikiHelp | LSL Wiki Mirror 7-7-7 Help is a group of LSL Wiki Mirror 7-7-7 mentors dedicated to helping & willing to help you with your LSL needs.

Teen Grid Scripting Groups

Group Name In alphabetical order | Description
Scripting Mentors | Scripting Mentors is a group of some good scripters who are willing to help you script. We are here to answer your questions about lsl and give you helpful tips
Scripters of the Teen Grid | Scripters of the Teen Grid is a good place to ask questions about LSL, but beware of flaming if you didn't look it up on the wiki first!


ScriptingTeachers

This is page is available for people who teach scripting in SL to list themselves. Please do so in alphabetical order by first name (or by group name if it's a group teaching system).

Note to users: These people are essentially advertising their services - but doing so to help you learn scripting. They may well charge for their services or similar. They are not quality controlled at this end, so caveat emptor.

If you are looking for a quick answer to a question, you might want to try ScriptingMentors instead. This is intended for people that run classes at pre-agreed times.

Name | Style of teaching | Topics covered | Times available (SL time) | Course times | Offline IMs
Dirty McLean | 30min or 1hr sessions. Group rates available. | Any topic from basic to advanced, including project consultation. | Often | Upon Request | YES
Eloise Pasteur | 1:1 or small group mainly. Classes usually about 90m each. | I teach a basic scripting course from no knowledge to working ability to make scripts. I will also teach specific skills to those wishing to extend their knowledge. | Classes start before 10am SL Time (avoids local evening) | Negotiated with learner | YES
ViceroLambert | private/group tutoring in one-hour sessions. | Classes:Communication Scripts; Tutoring: N/A | Random | Check events for classes | NO


AlternativeEditors

Alternate Editors

Since the built-in editor supports pasting text from the clipboard, a different alternate editor can be used to edit scripts, copying them into Second Life when they're ready to be saved. This is useful when the grid is down, or when you don't have access to a computer that can run SL. Several of these editors will even run off of a USB key (thumb drive?). Editors can be/include syntax checkers and syntax colorers/highlighters.

For some of the editors mentioned here, the Shill project may have more up-to-date syntax files.

Compilers/Debuggers

LSL-Editor

  • http://www.lsleditor.org/

LSL-Editor is a standalone Windows LSL script editor (and compiler, debugger), featuring (version 2.4):
  • Code highlighting (including Brackets matching)
  • Code auto completion (Like Visual Studio)
  • Auto insert events
  • Argument information on functions (typing)
  • Tabbed or MDI interface
  • LSLWiki or SecondLife color scheme
  • Code editing ( Copy / Cut / Paste / Delete / Undo / Redo / Find / Replace / Clipboard function)
  • Block commenting / uncommenting
  • Tooltip on keywords (LSL-Editor contains complete LSL guide!)
  • Auto-indent (using tabs or SecondLife indent scheme)
  • Help, on every keyword, uses www.LSLwiki.NET information
  • Compiling, Execution and Debugging outside SecondLife world
  • llDialog input screen
  • Printing
  • You don't have to install (!) the program, unzip en use it.

  • The program is updated on regular intervals, so check your update button

Windows XP , Windows 2000 , Windows 2003 , Windows Vista

  • .NET 2.x has to be installed (standard on most Windows machines)

Editors

BBEdit and TextWrangler

A BBEdit 7.0 language module for LSL is at http://adammarker.org/. The module will also work with TextWrangler 1.0. A BBEdit 8.2 "codeless" language module is at http://mohland.com/lsl.html. The module will also work with TextWrangler 2.1. While it does not support all the features as the aforementioned language module, it does support syntax coloring.

BBEdit is a code and HTML editor for the Macintosh. TextWrangler is BBEdit's little brother. They are products available from Bare Bones Software. Unfortunately, BBEdit has moved on while this language module has not. The language module "LSL" does not support Unicode, and so cannot be used with this version of BBEdit. Please remove the language module and contact its developer for assistance. -?
If anyone knows how to fix this, it would be of great value methinks. -MaxCase
I wrote the language module mentioned above, but I do not know how to make it support Unicode. I would be happy to share my source code with anyone who would like to give it a try. -AdamMarker

For the new BBEdit 8.5 use the language module 'lsl.plist' at http://mohland.com/lsl.html but see http://forums.secondcitizen.com/showthread.php?t=2631 for a little patch to apply to the module. -HerbertHoggard

Code-Genie

Code-Genie is mostly a text-editor that can dynamically color/highlight code based on rules, but it also has some other cool features such as code-indenting and block-checking (finding/matching sets of parentheses and braces). Check it out at: http://www.code-genie.com
I believe you can download the editor and try it free for awhile. -?
As of 13 Jan 2007, the Code Genie web site seems broken, but a web search will turn up otheer download sources for it.

There is a file that highlights code in Code-Genie almost exactly the same as when you're working with SL's built-in editor. It contains every built-in constant, function, event, data-type, operator, etc.

Crimson Editor

Crimson Editor is a small, fast, freeware/donationware source code editor for Windows. Available at http://www.crimsoneditor.com/.

Very comparable to UltraEdit (but free), it's got all the features that you would expect: syntax highlighting, macros, unicode encodings, column mode, hooks to user tools, etc.

I've uploaded link and specification files for LSL to the Syntax Files section at http://www.crimsoneditor.com. These are complete for SL 1.7.2 and have been tested with Crimson Editor 3.70. TemplarBaphomet
I have uploaded new LSL Syntax files at the same site, for SL 1.10.0 also tested with Crimson Editor 3.70. AakanaarLaSalle
See thread for details.

EditPlus

EditPlus site: http://www.editplus.com

Get Ezhar's EditPlus syntax and autocompletion files at sl.sdfjkl.org (no longer maintained and incomplete - use SciTE-ez instead).

I have updated syntax files for LSL 1.7.1 for EditPlus 2. Get the file at this thread. Download link is back now

Emacs

Phoenix Linden posted an LSL mode for Emacs in a Technical Issues forum thread.

An alternative emacs mode for LSL has been posted in the same forum (http://forums.secondlife.com/showthread.php?p=886077#post886077) that supports indentation consistent with the SL editor, and fairly complete syntax highlighting of keywords, types, function names, etc.

Gnome's Editor (GEdit / gtksourceview-1.0)

Text editor for Linux Gnome environment. Use with gtksourceview-1.0 and may be working with any other text editors and source editors in Gnome environment that use gtksourceview-1.0 module.

To install, just extract "lsl.lang" to: <Gnome Directory>/gtksourceview-1.0/language-specs
For ME it is at: /usr/share/gtksourceview-1.0/language-specs

To download: See http://forums.secondlife.com/showthread.php?p=1134163

jEdit

  • jEdit

The current support for jEdit is a syntax highlighter, created by KeilaronTomba. (Last updated on 04/25/2007 thanks to TrentMondrian. If the site seems unavailable, just try again. -?) Download the jEdit LSL edit mode (syntax highlighter) and follow the instructions below. (Note that the directories will not exist if you have not already run jEdit. Run jEdit first!)

Windows:
  • The edit mode is placed in %AppData%\..\.jedit\modes
  • Edit the %AppData%\..\.jedit\modes\catalog file as below.
Linux:
  • The edit mode is placed in ~/.jedit/modes
  • Edit the ~/.jedit/modes/catalog file as below.
Mac OS X:
  • The edit mode is placed in the modes folder (found in the jEdit application folder).
  • Edit the catalog file (also found in the modes folder) as below.

Note that this file does not include parsing.

Editing the catalog file: Instructions are inside, but if you are confused or uncertain, just use the following line. Any files with the .lsl extension will be recognized.
<MODE NAME="lsl" FILE="lsl-edit.xml" FILE_NAME_GLOB="*.lsl" />

Notepad++

Notepad++ can be obtained at this link: http://notepad-plus.sourceforge.net/uk/site.htm

Follow the link below to a message thread on the main forums that contains a zip file with LSL syntax highlighting updated for SL 1.10.0 and autocomplete abilities current to SL 1.8. The first post in the thread contains instructions for installing the language file. Contact Samson Havercamp for questions.

http://forums.secondlife.com/showthread.php?p=1070270#post1070270

For those of you that cannot access the forum use this link for a similar file. Just open the link and follow the instructions. (includes syntax higlighting similar to that of the in-game editor) If you have any questions contact Roiie Basevi in game, thank you.
http://info-tech.007ihost.com/lsl/index.html

Mirror: userDefineLang.xml (syntax highlighting) and lsl.api (Auto-complete)

See the User Manual (pdf) for installation instructions.

SciTE-ez

A customized package of SciTE, sporting syntax highlighting, folding, autocompletion and tooltips for the whole LSL API. Also includes syntax highlighting and advanced features for many other languages, such as PHP and Perl.

Available at sdfjkl.org
This is no longer supported at this site, but updated syntax files can be had at http://adammarker.org/shill/.

Recommended by SevenEightTwo, CirrMarat, DavidGall, CatherineOmega, ChristopherOmega, KeknehvPsaltery, StrifeOnizuka, VirusCollector, and of course, EzharFairlight.

To use lslint with SciTE-ez, first make sure lslint.exe is in the same folder as scite.exe is. Then pull down the Options menu and select "Open lsl.properties". Add this line:
command.build.$(file.patterns.lsl)="$(SciteDefaultHome)\lslint.exe" "$(FileName).lsl"
This enables the Build option in the Tools menu. When you click Tools>Build or press F7, lslint runs and checks the script.

Smultron

Smultron can be found at http://smultron.sourceforge.net/. It's a mac freeware text editor. Sadly it doesn't have an lsl syntax module yet, but despite that I now use it in preference to BBEdit for 90% of my text editing needs... if they add saved greps it will be 100%. It does some nice things like autoindent, group indents etc. in a very friendly fashion, and works wonderfully for php etc.

SynEdit

Thread
Here's my little attempt to make a LSL syntax highlighter for SynEdit.

Should work with latest CVS version of SynEdit, not tested in Kylix or Lazarus, but should work without much modification, if any.

Will be registered as TSynHighlighterLSL.

Pascal source code here -?

TextMate

LeonAsh has posted an LSL bundle for TextMate in the forums. TextMate is a commercial Mac OS X text/program editor from Macromates. The thread includes some bonus snippets, plus commands to call lslint and locate a function in the LSL Wiki.

An expanded bundle by Piero Padar: instructions and download link

TextPad

TextPad is a powerful, general purpose editor for plain text files. A syntax definition file is available for LSL.

Also check this forum thread for a TextPad syntax definition file and clip library created by SeanGorham.

UltraEdit

UltraEdit features syntax highlighting and block indication (like Code-Genie). See this forum thread for information on syntax highlighting files.

Update: Alondria has a syntax file current as of SL 1.5-Pre-2 (I think I got it all). See this thread.

Update: I have written a new Wordfile and put it up here. This file contains only the entries for the LSL highlighting and as far as I can tell current up to SL 1.6.12(6) (the current version as I wrote it). If you find anything missing please let me know. -GrazelCosmo

Update: I have made a new wordfile for LSL that has all functions and constants up to 1.14.1.0. -StryfeLowell

vim

Hello, Gabriel Spinnaker here. Despite taking it upon myself to update the LSL Vim syntax file when I discovered lo those many years ago that an out-of-date one existed, I haven't actually done so in what one might charitably describe as "a long-ass time" (to give you some idea of how long-ass a time, the last time I updated it, we thought we might see SL 2.0 soon). I encourage all and sundry to visit http://adammarker.org/shill/ for a somewhat more up-to-date Vim syntax file.

Installing the Vim syntax file

On Linux/*BSD/Mac OS X

- If they do not already exist, create the directories ~/.vim and ~/.vim/syntax.
- Copy lsl.vim to ~/.vim/syntax.

On Windows

- If they do not already exist, create the directories %USERPROFILE%\vimfiles and %USERPROFILE%\vimfiles\syntax.
- Copy lsl.vim to %USERPROFILE%\vimfiles\syntax.

On either platform

- After copying lsl.vim to the appropriate location, add the following lines to your .vimrc (on Linux/*BSD/Mac OS X, this will be in ~/.vimrc; on Windows, %USERPROFILE%\_vimrc):
augroup filetypedetect
au BufNewFile,BufRead *.lsl	setf lsl
augroup END

Other notes

On Linux/*BSD/Mac OS X, it may be necessary to convert lsl.vim to use Unix-style line endings. You'll know whether this is the case if Vim complains of being unable to parse lsl.vim properly when you try to edit an LSL file. To convert it to Unix-style line endings, simply open it in Vim and type :set ff=unix, then save and quit.

There is a dictionary file available at the URL above which can be used with Vim's autocomplete function (Ctrl-P) by running the command :set complete=k<path-to-file> when editing an LSL file. (It is probably also possible to rig Vim up to load the dictionary automatically when editing LSL.)

MoinMoin Wiki

Code can also be edited in a MoinMoin Wiki of your own and have the code highlighted with the LSL parser plugin at http://moinmoin.wikiwikiweb.de/parser/lsl.py. See also page in SecondTux Wiki

Syntax Checkers

lslint

Command-Line LSL Syntax Checker
lslint is a tool to check the syntactic and semantic validity of LSL scripts. It is basically a compiler that doesn't compile, but has helpful error messages, warnings, and checks for some common problems in LSL. It is mainly for use with an external editor.
http://w-hat.com/lslint/


Errors

There are lots of LSL errors that can occur in SL. This page describes error messages that can arise when a script is compiled, or when it tries to do something it isn't allowed to do. Problems with a script itself are called "bugs" or "glitches".

Note: Errors caused by incorrect parameter lists in calls to llParticleSystem, llSetPrimitiveParams, and llGetPrimitiveParams aren't shown in chat but instead as popup windows in the online notification area (lower right) and can be reviewed by opening the chat history window (CTRL-H).

Compiler | Run-Time | Build

Compiler Errors

(occur when script is compiled)

Error Message | Explanation | Example (error) | Fix
SyntaxError | Compiler cannot understand what is trying to be done. | if (llGetLitLength(l) > 0) retun TRUE; | if (llGetListLength(l) > 0) return TRUE;
Type mismatch | A variable was used of the wrong type | integer i; i = "cat"; | string i; i = "cat";
Function call mismatches type or number of arguments | Check parameter types and number of parameters passed to function. | llSetTexture("wood"); | llSetTexture("wood", ALL_SIDES);
Name not defined within scope | Access to undeclared variable or user-defined function. | llSetText("monkeys",blue,1); | vector blue = <0,0,1>; llSetText("monkeys!",blue,1);
Name previously declared within scope | Declaration of a variable or function with a name that already exists. | list myThings; string myThings; | list lMyThings; string szMyThings;
Uninitialized variables can't be included in lists | An undeclared variable was added to a list. | list cactus=[10, var1] | integer var1 = 3; list cactus=[10, var1];
Use of vector or quaternion method on incorrect type | Tried to get a component of a non-component type. | float fNotAVector; fNotAVector.x = 3.0; | vector vActuallyAVector; vActuallyAVector.x = 3.0
Lists can't be included in lists | self-explanatory | list L = [2, 3]; list M = [5, L]; | list L = [2, 3]; list M = [5] + L; or use a fake array
Return statement type doesn't match function return type | Return type must match function declaration. | string foo() { integer x = 3; return x; } | string foo() { integer x = 3; return (string)x; }
Function returns a value but return statement doesn't | Replace return; with return something; | string function() { return; } | string function() { return "Test"; }
Not all code paths return a value | Add more returns. | function() { if (1) return TRUE; else x++; } | function() { if (1) return TRUE; else {x++; return FALSE} }
Dead code found beyond return statement | Don't put code after a return. | bar() { llSay(0, "I like babies."); return; llSay(0, "Not."); } | bar() { llSay(0, "I like babies."); llSay(0, "Not."); return;}
Global functions can't change state | This is a limitation of the virtual machine. | foo() { state bar; } | See hacks.
Expression must act on LValue | assignment to a non-variable | 2 += 3; | integer x = 2; x += 3;
Declaration requires a new scope -- use { and } | declared a variable in a single line flow control situation | do integer a = 6; while(1); | do{ integer a = 6; }while(1);
Token buffer overflow | The compiler is broken, report as bug and send script to LL |  |
Not enough space for parser stacks | The compiler is likely broken, report as bug and send script to LL |  |
Parser stack overflow | The compiler is broken, report as bug and send script to LL |  |
Byte code assembly failed -- out of memory | The code is too big to even compile into 16k. |  | Split scripts (link_message)
No scanner |  |  |

An error will also occur if you run out of disk space and try to save a script.

Run-Time Errors

(occur when something bad happens when the script is already compiled and running)

Error Message | Explanation
MathError | Occurs when a mathematical operation returns NaN or infinity.
StackHeapCollision | ran out of memory
Chat Overrun Chat string was > 255 characters
This shouldn't happen anymore; LL fixed it so chat functions truncate rather then trigger this error.
Too many listens | Too many open llListen handles; remove some with llListenRemove.
Bounds Check Error | Apparently, happens if the message queue overflows. Managed to cause this by sending link messages to 50 prims at once. It may also occur if you enter too many items into the list for a call to llGiveInventoryList()
Heap Error | This one is usually caused by a problem with the SL virtual machine's code. Occasionally, it can be caused by the state-change-within-function hack, or the script jumped around a variable declaration and tried to access the variable's value. I have had one instance where 2 scripts trying to take the same control inputs has caused this error - Ice
Hit Sandbox Limit An object with STATUS_SANDBOX set tried to cross a sim boundary or went more than 10 meters from the location where STATUS_SANDBOX was set.
This isn't really an error; more of a notification.
Lists may not contain lists A list cannot be nested in another list, but the script will compile if tried.
list a = [[1]];
llx error running rule #y: non-z rule. Function llx is passed a list as a parameter, y is the index value where there is a type mismatch, at this index it is expecting the value to be of type z
Functions: llSetPrimitiveParams, llGetPrimitiveParams, llParticleSystem
llx error running rule #y: unknown rule. Function llx is passed a list as a parameter, y is the index value where there is an unknown rule type.
Functions: llSetPrimitiveParams, llGetPrimitiveParams, llParticleSystem

Build Errors

(occur when something bad happens when building; not necessarily script-related, but can be)

Error Message | Explanation | Fix
Could not save the script because the object it is on could not be found. The object may be out of range or may have been deleted. Also, "Script (object out of range)" in script window titlebar. | The script is too far away from your avatar. | Move your avatar closer (back in range) of the script (or move the object the script is in closer to your avatar).
Can't enable physics for objects that interpenetrate others | A prim/object is trying to be made physical but is penetrating another prim/object or the ground. | Move prims/objects away from each other and/or the ground. The physics engine's accuracy is only good to 10cm between objects so a prim will fail to become physical within that distance. This error can even occur when the object being set physical is phantom.' -Ice
Pieces too far apart | Prims are too far apart when linking. More info: link, PiecesTooFarApart | move prims closer, resize prims, add more closely spaced prims
LSL


Hacks

Hacks are usually undocumented features, surprising unexpected consequences, or unintended uses of features of LSL and Second Life. Given the crazy-glue-and-duct-tape nature of hacks, there is no guarantee that a hack that works today will work tomorrow, or will work in exactly the same way.

This page is not a guide to hacking the client, an act which is against the TOS.

Agent Key Determination

With sensor, collision, and touch events, the object type being dealt with can be detected with llDetectedType. This is not so with listen events. To determine whether the key returned by a listen is an agent or not, it must be determined whether the object with that key owns itself.

Example
integer isAgent(key id)
{
    return (llGetAgentSize(id) != ZERO_VECTOR);
}

Example
default
{
    state_entry()
    {
        llListen(0,"",NULL_KEY,"");
    }

    listen(integer channel, string name, key id, string msg)
    {
        if(isAgent(id))
        {
            llSetColor(<0,1,0>,ALL_SIDES); //Prim turns green if an Agent speaks
        } else
        {
            llSetColor(<1,0,0>,ALL_SIDES); //Prim turns red if a scripted object speaks
        }
    }
}
Very useful in an intercom system, so you don't relay spammy vendor ads too!

Note: this will not work across sim borders as llGetAgentSize only works on keys of agents in the current sim. Using llGetOwnerKey on a key not in the simulator will return the key passed to it. If you use it to check if a key is an agent it will falsely identify invalid keys (if using it on a key returned from a listen and the chat originated from a neighboring sim, the key will be invalid). -BW

Short-Distance (In-Sim) faked "Teleportation"

If using a large offset vector with llSitTarget, and then llUnSit when detecting the changed event, an effective short-range (up to 300m as of 1.3.0) teleporter can be made that works when a resident chooses to "Sit" on the object. (Note that this is not the same as actually teleporting; it just looks that way to the user.)

2-String Linked Message Transmission

Because a key is really just a fancy string, a second string can be substituted instead of the key and llMessageLinked can send it without typecasting.

However, because there's no limit to the length a string can be, it's primarily useful as a way to organize data by reducing the amount of work the recieving script must do to parse input. Also, having two strings already seperated is marginally more efficient then having one string and later parsing it into two.

Note: as keys cannot be directly typecast to or from floats, integers, rotations, or vectors, if the intended second message is one of these types, first cast it to a string, then to the type of choice. The inverse is true when recieving the message.

Example
integer num = 1;
string str = "hello";
string str2 = "goodbye";

llMessageLinked(LINK_SET, num, str, (key)str2);

Permanent Small-Memory Information Storage

Besides using a script's memory as permanent storage (and just never resetting it), a script can store short strings in the object name (using llSetObjectName/llGetObjectName) and description (llSetObjectDesc/llGetObjectDesc) of each prim. If the object is not going to change it's name, use child prims in a link set for that. Floats can also be stored in other object properties such as texture rotation, offset, alpha, scale, etc. Basically anything that has a get/set combination. To store integers, you can simply convert them to floats and back by scaling them down to fit into the usual 0 - 1.0 range.

llSetTexture can also store strings using the typecast (but the object will always have to have full permissions to read back the data), as well as llSetAlpha for floats, llSetColor for vectors. Combined, a lot of information can be stored permanently.

Note: The llSetTexture hack no longer works attempting to specify a texture that is not a valid texture key or not the name of a texture in the objects inventory will return an error "Couldn't find texture (your string)"

In-Function State Change

Usually, the compiler gives an error when the state tries to change from within a function. There is a workaround to this though: put the state change within a conditional statement's brackets.

Example
changeState()
{
    state foo;
}
// Doesn't compile.

changeState()
{
    if(1 == 1)
    {
        state foo;
    }
}
// Compiles.

See this page for more details.

llSetPos > 10m per call

llSetPrimitiveParams executes its rules as it comes across them, so if a list has multiple PRIM_POSITIONs in it, each one will be executed. This allows the 10m limit on llSetPos to be avoided.

See WarpPos for more details and code.

Detect Which Grid Script is running on

If you use an external system to keep track of sales on your vendor, you don't want People on Beta Grid messing up your tidy book keeping, by simply buying something from you. Here is a simple but slow routine to identify which Grid a script is running on (if you know a faster way than llGetSimulatorHostname, please correct me)

Example
integer idGrid()
{
    list Grids = ["agni","teen","dmz","colo","siva","durga","aditi"];
    string server = llGetSimulatorHostname();
    list serverParsed = llParseString2List(server,["."],[]);
    string grid = llList2String(serverParsed, llGetListLength(serverParsed) - 3);

    return llListFindList(Grids, [grid]);

}

Example
default
{
    touch_start(integer total_number)
    {
        integer grid = idGrid();

        if(grid == 0)
        {
            llSay(0, "You are on the Main Grid");
        }else
        if(grid == 1)
        {
            llSay(0, "You are on the Teen Grid");
        }else
        if(grid == 2)
        {
            llSay(0, "You are on the Linden Only Grid");
        }else
        if(grid >= 3)
        {
            llSay(0, "You are on the Beta Grid");
        }else
        {
            llSay(0, "Where the fuck are you?");
        }
    }
}

Local Application Launching via llLoadURL

llLoadURL functionality can be altered to allow for user initiated interactions between the SecondLife Client and applications running on the same computer workstation.

On Windows machines, the HKEY_CLASSES_ROOT\HTTP\shell\open\command can be modified to a batch or command line processing application, resulting in llLoadURL calls executing the specified application. This has the unfortunate side effect of breaking any other standard uses of llLoadURL in world.

Alternatively, changes could be made to a machines host file or the machine's local IP address could be specified in a URL and a webserver run on the local machine.

Discussion of usage: http://forums.secondlife.com/showthread.php?t=110251

LSL Style Guide | Lag


Known Bugs

LSL has several known bugs. When adding bugs to this list, please indicate the SL version number and the date it was observed. Bugs are often confused with annoyances, so before posting here try to see if the "bug" fits the criteria of an annoyance first.

Only the last jump to a label actually gets executed.

jump end;
llOwnerSay("hi");
jump end;
llOwnerSay("hi again");
jump end;
llOwnerSay("help i need somebody");
@end;

Still true in 1.10.3, 18-June-2006. --RobDemar

llGetFreeMemory returns historic free memory, not current

llGetFreeMemory is supposed to return the current free memory, however, it doesn't. It only ever returns the minimum free memory up until that point, even if the script has since freed it. Needless to say, this makes using it somewhat difficult.
Workaround: None.

llSetScale function blocked by llSetPos

For an unknown reason, if child prims in a link set call llSetScale after llSetPos is called in the root, the prims will not scale. This bug can be repeated more easily by calling llSetScale roughly at the same time llSetPos is called.

Must Interleave multiple textures and colors in the same llSetPrimitiveParams call (1.10.1)
If you create a list with different PRIM_TEXTUREs for each side, then a list with different PRIM_COLORs for each side, concatenate them and feed them to llSetPrimitiveParams, you will get the last defined PRIM_COLOR on all sides. The same goes for the other way round, where you will get the last PRIM_TEXTURE on all sides.
Workaround: Make sure you interleave (define both color and texture) for each side in the list.

Mouselook bank and steer don't work with decoupled camera until sim crossing

If a vehicle has the flags VEHICLE_FLAG_CAMERA_DECOUPLED and VEHICLE_FLAG_MOUSELOOK_STEER or VEHICLE_FLAG_MOUSELOOK_BANK, the camera won't actually be decoupled until the vehicle crosses a sim border. After that it works correctly.

Andrew Linden has acknowledged this problem in SL 1.4.5.

This has been fixed since, has it not?
Note: Sorta. If the vehicle is set decoupled and mouselook steering before crossing a sim border, you're gold. If you cross a sim border, then turn on mouselook steering and decouple the camera, you have to cross a sim border again to decouple the camera.

Minus sign included in identifier

If there aren't spaces between variables and operators, the script won't compile properly. See this posting for more.

integer foo = 1;
integer bar = foo-2; // will not compile. It results in a compiler error.
This is because the compiler reads "integer bar = foo-2;" like this: "integer bar -2;" which makes no sense to it.
No, this isn't the reason why; the real reason is because -2 means a negatively-signed integer with a value of "two". So what you are saying to the compiler is, integer bar = foo2 - just that "2" is negative. Since there is not an operator specified, the compiler borks, spitting out a syntax error.
You can also add the negative number bar = foo + -2

Workaround: Always use spaces after the minus operator.
integer foo = 1;
integer bar = foo - 2; // Compiles fine.

Scripts given with llGiveInventory has to be recompiled before they can run (1.10.1)
After giving scripts with llGiveInventory you can't start the script with the menu Tools|Set Scripts to running in selection. You have to Tools|Recompile scripts in selection first.

Physical objects such as vehicles lose their physical status after a sim crossing

This can happen because the the physics engine (Havok) may detect that a vehicle crossing a sim may arrive in an interpenetrated state, and unsets the object's physical status.

Workaround:
Have a timer that checks physical status:
llSetTimerEvent(2); // Somewhere appropriate to the script.

timer()
{
    if ( !llGetStatus(STATUS_PHYSICS) )
    {
        llSetPos(llGetPos+<0.0, 0.0, 1.0>);
        llSetStatus(STATUS_PHYSICS, TRUE);
    }
}

llGetScriptName does not work on duplicated scripts

  • If you have multiple copies of the same script in an object differentiated solely by name, llGetScriptName() will not reflect the different names.

Workaround: Edit the scripts so that each is unique. Differentiated by an unused global constant is sufficient.
Be careful with this. I was playing with this the other day and the problem I think is deeper than just llGetScriptName(), which I think is just one symptom of weird happenings when you drag a copy of the same script onto one object twice. Just a warning to be careful. :) -- Ama Omega
Hmm. I don't believe this is true anymore; it must have been fixed at some point. I use multiple copies of the same script in many places, and depend on llGetScriptName returning the name accurately (so I can parse the digit at the end). This has never failed. -- GaiusGoodliffe
The UUID of each script is the same when you use multiple copies, this still remains and may have been the cause of the original problem. But llGetScriptName() now returns the each name correctly. -- Harleen Gretzky

llResetOtherScript doesn't work on crashed scripts

Resetting a script that has encountered a runtime error (such as one that has experienced a stack/heap collision) with llResetOtherScript does nothing.

Functions that cause damped motion called inside attachments screw up the avatar
If a function that causes damped motion is called within an attachment, the avatar will "sink" to about knee-level below the ground/prim-based platform until the avatar teleports, relogs, or sits on an object. In this sunken state, the avatar looks as if its legs are broken.

Workaround: Sit on a prim and stand up. The avatar's legs should be fixed -- at least temporarily.

The timer reported by llGetTime resets on sim crossings

The timer used by llGetTime() is reset on sim crossings/object reinstantiations.

Workaround: Don't rely on llGetTime for objects that cross sims. Use llGetWallclock and llGetDate to determine the time. Also note that llGetDate returns a UTC date and llGetWallclock returns PST seconds since midnight. (e.g.: Dateline is at 1600 PST!) Note, however, this timer is not as precise as llGetTime.

This isnt a bug, since internally, when crossing a simulator border, avatars and objects are quickly derezzed on the departing side, and rerezzed on the destination side.
No, what you are describing is the likely cause of the bug. While there is an explanation for why it exists, it is still a bug. This behavior has been bug-reported, and accepted as a bug. The on_rez event is not invoked during sim-crossings, which would indicate that that your explanation is not consistent with the intended behaviour of llGetTime. Moreover, this bug was introduced by a revision of SL. This behavior is not consistent with previous versions.
According to the llGetTime page this value is server-specific and therefore per definition cannot be relied on for monotonous results in the first place. Isn't this a case of RTFM rather than of a bug?:)

sensor events received after llSensorRemove contain wrong llDetected* data.

When a very fast sensor interval is defined using llSensorRepeat, and llSensorRemove is called, it seems that sensor events that have been queued fail to dequeue. I've not experienced more then one erronious sensor event execution at a time though. When the erronious sensor event is executed, values from the llDetected* functions no longer make sense. I believe they are set to their "zero" values.

Example of the error-inducing code:
vector objectPos;
default {
    state_entry() {
        llSensorRepeat("SomeObject", NULL_KEY, ACTIVE|PASSIVE, 96, TWO_PI, 0.1);
    }
    sensor(integer detectedNumber) {
        vector objectPos = llDetectedPos(0);
    }
    changed(integer change) {
        if (change & CHANGED_LINK) {
            if (llAvatarOnSitTarget() == NULL_KEY) {
                llSensorRemove();
            }
        }
    }
}

Workaround:
Create a global variable set to TRUE when llSensor or llSensorRepeat is called, and FALSE when llSensorRemove is called. Then, check that the global is TRUE when inside the sensor event.

integer isSensorEnabled = FALSE;
vector objectPos;

// Convenience functions to assist in managing the sensor global variable:
LLSensor(string name, key id, integer type, float range, float arc) {
    isSensorEnabled = TRUE;
    llSensor(name, id, type, range, arc);
}

LLSensorRepeat(string name, key id, integer type, float range, float arc, float interval) {
    isSensorEnabled = TRUE;
    llSensorRepeat(name, id, type, range, arc, interval);
}

LLSensorRemove() {
    isSensorEnabled = FALSE;
    llSensorRemove();
}

default {
    state_entry() {
        LLSensorRepeat("SomeObject", NULL_KEY, ACTIVE|PASSIVE, 96, TWO_PI, 0.1);
    }
    sensor(integer detectedNumber) {
        // Kill the function if sensors aren't abled (event is eronnious).
        if (!isSensorEnabled) return;

        vector objectPos = llDetectedPos(0);
    }
    changed(integer change) {
        if (change & CHANGED_LINK) {
            if (llAvatarOnSitTarget() == NULL_KEY) {
                LLSensorRemove();
            }
        }
    }
}

Objects set phantom using llVolumeDetect fall through the ground.

Unlike objects set phantom using llSetStatus(STATUS_PHANTOM, TRUE), object set to phantom using llVolumeDetect(TRUE) will fall through the ground.

Workaround: Make sure prims using llVolumeDetect are never physical.

llList2Vector and llList2Rot return zero-values instead of typecasting when retrieving a list element that is not already a vector or rotation.

list myList = ["<1, 2, 3>", "<4, 5, 6, 7>"];
llList2Vector(myList, 0); // Returns ZERO_VECTOR (<0, 0, 0>)
llList2Rot(myList, 1); // Returns ZERO_ROTATION (<0, 0, 0, 1>)

This behavior is not consistant with the behavior of llList2String, llList2Float, and llList2Integer, which properly typecast the requested element when it is not the type they return.

Workaround:
Extract all list elements as strings, then manually typecast to the desired type.
// Instead of using:
llList2Rot(myList, 1);
// Use:
(rotation) llList2String(myList, 1);

Compiler loses ability to type-check return values in function's conditional statements.

Conditional statements within functions throw off the compiler's ability to discern if the value passed back from the function is valid or not. Things like this compile:
string myFunction() {
    if (TRUE) {
        return;
    }
    return "HelloWorld";
}

The virtual machine will return a null (zero) value to the caller when it encounters the return;. In this particular case, since strings and lists are allocated within the script's heap, rather then its stack, and myFunction returns a string, it will raise a bounds check error.

Stuff like this makes the state-change-within-functon hack possible. :)

If one phrase (variable name, value, etc.) in a script is longer than 16384 characters, compilation will crash the client.

Lindens have other things to do with their time than to fix this esoteric bug...
It's still a bug, it should be fixed, even if it has low priority. -Malarthi Behemoth
Fixing it would require a) allocating more memory for the tokenizer or b) rewriting the tokenizer. -BW
LL has Mono in the works, so re-writing the tokenizer's already happening, try to keep your token smaller than a script heap and you should live till then. - Ice

llMoveToTarget on sitting avatar splits avatar and agent
Using llMoveToTarget on an avatar that is sitting on the ground will cause them to be "split in two", with the avatar at one spot, and the agent at the other.

Rapid key presses or clicks can be missed by the client and interepretted as a single key or click event.

Quick sequences of two keys can also be indicated incorrectly (key missed), though it's not as frequent. This behavior was witnessed with v1.8.1 on Jan 23, 2006.

This script can be used to demonstrate the issue:

// control bug // ready jack // 23 jan 2006 // v0.2

// There seems to be and issue detecting quick taps and double clicks.
// This script is an attempt to demonstrate and narrow down the issue.

default {
    state_entry() {
        llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
    }

    touch(integer count) {
        llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
    }

    run_time_permissions(integer perms) {
        integer desired_controls = CONTROL_FWD | CONTROL_LBUTTON | CONTROL_UP;

        if (perms & PERMISSION_TAKE_CONTROLS) {
            llTakeControls(desired_controls, 1, 0);
        }
    }

    control(key id, integer down, integer new) {
        integer pressed = down & new;
        integer held = down & ~new;
        integer released = ~down & new;

        llOwnerSay((string) pressed + "."
                + (string) held + "."
                + (string) released);

        if (pressed & CONTROL_LBUTTON) llOwnerSay("click");
        if (pressed & CONTROL_UP) llOwnerSay("up");
        if (pressed & CONTROL_FWD) llOwnerSay("forward");
    }
}

We'd like to always see output for two presses of the forward button like this:

control example: 1.0.0
control example: forward
control example: 0.0.1
control example: 1.0.0
control example: forward
control example: 0.1.0
control example: 0.1.0
control example: 0.0.1

Unfortunately often as not when pressing quickly we'll see something like this:

control example: 1.0.0
control example: forward
control example: 0.1.0
control example: 0.1.0
control example: 0.1.0
control example: 0.1.0
control example: 0.0.1

Notice that it tends to have more held events. Seems that the client is simply missing the intervening release and press.

The same situation goes for mouse clicks.

For action oriented development this is a pretty lame situation. Seems like not screwing up user input would be a high priority for any software period. If a lousy application like MS Word can get double clicks right, SL should be able to. So I work hard on a project and the whole effort suffers because users get frustrated that the moves they try work inconsistently. At first I thought is was my logic, but after further exploration I find that the raw data coming into the control() function is bad. And users don't know any better so their gut instinct about the project is that it's poorly done and the whole effort is put in a bad light.

Physics for scripts in attachments are disabled after re-compile or adding the script from inventory.

Not sure when this started. I'd guess 1.8 range is reasonable to assume. But if you have an attachment that does dynamic operations involving physics the call will execute but nothing will happen. This state occurs for newly added scripts or scripts that are compiled in place. The condition can be corrected by re-attaching the scripted object.

Affected functions identified so far:

llMoveToTarget
llSetBuoyancy
llSetForce
llApplyImpulse

And possibly any other kinetic function.

Also, it seems that kinetic functions inside non-root prims in attachments are non-functional regardless. Not sure if this is new but I don't remember it being like that. Only tested briefly, so confirmation would be appreciated.

You can also workaround this problem by sitting on an object and then standing back up after recompilation.

False release/press events generated for CONTROL_LEFT and CONTROL_RIGHT. Found in 1.8.3 on Feb, 23 2006.

When capturing the CONTROL_LEFT and CONTROL_RIGHT controls extra false release and press events are generated when holding those keys down. This occurs in normal and mouselook modes. The other controls behave correctly.

The following script may be used to demonstrate the bug:

// control left right bug // ready jack // 23 feb 2006 // v0.2

default {
    state_entry() {
        llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
    }

    run_time_permissions(integer perm) {
        if (perm & PERMISSION_TAKE_CONTROLS) {
            llTakeControls(CONTROL_ROT_LEFT | CONTROL_LEFT, TRUE, TRUE);
        }
    }

    control(key id, integer down, integer new) {
        if (down & new & CONTROL_ROT_LEFT) llOwnerSay("rot_left press");
        if (down & new & CONTROL_LEFT) llOwnerSay("left press");
        if (~down & new & CONTROL_ROT_LEFT) llOwnerSay("rot_left release");
        if (~down & new & CONTROL_LEFT) llOwnerSay("left release");
    }
}

For the unaffected controls doing a short press and hold will generate output like this:

Object: rot_left press
Object: rot_left release

The press appears when the key is pressed and the release appears when you release the key as expected.

A single press-hold-release strafing left or right will usually generate extra events like so:

Object: left press
Object: left release
Object: left press
Object: left release

The first two appear almost immediately and then it pauses on the second press while you hold the key and then the release is generated.

Needless to say this is extremely annoying for any sort of precision tap vs. hold detection :-(

Stopping rotation with llTargetOmega(..., 0, ...) doesn't work reliably

When rotating an object with llTargetOmega(<0,0,1>, PI, 1) or similar, it is not reliably possible to stop it again by setting the rotation speed to 0. Setting it to a number very close to 0, e.g. 0.00000001 does work however.

Annoyances


Debugging

So you've written a script, thought you had everything perfect, tried compiling it, and BOOM!

Down went the simulator :-)

(Un)fortunately, that's not how many debugging escapades begin. Even so, it can be a rather daunting task if you don't know where or how to start fixing your script when something goes wrong. "Debugging" as we scripterati call it, is the process by which bugs, mistakes, are removed from the script. There are two main kinds of problems we focus on when we debug, syntax errors and logic errors.

Syntax Errors

These are often called the easiest to fix errors, since the LSL compiler tells you exactly how to fix them when you press the "Save" button. NOT!
Often times, the LSL compiler complains about a line of text completely unrelated to the problem its actually having. Take this for example:

default
{
    state_entry()
    {
        integer hungry = TRUE;
        if (hungry)
            llSay(0, "I want BAGELS!");
        }
    }
}

When you hit the save button the LSL compiler spits out:
(9, 0) : ERROR: Syntax error
...and leaves the blinking cursor at the last line of the script. The LSL compiler (unlike the Python interpreter) has absolutely no idea where you put whitespace (spaces, newlines) in your script. To it, the above looks like:

default{state_entry(){integer hungry=TRUE;if(hungry)llSay(0,"I want BAGELS!");}}}

The compiler is pedantic about braces because they signify where you should create and destroy scopes and all the variables you declare within them. In order to keep all of this straight, the compiler increments a counter when it encounters an open brace, and decrements it when it encounters a closed brace. If this counter is nonzero once it reaches the end of the script, you either have too many or not enough opening or closing braces. Unfortunately, it tells you this by giving you the extremely general Syntax error. This is where good brace indention style comes in handy. The LSL compiler can't see whitespace, but *you* can! check to see if all of your open braces have a corresponding closed brace, and vice-versa.

Here's another snag people usually hit upon:
default
{
    state_entry()
    {
        integer hungry = TRUE;
        if (hungry)
        {
            llSay(0, "I want BAGELS!")
        }
    }
}

In this example, the LSL compiler pulls another confusing whitespace-dependant trick on us. Here, it spits out:
(8, 8) : ERROR : Syntax error
...leaving the blinking cursor before the closing brace after the llSay line. The LSL compiler does this because it's expecting a semicolon to end the llSay() statement, but runs into a closing brace (and because the LSL compiler is stupid, it simply stops at the closing brace). Easiest way to fix these problems - check that all your statements end with a semicolon.

Now you may be asking, "Is there any other way to check my code's syntax without using SL's LSL compiler?" I'm very happy to state YES! The dazzling Masakazu Kojima has written an external program, called lslint, that checks the syntax of LSL scripts. You can access it online here or join in the forum discussion about it here. It shows you in greater detail where your script's syntax is off, and even makes suggestions! It slices, dices and even makes julian fries!

Logic Errors

These beasts range from the simplest "why is this code not running" to the extreme "OMG MY OBJECT EXPLODED!" These errors are not caught by the LSL compiler, because they're errors in what you told SL to do. Computers are intrinsically stupid - they lack intuition - so they will do exactly what you tell them to do, no matter how much that differs from what you intend for them to do or from what any sane person would want them to do. Here are some of the most common ones:

Semicolon After Condition
default
{
    state_entry()
    {
        integer hungry = FALSE;
        if (hungry);
        {
            llSay(0, "I want BAGELS!");
        }
    }
}

This script compiles, but the llSay statement will always execute, even when hungry is FALSE (as is seen here). This is because the semicolon after the conditional statement completely negates the conditional's effects, turning it from a scope-starter into a statement that has no effect. To fix this, remove the semicolon from the end of the conditional statement.

Use of == Instead of =, and Vice-Versa
integer FOOD_ITEMS = INVENTORY_OBJECT;
integer hungry;
default
{
    state_entry()
    {
        // Use of == instead of =
        hungry == TRUE;
        if (hungry)
        {
            llSay(0, "I want BAGELS!");
        }

        // Use of = instead of ==
        integer foodCount = llGetInventoryNumber(FOOD_ITEMS);
        if (foodCount = 1)
        {
            llSay(0, "Time to stock up on groceries, this script is all that's left!");
        }
    }
}

Unfortunately, this script compiles without any problems, but will never print the "BAGELS!" line and will always print the "groceries" line. Check that variable assignments use the = operator, and equality comparisons use the == operator.

Both of these logic errors occur due to the way LSL's syntax has mimiced C's.

Similarly, watch out for Boolean vs. Bitwise operators
Just as "=" can end up where you meant to have "==", so can "&" vs. "&&" and "|" vs. "||". These bugs can be even more difficult to find because the potential exists that a bitwise operation will yield a result that can then be interpreted as a boolean value. Half of the time your code may seem to be working fine, depending on the circumstances!

General Solution: Isolate-and-Conquer
Logic errors, especially the ones that cause the script to sit there doing nothing when you expect it to do something, can be very tough to find when you're trying to fix them by tinkering with the entire script as a whole. Test small chunks of your script, one part at a time, to make sure they are doing exactly what you want them to. This is easiest if you have divided your script into separate functions.

Finding the source of logic errors can often be like searching for a needle in a haystack. Using this principle, you can throw chunks of haystack off to the side once you're done searching through them.

General Solution: Variable Dumps
If your script isn't easily separable into self-contained chunks, the isolate-and-conquer approach might not be the best solution for you. Instead, at key points in your script (where important data is manipulated or a loop is entered/exited) insert llOwnerSay statements that print out the contents of any relevant variables. This way you can tell what goes wrong with the manipulation or if your script goes into an infinite loop.

Errors


annoyances

Annoyances & Gotchas

Annoyances aren't technically bugs, but are often confused with them. Annoyances can result in unexpected script behavior, but are the result of intentional design decisions on the part of the SL developers.

IconSerpentine see your user page for answer on recursion.

There is no way to rotate an avatar using a script.

llPointAt and llStopPointAt have been deprecated since mid-beta without replacements. As of now, there exists no way to rotate an avatar using a script; llApplyRotationalImpulse, llRotLookAt, and llLookAt called from within an attachment do nothing, and llPushObject has no effect on an avatar's rotation.

Comparing lists for equivalence using the == operator solely compares list's lengths.

When two lists are compared with ==, like this:
["A"] == ["B"]
The operation returns TRUE if both list's lengths are equal, reguardless of the list's contents.
Hence, the above returns TRUE.
Workaround:
You must use a UserDefinedFunction to compare two lists for equivelance.
integer listsEqual(list src, list test) {
    return (llGetListLength(src) == llGetListLength(test)
        && (llListFindList(src, test) == 0 || src == []));
}

llGiveMoney returns no message if the owner has insufficient funds.

If the script attempts to pay out more money than the owner has available, the user will see an error message, but the script itself will not get any kind of response. As KellyLinden explains: "It is not possible for llGiveMoney to return a meaningful value, the data is simply not available when and where it would be needed to make that happen."

Persistence of some scripted behaviours

Like position, rotation, size, colour, and texture, particles and sit targets are properties of prims, not scripts. Thus, if a script sets a particle system or sit target on a prim, then that script is deleted from the object's inventory, the particles or sit target remain. (The astute reader will note that the ability to have sit targets and particles without a script could be considered more a possible hack than an annoyance.)

Sensors only return 16 closest objects

If more than 16 objects are sensed, the result may seem unreliable.

Teleporting changes key of attachments

This isn't technically a bug, but it CAN be annoying for scripts that communicate with the outside.
Workaround:
Your best bet might be to use a redirect script in a stationary object somewhere, then have your attachments communicate with it using email or XML-RPC. It's not an especially good use of resources, but if using email for communications, your delay shouldn't be that much longer.
However, you should bear in mind that this is useful for detecting when a user has teleported.

"No longer true under 1.9 though iirc logging out and back in still does - RodrickHarrington"

llSetPos and llSetPrimitiveParams changes object's position by, at most, 10 meters

Workaround:
// keep moving until there--careful, gets stuck when obstructed by land
while (llVecDist(llGetPos(), dest) > 0.001) llSetPos( dest );

See the WarpPos library function that takes advantage of the fact that llSetPrimitiveParams only checks for the 10m distance for each item in the list

Calling state foo; while in state foo does nothing

Workaround:
This is a bit kludgy, but create a "mini-state" that simply switches back to the state you want to reflect into.

Instead of:
state foo {
    touch_start(integer n) {
        state foo;
    }
}
Use:
state foo {
    touch_start(integer n) {
        state back2Foo;
    }
}
state back2Foo { state_entry() { state foo; } }

Integer division rounds immediately.

If you execute a statement like this:
float myVal = 1 / 8;
myVal will be set to a value of 0. This can lead to very confusing results with operations that use both float and integer values (like the one below).
float foo = 2 * PI * (1 / 8);
foo will equal 0, because 1 / 8, when rounded, equals 0. Division with two integers results in a rounded result.

Workaround: Make sure to add ".0" to your numeric literals, explicitly making them floats:
foo = 2.0 * PI * (1.0 / 8.0);
This statement correctly evaluates (1.0 / 8.0) as a decimal value, so foo will equal approximately 0.7854.

Getting the "Link failed -- pieces too far apart" message.

See PiecesTooFarApart to get more detailed information on this error.

Unlinking physical objects causes them to fall (or explode) out of place

If physical objects are unlinked, the individual prims will fall to the ground (or explode away from each other). While linked, they will maintain their position. The first time you see this by accident, it can be quite annoying. But it can be a very cool result. If you really need to do this, turn your link set phantom before turning it physical, and simulate an "exploding" effect using llApplyImpulse.

Note: Please refrain from doing this in non-sandbox sims. The SecondLife physics engine (Havok) is not good at handling interpenetrating physical objects. In other words, this CAN cause a lot of lag! :)
And don't do it just to piss people off either... Use it when you want an effect, or it has a practical purpose... - Malarthi Behemoth

llLookAt and llApplyRotationalImpulse not working on attachments

From a scripted object attached to an avatar:
- llApplyRotationalImpulse() does nothing.
- llLookAt() screws up the avatar until the next teleport. The avatar will "sink" halfway below the ground/prim-based platform until that time.
This is because avatars are like prims that cannot rotate about the X or Y axis (see llSetStatus, STATUS_ROTATE_Y and STATUS_ROTATE_X). The second symptom is related to the damped-motion-attachment bug.

llRezObject gives no error message if it fails because the position passed to it is greater than 10m away from the position of the object the script is in.

llParticleSystem has maximum velocity x,y,z values of 100, which can change the direction of the vector, i.e. if you set a particle velocity of <150.0, 0.0, 200.0> , the real velocity will be <100.0, 0.0, 100.0>.
To keep this effect from changing the direction of the vector, do something like vec=min(100. , llVecMag(vec)) * llVecNorm(vec); (where min is something like float min(float a,float b) {if (a<b) return a; return b;}), which 'truncates' the vector to manitude 100.

bugs


Lag

What is lag, and how can it be reduced? In networking parlance, "lag" refers to the delay incurred when sending data from one server to another over a network. In SL, it's commonly used to describe any slowdown experienced, be it low framerate, a delay in movement, or a sim running slowly.

What is server-side lag? | What is client-side lag?
Reducing server-side lag | Reducing client-side lag

Server-Side

Each simulator within SL runs on its own independent CPU. This means that badly written, inefficient scripts can affect other residents in a sim. Lag is not just limited to physical objects, either. Things like listeners and sensors can do more than one might realize. There's often a more efficient--and frequently simpler--way to implement things.

These functions can help monitor a sim's "health":
  • llGetRegionTimeDilation indicates the sim's time dilation--the delay experienced when the sim cannot keep up with the processing of its tasks even after reducing the time allocated to scripts and physics.
  • llGetRegionFPS indicates the mean number of frames per second the simulator is running at. Note: llGetRegionFPS does not return a value that can be consistently used to benchmark performance of a sim. llGetRegionTimeDilation is far more straightforward and will correctly identify when the sim is performing poorly.

Client-Side

There are various client-related things that are central to lag: texture and geometry changes must be transmitted from the server to the client, and this information can add up. SL requires a lot of bandwidth as it is. Frequent texture and audio changes can result in a queue for the client that just keeps growing and growing if the client can't download it fast enough.

Once downloaded, the amount of data can slow down the client if the computer it's running on doesn't have a lot of system or video RAM. Low video RAM can also result in textures being dropped, leading to blurry or missing textures on objects and avatars. Rendering speeds can also be affected by having to draw avatars or large numbers of prims, particularly when textured with alpha textures, can also impact rendering speeds, especially on slower computers.

(It is confusing to technical people to call any slowdown at all lag--technically, the "tardiness" of network data, which happens with long distances, busy servers, and having much too little bandwidth) because in SL it will regularly mean it applies only to the reporting person whose settings are taxing their computer.)

So how does one fix slowdown? As someone who is experiencing (rendering) lag, there are two options: either upgrade the computer or tweak your preferences. For the latter, play around with lowering the details in the preferences window (Ctrl+P), or even turn off less necessary rendering via the debug (enable using Ctrl+Alt+D) menu. The preferences are often enough: dropping the draw distance ("Adv. Graphics" tab, and ensure "Disable Far Clip" in the "Graphics" tab is not checked) can help tremendously. "Local Lighting" is also another lag inducer, particularly since SL 1.7, so disabling that can help too.

To make sure one's created objects aren't causing extra rendering lag, avoiding lots of polygons and textures is a good idea, but most people have computers that can keep up these days. It's server-side lag that we can really do something about.

Q: What about llSetTextureAnim? Someone told me to take it down because it was generating lag. Isn't it supposed to be client-side?
A: This person did not know what they were talking about. Unless you are constantly calling llSetTextureAnim from your script, texture animations cause only a single object update. llSetTextureAnim does not affect sim load. It causes only very minor client load. However, it should be noted, animated textures tend to be big which can cause increased loading times and can lower your framerate because more texture data needs to be swapped in and out of texture memory when changing your view.

Lag Reduction Tips: Server-Side

  • Instead of using many llSetColor updates to make a flashing object, for example, use llSetTextureAnim to work with a texture made up of multicolored squares. It reduces the amount of information the server needs to send to the client (one object update, and one texture, rather than possibly thousands of object updates over a relatively short period of time).
  • Instead of using a dynamics function to spin an object, use llTargetOmega. This both reduces the amount of lag on the server caused by calculating physics, as well as stops the server from having to transfer all the rotation updates for that object. It often looks better too. Also consider using llSetTextureAnim for predictable object movement.
  • Do you really need llSensorRepeat? Probably not. Use llSensor instead.
  • Instead of using sensors, things like doors that automatically open (rather than using a touch) can use the collision, collision_start, or collision_end events, or llVolumeDetect.
  • Does your overly complex script need to run when nobody's around? If not, why not make it so it doesn't? Using a timer or llVolumeDetect, even llSensorRepeat can dramatically reduce the amount of computation required by the server and increase sim speeds for everyone.
  • If you do use llSensorRepeat, think about how long an interval you really need. If you want to have an object look at an avatar, why not have it do llSensorRepeat every 10 seconds or so, then if it detects someone, change it to every 0.5 seconds. When your script no longer detects them, bump it back to 10 seconds.
  • Keep your sensor range as small as it needs to be. If you only need to detect someone's presence within 5m, don't make your sensor radius 90m. (And if your sensor range is only 5m, consider using llVolumeDetect--not a sensor.)
  • Use a minimum number of llListen calls in your script. Use one listen and utilize IF conditional statements in the listen event in favor of using multiple listens on the same channel using the llListen filters. If you do not need to keep a listen "open" at all times, remove it using llListenRemove or by changing state and re-add the listen only when necessary, such as when an avatar gives your object money or touches the object. If you need to communicate between scripts within the same prim or linked set, consider using llMessageLinked instead.
  • Corollary to the previous suggestion: If you have 10 listen scripts in one object (for example, you have 10 scripts in the object that all need to listen), consider using one script to listen, and just use llMessageLinked to talk to the others.
  • Also, if you have a lot of control scripts you want to 'say' commands to, consider having them listen on a channel besides 0. That way your scripts wont be parsing all the random chatter from normal conversation. Then use the channel message method. A side benefit is that no one will see your commands and you can bind them to keys using gestures.
  • If you have a script that rezzes an object, then llSays text to that object and the two never again communicate, have them use llListenRemove or change state when they're done talking.
  • If you're in a FOR loop or a long list of IF conditional statements and you've already completed your task, try using "else if" (when only 1 of the "if" conditions ever needs to be met), or use a return to get out of that event. Why bother running through all those extra iterations and/or comparisons? - This may also be the one place a jump statement is worth using; just don't jump anywhere other than the line of code immediately after your FOR or WHILE loop.
  • If you want to check a really large list of strings, (names, for example) against a single string, instead of using a FOR loop to check each list entry against the string, just use llListFindList.
  • If you're trying to sort a list into alphabetical order for ease of searching, don't use llListSort and a character lookup table and compare substrings and all that crazy stuff. Just use llListFindList. Sure, it's impressive, but you don't need to reinvent functions.
  • If you want to make your script "throttle back" depending on sim performance according to llGetRegionTimeDilation and llGetRegionFPS, don't use a timer with a short interval, as that can cause lag itself. You'll want the interval to be on the order of minutes, not seconds.
  • Stuck trying to figure out channels for a script? Spamming several objects with the same llListen channel when you just don't have to? Try this.
  • Also try llKey2Name for passing data between objects. It can be used anywhere in the sim!
  • An advanced trick for Pros: Use llRemoteLoadScriptPin for passing functions in context. Major lag reduction can be done in this manner, provided you (1) have rights to/own all the objects in play and (2) know their pins.
  • Don't put listeners in poseballs! If you need to communicate with a user, use touch_start or llDialog with a timeout.
  • Whenever applicable, use llSleep in place of a timer. This works perfectly if your script has nothing to be doing in between triggers.

Lag Reduction Tips: Client-Side

  • Use the smallest possible texture size, rounding to the nearest power-of-two size (128x128, 256x256, 512x512, etc). Also, textures don't have to be a perfect square--256x128 is a perfectly valid size. Note that SL will resize your non power-of-two images for you before uploading, but that it may not look anywhere as good as if you resized them yourself). Only upload 32-bit TGAs if your texture actually requires alpha transparency; otherwise use 24-bit images. Remember, a texture scaled from 512x512 to 256x256 and then run through a sharpen filter will often look better in addition to requiring 1/4 the video memory.
  • Use as few llTargetOmega calls at once as possible. While reducing server-side lag, it does still place a considerable amount of strain on your client.
  • If you upload a texture that is not square (i.e., 256X128) it will look "stretched" in the texture viewer in the client. That's OK, when you place the texture on a prim, set the per-face settings so that it looks right.
  • If you're not actually playing audio, don't even use llPreloadSound. Why bother forcing the client to download it if they're not even going to hear it? I've seen jukeboxes that preloaded 250 audio clips. That was just insane.
  • Corollary: Don't preload audio you don't plan on playing. For example, if the preloaded audio is for a game, don't attempt to make it preload unless it looks like someone is actually about to play the game.
  • Check that your scripts aren't forcing many full-primitive updates. To do this, activate the debug menu (Ctrl+Shift+D), then click "Show Updates". Full-primitive updates are highlighted with red cone-halos, terse primitive updates are highlighted with blue cone-halos, and primitive de-rez requests are highlighted with green cone-halos. Full-primitive updates force the server to re-transmit the entire status of the primitive to the client. These updates are also bandwith-intensive, so if a client recieves too many of these it may max out its bandwith and start dropping packets. (Note: "packet loss" literally means that packets are lost, whereas network latency, what we traditionally think of as "lag", only results in slow transfer speeds, not data loss.) Sadly, certain simple functions (such as llSetText, last time I checked) perform full-primitive updates every time they're used, so use them carefully.
Style Guide | Simulator | Server | Client


StyleGuide

LSL Style Guide

This document describes good LSL coding practice. Were you looking for the WikiStyleGuide?

Style in coding is usually summed up in two categories; Cleanliness and Speed.

Cleanliness

This is easy once you know the rules, but there are a few things that most programmers are picky about. The biggest one is indents/tabs. Take this code, for example:

default
{
state_entry()
{
if (x==1)
llSay(0, "X is: "+(string)1);
}
}

This is not properly indented/tabbed. After every bracket, you go in an additional tab. When you close the bracket, you go back out a tab. The LSL editor takes care of this... in a limited fashion, but you should always watch your tabs. That same code, properly tabbed, looks like:

default
{
     state_entry()
     {
          if (x == 1)
               llSay(0, "X is:" + (string) 1);
     }
}

This code is properly tabbed, and it makes following FlowControl much easier. Other than that, keep your hard-returns to the end of every line of code, and you should be fine.

This compares other styles of tabbing out your scripts, check it out! :)

Cleanliness Tips: (Please contribute!)
  • Put spaces between operators and after commas (,).
  • Separate lines longer than about 80 characters. (Lines that wrap around).
  • Don't mix coding styles. When editing code you didn't create, stick with the existing style (or change the style throughout).
  • Functions should be short and sweet. A good rule is that a function longer than a screen height or two should be refactored into multiple functions.
  • Avoid excessive comments. Well written code explains itself. For more complicated functions, put general comments concerning what you are trying to do at the top of the function. Leave comments out of the body of the function.
  • Don't be lazy when it comes to variable names. For example, 'message' is better than 'msg'. With good names, you won't need as many comments, so less typing over all. Also, if you use whole words, you won't have to remember your particular abbreviation later on in the code. Make your names describe the role of the data, not the type. With properly factored code, the type declaration won't be far off. Boo... msg is essential in things like listen events.
  • Start global variables with an indicator (most LSL script I've seen uses interCaps style variable names with a lower case 'g' at the front for globals). Declare globals at the very top of the script unless they are part of a code snippet intended to be copied between many scripts. In that case, make sure the snippet is cleary marked at the beginning and end and put the globals at the top of the snippet.
  • When coding a snippet, prepend an appropriate name to all the global variables and functions. For example: if the snippet has to do with fuss, name its variables like gFussListen instead of gListen. This way your code snippet will be less likely to step on names a user might place in their regular code. Local variables defined inside functions don't matter through the graces of scope.
  • Use brace ({}) for conditional statements. If it's only a single if condition with a single very short statement, put it on one line with no braces. If there's an else statement, or the conditional is closely nested in other conditionals, always use braces--even for a single statement.
  • Stick to one naming style, either interCaps or under_scores, not a mix of the two. This way you won't have to remember which form a particular variable takes when you want to use it. Most code in SL uses interCaps. Or, you can have a *nix fetish and have it all lowercase. "Constants" are usually capitalized and separated by underscores, e.g. CHANNEL_NUMBER.

The Java Code Conventions can also be a good place to look for tips.

Speed

The other style point is speed, which is a much more difficult thing to master. Basically, you are looking for faster-executing code here. Using x++; is slower than using x=x+1; Simple things like that can speed your code up a great deal. LSL shouldn't be compared to programming a PC. It's more like programming an embedded system. You have very little memory and CPU to work with, so writing faster and less memory-intensive code is paramount.

Indeed. You have only 16kb of available memory, which has to be shared between bytecode, stack and heap. You also are going to have a hard time telling LSL exactly what to do with that memory, since you have no arrays or pointers. Memory is your single most valuable resource, and you see how much you have left by calling llGetFreeMemory(). -AF

If you wish to throw caution to the wind take a look at this thread:
"How to optimize code and when not to"

Tweaks: (Please contribute!)

  • Using x++; is slower than using ++x;, x += 1;, x = x + 1 or x = 1 + x.
    • ++x;, x += 1; and x = x + 1 result in the same bytecode; x = 1 + x just swaps the order that the values are pop'ed into the stack but is otherwise the same.
    • Evidence: bytecode, script & annotated bytecode

  • If you've entered in debug code such as if(DEBUG)llSay(0,"testing");, that will slow your script down. Comment it out or remove it when it's no longer needed.

  • Keep function calls to a minimum. If you have results from functions that don't change, its best to store them in variables instead of repeatedly calling functions to get them.

  • Hoist loop invariants out of the loops yourself; the compiler doesn't do this for you. For example, for(i = 0; i < llGetListLength(list); i++) calls llGetListLength() mutiple times, but this does only once:
integer len = llGetListLength(list);
for(i = 0; i < len; i++)

Has anyone tested if declaring variables outside of loops helps speed things up? -Chris

I just did. I'd recommend getting the length outside the loop. The difference was about an order of magnitude in the test I put together. You can see the script and some sample results on my page. - RJ

It actualy depends if you set a default value. string a;while(b){a = llGetSubString(a,--b,b);} is going to be marjonaly slower then while(b){string a = llGetSubString(a,--b,b);} only because the null declaration has been removed. -BW

  • The LSL compiler is not an optimizing compiler.
    • It will not...
      • inline functions
      • unroll loops
      • convert your recursive functions to iterative functions using tail end recursion
      • remove unused functions or variables
      • recycle memory space for local variables (each local variable gets its own unique memory space)
      • free local variables after their values are no longer needed

  • The 'list' data type is downright awful. As near as I can tell, it seems to be some sort of implementation of a linked list. Since LSL is a strongly typed language without parametrized type support (templates), lists have to use dynamic typing to keep track of the data they contain. The result? Each node has about a 20-40 byte overhead to keep track of the data stored therein. In addition, there is no iterator object so looping through a list is an O(n^2) operation. As if this wasn't bad enough, it gets worse. If you want to update an object in your list you have to reallocate and deallocate the entire thing twice on the stack using the insertion and deletion operators.

The list does advantages though, they do provide a convenient syntax for storing SMALL data structures. Just avoid storing anything with more than 10-20 items in a list and you'll be ok.

For integer arrays, there is a kludge you can use to get around using lists. Strings are much faster and take up about 1/40th the amount of memory for very small values. Here's the trick:

string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*() []{}-=_+;':,.<>/\|`~";

string stack = "";
integer s = 0;

//Push back an integer
push(integer val)
{
     stack += llGetSubString(letters,val,val);
     ++s;
}

//Pop off a value
integer pop()
{
     if(s)
         return llSubStringIndex(letters,llGetSubString(stack,--s,s));
     return -1;
}

You could take this a step forward, using two or more characters to encode a value instead of just one.

In my own recent tests (in 1.6.9), storing integer data in strings is about half the speed of storing them in lists. My case was a bit different than the above example, but actually the conversions should have been faster (using typecasting). Any else have any other recent experience? -- ZarfVantongerloo

  • There is a mysterious sleeping period after each loop iteration. This probably done so the server can catch it's breath and update other objects. However, if you can put off the next loop iteration as long as possible your script can get more done faster. Here's an example of the trick:

default
{
    state_entry()
    {
        llSay(0, "Timing normal loop");
        string s = "";
        integer i;

        float t = llGetTime();
        for(i=0; i<1000; i++)
            s += (string)i;
        t = llGetTime() - t;

        llSay(0, "Time: "+(string)t+" seconds.");

        llSay(0, "Testing unrolled loop x10");
        string q = "";

        t = llGetTime();

        for(i=0; i<1000; )
        {
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
        }

        t = llGetTime() - t;
        llSay(0, "Time: "+(string)t+" seconds.");
    }
}

On average, the second loop runs about 6-8 seconds faster. Here were the times:

Normal:
30.575254
31.783129
28.784805

Unrolled:
22.258141
24.367455
22.544336

Time will vary dramatically with server load, but it is still clear that this is much faster. The tradeoff is the extra memory involved in unrolling your loop. - AF

Counter Opinion: Tweaks Considered Harmful

Unless your script is running up against the memory limit (you are getting Stack-Heap collisions), or you are writing performance critical code (and if you're thinking about this question, then you're not), don't worry about tweaking your code at all.

If you are running up against the memory limit, consider if any functionality of the script can be moved to a helper script. For example, if you have a giant list of strings that are used with llSetText, put these in a separate script with the llSetText code, and send the list index to this script using a link message

The most important attribute of code is correctness that is, does it do what it should. The next most important are readibility and maintainability, that is can you (after leaving the computer to watch Buffy for an hour) or someone else figure out what the code's supposed to do, easily spot bugs, and fix them without the whole script falling down like a house of cards? Tweaking and optimizing your code runs counter to all of these important attributes. However, it is good practice to write your code efficiently the first time, while keeping it neat. For example, using the ++i instead of i++ is a good habit.

First make your code work.
Then make it correct.
Then make it neat.
Then add any extra features you want.
Then if --and only if-- performance is an issue, look for slow bits you can improve (the best place to look for these is loops, are you doing something every iteration that you only need to do once?)
Then paint the house, wash the dog, bake cookies, do the dishes, vacuum the rug.
Then tweak and optimize your code in any way that does not sacrifice readibility or correctness.

Tweaking and optimizing your code is often clever, and many times has no effect if you do it blindly. Try to find where the slow points are, and optimize those (and only after finishing everything else.) Or you can do it right the first time. It's easier to do it right once than to try and optimize it without breaking it.

BBC's opinion: if you are writing a script that has to be fast, your design sucks. Never design for speed, design for usability.
BW's thought: some scripts have to be designed for speed: rapid firing guns. Other scripts have to optimized to stay below the 16k memory limit: databases. It's best to optimize it after you have a working model. For databases simple compression isn't a bad idea. For guns and other scripts that need speed it's best to reduce the amount of math that is required and overall function calls; with speed the name of the game is: "good enough" not "perfect".
I think that writing scripts to be efficient is a good practice. No one wants a script that takes five seconds to do one basic function. Also, efficient scripts put less load on the sim, and is usually easier to read. --KeknehvPsaltery

If you're writing code that is slow, you aren't taking into consideration people's patience for your script. In a language like C++ with little luxuries like arrays and an almost unlimited memory allowance that work efficiently and quickly I say good design is god. However, LSL has many little quirks and if we did things the way that work the most logically our code would be horribly slow to the point of unusability not to mention cause memory problems. For instance I'm making a simple tic tac toe game that checks for a winner each time a square is clicked. I tried doing that with lists and somehow thought people wouldn't want to have to wait the 5 seconds to make each and every move that it wound up taking. That might work with chess, but not so much with tic tac toe. My results have been much more successful using bitwise math even though it's less readable and using vectors as 3 deep mini arrays even though it's almost incomprehensible to a person looking at it for the first time.
In short, I think that speed and memory are far more neccessary than clean, cookie cutter, leave it to beaver code. What you need to do is be all kinds of tweaky, crazy and edgy to dodge the LSL pitfalls and gotchas but comment and document each and every piddly bit thoroughly.
/ / = GOD -bafiliusGoff
WikiStyleGuide | Hacks | Lag | NamingConventions


Script Library

There are many scripts that have become buried in the Scripting Library forum, were lost with the death of the scripting forums, or sit idle in inventories that could be useful and should be more accessible. Other scripters may be in the same situation. This wiki is a well-suited medium for a script library. Feel free to add scripts to the script library by creating new pages for them and linking to those pages here.

Proper etiquette for the library is to not edit someone else's script because you think your way is better, but rather to post your own version, linked from and back to the original.

There is a companion particle library that contain particle systems.

Rules for posting:
1. Prefix pages for the script library with "Library" to keep the Wiki namespace clean. Example: LibraryEventLottery
2. Your script must be tested and working. If it's not, stick it on your own username page until it is. This is a list of working, usable scripts.
3. Add a link to your script's page here. Link back to this page from your script's page. Also, keep this list in alphabetical order.
4. Don't add scripts that duplicate the same functionality as an existing script. If yours does, explain why.

Name | Creator | Description
Hello Avatar | Linden Lab | SL's default script
ASCII | ChadStatosky | ASCII <-> int
AirSeaAltimeter | OoPsGalatea | A pretty decent air/sea altimeter.
AltPickPocket | MitzpatrickFitzsimmons | Transfer Money from your Alt's account without logging them in.
AvatarInSim | SiRiSAsturias | Detect if an avatar is in the same sim or not... reliably.
Barometer | CidJacobs | Check Barometric Pressure.
Bezier Curve Demo | LionelForager | Draws a bezier curve in 3D given by 3 control points.
Builders' Buddy | NewfiePendragon | Large building positioning/movement utility.
Builders' Tabs | TimelessPrototype | Advanced precision builder's tool.
Camera Sync | MeyermagicSalome and NomadPadar | Camera Synchronization utility
Color Scripts | KagePixel | Convert from HSB to RGB and back.
Channel Number Generator | PetreLamar | Given a text string, generate a (relatively) unique channel number.
PublicBillboard | Chasingred3Ixtab | A Public Cycling Texture Billboard.
Conversation AI | AmaOmega | Simple conversation tree (Eliza-like).
Camera Follower | ArkLehane | Follows just above your camera.
Chat Extension Module | ChristopherOmega | Allows transfer of >255 character strings over chat, via Packet Chat Protocol.
Create a flying pet | CatFart | A worked example on how to create a small flying object using physics-based movement.
Case Insensitive Listen rezzer | Chasingred3Ixtab | Case insensitive listen rezzer.
DanceBall | AsiraSakai | This script plus dance animations in a prim create a clickable danceball.
Day of the week | RichardSolvang | Figure out what day of the week a given date is.
Deluxe Door | EzharFairlight | A door with some extras. See also: the new Timeless Linked Door.
Dialog Module | StrifeOnizuka | Create and handle dialog boxes through linked messages.
Dump Prim Parameters | PierreJosephProudhon | Dump prim parameters to a LSL code string that can be fed to llSetPrimitiveParams
Eliza Chatbot | DedricMauriac | Parady of a Rogerian therapist
Email Extension Module | ChristopherOmega | Allows scripts to send emails without delay and receive emails without a timer.
Simple Email Parser | PlowboyLifestyle | Breaks an email header into local variables with some error checking.
Event Lottery | AmaOmega | Randomly choose a winner from a list of people without duplicates.
Experimental Rotation Math Library | StrifeOnizuka | Collection of functions for working with rotations.
Fade Texture | NashBaldwin | Smoothly drop texture alpha using exponential fall-off.
Fast Find | AmaOmega | Fast find number of lines in a notecard (binary search).
Fix Repeats Per Meter | NashBaldwin | Make a building by roughly positioning walls, then drop this script on each prim. It rescales to grid, positions to grid, and fixes repeats per meter, then deletes self.
Flight Assist | MorphWollongong | Flight assist HUD controller script
Float to Hex | StrifeOnizuka | Very useful for floats transporting.
Fractal Generator | SeifertSurface | Generates fractal "trees". User friendly.
General Menu Engine | ZonaxDelorean | A menu system engine that lets you define menus then easily use them.
Get Day of Week | DoteDoteEdison | Alternate method to get day of week from llGetUnixTime.
Give Inventory | DedricMauriac | Gives inventory and checks permissions
Go Game | JonathanShaftoe | A fully working Go game board.
IGS (In Game Data Server) | ViceroLambert | A simple LSL server.
Keyboard while typing | MaxCase | A simple keyboard while you type.
Link Numbers Getter | KankerGreenacre | Simple function for keeping track of the links in your object.
Listening Texturer | VirusCollector | Small script to change a texture to any key you say.
Millisecond Time | MinskOud | Timezone-aware and millisecond-granuality time and formatting functions.
MitzFader | MitzpatrickFitzsimmons | Object Alpha Fader Transition Effect
Multidimensional Array API | ChristopherOmega | Store data in lists as you would in a standard C++/Java multidimensional array.
MultiTitler | TigroSpottystripesKatsu | A basic titler that enables you to wear more than one titler at once and set them individually
NewbieNag | KexGodel | Give a notecard to a new resident below a certain age
Notice Giver | Joker Opus | Gives notices to anyone within 96 meters
Parametric Surface Rezzer | SeifertSurface | Rezzes a surface defined by an arbitrary parametric equation of two variables.
Passwall | AsiraSakai | This script will allow you to set up a solid wall that lets certain people pass through it.
Pose Ball Script, version 4.3.2 | SimonRaven and others | pose ball script, updated.
Particle Script 1.1 | KeknehvPsaltery | A very efficient script for the creation and inclusion of particle systems. Albeit not the best if you are just learning, but excellent if a particle system is needed for something else.
Particle Script 1.2 | JamesHanner | An added feature to the above script.(Has a new key setter instead of just owner and self)
Particle Script 0.5 | AmaOmega | Create particle systems and play around with the settings.
Particle Script 0.5a | EepQuirk | An optimized version of the above script.
Polygon Former | JontteGremlin | Code snippet that will form a triangle between 3 vectors. Can be used, for example, in 3d model loaders.
PortRing Creator | Neo Rebus | A system to automatically create a teleporter system from a central "base" location to a number of outlying "stations".
Pseudo-Random Number Generator | StrifeOnizuka | For use with secure communication through insecure mediums to reducing the risk of the communications protocol being hacked.
Radar | SableTill | Fairly complete scanner that includes details on distance, time in vicinity and direction
RadioStation | AsiraSakai | Add lists of radio stations, descriptions, and authorized station changers, and authorized users can change parcel music urls.
Random Hovertext Colour | KopiloHallard | Sets hovertext on an object and changes the colour to a random colour every 0.5 seconds
Rope Constraint | ComradePodolsky | Links two objects by a physical emulation of a rope-like constraint and uses particles to visually represent it.
Runtime Environment | ReadyJack | Detailed detection of object environment changes like dropping or wearing. Also detects logins!
Sailboat Script | KankerGreenacre | Flying Tako sailboat
Self Upgrading Script | MarkovBrodsky | Keeps only latest version on prim
Self Upgrading Script Enhanced | CronStardust | Keeps only latest version of the script on prim (Even with multiple adds of the same script!)
Self Upgrading Script Alt | DamekTretiak | Updates each child-prim with a given script
Serialize | ShadowGretzky | Implements 'float-safe', reasonably quick serialization and unserialization of list types
Set Text Once | EpikTetus | Drop it on an object, say "/3 This is Red", the floating text changes and the script deletes itself.
Settings Notecard | KonigmannLippmann | Loads values from a notecard so scripts can be customized by user.
Simple Cypher | AmaOmega | A simple password-based substitution cypher.
Simple Disappearing HoverText | KonigmannLippmann | A simple demonstration of how to remove hover text when an avatar sits on the prim.
SLPrint | AmaOmega | A function to print formated data, including hexidecimal and fixed-point floats.
Strided List Functions | AakanaarLaSalle | Functions for working with strided lists.
Swarm | ApotheusSilverman | This implements the widely-available swarm algorithm in SL.
Teleporter | AsiraSakai | This teleports the object and the person sitting on it within the simulator.
TextureSwitcher | StrifeOnizuka | Texture switcher that will step through the textures in an object's inventory, displaying them on all sides of the object.
Thermometer | CidJacobs | Check Temperature.
TimeElapsed | ChromalBrodsky | When called twice, will give the time elapsed with millisecond precision.
Timeless Linked Door | TimelessPrototype | Full-featured door script for doors that are linked to other prims.
Timer Enhancer | ChristopherOmega | Grants more precise control over timers.
Unescape | StrifeOnizuka | Unescape escape codes in strings.
Universal Prim Torture Script | StrifeOnizuka | Upgradable universal prim torture script.
Product Update Server | Comet Aero | Updates product upon request
Vendor Script | AmaOmega | A simple vendor
Vigenere Cipher | DolusNaumova | A polyalphabetic substitution cipher.
WarpPos | KeknehvPsaltery | A function that allows one to do instantaneous non-physical movement
NumRs | NornAlbion | Display numbers on a prim - maximum of 3 per prim
Position HUD | GigsTaggart | Moves a HUD to a same position in any attach point.
Timestamp Difference | BenFassbinder | Calculate the difference in seconds between two timestamps.
Wind Turbine | CidJacobs | Estimates approximate watts stored from wind powered turbines.
XyText | XylorBaysklef | Display text (up to 6 characters) on a prim
XyText 1.2 | KermittQuirk, ThraxisEpsilon | Display text (5 or 10 characters per prim)
LibraryMultiChannelListener | AnorcaCalamari | Listen on any set of 50 channels
LibraryInstantMessageAnyone | AnorcaCalamari | Instant messager script
LibraryTrueTele | AnorcaCalamari | Teleports you anywhere you are allowed
LibraryParticleGarden | ZaraVale | Randomly rezzes textures in your object, to form a particle garden, the flowers also grows slowly!
LibraryParticleSignboard | ZaraVale | 1 Prim Signboard that can change via a chat channel
LibraryPrecisionTime | SendaoGoodman | Precision timer for accurate timing in SL
LibrarySenWarp | SendaoGoodman | Warppos replacement.
LibrarySenRot | SendaoGoodman | Smooth nonphysical rotation. May cause lag. ;)
Word Scrambler | SecondProctor | Scrambles anything you want.
RandomVector | WarKirbyMagojiro | Produces a random vector
clrchanger | SecondProctor | Changes the color of a whole object from 1 script.

Q: What's the difference between the Script Library and the Examples page?
A: The Script Library is for full scripts. Examples is best suited for explaining specific functions or parts of scripts.

Particle Library | Examples


clrchanger

This will quickly change the color of a whole objects from only script.
integer prims=llGetNumberOfPrims();//Gets total prims of the object it is in.
change_color(vector color)
{
    integer x;//For the For loop.
    llSetColor(color,-1);//Sets the prims this scripts in. (no use unless in main prim)
    for(x=-1;x<(prims+2);x++)//A for loop to get each prim in the object.
    {
        llSetLinkColor(x,color,-1);//Sets the color of each prim in the object COLOR.
    }
}
default
{
    state_entry()
    {
        change_color(<0,128,255>);//Will Set the the object to a light, nice shade of blue.
    }
}


ExampleParticleScript

// Particle Script 0.5
// Created by Ama Omega
// 3-26-2004

// Mask Flags - set to TRUE to enable
integer glow = TRUE;            // Make the particles glow
integer bounce = FALSE;          // Make particles bounce on Z plane of object
integer interpColor = TRUE;     // Go from start to end color
integer interpSize = TRUE;      // Go from start to end size
integer wind = TRUE;           // Particles effected by wind
integer followSource = TRUE;    // Particles follow the source
integer followVel = TRUE;       // Particles turn to velocity direction

// Choose a pattern from the following:
// PSYS_SRC_PATTERN_EXPLODE
// PSYS_SRC_PATTERN_DROP
// PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY
// PSYS_SRC_PATTERN_ANGLE_CONE
// PSYS_SRC_PATTERN_ANGLE
integer pattern = PSYS_SRC_PATTERN_EXPLODE;

// Select a target for particles to go towards
// "" for no target, "owner" will follow object owner
//    and "self" will target this object
//    or put the key of an object for particles to go to
key target = "self";

// Particle paramaters
float age = 1;                  // Life of each particle
float maxSpeed = 1;            // Max speed each particle is spit out at
float minSpeed = 1;            // Min speed each particle is spit out at
string texture = "";                 // Texture used for particles, default used if blank
float startAlpha = 1;           // Start alpha (transparency) value
float endAlpha = 0.1;           // End alpha (transparency) value
vector startColor = <0,1,0>;    // Start color of particles <R,G,B>
vector endColor = <1,0,0>;      // End color of particles <R,G,B> (if interpColor == TRUE)
vector startSize = <1,1,1>;     // Start size of particles
vector endSize = <1,0,1>;       // End size of particles (if interpSize == TRUE)
vector push = <0,0,0>;          // Force pushed on particles

// System paramaters
float rate = 1.5;            // How fast (rate) to emit particles
float radius = 1;          // Radius to emit particles for BURST pattern
integer count = 1;        // How many particles to emit per BURST
float outerAngle = 1.54;    // Outer angle for all ANGLE patterns
float innerAngle = 1.55;    // Inner angle for all ANGLE patterns
vector omega = <0,0,0>;    // Rotation of ANGLE patterns around the source
float life = 0;             // Life in seconds for the system to make particles

// Script variables
integer pre = 2;          //Adjust the precision of the generated list.

integer flags;
list sys;
integer type;
vector tempVector;
rotation tempRot;
string tempString;
integer i;

string float2String(float in)
{
    return llGetSubString((string)in,0,pre - 7);
}

updateParticles()
{
    flags = 0;
    if (target == "owner") target = llGetOwner();
    if (target == "self") target = llGetKey();
    if (glow) flags = flags | PSYS_PART_EMISSIVE_MASK;
    if (bounce) flags = flags | PSYS_PART_BOUNCE_MASK;
    if (interpColor) flags = flags | PSYS_PART_INTERP_COLOR_MASK;
    if (interpSize) flags = flags | PSYS_PART_INTERP_SCALE_MASK;
    if (wind) flags = flags | PSYS_PART_WIND_MASK;
    if (followSource) flags = flags | PSYS_PART_FOLLOW_SRC_MASK;
    if (followVel) flags = flags | PSYS_PART_FOLLOW_VELOCITY_MASK;
    if (target != "") flags = flags | PSYS_PART_TARGET_POS_MASK;
    sys = [  PSYS_PART_MAX_AGE,age,
                        PSYS_PART_FLAGS,flags,
                        PSYS_PART_START_COLOR, startColor,
                        PSYS_PART_END_COLOR, endColor,
                        PSYS_PART_START_SCALE,startSize,
                        PSYS_PART_END_SCALE,endSize,
                        PSYS_SRC_PATTERN, pattern,
                        PSYS_SRC_BURST_RATE,rate,
                        PSYS_SRC_ACCEL, push,
                        PSYS_SRC_BURST_PART_COUNT,count,
                        PSYS_SRC_BURST_RADIUS,radius,
                        PSYS_SRC_BURST_SPEED_MIN,minSpeed,
                        PSYS_SRC_BURST_SPEED_MAX,maxSpeed,
                        PSYS_SRC_TARGET_KEY,target,
                        PSYS_SRC_ANGLE_BEGIN,innerAngle,
                        PSYS_SRC_ANGLE_END,outerAngle,
                        PSYS_SRC_OMEGA, omega,
                        PSYS_SRC_MAX_AGE, life,
                        PSYS_SRC_TEXTURE, texture,
                        PSYS_PART_START_ALPHA, startAlpha,
                        PSYS_PART_END_ALPHA, endAlpha
                            ];

    llParticleSystem(sys);
}

default
{
    state_entry()
    {
        updateParticles();
    }

    touch_start(integer num)
    {
        llWhisper(0,"...Generating List...");
        for (i=1;i<42;i+=2)
        {
            type = llGetListEntryType(sys,i);
            if(type == TYPE_FLOAT)
            {
                tempString = float2String(llList2Float(sys,i));
                sys = llDeleteSubList(sys,i,i);
                sys = llListInsertList(sys,[tempString],i);
            }
            else if (type == TYPE_VECTOR)
            {
                tempVector = llList2Vector(sys,i);
                tempString = "<" + float2String(tempVector.x) + ","
                    + float2String(tempVector.y) + ","
                    + float2String(tempVector.z) + ">";
                sys = llDeleteSubList(sys,i,i);
                sys = llListInsertList(sys,[tempString],i);
            }
            else if (type == TYPE_ROTATION)
            {
                tempRot = llList2Rot(sys,i);
                tempString = "<" + float2String(tempRot.x) + ","
                    + float2String(tempRot.y) + ","
                    + float2String(tempRot.z) + ","
                    + float2String(tempRot.s) + ">";
                sys = llDeleteSubList(sys,i,i);
                sys = llListInsertList(sys,[tempString],i);
            }
            else if (type == TYPE_STRING || type == TYPE_KEY)
            {
                tempString = "\"" + llList2String(sys,i) + "\"";
                sys = llDeleteSubList(sys,i,i);
                sys = llListInsertList(sys,[tempString],i);
            }
        }
        sys = llListSort(sys,2,TRUE);
        if (target == "") sys = llDeleteSubList(sys,38,39);
        else if (target == llGetKey() )
            sys = llListInsertList(llDeleteSubList(sys,39,39),["llGetKey()"],39);
        else if (target == llGetOwner() )
            sys = llListInsertList(llDeleteSubList(sys,39,39),["llGetOwner()"],39);
        if (texture == "") sys = llDeleteSubList(sys,24,25);
        if (!interpSize) sys = llDeleteSubList(sys,12,13);
        if (!interpColor) sys = llDeleteSubList(sys,6,7);

        llWhisper(0,"[" + llList2CSV(llList2List(sys,0,21)) + ",");
        llWhisper(0,llList2CSV(llList2List(sys,22,-1)) + "]");
    }
}

Examples / llParticleSystem


ExampleParticleScript1

// Particle Script 0.5
// Created by Ama Omega
// 3-26-2004
// tweaked by Eep Quirk, 3/18/05,6/19/06

integer on;

particles()
{
// mask flags - set to TRUE (or 1) to enable
integer bounce = 0;    // Make particles bounce on Z plane of object
integer glow = 1;        // Make the particles glow
integer interpColor = 1;    // Go from start to end color
integer interpSize = 1;    // Go from start to end size
integer followSource = 1;    // Particles follow the source
integer followVel = 1;    // Particles turn to velocity direction
integer wind = 1;        // Particles affected by wind

//pattern:
//integer pattern = PSYS_SRC_PATTERN_ANGLE;
//integer pattern = PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY;
//integer pattern = PSYS_SRC_PATTERN_ANGLE_CONE;
//integer pattern = PSYS_SRC_PATTERN_DROP;
integer pattern = PSYS_SRC_PATTERN_EXPLODE;

// Select a target for particles to go towards
// "" for no target, "owner" will follow object owner
//    and "self" will target this object
//    or put the key of an object for particles to go to
//key target = "";
key target = "self";
//key target = "owner";

// particle parameters
float age = 1;                  // Life of each particle

float maxSpeed = 1;            // Max speed each particle is spit out at
float minSpeed = 1;            // Min speed each particle is spit out at

string texture = "";           // Texture used for particles, default used if blank
float startAlpha = 1;           // Start alpha (transparency) value
float endAlpha = .1;           // End alpha (transparency) value (if interpColor = TRUE)

vector startColor = <0,1,0>;    // Start color of particles <R,G,B>
vector endColor = <1,0,0>;      // End color of particles <R,G,B> (if interpColor = TRUE)

vector startSize = <1,1,0>;     // Start size of particles <x,y>
vector endSize = <1,1,0>;       // End size of particles (if interpSize == TRUE)

vector push = <0,0,0>;          // Force pushed on particles

// system parameters
float life = 0;             // Life in seconds for the system to make particles
integer count = 1;        // How many particles to emit per BURST
float rate = 1.5;            // How fast (rate) to emit particles
float radius = 1;          // Radius to emit particles for BURST pattern
float outerAngle = 1.54;    // Outer angle for all ANGLE patterns
float innerAngle = 1.55;    // Inner angle for all ANGLE patterns
vector omega = <0,0,0>;    // Rotation of ANGLE patterns around the source

integer flags = 0;

    if (target == "owner") target = llGetOwner();
    if (target == "self") target = llGetKey();
    if (glow) flags = flags | PSYS_PART_EMISSIVE_MASK;
    if (bounce) flags = flags | PSYS_PART_BOUNCE_MASK;
    if (interpColor) flags = flags | PSYS_PART_INTERP_COLOR_MASK;
    if (interpSize) flags = flags | PSYS_PART_INTERP_SCALE_MASK;
    if (wind) flags = flags | PSYS_PART_WIND_MASK;
    if (followSource) flags = flags | PSYS_PART_FOLLOW_SRC_MASK;
    if (followVel) flags = flags | PSYS_PART_FOLLOW_VELOCITY_MASK;
    if (target != "") flags = flags | PSYS_PART_TARGET_POS_MASK;
    llParticleSystem([  PSYS_PART_MAX_AGE,age,
                        PSYS_PART_FLAGS,flags,
                        PSYS_PART_START_COLOR, startColor,
                        PSYS_PART_END_COLOR, endColor,
                        PSYS_PART_START_SCALE,startSize,
                        PSYS_PART_END_SCALE,endSize,
                        PSYS_SRC_PATTERN, pattern,
                        PSYS_SRC_BURST_RATE,rate,
                        PSYS_SRC_ACCEL, push,
                        PSYS_SRC_BURST_PART_COUNT,count,
                        PSYS_SRC_BURST_RADIUS,radius,
                        PSYS_SRC_BURST_SPEED_MIN,minSpeed,
                        PSYS_SRC_BURST_SPEED_MAX,maxSpeed,
                        PSYS_SRC_TARGET_KEY,target,
                        PSYS_SRC_ANGLE_BEGIN,innerAngle,
                        PSYS_SRC_ANGLE_END,outerAngle,
                        PSYS_SRC_OMEGA, omega,
                        PSYS_SRC_MAX_AGE, life,
                        PSYS_SRC_TEXTURE, texture,
                        PSYS_PART_START_ALPHA, startAlpha,
                        PSYS_PART_END_ALPHA, endAlpha
                            ]);
}

default
{
    state_entry()
    {
        on = 1;
        particles();
    }

    touch_start(integer touches)
    {
        if (on == 1)
        {
            on = 0;
            llParticleSystem([]);
        }
        else
        {
            on = 1;
            particles();
        }
    }
}

Put a timer that calls particles() and add some math functions (like llFrand) in particles() for some interesting results:

integer on = 1;
float time = 1;

default
{
    state_entry()
    {
        particles();
        llSetTimerEvent(time);
    }

    timer()
    {
        particles();
    }

    touch_start(integer touches)
    {
        if (on == 1)
        {
            on = 0;
            llSetTimerEvent(0);
            llParticleSystem([]);
        }
        else
        {
            on = 1;
            particles();
            llSetTimerEvent(time);
        }
    }
}

Examples | Particles


FunctionUnescape

Takes a string and unescapes "\" style escape codes
Code | Replace
\\ | \
\" | "
\s | space
\n | new line
\t | tab (4 spaces)
\uxxxx | xxxx is a hex string, of the unicode character 0000xxxx
\Uxxxxxxxx | xxxxxxxx is a hex string, of the unicode character xxxxxxxx
\hXxxx... | X is a hex character, it's value is the number of pairs of hex characters that follow it, is a raw byte of a utf-8 string, be sure they are valid characters
Two versions of the function, one works entirely by string functions, the other by list & string.
The one that uses lists is slower.

string byte2hex(integer x)
{//Helper function for use with unicode characters.
    string hexc="0123456789ABCDEF";
    return llGetSubString(hexc, x = ((x >> 4) & 0xF), x) + llGetSubString(hexc, x & 0xF, x & 0xF);
}

string Unescape(string a)
{
//Please visit the website for instructions about this function (and view it's talk page for the authors notes)
//http://lslwiki.com/lslwiki/wakka.php?wakka=FunctionUnescape
    string  b = a;
    integer c = -1;
    integer d;
    integer e;
    integer f = 0;
    string g;
    while(d = -~llSubStringIndex(b, "\\"))
    {
        c += d;
        if(2 < e = llSubStringIndex("uUhts\"\\n",llGetSubString(b,d,d)))
            a = llInsertString(llDeleteSubString(a,c,-~c), c, llGetSubString("     \"\\\n",e * (e !=3), e));
        else if(e==2)//rx[11,22,33,44,55,66,77,88,99,AA,BB,CC,DD,EE,FF]
        {
            g = "";
            e = (integer)("0x"+llGetSubString(b,-~d,-~d)) << 1;
            if(d - ~e >= (f = llStringLength(b)))
            {
                e = (f - d) & -2;
                b += "0";
            }
            if((f = e))//this may look like a mistake, it's not
            {
                do
                    g = "%"+llGetSubString(b,d + e,d - ~e) + g;
                while((e-=2) > 0);
            }
            a = llInsertString(llDeleteSubString(a,c, c + 2 + f),c, g = llUnescapeURL(g));
            c += ~-llStringLength(g);//add to c so we don't accidentily unescape result
        }
        else if(~e)// \uXXXX or  \UXXXXXXXX
        {
            a = llDeleteSubString(a, c, c + 5 + (e = e << 4));
            if(0 < e = (integer)("0x"+llGetSubString(b,-~d, d +4 + e)))
            {
                if (e >= 0x4000000)// || i < 0)//if only unicode supported negives...
                    f = 5;
                else if (e >= 0x200000)
                    f = 4;
                else if (e >= 0x10000)
                    f = 3;
                else if (e >= 0x800)
                    f = 2;
                else
                    f = (e >= 0x80);
                g = "%" + byte2hex((e >> (6 * f)) | ((0x7F80 >> f) << !f));
                while(f)
                    g += "%" + byte2hex((((e >> (6 * (f=~-f))) | 0x80) & 0xBF));
                a = llInsertString(a, c, llUnescapeURL(g));
            }
        }
        b = llDeleteSubString(a,0,c);
    }
    return a;
}

string UnescapeTight(string a)
{//Requires less memory then the other two versions
//Please visit the website for instructions about this function (and view it's talk page for the authors notes)
http://lslwiki.com/lslwiki/wakka.php?wakka=FunctionUnescape
    string  b = a;
    integer c = -1;
    integer f = 0;
    @loop;
    integer d = -~llSubStringIndex(b, "\\");
    if(d)
    {
        integer e = llSubStringIndex("uUhts\"\\n", llGetSubString(a, -~(c += d), -~c));
        if(2 < e)
            a = llInsertString(llDeleteSubString(a,c, -~c), c, llGetSubString("     \"\\\n", e * (e != 3), e));
        else if(e == 2)
        {
            if(c+(e = (integer)("0x"+llGetSubString(a,c+2,c+2)) << 1)+2 >= (f = llStringLength(b)))
                e = (f + ~d) & -2;
            if((f = e))//this may look like a mistake, it's not
            {
                b = "";
                do
                    b = "%"+llGetSubString(a,c - ~e,c + e + 2) + b;
                while((e-=2) > 0);
            }
            a = llInsertString(llDeleteSubString(a,c, c + f + 2),c, b = llUnescapeURL(b));
            c = c + ~-llStringLength(b);//add to c so we don't accidentily unescape result
        }
        else if(~e)// \uXXXX or  \UXXXXXXXX
        {
            a = llDeleteSubString(a, c, c - ~(e = 4 << e));
            if(0 < e = (integer)("0x" + llGetSubString(b, -~d, d + e)))
            {
                b = "%" + byte2hex((e >> (6 * f)) | ((0x7F80 >> f) <<
                    !(f = ((e >= 0x80) + (e >= 0x800) + (e >= 0x10000) + (e >= 0x200000) + (e >= 0x4000000)))));
                while(f)
                    b += "%" + byte2hex((((e >> (6 * (f=~-f))) | 0x80) & 0xBF));
                a = llInsertString(a, c, llUnescapeURL(b));
            }
        }
        b = llDeleteSubString(a,0,c);
        jump loop;
    }
    return a;
}

string UnescapeSlow(string a)
{
//Please visit the website for instructions about this function (and view it's talk page for the authors notes)
//http://rpgstats.com/wiki/index.php?title=FunctionUnescape
//                        0    1   2    3   4   5   6   7
    list b = llCSV2List("\\\\,\\n,\\\",\\t,\\s,\\h,\\u,\\U");//"//wiki fix; slightly less memory then a staticly allocated list
    list c = llParseString2List(a,[a = ""],b);//totaly wrong way to wipe "a" but it cleans up the stack nicely
    integer d = ([] != c);
    string f;
    integer g;
    integer e;
    while(d & 0x80000000)
    {
        if(~g = llListFindList(b, llList2List(c,d,d)))
        {
            if(g < 5)
                f += llGetSubString("\\\n\"\t",g,g - ((g == 3) << 2));
            else
            {
                e = ((g > 5) << (g == 7)) << 2;
                string h = llList2String(c, -~d);
                integer i = llStringLength(h);
                @funk;
                if(d < -2 && e > i)//no point if there is only 1 entry left.
                {//misformated string handler
                    i = llStringLength(h = llDeleteSubString(a = (string)llDeleteSubList(c, 0, d), e, -1));
                    d = -2 - ((c = llParseString2List(llGetSubString(a, e, -1),[a = ""],b)) != []);
                }
                if(g == 5)
                {
                    if((g = (integer)("0x"+llGetSubString(h,0,0))))
                    {//no point if it is zero
                        e = -~(g << 1);
                        g = 8;
                        jump funk;//we adjust e to reflect the dynamic nature of the value.
                        //then we can parse it staticly, well almost.
                    }
                    else if(~llListFindList(b, [h]))//catch for "\\\\"
                    {//oh you are evil, trying to break the script, we won't have any of that
                        d = ~((c = llParseString2List(llDeleteSubString((string)llDeleteSubList(c, 0, d), 0, 0),[],b)) != []);
                    }
                    else//remove the required character and clean-up the list.
                    {//Since we are using negitive indexs, we don't need to recalc them
                        c = llDeleteSubString(h,0,0) + llDeleteSubList(c, 0, -~d);
                    }
                }
                else
                {
                    if(g == 8)
                    {//adds extra characters.
                        if(i & 0xFFFffFFE)
                        {
                            a = llGetSubString(h, 1, i = (e & -2));
                            do
                                a = llInsertString(a,i-=2,"%");
                            while(i);
                        }
                    }
                    else //if(g == 6 || g == 7)
                    {
                        a = "";
                        if(0 < i = (integer)("0x"+llDeleteSubString(h, e, 0x7FFFFFFE)))
                        {
                            if (i >= 0x4000000)// || i < 0)//if only unicode supported negives...
                                g = 5;
                            else if (i >= 0x200000)
                                g = 4;
                            else if (i >= 0x10000)
                                g = 3;
                            else if (i >= 0x800)
                                g = 2;
                            else
                                g = (i >= 0x80);
                            a = "%" + byte2hex((i >> (6 * g)) | ((0x7F80 >> g) << !g));
                            while(g)
                                a += "%" + byte2hex((((i >> (6 * (g=~-g))) | 0x80) & 0xBF));
                        }
                    }
                    f += llUnescapeURL(a) + llDeleteSubString(h,0,~-e);
                }
            }
        }
        else
            f += llList2String(c,d);
        d += 2 >> !e;
    }
    return f;
}

Testbed:
default
{
    state_entry()
    {
        llOwnerSay((string)llGetFreeMemory());
        string a = "-\\h3414243\\h3444546\\\"\\uFFFD";
        integer c = 50;
        integer b = c;
        float e;
        llResetTime();
        do
            Unescape(a);
        ;while(--b);
        e = llGetTime();
        llOwnerSay(llList2CSV([Unescape(a),e,e/c]));
        llSleep(1);
        b = c;
        llResetTime();
        do
            UnescapeTight(a)
        ;while(--b);
        e = llGetTime();
        llOwnerSay(llList2CSV([UnescapeTight(a),e,e/c]));
        llSleep(1);
        b = c;
        llResetTime();
        do
            UnescapeSlow(a)
        ;while(--b);
        e = llGetTime();
        llOwnerSay(llList2CSV([UnescapeSlow(a),e,e/c]));
    }
}


Hello Avatar

This is the default script you see when you create a new script.

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

Hello Avatar is a variant of Hello World, a common first program or script in a language. They are often used as a compiler test, to make sure that the compiler itself actually works.


IGS

Making A IGS (In Game Server) Written By ViceroLambert

Scripted LSL (Linden Second Language) Servers in SL (Second Life) run about the same way as a Real Life on would work. The "llEmail Tags" would work as a port going from one area to the next working over a wide area like from a Workstation to a DNS (Domain Name Server). But instead of ports and wires llEmail sends directly to a prims key or a outside email address.

Here is a example of a LSL server that when it gets a command from a out of sim device it sends back updated information. Just like a FTP (File Transfer Protocal) Server.

Device - Sends A Information Request For Texture When It Hears startup on Channel 0.
default
{
    state_entry() //Starts State
    {
        llListen( 0, "", NULL_KEY, "startup" ); //Starts to listion for startup
    }

    listen( integer channel, string name, key id, string message ) //chat collection
    {
        if ( id == llGetOwner() ) //Only allow owner to say this command.
        {
            llEmail("03ada148-cd2f-f9ed-60c4-e79251022fd2@lsl.secondlife.com", "request_startup_1", ""); //Send Message too prim key 03ada148-cd2f-f9ed-60c4-e79251022fd2.
        }
        else //If the chater is not the owner do somthing below this line.
        {

        }
    }
}

Server - Gets the request and resends the texture data to who ever sent the email.
default {
    state_entry() {

        llSetTimerEvent(0.1); //Starts Timer Event
    }

    timer() {
        llGetNextEmail("", "request_startup_1"); //Waits for email.
    }

    email(string time, string address, string subj, string message, integer num_left) { //Email Data

     llEmail(address+"@lsl.secondlife.com","6159fd53-d0ee-1a7b-c53c-d0df8989982a", ""); // When email is recived send main data back to address that was stringed on email data.

    }
}

Device - Gets the information and sets the texture UUID from the server to the prim.

default {
    state_entry() {

        llSetTimerEvent(0.1);
    }

    timer() {
        llGetNextEmail("", ""); //Gets any emails coming to this object,
    }

    email(string time, string address, string subj, string message, integer num_left) { //email data

    llSetTexture(subj,ALL_SIDES); //Gets the subject of the email which holds the texture and sets it to all sides of the prim.

    }
}

I hope this helps you. If you have any questions feel free to contact me during my hours of 6-7pm Game Time.


LibraryAirSeaAltimeter

I wrote this for some friends who were new to SL, as a tutorial...then gave it away as a gift to some, since I feel it is more substantial than rating someone. I realized that it would be most substantial to share it with everyone.

default
{
    state_entry()
    {
        llSetTimerEvent(.01); // How often your timer() event updates
    }

    timer()
    {
        vector pos = llGetPos(); // Gets your current position
        vector size = llGetAgentSize(llGetOwner()); // Get the dimensions of your agent
        size.z = size.z / 2.0; // Halve the height element of your agent, get altitude from feet
        float aboveground = ((float)pos.z - llGround(<0.0,0.0,0.0>) - size.z); // Distance above ground
        if (aboveground < 0.0) // If it thinks your feet are below ground
        {
            aboveground = llSqrt(aboveground * aboveground); // - flip it
        }
        if (aboveground < 0.09) // Check the margin of error for zeroing - you can redefine this
        {
            aboveground = 0.0; // Zero it out
        }
        vector Speed = llGetVel(); // LSL function that should be fixed in next release.
        float RealSpeed = llVecMag(Speed); // Convert it to velocity you can use.
        float abovewater = llWater(<0.0,0.0,0.0>) - pos.z; // difference between yourwater height
        if (pos.z >= llWater(<0.0,0.0,0.0>)) // If at or above water
        {
            abovewater = llSqrt(abovewater * abovewater);
        }
        if (pos.z < llWater(<0.0,0.0,0.0>)) // If underwater
        {
            abovewater = abovewater - (abovewater * 2.0); // Push the number into the negative range
        }
        llSetText("Sea Lvl ALT: " + (string)abovewater + "\n" + "Grnd Lvl ALT: " + (string)(aboveground) + "\nSpeed: " + (string)RealSpeed, <1.0,0.0,0.0>, 1.0); // Emit string
    }
}

I will link all future contributions to the LSL Wiki Mirror 7-7-7 from the OoPsGalatea page.


LibraryAltPickPocket

I wrote this script initially for the purpose of keeping an ATM type object that would allow me to take the $L from my Alt's accounts at will, without the need to log out and log the Alt in and make a manual payment.

I have also used this script to tease unsuspecting friends, as they watch in amazement how their $L are cleaned out and payed to me.
Of course, I always gave them their $L back afterwards, but there are other uses for this script too... if used ethically.

It should be noted that this script can be used for unethical purposes, which re-enforces the fact that it needs to be made public.
I think that everyone should be made aware of the fact that if you are not paying attention to what you are doing in SL, you might be paying a higher price for this lesson.

//-----------------------------------------------------------------------------------
// AltPickPocket Script C 2005-2007
// Formerly Known as PureEvil
// By Mitzpatrick Fitzsimmons
//-----------------------------------------------------------------------------------

//----------------------------DISCLAIMER!--------------------------------------------
// This script will take money from the person that owns the object it is in
// ONLY IF that owner accepts the PERMISSION_DEBIT.

// The UUID of the "thief" variable is to whom the money is paid to.
// I accept no responsability for the USE or MISUSE of this script.
// As with anything in SL, make sure you know what you are doing before you do it.
//-----------------------------------------------------------------------------------

key thief = "605dc2e5-3bdf-427e-a38d-98390c124249"; // Insert the key of the person who is going to get the money here.
list ammount = [32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]; // Use a list to enumerate payments until the account is cleaned out.

// The pay function here will use the integers in the ammount list to transact payments to the thief UUID in incremental
// amounts until the list ends. In most cases this will deplete the account of the owner (unless there is more money in that
// owners account than is in the largest list amount).
pay () {
integer m = llGetListLength(ammount);
integer i = 0;
    while (i<m) {
        llGiveMoney(thief,llList2Integer(ammount,i));
        i++;
    }
}

default
{
     on_rez(integer number)
    {
        llResetScript(); // Make sure Ownership Changes properly.
    }

    state_entry()
    {
        llSetObjectName("AltPickPocket"); // Set the Object Name
        llRequestPermissions(llGetOwner(),PERMISSION_DEBIT);// Request Owners permission to debit money.
    }

    touch_start(integer total_number)
    {
        if(llDetectedKey(0) == thief) // if the thief is touching the object
        {
            llSay(0, "You are the Authorized."); // Tell them they are Authorized
            pay(); // Then Pay them
        }else{ // If it is not the thief touching the object
            llSay(0, "You are NOT Authorized!"); // then deny them
        }
    }

    run_time_permissions(integer number)
    {
        if(number >0)
        {
            llOwnerSay("Activated!");
            llSetColor(<0,1,0>, ALL_SIDES);
            llSetText("Active",<0,1,0>,1);
        }else{
            llSay(0, "PickPocket has no permissions");
            llSetColor(<1,0,0>, ALL_SIDES);
            llSetText("InActive",<1,0,0>,1);
        }

    }
}


LibraryASCII

string ASCII = "             \n                   !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
integer ord(string chr)
{
    if(llStringLength(chr) != 1) return -1;
    if(chr == " ") return 32;
    return llSubStringIndex(ASCII, chr);
}
string chr(integer i)
{
    i %= 127;
    return llGetSubString(ASCII, i, i);
}


LibraryAvatarInSim

The function below determines if the avatar of the key passed to it is in the same sim as the script calling it.

float InSim(key id)
{
     return (llGetObjectMass(id));
}

Example Function Usage:
float InSim(key id)
{
     return (llGetObjectMass(id));
}

default
{
     touch_start(integer t)
     {
          if (InSim(llDetectedKey(0)))
          {
               // AV In Same Sim
          }
          else
          {
               // AV Not In Same Sim
          }
     }
}

Example Non-Function Usage:
default
{
     touch_start(integer t)
     {
          if (llGetObjectMass(llDetectedKey(0)))
          {
               // AV In Same Sim
          }
          else
          {
               // AV Not In Same Sim
          }
     }
}


LibraryBarometer

Barometer

This script will calculate air pressure at the objects vector plus offset.

// llAirPressure(vector v) Script
// Created by: Cid Jacobs
// Original by: Cid Jacobs
// Last Updated: 11-17-05
// Notes: Speak with Cid Jacobs about any concerns.
//
//
//--------------------Globals
//Global Memory Storage For Calibration Offset
float Calibration = 101.32500;
//Global Memory Storage For User Definable Vector Offset
vector Offset = <0,0,10>;
//
//
//--------------------User Defined Function
float llAirPressure(vector Offset)
{
    //Extrapolate Task's Local Coordinates
    vector Position = llGetPos();
    //Calculate And Calibrate Air Pressure
    float Base_Reading = llLog10(5- (((Position.z - llWater(ZERO_VECTOR)) + Offset.z)/15500));
    //Total Sum Air Pressure
    float KiloPascal = (Calibration + Base_Reading);
    return KiloPascal;
}
//
//
//--------------------States
//Mandatory Default State
default
{
    //Triggered By The Start Of Agent Clicking
    touch_start(integer num_detected)
    {
        //Say Current Total Air Pressure at Task + V
        llSay(0,"Current air pressure is: "
        +
        (string)llAirPressure(Offset)
        +
        " KiloPascal.");
    }
}


LibraryBezierCurveDemo

Bezier Curve Demo version 1.0.1

Author: LionelForager
This set of scripts will draw a bezier curve segment controlled by 3 control points.

It tries to be the foundation of a more general graph server in SL.

The idea and first version of the bezier curve demo script, was given to me by CatherineOmega. She had a working script that rezzed small spheres in each point position.
Then i added some modifications and the possibility of drawing segments made up of cylinders to rezz a line instead of just points.

The complete bezier server consists of several parts:
  • Server:An object that controls and creates the graph. We call this
  • Control Points p1,p2,p3: control points that controls the begining, direction and end of the bezier segments.
  • Point Mark: the object (may be a sphere or other) contained in the server that is rezzed in each created point mark position.
  • Segment: the object (a cylinder) contained in the server that is rezzed when a line segments are required instead of point marks.

A description of each part will be given.

Server object

Description

The server object is the one which calculates the point coordinates interpolated using a bezier curve.
It rezzes the segments or point marks.
It shows the positions of the three control points in its display text.
Commands to it are given in the channel 1.
It receives the positions in that channel from the control points, and when it has all three, draws the curve.
When a new position is received from a control point, it clears the previously rezzed objects and draws new ones.
The server knows which control point has moved by the name of the object that has said the command it receives.

Commands - How to use

Once you have rezzed the bezier server, you can say commands to it through channel #1 to control its behaviour.
Commands are:
  • "/1 markers on": activate the drawing of markers. This is the default option.
  • "/1 markers off": deactivate them.
  • "/1 lines on": activate the drawing of a line made of segments.
  • "/1 lines off": deactivate the drawing of segments. This is the default option.
  • "/1 segments n": set the number of segments that should be drawn to n.
  • "/1 start": this command forces the drawing of the curve.
  • "/1 mu float": draws a point corresponding to the given value of mu parameter.

How to create de server

To create the bezier demo server object, create a prim that you like and put the following script in its inventory.
Then you should create the segment and point mark objects (see descriptions below) and put them in the inventory of the server object too (see bellow).

// Bezier Curve Demo 1.0.1
// Catherine Omega Heavy Industries
// Modified by Lionel Forager 29-12-2006: Added segment drawing between points. Corrected minor bugs.

key gOwner; // object owner

float diam=0.05; //diameter of line segments.

list gPoints; // list of vectors to feed to bezier3
string gObject="Point Mark"; // name of object to rez as the mark in each point
string gSegment="Segment"; // name of object to rez as the mark in each point

integer gNumSegments = 10; // number of segments to rez
integer gMarks= TRUE; //Draw marks in the point positions.
integer gLines= FALSE; //Don't draw segments between points.

float gMu;
vector gPos1;
vector gPos2;
vector gPos3;

//codes a line number and a segment in a unique ID integer
integer codeID(integer line, integer segment)
{
    //Code de id: line# in 16 most significant bits, segment# in 16 less significant bits
    return ( (line << 16) + segment);
}
//Rezz a segment between 2 given points and assigning it to a line # and a segment #
drawLineSegment(vector p1,vector p2,integer line,integer seg)
{
    //Calc the position of the center of the segment
    vector center= (p1+p2)/2.;
    vector localZ= p2 - p1;
    //Get distance between points
    float distance= llVecMag(localZ);
    //Normalize the vector
    localZ= localZ / distance;

    vector xAxis;
    //Let's choose as x local axis the direction of a vector normal to localZ and
    //contained in the plain given by localZ and global X or global Y, the one that is less parallel to localZ
    if ( localZ.x < localZ.y ) xAxis= <1.,0.,0.>;
    else xAxis= <0.,1.,0.>;
    //localX is the one contain in the plane given by localZ and xAxis that is normal to localZ.
    //that's it localX= xAxis - localZ*xaxis*localZ. We should normalize the vector to get a unitary one.
    vector localX= xAxis - (localZ * xAxis) * localZ;
    localX= llVecNorm(localX);
    //Now get the rotation to put axis Z oriented pointing to the direction between the two given points.
    rotation rot= llAxes2Rot(localX,localZ % localX, localZ);
    //Crear el objeto en la posici?n del server.
    llRezObject(gSegment,center,ZERO_VECTOR,rot,codeID(line,seg));
    llSay(line,"segInit "+(string)seg+","+(string)diam+"," + (string)distance);

}
//Three control point Bezier interpolation
//mu ranges from 0 to 1, start to end of the curve
vector bezier3(vector p1, vector p2, vector p3, float mu)
{
   float mum1;
   float mum12;

   float mu2;
   vector p;

   mu2 = mu * mu;
   mum1 = 1 - mu;
   mum12 = mum1 * mum1;

   // p= (1-mu)^2 p1 + 2*mu*(1-mu)*p2 + mu^2 * p3
   p.x = p1.x * mum12 + 2. * p2.x * mum1 * mu + p3.x * mu2;
   p.y = p1.y * mum12 + 2. * p2.y * mum1 * mu + p3.y * mu2;
   p.z = p1.z * mum12 + 2. * p2.z * mum1 * mu + p3.z * mu2;

   return(p);
}

start(integer segments)
{
     if (gPos1 != ZERO_VECTOR && gPos2 != ZERO_VECTOR && gPos3 != ZERO_VECTOR)
    {
        gNumSegments = segments;
        float increment = 1.0 / gNumSegments;

        float current_mu;

        integer i;
        vector oldPos= gPos1;
        vector pos;

        //Render the mark at the position of the first point
        if( gMarks ) llRezObject(gObject,gPos1,ZERO_VECTOR,ZERO_ROTATION,0);

        for (i = 1; i <= gNumSegments; i++)
        {
            current_mu= i * increment;
            pos = bezier3(gPos1,gPos2,gPos3,current_mu);
            if(gMarks) llRezObject(gObject,pos,ZERO_VECTOR,ZERO_ROTATION,0);
            if( gLines ) drawLineSegment(oldPos,pos,1,i);
            oldPos= pos;
        }
    }
}

updateText()
{
    vector color = <1,0,0>;
    if (gPos1 != ZERO_VECTOR && gPos2 != ZERO_VECTOR && gPos3 != ZERO_VECTOR)
    {
        color = <0,1,0>;
    }

    string output = (string)gPos1 + "\n" + (string)gPos2  + "\n" + (string)gPos3;
    llSetText(output, color, 1.0);
}

// get gCommand and gSubCommand (now improved to handle null gSubCommands.)
string gCommand;
string gSubCommand;

string gCommandLower;
string gSubCommandLower;

parseCommands(string input, string divider)
{
    integer command_divide = llSubStringIndex(input,divider);
    if (command_divide == -1)
    {
        gCommand = llGetSubString(input, 0, command_divide);
        gSubCommand = "";
    }
    else
    {
        gCommand = llGetSubString(input, 0, command_divide - 1);
        gSubCommand = llGetSubString(input, command_divide + 1, llStringLength(input));
    }

    gCommandLower = llToLower(gCommand);
    gSubCommandLower = llToLower(gSubCommand);
}

default
{
    state_entry()
    {
        gOwner = llGetOwner();
        //gObject = llGetInventoryName(INVENTORY_OBJECT,0);

        updateText();

        llListen(1,"","","");
        llSay(0,"Online.");
    }

    listen(integer channel, string name, key id, string m)
    {
        if (llGetOwnerKey(id) == gOwner)
        {
            if (id == gOwner)
            {
                parseCommands(m," ");

                if (gCommandLower == "mu")
                {
                    vector pos = bezier3(gPos1,gPos2,gPos3,(float)gSubCommand);
                    llRezObject(gObject,pos,ZERO_VECTOR,ZERO_ROTATION,0);
                }

                else if (gCommandLower == "start")
                {
                    start((integer)gSubCommand);

                    llSay(0,"Done.");
                }

                else if (gCommandLower == "segments")
                {
                    gNumSegments = (integer)gSubCommand;
                }
                else if (gCommandLower == "markers")
                {
                    if( gSubCommand != "off" ) {
                        gMarks= TRUE;
                        llSay(0,"Markers on");
                    }
                    else {
                        gMarks=FALSE;
                        llSay(0,"Markers off");
                    }

                }
                else if (gCommandLower == "lines")
                {
                    if( gSubCommand != "off" ) {
                        gLines= TRUE;
                        llSay(0,"Lines on");
                    }
                    else {
                        gLines=FALSE;
                        llSay(0,"Lines on");
                    }
                }
            }

            else
            {
                // make sure it's actually a vector
                if ((vector)m != ZERO_VECTOR)
                {
                    if (llToLower(name) == "p1")
                    {
                        gPos1 = (vector)m;
                        llSay(1,"clear");
                        updateText();
                        start(gNumSegments);
                    }

                    else if (llToLower(name) == "p2")
                    {
                        gPos2 = (vector)m;
                        llSay(1,"clear");
                        updateText();
                        start(gNumSegments);
                    }

                    else if (llToLower(name) == "p3")
                    {
                        gPos3 = (vector)m;
                        llSay(1,"clear");
                        updateText();
                        start(gNumSegments);
                    }
                }
            }
        }
    }

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

Control Points

Description

The Control Point objects are the prims that you can position in place to control the shape of the bezier curve.
A Bezier curve has three control points (p1,p2, and p3).
The curve passes through p1 and p3 and p2 controls the curvature and tangent direction of the curve in p1 and p3.
When a control point is moved or you touch it, it says its position through channel #1 to the bezier server.

To use them, just rezz one object of each type (p1,p2 and p3) and move it to the position you want. The curve should be drawn if you have rezzed the bezier server first.

Commands - How to use

The control points don't obey any command. They just have a simple script to say their position to the bezier server trough channel #1

How to Create the Control Points

Create three prims (may be a simple sphere prim) with different colors and name them p1, p2 and p3 (be sure to name it that way: the server uses the name to differentiate which point has been moved or touched).

Then put the following script in each control point.

reportPos()
{
    vector pos = llGetPos();
    llSay(1, (string)pos);
}

default
{
    touch_start(integer total_number)
    {
        if (llDetectedKey(0) == llGetOwner())
        {
            reportPos();
        }
    }

    moving_end()
    {
        reportPos();
    }
}

Point Mark

Description

The Point Marks are the prims drawn in each interpolated point of the bezier curve when markers mode is active.
You can use any prim, as a small sphere for example.
The Point Mark object should be in the inventory of the bezier server object, as it is rezzed from the script of that object.
The Point Marks are very simple, as they don't need to communicate with other scripts.
For convenience, they just listen in channel # 1 for clear commands, to delete themselves.

Commands - How to use

The point marks listen in channel #1 for clear commands to delete themselves.
say "/1 clear": deletes all point marks.

How to Create the Point Mark object

Create the prim or object you want to be rezzed in each point position. Name it Point Mark. Then put the following script in it.
Take the object to your inventory and copy it from there to the inventory of the bezier server object.

default
{
    state_entry()
    {
        llListen(1,"","","");
    }

    listen(integer channel, string name, key id, string m)
    {
        if (llGetOwnerKey(id) == llGetOwner())
        {
            if (m == "clear")
            {
                llDie();
            }
        }
    }
}

Segment

Description

The Segments are the prims drawn to make the lines of the bezier curve when you set lines mode on.
The prim should be a cylindre.

The Segment object should be in the inventory of the bezier server object, as it is rezzed from the script of that object.
The bezier server object rezzed the cylindre, rotates it to orient it properly in the direction of the line and then gives it a command to initialize its size correctly (as the size of an object cannot be stablished when rezzed and the parameters of a prim can only be altered by itshelf).

Commands - How to use

Each segment object listens in the channel corresponding to its line number for clear commands to delete themselves.
say "/line# clear": deletes all point marks.
The segment object listen for seginit commands from the bezier server object in its line# channel addressed to its segment number.
The channel number and segment number are coded in an integer that is passed to the segment when rezzed.
In this example the line # is always 1.
The command is:
"/line# segment#,diam,size" where segment# is the number of the segment to be initilized, diam is the diameter fo the cylindre and size is its lenght.

How to Create the Segment Object

Create a cylindre of any size, set a blank texture and choose its color. Save it with name Segment.
Put the following script in it.
Then, take the object to your inventory and copy it from there to the inventory of the bezier server object.

//Author: Lionel Forager (c) 2007
//segmentServer 1.0.1
//This script is designed to work in conjunction with graphics sever 1.0
//It renders the segments in a line, resizing them, changing colors, etc.

//#### internal variables ###

//Line number that the segment belongs to.
//It is used as a line # id and it is the channel # to comunicate with the graphics server
integer lineNum;
integer segmentNum;
list parseCommands(string msg)
{
    msg= llToLower(msg);
    return llParseString2List(msg,[" ",","],[]);
}
default
{
    on_rez(integer param)
    {
        //get the line# and sement# coded in the param
        //line# is in 16 most significant bits
        lineNum= param>>16;
        //segment# is in 16 less significant bits. Clear 16 most significant bits.
        segmentNum= param^(lineNum<<16);

        if( (lineNum == 0)||(segmentNum==0) )
            llWhisper(0,"This object is not designed to be created directly, it works in collaboration with graphics server.");
        else llListen(lineNum,"",NULL_KEY,"");
    }

    listen(integer channel, string name, key id, string m)
    {
        //ignore commands not given by the owner.
        if (llGetOwnerKey(id) != llGetOwner()) return;
        list cmds= parseCommands(m);
        m= llList2String(cmds,0);
        if( m == "clear")
        {
            llDie();
        }
        else if (m=="seginit" ) {
            integer seg= llList2Integer(cmds,1);
            //if command is not for current segment, just ignore it.
            if( seg != segmentNum) return;
            float diam= llList2Float(cmds,2);
            float size= llList2Float(cmds,3);
            llSetScale(<diam,diam,size>);
        }
    }
}


LibraryBuildersBuddy

Builders' Buddy

This pair of scripts makes it easy to package a large, complex linked set of objects into a single package, and then build the entire complex at once. Basically, it works by choosing one object to be the "base", and then putting a copy of all the other objects into base's inventory after recording their relative position and rotation. The instructions expect intermediate building skills, and are in the scripts themselves, so they won't be repeated here.

The first script is also a decent example of using a menu, and of parsing chat input.

Primary Root Prim Script

///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.6 (Base Prim)
// by Newfie Pendragon, March 2006
//
// This script is distributed with permission that it may be used in
// any way, or may be further modified/included in resale items.
// HOWEVER, if this script is used as part of a for-sale product,
// it is required that appropriate credit be given to Newfie for
// the script (or portions used in derivatives).  That's a fair price
// in exchange for unlimited use of this script, dontcha think?
//
//  SL Forum thread and new versions found here:
//  http://forums.secondlife.com/showthread.php?t=96792
///////////////////////////////////////////////////////////////////////////////
//
//  Script Purpose & Use
//
//  Functions are dependent on the "component script"
//
//  QUICK USE:
//  - Drop this script in the Base.
//  - Drop the "Component" Script in each building part.
//  - Touch  your Base, and choose RECORD
//  - Take all building parts into inventory
//  - Drag building parts from inventory into Base Prim
//  - Touch your base and choose BUILD
//
//  OTHER COMMANDS from the Touch menu
//  - To reposition, move/rotate Base Prim choose POSITION
//  - To lock into position (removes scripts) choose DONE
//  - To delete building pieces: choose CLEAN
//
///////////////////////////////////////////////////////////////////////////////
//
//  History
//
// v1.0 - 20060328 - Newfie Pendragon
//      - Original Version
// v1.1 - 20060331 - Kalidor Lazarno
//      - Added a Dialog Engine to the base script
// v1.5 - 20060612 - Androclese Antonelli
//      - Added a random number generator to the dialog engine to elimintate
//        problems with multiple BB boxes cross-talking
//      - Added a timer to the listen command to put it asleep after 10sec.
//      - Added a Menu Description
//      - Added n "creator" flag so the owner could use the same object with full
//        menu options and only a single flag change
//      - Added an "ingroup" flag to enable/disable the same group use function
//      - Non-Admin usage cleans the inventory items as they spawn
// v1.6 - 20060624 - Newfie Pendragon
//      - Added active repositioning (building moves as the base piece moves)
//      - Added "Reset" Option to unlink parts from base temporarily
//      - Modified creator flag to automatically set based if owner is creator
//      - Minor changes to improve code readability (for those learning LSL)

///////////////////////////////////////////////////////////////////////////////
// User Variables
///////////////////////////////////////////////////////////////////////////////

// Set to TRUE to allow group members to use the dialog menu
// Set to FALSE to disallow group members from using the dialog menu
integer ingroup = TRUE;

//Name each option-these names will be your button names.
string optRecord = "Record";
string optReset = "Reset";
string optBuild = "Build";
string optPos = "Position";
string optClean = "Clean";
string optDone = "Done";

//Menu option descriptions
string descRecord = " : Record the position of all parts\n";
string descReset = " : Forgets the position of all parts\n";
string descBuild = " : Spawn inv. items and position them\n";
string descPos = " : Reposition the parts to a new location\n";
string descClean = " : De-Rez all pieces\n";
string descDone = " : Remove all BB scripts and make the parts permanent.\n";

//How often (in seconds) to check for change in position when moving
float fMovingRate = 0.25;

//How long to sit still before exiting active mode
float fStoppedTime = 30.0;

//Minimum amount of time (in seconds) between movement updates
float fShoutRate = 0.25;

// Channel used by Base Prim to talk to Component Prims
// This channel must be the same one in the component script
// A negative channel is used because it elimited accidental activations
// by an Avatar talking on obscure channels
integer PRIMCHAN = -19730611;

///////////////////////////////////////////////////////////////////////////////
// DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN
///////////////////////////////////////////////////////////////////////////////
integer MENU_CHANNEL;
integer MENU_HANDLE;
key agent;
key objectowner;
integer group;
string title = "";
list optionlist = [];
integer bMoving;
vector vLastPos;
rotation rLastRot;
integer iListenTimeout = 0;

//To avoid flooding the sim with a high rate of movements
//(and the resulting mass updates it will bring), we used
// a short throttle to limit ourselves
announce_moved()
{
    llShout(PRIMCHAN, "MOVE " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
    llResetTime();        //Reset our throttle
    vLastPos = llGetPos();
    rLastRot = llGetRot();
    return;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
default {
    ///////////////////////////////////////////////////////////////////////////////
    changed(integer change) {
        if(change & CHANGED_OWNER)
        llResetScript();
    }

    ///////////////////////////////////////////////////////////////////////////////
    state_entry () {
        //Use which menu?
        if ( llGetCreator() == llGetOwner() ) {
            //Display all options
            optionlist = [optPos, optClean, optDone, optRecord, optReset, optBuild];
            title = optRecord + descRecord;
            title += optReset + descReset;
            title += optBuild + descBuild;
            title += optPos + descPos;
            title += optClean + descClean;
            title += optDone + descDone;

        } else {
            //Display limited options
            optionlist = [optBuild, optPos, optDone];
            title = optBuild + descBuild;
            title += optPos + descPos;
            title += optDone + descDone;
        }

        //Record our position
        vLastPos = llGetPos();
        rLastRot = llGetRot();

    }

    ///////////////////////////////////////////////////////////////////////////////
    touch_start (integer total_number) {
        group = llDetectedGroup(0); // Is the Agent in the objowners group?
        agent = llDetectedKey(0); // Agent's key
        objectowner = llGetOwner(); // objowners key
        // is the Agent = the owner OR is the agent in the owners group
        if ( (objectowner == agent) || ( group && ingroup )  )  {
            iListenTimeout = llGetUnixTime() + 10;
            MENU_CHANNEL = llFloor(llFrand(-99999.0 - -100));
            MENU_HANDLE = llListen(MENU_CHANNEL,"","","");
            llDialog(agent, title, optionlist, MENU_CHANNEL);
            llSetTimerEvent(fShoutRate);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    listen(integer channel, string name, key id, string message) {
        if ( message == optRecord ) {
            llOwnerSay("Recording positions...");
            llShout(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
            return;
        }
        if( message == optReset ) {
            llOwnerSay("Forgetting positions...");
            llShout(PRIMCHAN, "RESET");
            return;
        }
        if ( message == optBuild ) {
            vector vThisPos = llGetPos();
            rotation rThisRot = llGetRot();
            integer i;
            integer iCount = llGetInventoryNumber(INVENTORY_OBJECT);

            //Loop through backwards (safety precaution in case of inventory change)
            llOwnerSay("Rezzing build pieces...");
            for( i = iCount - 1; i >= 0; i-- )
            {
                llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), vThisPos, ZERO_VECTOR, rThisRot, PRIMCHAN);
                if ( llGetCreator() != llGetOwner() )
                    llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, i));
            }

            llOwnerSay("Positioning");
            llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
            return;
        }
        if ( message == optPos ) {
            llOwnerSay("Positioning");
            vector vThisPos = llGetPos();
            rotation rThisRot = llGetRot();
            llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
            return;
        }
        if ( message == optClean ) {
            llShout(PRIMCHAN, "CLEAN");
            return;
        }
        if ( message == optDone ) {
            llShout(PRIMCHAN, "DONE");
            //llDie();
            llOwnerSay("Removing mover scripts.");
            return;
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    moving_start()
    {
        if( !bMoving )
        {
            bMoving = TRUE;
            llSetTimerEvent(0.0);    //Resets the timer if already running
            llSetTimerEvent(fMovingRate);
            announce_moved();
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    timer() {
        //Were we moving?
        if( bMoving )
        {
            //Did we change position/rotation?
            if( (llGetRot() != rLastRot) || (llGetPos() != vLastPos) )
            {
                if( llGetTime() > fShoutRate ) {
                    announce_moved();
                }
            }
        } else {
            //Have we been sitting long enough to consider ourselves stopped?
            if( llGetTime() > fStoppedTime )
                bMoving = FALSE;
        }

        //Open listener?
        if( iListenTimeout != 0 )
        {
            //Past our close timeout?
            if( iListenTimeout <= llGetUnixTime() )
            {
                iListenTimeout = 0;
                llListenRemove(MENU_HANDLE);
            }
        }

        //Stop the timer?
        if( (iListenTimeout == 0) && ( !bMoving ) )
        {
            llOwnerSay("Stopping Timer");
            llSetTimerEvent(0.0);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    on_rez(integer iStart)
    {
        //Reset ourselves
        llResetScript();
    }
}

Component Pieces Script

///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.7 (Component Pieces)
//
// by Newfie Pendragon, March 2006
//
// This script is distributed with permission that it may be used in
// any way, or may be further modified/included in resale items.
// HOWEVER, if this script is used as part of a for-sale product,
// it is required that appropriate credit be given to Newfie for
// the script (or portions used in derivatives).  That's a fair price
// in exchange for unlimited use of this script, dontcha think?
//
//  SL Forum thread and new versions found here:
//  http://forums.secondlife.com/showthread.php?t=96792
///////////////////////////////////////////////////////////////////////////////

// INSTRUCTIONS
//  This is the *Component Piece* half of the Builders' Buddy system.
//  Drop it into each 'piece' of the building.  Drop the Base Prim Script
//  into the prim  that will be the container/box that will be used to
//  store the building once completed.  It can be in each individual
//  prim, but if you link as much as possible (and put the script in the link
//  set), it'll be much more neighbourly and less strain on the sim.
//
// QUICK USE:
//  - Drop this script in the Base.
//  - Drop the "Component" Script in each building part.
//  - Touch  your Base, and choose RECORD
//  - Take all building parts into inventory
//  - Drag building parts from inventory into Base Prim
//  - Touch your base and choose BUILD
//
// OTHER COMMANDS
//  - To reposition, move/rotate Base Prim Type: /12345 MOVE
//  - To lock into position (removes scripts) Type: /12345 DONE
//  - To delete building pieces: /12345 CLEAN
///////////////////////////////////////////////////////////////////////////////
//  History
//
// v1.0 - 20060328 - Newfie Pendragon - Original Version
// v1.5 - 20060612 - Androclese Antonelli
//        - (See base script for details)
// v1.6 - 20060624 - Newfie Pendragon
//      - Added active repositioning (pieces move as the base piece moves)
//      - Pieces use WarpPos technique to instantanetly move large distances
//        - Pieces no longer move until the the "Record" option has been used
//        at least once
//      - Pieces will not move if base is not same owner as the pieces
//      - Pieces no longer 'bounce' when hitting the ground
// v1.7 - 20060821 - Correction for non-zero rotation (thanks Ed44 Gupta!)
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// Configurable Settings
float fTimerInterval = 0.25;        //Time in seconds between movement 'ticks'
integer PRIMCHAN = -19730611;    //Channel used by Base Prim to talk to Component Prims;
                            // This must match in both scripts

//////////////////////////////////////////////////////////////////////////////////////////
// Runtime Variables (Dont need to change below here unless making a derivative)
vector vOffset;
rotation rRotation;
integer bNeedMove;
vector vDestPos;
rotation rDestRot;
integer bRecorded = FALSE;

////////////////////////////////////////////////////////////////////////////////
string first_word(string In_String, string Token)
{
    //This routine searches for the first word in a string,
    // and returns it.  If no word boundary found, returns
    // the whole string.
    if(Token == "") Token = " ";
    integer pos = llSubStringIndex(In_String, Token);

    //Found it?
    if( pos >= 1 )
        return llGetSubString(In_String, 0, pos - 1);
    else
        return In_String;
}

////////////////////////////////////////////////////////////////////////////////
string other_words(string In_String, string Token)
{
    //This routine searches for the other-than-first words in a string,
    // and returns it.  If no word boundary found, returns
    // the an empty string.
    if( Token == "" ) Token = " ";

    integer pos = llSubStringIndex(In_String, Token);

    //Found it?
    if( pos >= 1 )
        return llGetSubString(In_String, pos + 1, llStringLength(In_String));
    else
        return "";
}

////////////////////////////////////////////////////////////////////////////////
do_move()
{
    integer i = 0;
    vector vLastPos = ZERO_VECTOR;
    while( (i < 5) && (llGetPos() != vDestPos) )
    {
        list lParams = [];

        //If we're not there....
        if( llGetPos() != vDestPos )
        {
            //We may be stuck on the ground...
            //Did we move at all compared to last loop?
            if( llGetPos() == vLastPos )
            {
                //Yep, stuck...move straight up 10m (attempt to dislodge)
                lParams = [ PRIM_POSITION, llGetPos() + <0, 0, 10.0> ];
                //llSetPos(llGetPos() + <0, 0, 10.0>);
            } else {
                //Record our spot for 'stuck' detection
                vLastPos = llGetPos();
            }
        }

        //Try to move to destination
        //Upgraded to attempt to use the llSetPrimitiveParams fast-move hack
        //(Newfie, June 2006)
        integer iHops = llAbs(llCeil(llVecDist(llGetPos(), vDestPos) / 10.0));
        integer x;
        for( x = 0; x < iHops; x++ ) {
            lParams += [ PRIM_POSITION, vDestPos ];
        }
        llSetPrimitiveParams(lParams);
        //llSleep(0.1);
        i++;
    }

    //Set rotation
    llSetRot(rDestRot);
}

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
default
{
    //////////////////////////////////////////////////////////////////////////////////////////
    state_entry()
    {
        //Open up the listener
        llListen(PRIMCHAN, "", NULL_KEY, "");
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    on_rez(integer iStart)
    {
        //Set the channel to what's specified
        if( iStart != 0 )
        {
            PRIMCHAN = iStart;
            state reset_listeners;
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    listen(integer iChan, string sName, key kID, string sText)
    {
        string sCmd = llToUpper(first_word(sText, " "));

        if( sCmd == "RECORD" )
        {
            sText = other_words(sText, " ");
            list lParams = llParseString2List(sText, [ "|" ], []);
            vector vBase = (vector)llList2String(lParams, 0);
            rotation rBase = (rotation)llList2String(lParams, 1);

            vOffset = (llGetPos() - vBase) / rBase;
            rRotation = llGetRot() / rBase;
            bRecorded = TRUE;
            llOwnerSay("Recorded position.");
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "MOVE" )
        {
            //Don't move if we've not yet recorded a position
            if( !bRecorded ) return;

            //Also ignore commands from bases with a different owner than us
            //(Anti-hacking measure)
            if( llGetOwner() != llGetOwnerKey(kID) ) return;

            //Calculate our destination position
            sText = other_words(sText, " ");
            list lParams = llParseString2List(sText, [ "|" ], []);
            vector vBase = (vector)llList2String(lParams, 0);
            rotation rBase = (rotation)llList2String(lParams, 1);

            //Calculate our destination position
            vDestPos = (vOffset * rBase) + vBase;
            rDestRot = rRotation * rBase;

            //Turn on our timer to perform the move?
            if( !bNeedMove )
            {
                llSetTimerEvent(fTimerInterval);
                bNeedMove = TRUE;
            }
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "DONE" )
        {
            //We are done, remove script
            llRemoveInventory(llGetScriptName());
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "CLEAN" )
        {
            //Clean up
            llDie();
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "RESET" )
        {
            llResetScript();
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    timer()
    {
        //Turn ourselves off
        llSetTimerEvent(0.0);

        //Do we need to move?
        if( bNeedMove )
        {
            //Perform the move and clean up
            do_move();
            bNeedMove = FALSE;
        }
        return;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
state reset_listeners
{
    //////////////////////////////////////////////////////////////////////////////////////////
    state_entry()
    {
        state default;
    }
}


LibraryBuildersTabs

Builders' Tabs

Abbreviation: BTs (eg. "This house uses BTs"), pronounced: b(ee) t(ee)s.

Builders' Tabs were invented and sold by me (TimelessPrototype). However, since they have proved to be the builder's right hand tool for precision building with ease - especially for unusually-shaped buildings - I thought I should share the script for all to enjoy. I DO NOT support this script so please don't ask me about it. Please use the SL forums and ask for help there. Thank you. However, the intellectual property is still mine. You may not sell Builders' Tabs unless they are part of your pre-fabricated product (eg. housing modules with touch assembly using Builders' Tabs). If you are making a raging profit, please show your financial appreciation by donating to TimelessPrototype in SL.

Please follow these instructions carefully to make the Builder's Tab.

1. Create two prims. One prim serves as a Tab, and the other serves as the root prim.
2. Link the two prims. You could have any number of prims in this link set with one or more Tab prims.
3. In the root prim, drop in the following script and rename the script to "mover".
// By Timeless Prototype
// 2005-01-17

vector   targetPos = ZERO_VECTOR;
rotation targetRot = ZERO_ROTATION;
integer  timeOut;
integer  pendingMove = 0;
integer  pendingLockDelete = 0;
integer  lockTimeOut;

init()
{
    if (! (llGetPermissions() & PERMISSION_CHANGE_LINKS))
    {
        llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
    }
}

default
{
    state_entry()
    {
        init();
    }

    on_rez(integer start_num)
    {
        init();
    }

    run_time_permissions(integer perm)
    {
        if (! (llGetPermissions() & PERMISSION_CHANGE_LINKS))
        {
            llSay(0, "Without permission to change links you cannot do a final lock.");
        }
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        // We will be called with something like:
        // llMessageLinked(LINK_SET, 1, "move_to|" + (string)targetRootPos + "|" + (string)targetRootRot, NULL_KEY);
        list words   = llParseStringKeepNulls(str, ["|"], []);
        string word0 = llList2String(words, 0);
        string word1 = llList2String(words, 1);
        string word2 = llList2String(words, 2);

        if (word0 == "move_to")
        {
            targetPos = (vector)word1;
            targetRot = (rotation)word2;
            timeOut = 10;
            pendingMove = 1;
            llSetTimerEvent(0.2);
        }
        else if (word0 == "final_lock_all")
        {
            if (llGetLinkName(sender_num) == "Tab" && num == sender_num)
            {
                // Delink the sender.
                llBreakLink(num);
                pendingLockDelete = 1;
                lockTimeOut = 5 * 10; // 10 seconds
                llSetTimerEvent(0.2);
            }
        }

    }

    timer()
    {
        if (pendingMove == 1)
        {
            timeOut--;
            if ((llGetPos() == targetPos && llGetRot() == targetRot) || timeOut <= 0)
            {
                if (pendingLockDelete == 0)
                {
                    llSetTimerEvent(0.0);
                }
                llSay(0, "Finished moving.");
                pendingMove = 0;
            }
            else
            {
                vector myPos = llGetPos();
                vector moveTo = targetPos;

                if (llVecDist(targetPos, myPos) > 5.0)
                {
                    moveTo = llGetPos() + (llVecNorm(targetPos - myPos) * 8.0);
                }

                llSetPos(moveTo);
                llSetRot(targetRot);
            }
        }

        if (pendingLockDelete == 1)
        {
            lockTimeOut--;
            if (lockTimeOut <= 0)
            {
                pendingLockDelete = 0;
                llSetTimerEvent(0.0);
                // Relay
                llSay(23, "final_lock_all");
                llSay(0, "Builder's Tabs removed");
                // Remove our script
                llRemoveInventory(llGetScriptName());
            }
        }
    }
}

4. In the non-root prim (the Tab prim), drop in the following script and rename the script to "tab".
// Tip: zero rot the Tab prim then link to east side of root prim.
// You can link it anywhere to the root prim, but when you first use it,
// this is a good demonstrational position.

integer  lh;
vector   targetJointPos = <0,0,0>;
rotation targetJointRot = ZERO_ROTATION;
integer  nodeActive = 0;
integer  pendingTabDelete = 0;

init()
{
    if (llGetObjectName() != "Tab")
    {
        llSetObjectName("Tab");
    }
}

restoreColor()
{
    llSetColor(<0,0,1>, 0); // +ve Z axis (face)
    llSetColor(<0,1,0>, 1); // -ve Y axis (face)
    llSetColor(<1,0,0>, 2); // +ve X axis (face)
    llSetColor(<0,1,0>, 3); // +ve Y axis (face)
    llSetColor(<1,0,0>, 4); // -ve X axis (face)
    llSetColor(<0,0,1>, 5); // -ve Z axis (face)
    llSetPrimitiveParams([ PRIM_MATERIAL, PRIM_MATERIAL_WOOD ] );
}

show()
{
    string plus = "28aa0989-55ef-0abe-332d-5fb243bdc76c";
    string minus = "1f69b45a-6975-6353-87b8-d856f491430b";
    llSetTexture(plus, ALL_SIDES);
    llSetTexture(minus, 1);
    llSetTexture(minus, 4);
    llSetTexture(minus, 5);
}

hide()
{
    llSetTexture("f54a0c32-3cd1-d49a-5b4f-7b792bebc204", ALL_SIDES);
    llSetPrimitiveParams([ PRIM_MATERIAL, PRIM_MATERIAL_WOOD ] );
}

default
{
    state_entry()
    {
        init();
        lh = llListen(23, "", "", "");
        restoreColor();
        show();
    }

    on_rez(integer start_num)
    {
        llResetScript();
    }

    changed(integer change)
    {
        if (change & CHANGED_LINK)
        {
            if (pendingTabDelete == 1)
            {
                if (llGetLinkNumber() < 2)
                {
                    llDie();
                }
                else
                {
                    llSetTimerEvent(0.0);
                    llSetTimerEvent(1.0);
                }
            }
        }
    }

    timer()
    {
        llSetTimerEvent(0.0);
        llMessageLinked(LINK_SET, llGetLinkNumber(), "final_lock_all", NULL_KEY);
    }

    touch_start(integer total_number)
    {
        if (targetJointPos == <0,0,0> && llDetectedKey(0) == llGetOwner())
        {
            if (nodeActive == 0)
            {
                // TESTED, LEAVE THIS ALONE!
                llTriggerSound("268bed48-92c0-5ff3-7d39-cbec55dc69a1", 1.0);
                llShout(23, "joint|" + (string)(llGetRootPosition() + (llGetLocalPos() * llGetRootRotation())) + "|" + (string)(llGetLocalRot() * llGetRootRotation()));
                llSetColor(<1,0,1>, ALL_SIDES);
                llSetPrimitiveParams([ PRIM_MATERIAL, PRIM_MATERIAL_LIGHT ] );
                nodeActive = 1;
                // END OF TESTED
            }
            else
            {
                llTriggerSound("268bed48-92c0-5ff3-7d39-cbec55dc69a1", 1.0);
                llShout(23, "cancel_join");
                restoreColor();
                nodeActive = 0;
            }
        }
        else if (llDetectedKey(0) == llGetOwner())
        {
            llTriggerSound("268bed48-92c0-5ff3-7d39-cbec55dc69a1", 1.0);
            llShout(23, "cancel_join");
            // Begin move to last known location

            rotation targetRootRot =  (ZERO_ROTATION / (<0.0, 0.0, 1.0, 0.0> * llGetLocalRot())) * targetJointRot;

            vector targetRootPos = targetJointPos - (llGetLocalPos() * targetRootRot);

            llMessageLinked(LINK_SET, 1, "move_to|" + (string)targetRootPos + "|" + (string)targetRootRot, NULL_KEY);
            restoreColor();
            nodeActive = 0;
            targetJointPos = <0,0,0>;
            targetJointRot = ZERO_ROTATION;
        }
    }

    listen(integer channel, string name, key id, string message)
    {
        if (llGetOwner() == llGetOwnerKey(id) || llGetOwner() == id)
        {
            list words   = llParseStringKeepNulls(llToLower(message), ["|"], []);
            string word0 = llList2String(words, 0);
            string word1 = llList2String(words, 1);
            string word2 = llList2String(words, 2);

            if (word0 == "joint")
            {
                targetJointPos = (vector)word1;
                targetJointRot = (rotation)word2;
                restoreColor();
                nodeActive = 0;
            }
            else if (word0 == "cancel_join")
            {
                targetJointPos = <0,0,0>;
                targetJointRot = ZERO_ROTATION;
                restoreColor();
                nodeActive = 0;
            }
            else if (word0 == "show")
            {
                show();
            }
            else if (word0 == "hide")
            {
                hide();
            }
            else if (word0 == "final_lock_all")
            {
                llSleep(llFrand(1.0));
                pendingTabDelete = 1;
                llSetTimerEvent(1.0);
            }
        }
    }

    moving_start()
    {
        if (nodeActive == 1)
        {
            llShout(23, "cancel_join");
            restoreColor();
            nodeActive = 0;
        }
    }
}

5. Take a copy of this Builder's Tab into your inventory. This will become your Base Tab that gets used to make modules that can be touch assembled.

Test your new Builders' Tabs:

1. Rez two of these Builders' Tabs
2. Touch the Tab on each and watch them magically align the tabs on themselves.

The following is derived from the help notecard that came with the Builders' Tabs Kit:

BUILDER'S TAB KIT version 2.1
bringing 'Touch to Assemble' to your buildings
by Timeless Prototype

NOTE: ADVANCED BUILDING SKILLS REQUIRED FOR PRECISION PLACEMENT OF BUILDER TABS!!!

What is it?
Ever needed to join up building parts over and over again? Builder's Tabs introduce an easy way to Touch to Assemble your buildings.

The Builder's Tab is a prim that you link one or more of to your building modules. A building module is typically a component of a building, perhaps a wall, an entire room or a roof for example. You place the 'mover' script into the building module's root prim. When you touch the first Tab that is where the other module must move to. Touch the Tab on the other module to begin the movement.

The target module stays in place and the second module will move and rotate as necessary so that the touched prims (Tabs) overlap exactly.

You WILL REQUIRE precise building skills if you want the modules to join up precisely by correctly placing the Tabs.

What Commands Are There?
Say:
'/23 hide' - to hide the Tabs from view.
'/23 show' - to show the Tabs again.
'/23 final_lock_all' - de-links the tabs and deletes the tab prims and the mover scripts. Only the current Owner can issue this command.

How do I get started?
1. Rez the Builder's Tab.
2. De-link the Tab from the example root prim.
3. Position the tab on your building module.
4. Link the Tab (or more than one Tab if you like) to your building module. The building module should be Shift selected LAST so that the Tabs are NEVER the root prim.
5. Copy the 'mover' script from the example Builder's Tab's root prim's contents into your inventory.
6. Copy the 'mover' script from your inventory into the root prim of your building module.
7. Your building module is now ready for touch assembly. Take a copy of your work. You can add as many Tabs to a single module as you like.
8. Test it.

Tips:
The Tabs when touched will move to overlap eachother exactly. They will also rotate 180 degrees around the local Z axis. So with this in mind, always keep the RED face with the PLUS sign facing outwards.

Script Library | Examples


LibraryCamera_Sync

Camera Sync

Camera sync allows for multiple users to synchronize their cameras, for use by builders in joint projects or in tutorials and demonstrations.

Camera Tween - Smoothly adjusts the position and rotation of the camera over a given number of steps (eight works well):
camTween(rotation camRot_origin, vector camPos_origin, rotation camRot_target, vector camPos_target, float steps)
{
    //Keep steps a float, but make sure its rounded of to the nearest 1.0
    steps = (float)llRound(steps);

    //Calculate camera position increments
    vector posStep = (camPos_target - camPos_origin) / steps;

    //Calculate camera rotation increments
    rotation rotStep = (camRot_target - camRot_origin);
    rotStep = <rotStep.x / steps, rotStep.y / steps, rotStep.z / steps, rotStep.s / steps>;

    float cStep; //Loop through motion for cStep = current step, while cStep <= Total steps
    for(cStep = 0.0; cStep <= steps; cStep++)
    {
        //Set next position in tween
        vector camPos_next = camPos_origin + (posStep * cStep);
        rotation camRot_next = camRot_origin + <rotStep.x * cStep, rotStep.y * cStep, rotStep.z * cStep, rotStep.s * cStep>;

        //Set camera parameters
        llSetCameraParams([
            CAMERA_ACTIVE, 1, //1 is active, 0 is inactive
            CAMERA_BEHINDNESS_ANGLE, 0.0, //(0 to 180) degrees
            CAMERA_BEHINDNESS_LAG, 0.0, //(0 to 3) seconds
            CAMERA_DISTANCE, 0.0, //(0.5 to 10) meters
            CAMERA_FOCUS, camPos_next + llRot2Fwd(camRot_next), //Region-relative position
            CAMERA_FOCUS_LAG, 0.0 , //(0 to 3) seconds
            CAMERA_FOCUS_LOCKED, TRUE, //(TRUE or FALSE)
            CAMERA_FOCUS_THRESHOLD, 0.0, //(0 to 4) meters
            CAMERA_POSITION, camPos_next, //Region-relative position
            CAMERA_POSITION_LAG, 0.0, //(0 to 3) seconds
            CAMERA_POSITION_LOCKED, TRUE, //(TRUE or FALSE)
            CAMERA_POSITION_THRESHOLD, 0.0, //(0 to 4) meters
            CAMERA_FOCUS_OFFSET, ZERO_VECTOR //<-10,-10,-10> to <10,10,10> meters
        ]);
    }
}

Camera Default - Sets the camera parameters back to their defaults:
camDefault()
{
    llSetCameraParams([
        CAMERA_ACTIVE, FALSE, //1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 10.0, //(0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.0, //(0 to 3) seconds
        CAMERA_DISTANCE, 3.0, //(0.5 to 10) meters
        CAMERA_FOCUS_LAG, 0.1 , //(0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, //(TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 1.0, //(0 to 4) meters
        CAMERA_PITCH, 0.0, //(-45 to 80) degrees
        CAMERA_POSITION_LAG, 0.1, //(0 to 3) seconds
        CAMERA_POSITION_LOCKED, FALSE, //(TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 1.0, //(0 to 4) meters
        CAMERA_FOCUS_OFFSET, ZERO_VECTOR //<-10,-10,-10> to <10,10,10> meters
    ]);
}

Camera Match - Sets a prim's position and rotation to that of the user's (the one we have permissions for) camera:
warpPos(vector destpos)
{
    integer jumps = (integer)(llVecDist(destpos, llGetPos()) / 10.0) + 1;
    list rules = [PRIM_POSITION, destpos];
    integer count = 1;
    while (( count = count << 1 ) < jumps)
    {
        rules = (rules=[]) + rules + rules;
    }
    llSetPrimitiveParams(rules + llList2List(rules, (count - jumps) << 1, count));
}
camMatch()
{
    warpPos(llGetCameraPos());
    llSetRot(llGetCameraRot());
}

Script Library|WarpPos


LibraryCameraFollower

Camera Follower

Originally created by ArkLehane who edited the version posted here and credited to him on this Wiki out of existance.
Re-created and rebuilt by WolfWings Majestic because the script idea is useful, but the original version could be improved on.

Moves an object to the position of your camera.

integer channel = 0;

default {
    state_entry() {
        llRequestPermissions(llGetOwner(),PERMISSION_TRACK_CAMERA);
    }
    run_time_permissions(integer perm) {
        if (perm & PERMISSION_TRACK_CAMERA) {
            state camera_captured;
        }
    }
}

state camera_captured {
    state_entry() {
        llListen(channel,"",llGetOwner(),"");
        llSetStatus(STATUS_PHYSICS,TRUE);
        llSetTimerEvent(0.1);
    }
    timer() {
        llMoveToTarget(llGetCameraPos()+<0.0,0.0,1.0>,0.1);
    }
    listen(integer channel, string name, key id, string message) {
        if (llToLower(message) == "hold") {
            state camera_captured_disabled;
        }
    }
}

state camera_captured_disabled {
    state_entry() {
        llListen(channel,"",llGetOwner(),"");
        llSetStatus(STATUS_PHYSICS,FALSE);
    }
    listen(integer channel, string name, key id, string message) {
        if (llToLower(message) == "follow") {
            state camera_captured;
        }
    }
}

ScriptLibrary


LibraryCaseInsensitiveListenRezzer

Non-Case Sensitive Listen Rezzer

Heh, long title. With this script you can rez objects at the set coordanates through chat. But, the special thing is that its non-case sensitive so you can say your command in any case and it will still work. Any problems? Just fix it or IM me in world with the problem!

Offset Coordanates From Objects Version

Spacific Regonal Coordanates

//Deluxe Listen Rezzer
//Created By : Chasingred3 Ixtab
//Created For : Zippo Nabob
//No need to type in the right CaSe to make your
//Commands to work :)

integer channel = 0;//What channel you want it to listen on

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////1st Object Info////////////////////////////
////////////////////////////////////////////////////////////////////////////
string object1command = "rez object1"; /////////////////////////////////
string object1Name = "Object1";//First objects name/////////////////////
vector object1Pos = <0,0,0>;//First Objects Position  Cordanates////////
rotation object1rotation = <0,0,0,1>;//First Objects Rotation///////////
////////////////////////////////////////////////////////////////////////////

//==========================================================================

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////2nd Object Info////////////////////////////
////////////////////////////////////////////////////////////////////////////
string object2command = "rez object2"; /////////////////////////////////
string object2Name = "Object1";//Objects name/////////////////////
vector object2Pos = <0,0,0>;//Objects Position - Cordanates////////
rotation object2rotation = <0,0,0,1>;//Objects Rotation///////////
///////////////////////////////////////////////////////////////////////

//==========================================================================

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////3nd Object Info////////////////////////////
////////////////////////////////////////////////////////////////////////////
string object3command = "rez object3"; /////////////////////////////////
string object3Name = "Object3";//Objects name/////////////////////
vector object3Pos = <0,0,0>;//Objects Position - Cordanates////////
rotation object3rotation = <0,0,0,1>;//Objects Rotation///////////
///////////////////////////////////////////////////////////////////////

//==========================================================================

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////4th Object Info////////////////////////////
////////////////////////////////////////////////////////////////////////////
string object4command = "rez object4"; /////////////////////////////////
string object4Name = "Object4";//objects name/////////////////////
vector object4Pos = <0,0,0>;//Objects Position - Cordanates////////
rotation object4rotation = <0,0,0,1>;//Objects Rotation///////////
///////////////////////////////////////////////////////////////////////

//==========================================================================

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////5th Object Info////////////////////////////
////////////////////////////////////////////////////////////////////////////
string object5command = "rez object5"; /////////////////////////////////
string object5Name = "Object5";//Objects name/////////////////////
vector object5Pos = <0,0,0>;//Objects Position - Cordanates////////
rotation object5rotation = <0,0,0,1>;//Objects Rotation///////////
///////////////////////////////////////////////////////////////////////

//==========================================================================

//Please, Dont touch anything down here :P

//==========================================================================
//==========================================================================
//==========================================================================
string obj1 = "";
string obj2 = "";
string obj3 = "";
string obj4 = "";
string obj5 = "";

default
{
    state_entry()
    {//llGetOwner --- NULL_KEY
        llListen(channel, "", llGetOwner(), "");
        string obj1 = llToLower(object1command);
        string obj2 = llToLower(object2command);//Changes all commands to
        string obj3 = llToLower(object3command);//lower case
        string obj4 = llToLower(object4command);
        string obj5 = llToLower(object5command);
    }

    listen(integer channel, string name, key id, string message)
    {
    string cmd;
    cmd = llToLower(message);//Turnes what YOU say to lower
                            //case letters
        if(cmd == obj1)
        {
            llRezObject(object1Name, object1Pos, ZERO_VECTOR, object1rotation, 42);

        }
        if(cmd == obj2)
        {
            llRezObject(object2Name, object2Pos, ZERO_VECTOR, object2rotation, 42);
        }
        if(cmd == obj3)
        {
            llRezObject(object3Name, object3Pos, ZERO_VECTOR, object3rotation, 42);
        }
        if(cmd == obj4)
        {
            llRezObject(object4Name, object4Pos, ZERO_VECTOR, object4rotation, 42);
        }
        if(cmd == obj5)
        {
            llRezObject(object5Name, object5Pos, ZERO_VECTOR, object5rotation, 42);
        }
    }

}

llRezObject | llToLower | llListen |


LibraryChannelNumberGenerator

Do you have a tough time choosing and remembering unique channel numbers? I did, so I wrote a script to do it for me, kind of like domain names on the Internet represent IP addresses.

Sometimes I use the object name as the string, sometimes the script name; it all depends. The choice is yours.

I keep my functions and their "test drivers" in scripts in a function library folder. Because LSL has no #include or anything, I copy just the comment and the function into other scripts.

//
// generateChannel(): Given a text string, generate a (relatively) unique channel number.
// This conversion is repeatable, so you can use it to easily communicate with a script by
// remembering a text string instead of a channel number.
//
// You can copy or modify this script if you give me credit.
// Petre Lamar, 2006-07-31
//
integer generateChannel(string text)
{
    list hexDigits = ["dummy", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
    string md5 = llMD5String(text, 0);
    integer length = llStringLength(md5);
    integer channelNumber = 1; // zero makes a bad multiplier :-)
    integer hexValue;
    string character;

    for (length--; length >= 0; length--)
    {
        character = llGetSubString(md5, length, length);
        hexValue = llListFindList(hexDigits, [ character ]);
        channelNumber *= hexValue;
        if (channelNumber == 0)
        {
            // zero times anything remains zero, so force it to one
            channelNumber++;
        }
    }

    return channelNumber;
}

default
{
    state_entry()
    {
        llWhisper(0, "Type something and I'll convert that into a channel number!");
        llListen(0, "", llGetOwner(), "");
    }

    listen(integer channel, string name, key id, string message)
    {
        integer result = generateChannel(message);
        llWhisper(0, "'" + message + "' yields '" + (string)result + "'");
    }
}


LibraryChatCodec

ChatCodec

This script module was created to extend LSL's chat functionality - in particular, to allow for the efficiant communication of text strings over 255 characters long. It does this by implementing the ExchangePacketChat protocol.

// Copyright (c) 2006 Francisco V. Saldana (Christopher Omega)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Handles object-object chat.
// Modules register a listen using addChatHandle(integer channel)
// which returns a boolean indicating if the handle was successfully registered.
// Modules unregister a listen using removeChatHandle(integer channel),
// channels are automatically unregistered when a module calls moduleReset.
// The receivedChatData(integer channel, string name, key id, string message)
// method is invoked when chat is heard on a registered channel.
// This module's chat method encodes arbitrary-length chat data in such a way
// that it can be fully reconstructed by objects using the same protocol
// on the receiving end.

// When the module receives the first packet of a message, time to wait (in seconds)
// for the next packet before it clears all in-transit message data.
integer MAX_SEND_WINDOW = 10;

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

m_receivedChatData(integer channel, string name, key id, string message) {
    callMethod(0, "receivedChatData", [channel, name, id, message]);
}

m_moduleReset(string moduleName) {
    callMethod(0, "moduleReset", [moduleName]);
}

list listReplaceList(list dest, list src, integer start) {
    string NULL = "";
    integer len = llGetListLength(dest);
    for (len = llGetListLength(dest); len < start; ++len) {
        dest += NULL;
    }
    return llListReplaceList(dest, src, start, start + llGetListLength(src) - 1);
}

list getModulesChannel(integer channel) {
    integer chanIndex = llListFindList(listenChannels, [channel]);
    if (chanIndex != LIST_INDEX_NOT_FOUND) {
        string modulesRegistered = llList2String(channelUsers, chanIndex);
        list moduleList = stringToList(modulesRegistered);
        return moduleList;
    } else {
        return [];
    }
}

integer addModuleChannel(string moduleName, integer channel) {
    integer chanIndex = llListFindList(listenChannels, [channel]);
    if (chanIndex != LIST_INDEX_NOT_FOUND) {
        list moduleList = getModulesChannel(channel);
        integer moduleIndex = llListFindList(moduleList, [moduleName]);
        if (moduleIndex == LIST_INDEX_NOT_FOUND) {
            moduleList += moduleName;
            channelUsers = listReplaceList(channelUsers,
                [listToString(moduleList)], chanIndex);
            return TRUE;
        }
    }
    return FALSE;
}

integer removeModuleChannel(string moduleName, integer channel) {
    integer chanIndex = llListFindList(listenChannels, [channel]);
    if (chanIndex != LIST_INDEX_NOT_FOUND) {
        list moduleList = getModulesChannel(channel);
        integer moduleIndex = llListFindList(moduleList, [moduleName]);
        if (moduleIndex != LIST_INDEX_NOT_FOUND) {
            moduleList = llDeleteSubList(moduleList, moduleIndex, moduleIndex);
            // This avoids problems with storing an empty string.
            if (moduleList != []) {
                channelUsers = listReplaceList(channelUsers,
                    [listToString(moduleList)], chanIndex);
            } else {
                // The module list is empty, so just delete the data at the index.
                channelUsers = llDeleteSubList(channelUsers, chanIndex, chanIndex);
            }
            return TRUE;
        }
    }
    return FALSE;
}

integer addChannelUser(string moduleName, integer channel) {
    integer channelIndex = llListFindList(listenChannels, [channel]);
    // If no one us already listening on the channel:
    if (channelIndex == LIST_INDEX_NOT_FOUND) {
        if (llGetListLength(listenChannels) < 64) {
            listenHandleIDs += llListen(channel, "", NULL_KEY, "");
            listenChannels  += channel;
            channelUsers    += moduleName;
            return TRUE;
        } else {
            return FALSE;
        }
    } else {
        // Add a channel user.
        addModuleChannel(moduleName, channel);
        return TRUE;
    }
}

removeChannelUser(string moduleName, integer channel) {
    integer channelIndex = llListFindList(listenChannels, [channel]);
    // If someone is using the channel
    if (channelIndex != LIST_INDEX_NOT_FOUND) {
        // Remove one channel user.
        integer curChannelUsers = llGetListLength(getModulesChannel(channel));
        integer isModuleRemoved = removeModuleChannel(moduleName, channel);
        if (isModuleRemoved) {
            curChannelUsers--;
            // If, now, no one is using the channel:
            if (curChannelUsers <= 0) {
                // Unregister it.
                integer listenHandleID = (integer) llList2String(listenHandleIDs, channelIndex);
                llListenRemove(listenHandleID);
                // Remove all list data concerning the channel.
                listenHandleIDs = llDeleteSubList(listenHandleIDs,  channelIndex, channelIndex);
                listenChannels  = llDeleteSubList(listenChannels,   channelIndex, channelIndex);
            }
        }
    } // Else channel never was registered.
}

fireReceivedChatDataEvent(integer channel, string name, key id, string message) {
    m_receivedChatData(channel, name, id, message);
}

string intToHex(integer x, integer len) {
    string hexc="0123456789ABCDEF";
    integer x0 = x & 0xF;
    string res = llGetSubString(hexc,x0,x0);
    x = (x >> 4) & 0x0FFFFFFF; //otherwise we get infinite loop on negatives.
    while (x != 0) {
        x0 = x & 0xF;
        res = llGetSubString(hexc,x0,x0) + res;
        x = x >> 4;
    }
    for (len -= llStringLength(res); len > 0; --len)
        res = "0" + res;
    return res;
}

// List of identifyers and the multipart messages that are currently being recieved.
list messagesInTransit;
string this;

list listenHandleIDs = [];
list listenChannels = [];
list channelUsers = [];

integer LIST_INDEX_NOT_FOUND = -1;

default {
    state_entry() {
        this = llGetScriptName();
        m_moduleReset(this);
    }

    link_message(integer sender, integer id, string parameters, key methodName) {
        if (methodName == "addChatHandle") {
            list paramList = stringToList(parameters);
            // Method signature:
            // addChatHandle(string moduleName, integer channel)
            string moduleName = llList2String(paramList, 0);
            integer channel = (integer) llList2String(paramList, 1);
            returnValue(id, methodName, [addChannelUser(moduleName, channel)]);
        } else if (methodName == "removeChatHandle") {
            list paramList = stringToList(parameters);
            // Method signature:
            // removeChatHandle(string moduleName, integer channel)
            string moduleName = llList2String(paramList, 0);
            integer channel = (integer) llList2String(paramList, 1);
            removeChannelUser(moduleName, channel);
        } else if (methodName == "moduleReset") {
            list paramList = stringToList(parameters);
            // Method signature:
            // moduleReset(string moduleName)
            string moduleName = llList2String(paramList, 0);
            integer numChannels = llGetListLength(listenChannels);
            integer i;
            for (i = 0; i < numChannels; ++i) {
                removeChannelUser(moduleName, llList2Integer(listenChannels, i));
            }
        } else if (methodName == "chat") {
            // Method signature:
            // chat(integer channel, string message)
            list paramList = stringToList(parameters);
            integer channel = (integer) llList2String(paramList, 0);
            string  message = llList2String(paramList, 1);
            if (channel != 0) {
                integer MAX_CHAT_LEN = 255;
                integer dataLen = MAX_CHAT_LEN - 8; // Three ints: 2byte ID, 1byte index and 1byte count.
                // That's four chars for the ID and 2 each for index and count.
                integer packetCount = llCeil(llStringLength(message) / (float)dataLen);
                string hexCount = intToHex(packetCount, 2);
                string hexID = intToHex((integer)llFrand(0xFFFE) + 1, 4);
                integer i;
                integer char;
                string packetData;
                for(i = 0, char = 0; i < packetCount; ++i, char += dataLen) {
                    // Send the message in 255 character "packets"
                    // Each packet is in the form:
                    // <message ID><packet index><packet count><packet data>
                    packetData = llGetSubString(message, char, char + dataLen - 1);
                    llShout(channel, hexID + intToHex(i, 2) + hexCount + packetData);
                }
            }
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(id, methodName, [TRUE]);
        }
    }

    listen(integer channel, string name, key id, string packet) {
        integer messageId = (integer) ("0x" + llGetSubString(packet, 0, 3)); // First 4 chars.
        if (messageId != 0) {
            integer packetIndex = (integer) ("0x" + llGetSubString(packet, 4, 5)); // Next 2 chars.
            integer packetCount = (integer) ("0x" + llGetSubString(packet, 6, 7)); // next 2 chars.
            string packetData = llDeleteSubString(packet, 0, 7); // Everything else.
            integer identifyerIndex = llListFindList(messagesInTransit, [messageId]);
            if (identifyerIndex == -1) {
                // New message
                messagesInTransit = [messageId, packetData] + messagesInTransit;
                identifyerIndex = 0;
            } else {
                // Message has more parts
                string message = llList2String(messagesInTransit, identifyerIndex + 1);
                message += packetData;
                messagesInTransit = listReplaceList(messagesInTransit, [message], identifyerIndex + 1);
            }

            if (packetIndex + 1 == packetCount) {
                // Message has been fully reconstructed.
                string message = llList2String(messagesInTransit, identifyerIndex + 1);
                fireReceivedChatDataEvent(channel, name, id, message);
                messagesInTransit = llDeleteSubList(messagesInTransit, identifyerIndex, identifyerIndex + 1);
            }
        } else {
            // Not a valid multipart message
            fireReceivedChatDataEvent(channel, name, id, packet);
        }
        llSetTimerEvent(MAX_SEND_WINDOW);
    }

    timer() {
        // A safety measure to account for objects that moved out of chat range
        // before sending the last packet of their message.
        messagesInTransit = [];
        llSetTimerEvent(0);
    }
}

To utilize the above script, paste it into a new script, name it "ChatCodec" and drop it into the inventory of an object. Other scripts in the object must declare these functions:

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

This would be the equivelant for llListen:
m_addChatHandle(string scriptName, integer channel) {
    callMethod(0, "addChatHandle", [scriptName, channel]);
}

This would be the equivelant for llListenRemove:
m_removeChatHandle(string scriptName, integer channel) {
    callMethod(0, "removeChatHandle", [scriptName, channel]);
}

This would be the equivelant for llWhisper, llSay, and llShout:
m_chat(integer channel, string message) {
    callMethod(0, "chat", [channel, message]);
}

And the equivelant for the listen event:
default {
    link_message(integer sender, integer callIdent, string params, key methodName) {
        if (methodName == "receivedChatData") {
            list paramList = stringToList(params);
            integer channel = (integer)llList2String(paramList, 0);
            string name = llList2String(paramList, 1);
            key id = (key)llList2String(paramList, 2);
            string message = llList2String(paramList, 3);
            // Code that was in listen() goes here.
            // ...
        }
    }
}

By using this module as a wrapper for the LL-provided chat functions, it automatically handles what's needed for the transfer of a 65025-character text string (maximum in theory). The receiving party must also be using this script or a script capable of decoding the message protocol.

A well-formed user of this script module will look similar to this:
// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

m_addChatHandle(string scriptName, integer channel) {
    callMethod(0, "addChatHandle", [scriptName, channel]);
}

m_removeChatHandle(string scriptName, integer channel) {
    callMethod(0, "removeChatHandle", [scriptName, channel]);
}

m_chat(integer channel, string message) {
    callMethod(0, "chat", [channel, message]);
}

m_moduleReset(string scriptName) {
    callMethod(0, "moduleReset", [scriptName]);
}

chatInit() {
    // Example chat handle.
    // llListen(10283, "", NULL_KEY, "");
    m_addChatHandle(llGetScriptName(), 10283);
}

default {
    state_entry() {
        m_moduleReset(llGetScriptName());
        chatInit();
    }

    link_message(integer sender, integer callIdent, string params, key methodName) {
        if (methodName == "receivedChatData") {
            list paramList = stringToList(params);
            integer channel = (integer)llList2String(paramList, 0);
            string name = llList2String(paramList, 1);
            key id = (key)llList2String(paramList, 2);
            string message = llList2String(paramList, 3);
            // Code that was in listen() goes here.
            // ...
        } else if (methodName == "moduleReset") {
            list paramList = stringToList(params);
            string scriptName = llList2String(paramList, 0);
            if (scriptName == "ChatCodec") {
                chatInit();
            }
        }
    }
}


LibraryColorScripts

Color Scripts

The following can be used to convert an HSB (HSV, HSL) vector into an RGB vector:
// Checks the limits of a single component.
float checkComp(float v, float t1, float t2)
{
    if (v < 0.0)
        v = v + 1.0;

    if (v > 1.0)
        v = v - 1.0;

    if ((v * 6) < 1)
        v = t1 + (t2 - t1) * 6.0 * v;
    else
    {
        if ((v * 2.0) < 1.0)
            v = t2;
        else
        {
            if ((v * 3.0) < 2.0)
                v = t1 + (t2 - t1) * ((2.0 / 3.0) - v) * 6.0;
            else
                v = t1;
        }
    }

    return v;
}

// Converts an HSB (HSV) colorspace to RGB colorspace
// Input is a vector:
//   x == hue (0 .. 359.9)
//   y == saturation (0 .. 1)
//   z == bright (value, lum) (0 .. 1)
// Output is a vector:
//   x == red (0 .. 1)
//   y == green (0 .. 1)
//   z == blue (0 .. 1)
vector hsb2rgb(vector hsb)
{
    vector rval;
    integer i;
    float t1;
    float t2;

    hsb.x /= 360.0;

    if (hsb.y == 0.0)
    {
        rval.x = hsb.z;
        rval.y = hsb.z;
        rval.z = hsb.z;
    }
    else
    {
        if (hsb.z < 0.5)
            t2 = hsb.z * (1.0 + hsb.y);
        else
            t2 = hsb.z + hsb.y - hsb.z * hsb.y;

        t1 = 2 * hsb.z - t2;

        rval.x = checkComp(hsb.x + (1.0 / 3.0), t1, t2);
        rval.y = checkComp(hsb.x, t1, t2);
        rval.z = checkComp(hsb.x - (1.0 / 3.0), t1, t2);
    }

    return rval;
}

Following can be used to convert an RGB vector to HSB:
// Converts an RGB colorspace to HSB colorspace
// Input is a vector:
//   x == red (0 .. 1)
//   y == green (0 .. 1)
//   z == blue (0 .. 1)
// Output is a vector:
//   x == hue (0 .. 359.9)
//   y == saturation (0 .. 1)
//   z == bright (value, lum) (0 .. 1)
vector rgb2hsb(vector rgb)
{
    vector rval;
    float min;
    float max;
    float h;
    float d;

    min = rgb.x;
    max = rgb.x;

    if (rgb.y < min)
        min = rgb.y;
    if (rgb.z < min)
        min = rgb.z;

    if (rgb.y > max)
        max = rgb.y;
    if (rgb.z > max)
        max = rgb.z;

    h = 0;
    rval.z = (max + min) / 2.0;

    d = max - min;

    if (max == min)
        rval.y = 0.0;
    else
    {
        if (rval.z < 0.5)
            rval.y = (max - min) / (max + min);
        else
            rval.y = (max - min) / (2.0 - max - min);
    }

    if (d != 0.0)
    {
        if (rgb.x == max)
            h = (rgb.y - rgb.z) / d;
        else if (rgb.y == max)
            h = 2.0 + (rgb.z - rgb.x) / d;
        else
            h = 4.0 + (rgb.x - rgb.y) / d;
    }

    h /= 6.0;
    if (h < 0.0)
        h = h + 1.0;

    rval.x = h * 360.0;

    return rval;
}

These functions don't do much range checking so you could end up with weird values if you're not carefull.


LibraryConversationAI

A simple script to handle simple conversation trees. Example conversation tree notecard is below the script.
///////////////////////
// User Variables //
///////////////////////

// The name of the note with the conversation tree.
string note = "conversation";

// The following variable determines if conversations
// Are exclusive to the person who initiates them.
integer exclusive = TRUE;

//////////////////////////
// System Variables //
//////////////////////////

// List layout
// How a line and conversation are arranged
integer STRIDE = 5;     // How many parts to a line
integer STATE = 0;      // The state of this command
integer NEXTSTATE = 1;  // The next state after this command
integer TRIGGER = 2;    // The trigger string
integer MESSAGE = 3;    // The text message
integer COMMAND = 4;    // The link command

// The current section of the notecard.
list currTriggers;
list currMessages;
list currNextStates;
list currCommands;

// The entire contents of the notecard.  Could get large.
// state, goto, trigger, message, command
list conversation;

// Others
list tempL;
integer i;
integer j;
integer l;
integer max;
key k;
key user = NULL_KEY;

/////////////////
// Functions //
/////////////////

// Replaces all occurances of find in src with rep
string replace(string src, string find, string rep)
{
    // Save our selves some work in an easy double edge case
    if (src == find) return rep;

    // Prevent infinite loops when find is in rep
    else if (llSubStringIndex(rep,find) != -1) return src;

    // Look for find in src
    l = llSubStringIndex(src,find);

    // While there are still find in src
    while (l != -1)
    {
        // It is at the begning of src
        if (l == 0)
            src = rep + llGetSubString(src,llStringLength(find),- 1);

        // It is at the end of src
        else if (l == llStringLength(src) - llStringLength(find))
            src = llGetSubString(src,0,0 - llStringLength(find) - 1) + rep;

        // It must be in the middle of src
        else
            src = llGetSubString(src,0,l - 1) + rep +
                    llGetSubString(src,l + llStringLength(find),-1);

        // See if there are more
        l = llSubStringIndex(src,find);
    }

    return src;
}

// Set the conversation state.
// Note this is a 'fake' state, not an LSL state.
// Conversation is assumed to be sorted by STATE
integer setState(integer s)
{
    // Grab a list of the states
    tempL = llList2ListStrided(conversation,0,-1,STRIDE);

    // Find the first occurance of the state s
    i = llListFindList(tempL,[(string)s]);

    // If the state doesn't exist, go to the zero state
    if (i == -1)
    {
        i = 0;
        s = 0;
    }

    // Find the first occurance of the state s + 1
    j = llListFindList(tempL,[(string)(s + 1)]);

    // If there is no next state, must be at the end of the list
    if (j == -1) j = 0;
    // We want through the last of s, which is right before s + 1
    j = j - 1;

    // Convert i and j to locations in conversation
    i = i * STRIDE;
    j = j * STRIDE;

    // Pull the chunks out of conversation into their own sections.
    currTriggers = llList2ListStrided(
                        llList2List(conversation,
                                    i + TRIGGER,
                                    j + TRIGGER),
                        0,-1,STRIDE);

    currNextStates = llList2ListStrided(
                        llList2List(conversation,
                                    i + NEXTSTATE,
                                    j + NEXTSTATE),
                        0,-1,STRIDE);

    currMessages = llList2ListStrided(
                        llList2List(conversation,
                                    i + MESSAGE,
                                    j + MESSAGE),
                        0,-1,STRIDE);

    currCommands= llList2ListStrided(
                        llList2List(conversation,
                                    i + COMMAND,
                                    j + COMMAND),
                        0,-1,STRIDE);

    return s;
}

//////////////
// States //
/////////////
default
{
    state_entry()
    {
        // We listen to everything
        llListen(0,"","","");

        llWhisper(0,"...setting up system...");
        i = 0;
        conversation = [];
        k = llGetNotecardLine(note,i++);
    }

    listen(integer chan, string name, key id, string mes)
    {
        if ( (exclusive && ( user == NULL_KEY || user == id)) || !exclusive)
        {
            // Cycle through every current trigger
            max = llGetListLength(currTriggers);
            for (i=0;i<max;i++)
            {
                // Look for any occurance of trigger in the line said
                j = llSubStringIndex(mes,llList2String(currTriggers,i));

                // We found one!
                if (j != -1)
                {
                    // Whisper the response, replacing -@- with players name.
                    llWhisper(0,replace(llList2String(currMessages,i),"-@-",name));

                    // If there is a link message command, give it.
                    if(llList2String(currCommands,i) != "NULL")
                    {
                        llMessageLinked(LINK_SET,
                            llList2Integer(currNextStates,i),   // Next State
                            llList2String(currCommands,i),      // Command
                            id);                                // Speaker
                    }

                    // Set the next state and user.
                    if ( setState((integer)llList2String(currNextStates,i)) ) user = id;
                    else user = NULL_KEY;

                    // Exit early since we found a response.
                    return;
                }
            }
        }
    }

    // Detect a change to trigger a reset of the conversation
    // This could be replaced by another reset mechanism
    changed(integer change)
    {
        if (change & CHANGED_INVENTORY)
        {
            llResetScript();
        }
    }

    // We stuff every line in the notecard into
    // conversation with the exception of comments
    dataserver(key query, string data)
    {
        if(query == k)
        {
            k = NULL_KEY;

            // Done reading the card
            if (data == EOF)
            {
                // Sort in chunks of STRIDE by STATE
                llListSort(conversation,STRIDE,TRUE);

                // All conversations start in state 0
                setState(0);

                // Yay, setup is done.
                llWhisper(0,"...setup complete...[" +
                    (string)(llGetListLength(conversation) / STRIDE) +
                    " lines :: " + (string)llGetFreeMemory() +
                    "b free]");
            }

            // Ignore blank lines and comments
            else if (llGetSubString(data,0,1) == "//" || data == "")
            {
                k = llGetNotecardLine(note,i++);
            }

            // A 'real' line
            else
            {
                // Parse the line to a list
                tempL = llParseString2List(data,["-=-"],[]);

                // If this line is the correct length
                if (llGetListLength(tempL) == STRIDE)
                {
                    //llWhisper(0," -=- " + llList2CSV(tempL));
                    conversation = conversation + tempL;
                    k = llGetNotecardLine(note,i++);
                }

                // If it is short, we assume no link message
                else if (llGetListLength(tempL) == STRIDE - 1) // No Link Message
                {
                    conversation = conversation + tempL + ["NULL"] ;
                    k = llGetNotecardLine(note,i++);
                }

                // The line is not formatted right.
                else
                {
                    llWhisper(0,"ERROR: line " +
                        (string)(i - 1) + ", [" + data + "]");
                }
            }
        }
    }
}

Here is an example conversation notecard. It goes on a notecard named "conversation" in the same object as the above script. This note will cause a link message to be sent at the begning and end of the conversation - for simple conversations this is totally unnecesary.
// State, Goto State, Trigger, Message
// This is a response that will never leave the 0 level, you can ask this as much as you want
0-=-0-=-Are you a computer?-=-Yes, I am!

// This starts an actual conversation, and moves to the next conversation level
0-=-1-=-Hello-=-Hi, -@-!-=-StartingConversation

// This is the next level of conversation
// The AI will only respond to these after someone says 'Hello'
1-=-1-=-What color is the sky?-=-Why, -@-, do you know, I think it's blue!

// This will end the conversation by going back to the 0 level
1-=-0-=-Bye-=-Ok, -@-!-=-EndingConversation

Another approach to scripting natural language AI and natural language interaction with commands is through an AIML like markup language. An example of such a markup would be lindenAIML (see LibraryLindenAIML).
Script Library | Examples


LibraryDanceBall

// Just add this script into a prim with up to 11 dances and touch the prim to start

string dances;
list dance_buttons;
integer chat_channel = -468;
integer dance_number;

stopAllDances()
{
    integer total_dances = llGetInventoryNumber(INVENTORY_ANIMATION);
    integer i = 0;
    while(i < total_dances)
    {
        llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, i++));
    }
}

default
{
    state_entry()
    {
        integer total_dances = llGetInventoryNumber(INVENTORY_ANIMATION);
        if(total_dances > 11) total_dances = 11;
        integer i = 0;
        while(i < total_dances)
        {
            dances = (dances = "") + dances + (string)(++i) + " " + llGetInventoryName(INVENTORY_ANIMATION, i) + "\n";
            dance_buttons = (dance_buttons = []) + dance_buttons + (string)i;
        }
        dance_buttons = (dance_buttons = []) + dance_buttons + "STOP";
        llListen(chat_channel, "", NULL_KEY, "");
    }

    touch_start(integer total_number)
    {
        llDialog(llDetectedKey(0), dances, dance_buttons, chat_channel);
    }

    listen(integer channel, string name, key id, string message)
    {
        if(message == "STOP")
            dance_number = -1;
        else
            dance_number = (integer)message - 1;
        llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION);
    }

    changed(integer change)
    {
        if(change & CHANGED_INVENTORY)
            llResetScript();
    }

    run_time_permissions(integer perms)
    {
        if(perms & PERMISSION_TRIGGER_ANIMATION)
        {
            stopAllDances();
            if(dance_number > -1)
            {
                llStartAnimation(llGetInventoryName(INVENTORY_ANIMATION, dance_number));
            }
        }
    }
}


LibraryDayofWeek

//By Richard Solvang
//Sunday, July 31, 2005
//Based on function referenced from http://mathforum.org/library/drmath/view/55837.html

string GetDay(float year, float month, float date)
{
    if(month < 3) {month += 12; --year;}        // These lines mathematicly translate
    integer N = (integer)date;                  // Date, Month and year
    N += (integer)(2.0 * month);                // into a number which represents
    N += llFloor(3.0 * (month + 1.0) / 5.0);    // a given day of the week.
    N += (integer)year;                         // 0 = Saturday     1 = Sunday
    N += llFloor(year / 4.0);                   // 2 = Monday       3 = Tuesday
    N -= llFloor(year / 100.0);                 // 4 = Wednesday    5 = Thursday
    N += llFloor(year / 400.0);                 // 6 = Friday
    N += 2;                                     //
    N = N % 7;                                  // The rest of the function
                                                // translates the number
                                                // into the name of the day.

    string day;
    if(N == 0) day = "Saturday";
    else if(N == 1) day = "Sunday";
    else if(N == 2) day = "Monday";
    else if(N == 3) day = "Tuesday";
    else if(N == 4) day = "Wednesday";
    else if(N == 5) day = "Thursday";
    else if(N == 6) day = "Friday";
    return day;
}

If this interests you take a look at http://mathforum.org/library/drmath/view/55837.html and http://mathforum.org/dr.math/faq/faq.calendar.html - RichardSolvang

ScriptLibrary


LibraryDetectedParticles

Added a "detected" key in addition to "owner" and "self".
Also changed the way it shuts off and on- by listening.

//This is an updated version of a normal particle script- James Hanner
// Mask Flags - set to TRUE to enable
integer glow = TRUE;            // Make the particles glow
integer bounce = FALSE;          // Make particles bounce on Z plane of object
integer interpColor = TRUE;     // Go from start to end color
integer interpSize = TRUE;      // Go from start to end size
integer wind = TRUE;           // Particles effected by wind
integer followSource = TRUE;    // Particles follow the source
integer followVel = TRUE;       // Particles turn to velocity direction

//Set these as the channel and message to use to turn on and off the system
integer ch = 0;
string messageon = "";
string messageoff = "";

// Choose a pattern from the following:
// PSYS_SRC_PATTERN_EXPLODE
// PSYS_SRC_PATTERN_DROP
// PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY
// PSYS_SRC_PATTERN_ANGLE_CONE
// PSYS_SRC_PATTERN_ANGLE
integer pattern = PSYS_SRC_PATTERN_EXPLODE;

// Select a target for particles to go towards
// "" for no target, "owner" will follow object owner
//    and "self" will target this object
// and "detected" to use the nearest person
//    or put the key of an object for particles to go to
key target = "self";

// Particle paramaters
float age = 1;                  // Life of each particle
float maxSpeed = 1;            // Max speed each particle is spit out at
float minSpeed = 1;            // Min speed each particle is spit out at
string texture = "";                 // Texture used for particles, default used if blank
float startAlpha = 1;           // Start alpha (transparency) value
float endAlpha = 0.1;           // End alpha (transparency) value
vector startColor = <0,1,0>;    // Start color of particles <R,G,B>
vector endColor = <1,0,0>;      // End color of particles <R,G,B> (if interpColor == TRUE)
vector startSize = <1,1,1>;     // Start size of particles
vector endSize = <1,0,1>;       // End size of particles (if interpSize == TRUE)
vector push = <0,0,0>;          // Force pushed on particles

// System paramaters
float rate = 1.5;            // How fast (rate) to emit particles
float radius = 1;          // Radius to emit particles for BURST pattern
integer count = 1;        // How many particles to emit per BURST
float outerAngle = 1.54;    // Outer angle for all ANGLE patterns
float innerAngle = 1.55;    // Inner angle for all ANGLE patterns
vector omega = <0,0,0>;    // Rotation of ANGLE patterns around the source
float life = 0;             // Life in seconds for the system to make particles

// Script variables
integer pre = 2;          //Adjust the precision of the generated list.

integer flags;
list sys;
integer type;
vector tempVector;
rotation tempRot;
string tempString;
integer i;

string float2String(float in)
{
    return llGetSubString((string)in,0,pre - 7);
}

updateParticles()
{
    flags = 0;
    if(target == "detected") target = llDetectedKey(0);
    if (target == "owner") target = llGetOwner();
    if (target == "self") target = llGetKey();
    if (glow) flags = flags | PSYS_PART_EMISSIVE_MASK;
    if (bounce) flags = flags | PSYS_PART_BOUNCE_MASK;
    if (interpColor) flags = flags | PSYS_PART_INTERP_COLOR_MASK;
    if (interpSize) flags = flags | PSYS_PART_INTERP_SCALE_MASK;
    if (wind) flags = flags | PSYS_PART_WIND_MASK;
    if (followSource) flags = flags | PSYS_PART_FOLLOW_SRC_MASK;
    if (followVel) flags = flags | PSYS_PART_FOLLOW_VELOCITY_MASK;
    if (target != "") flags = flags | PSYS_PART_TARGET_POS_MASK;
    sys = [  PSYS_PART_MAX_AGE,age,
                        PSYS_PART_FLAGS,flags,
                        PSYS_PART_START_COLOR, startColor,
                        PSYS_PART_END_COLOR, endColor,
                        PSYS_PART_START_SCALE,startSize,
                        PSYS_PART_END_SCALE,endSize,
                        PSYS_SRC_PATTERN, pattern,
                        PSYS_SRC_BURST_RATE,rate,
                        PSYS_SRC_ACCEL, push,
                        PSYS_SRC_BURST_PART_COUNT,count,
                        PSYS_SRC_BURST_RADIUS,radius,
                        PSYS_SRC_BURST_SPEED_MIN,minSpeed,
                        PSYS_SRC_BURST_SPEED_MAX,maxSpeed,
                        PSYS_SRC_TARGET_KEY,target,
                        PSYS_SRC_ANGLE_BEGIN,innerAngle,
                        PSYS_SRC_ANGLE_END,outerAngle,
                        PSYS_SRC_OMEGA, omega,
                        PSYS_SRC_MAX_AGE, life,
                        PSYS_SRC_TEXTURE, texture,
                        PSYS_PART_START_ALPHA, startAlpha,
                        PSYS_PART_END_ALPHA, endAlpha
                            ];

    llParticleSystem(sys);
}

default
{
    state_entry()
    {
        updateParticles();                //Start making particles
    llListen(ch, "", NULL_KEY, messageoff);
    }

    listen(integer channel, string name, key id, string message)
    {
    state off;        //Switch to the off state
    }
}

state off
{
    state_entry()
    {
        llParticleSystem([]);        //Stop making particles
        llListen(ch, "", NULL_KEY, messageon);
    }

    listen(integer channel, string name, key id, string message)
    {
    state default;
    }
}


LibraryDialogModule

This script is a standalone module for the handling of dialog boxes. All you, the script writer, has to do is build a linked message handler. Use the example access script as a starting point.

if you are using a version prior to 9.3 please upgrade as there is a bug which could result in the responce not being registered
8.x is not compatible with 9.x, if you are using a version prior to 8.4 please upgrade as there is a bug with bit 17

Security:
The dialog boxes each use a random unique negative channel, for each dialog box.
Channel numbers range from -2147483648 to -255 with the possibility of the lower 8 bits being zero.
The listens are bound to the user's key. This means that no script or other user can activate the interface.
By using a negative number there is no way for the user to type in a command and have it mistaken as a response.
Only responses that are valid buttons from the requested user (and in the mask) will result in a return; no 3rd party tampering is possible.
It would be very difficult for anyone to predict the channel number to listen on (2 billion is a big range).

The Request:
The link message string format is a list. The first element of this list is the integer that will be used as the second integer in the return the link message. The rest of this list is string that will be returned (in list form with the same separating character).

The link message key format is a list. The first element is the key of the user, if this is left blank (or NULL_KEY) the owner is assumed. The second element is the message, this can be any valid string. The third element is a float, which is the dialog box time to live. The forth element is an integer it is used as a mask for when a button is pressed if it will trigger a response. The rest of the list is for the buttons; an empty list can be used, but it will default to ["OK"] (there is no way around this, just cope).

The Response:
The link message string format is a list. It is whatever was passed as the second string minus the first entry of that list.
The link message key format is a list. The first element of this list is the number of the button pressed (or error code). The second entry is the text of the button pressed. The third entry is the key it used for creating the dialog box. The forth entry is the user name. The fifth entry is how much time was left in the time to live of the dialog box (not very useful but maybe it is).
The response always goes back to only the prim that requested the dialog box.
(string)dump(llList2List(parse(paramString),1,-1)),
(key)dump([integer buttonNum, string buttonText, key user, string userName, float aproxTimeLeft],"")

There is no way to create a dialog box that won't timeout. The value must be between 5.0 and 7200.0; or it will be replaced by the default timeout. The timeout won't make a box disapeer off the users screen, but the script will stop listening for results.

Notes:
Know your data; if your strings contain "|" then don't use "|" as the separator. If the script acts weirdly this is probably why. The separator can only be one character. The two different strings being passed in the linked message can use different separating characters.

Misc:
The ButtonMask has a few extra tricks.
The first 12 bits corespond to the 12 buttons (bits 0 -> 11)
Bits 16 through 22 are special bits for error handling and setup.
All other bits are ignored, do with them as you please but be warned they may be used in the future.
Bit | Hex Value | Event | Return Button
0 | 0x000001 | Button 0 Pressed | 0
1 | 0x000002 | Button 1 Pressed | 1
2 | 0x000004 | Button 2 Pressed | 2
3 | 0x000008 | Button 3 Pressed | 3
4 | 0x000010 | Button 4 Pressed | 4
5 | 0x000020 | Button 5 Pressed | 5
6 | 0x000040 | Button 6 Pressed | 6
7 | 0x000080 | Button 7 Pressed | 7
8 | 0x000100 | Button 8 Pressed | 8
9 | 0x000200 | Button 9 Pressed | 9
10 | 0x000400 | Button 10 Pressed | 10
11 | 0x000800 | Button 11 Pressed | 11
16 | 0x010000 | Dialog box times out | -1
17 | 0x020000 | Queue being cleared | -2
18 | 0x040000 | Target agent not found | -3
19 | 0x080000 | No buttons to listen for, bad mask or bad return channel | -4
20 | 0x100000 | Clears the queue then creates the dialog box | -2 via Bit 17
21 | 0x200000 | Same as bit 20 but only for boxes for the current user | -2 via Bit 17
22 | 0x400000 | Target agent has left the sim | -5

The queue is cleared in two ways: on_rez or when creating a dialog box requests it. When the script is rez'ed, the old dialog box requests become meaningless (because the script cannot receive the response). By having bit 17 on, it allows for this event to be returned to the script requesting the box. By having bit 20 turned on also causes the queue to be reset (exactly like an on_rez). Using bit 20 may have undesired effects if you are using this module with multiple scripts. Using bit 21 causes a partial clear. It clears out all boxes for the requested user; it is advisable to use this option instead of bit 20.

Todo:
When bits 20 and 21 are true have the inverse result happen. All boxes not of the current user would be cleared from the queue.

Tricks:
Pass a list of useful values as the return list. Have them correspond to the button list. Then use the code below to get that value from the return (remember if you have any of bits 16 -> 19 enabled you will get negative index values on errors).
llList2String(parse(c),(integer)llList2String(parse(d),0));

Example Access script to Dialog Module:
string sepchar = "|";
integer answer = 546;
integer DialogComm = 12;

dialog(key user, string message, float timeout, list buttons, integer buttonmask, integer retchan, list ret)
{
    llMessageLinked(llGetLinkNumber(), DialogComm, dump([retchan] + ret, sepchar),
        dump([user, message, timeout, buttonmask] + buttons, sepchar));
}

string dump(list a, string b)
{
    string c = (string)a;
    if(1+llSubStringIndex(c,b) || llStringLength(b)!=1)
    {
        b += "|\\/?!@#$%^&*()_=:;~{}[],\n qQxXzZ";
        integer d = -llStringLength(b);
        while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
            d++;
        b = llGetSubString(b,d,d);
    }
    return b + llDumpList2String(a, b);
}

list parse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [b],[]);
}

default
{
    state_entry()
    {
        integer rint = 6;
        string question = "Would you like a drink?";
        list Answers = ["Yes", "No", "Strongest"];
        integer AnswerMask = 0x005;
        list ExtraPassback = ["extra info to pass back"];
        dialog(llGetOwner(), question, 30,Answers, AnswerMask , answer, ExtraPassback);
    }
    touch_start(integer a)
    {
        dialog(llDetectedKey(0), "Hello\nDo you like me?", 30, ["Yes", "No"], 0xf0fff, answer, ["I think they like us"]);
    }
    link_message(integer a, integer b, string c, key d)
    {
        if(b==answer)
        {
            llWhisper(0,(string)b);
            llWhisper(0,llList2CSV(parse(c)));
            llWhisper(0,llList2CSV(parse(d)));
            //c is the return list
            //d is a list [button number pressed, user, time left, user name, button text]
        }
    }
}

Dialog Module:
//////////////////////////////////////////////////////////////////////////////////////
//
//    Dialog Module
//    Version 9.3 Release
//    Copyright (C) 2004-2006, Strife Onizuka
//    http://home.comcast.net/~mailerdaemon/
//    http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule
//
//    This library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Lesser General Public License
//    as published by the Free Software Foundation; either
//    version 2.1 of the License, or (at your option) any later version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with this library; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//Don't change anything else unless you *really* need to.

list handles;
list time;
list chans;
string users;//save memory
list answer;
list button;
list prim;
list mask;
list intchan;

integer ticks;

remove(integer a)
{
    llListenRemove(llList2Integer(handles,a));
    handles = llDeleteSubList(handles,a,a);
    time    = llDeleteSubList(time,   a,a);
    chans   = llDeleteSubList(chans,  a,a);
    answer  = llDeleteSubList(answer, a,a);
    button  = llDeleteSubList(button, a,a);
    prim    = llDeleteSubList(prim,   a,a);
    mask    = llDeleteSubList(mask,   a,a);
    intchan = llDeleteSubList(intchan,a,a);
    users   = llDeleteSubString(users,a*=36,a+35);
}

list TightListParse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}

clear()
{
    integer a = llGetListLength(handles);
    while(a)
    {
        llListenRemove(llList2Integer(handles,--a));
        if(llList2Integer(mask,a)&0x20000)
            llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
                    llList2String(answer,a), "|-2||"+llGetSubString(users,a * 36,(a * 36) + 35)+"||-1");
    }
    llSetTimerEvent(ticks = 0);
}

default
{
    state_entry()
    {

            llOwnerSay("Dialog Module, Version 9.2, Released Under the GNU Lesser General Public License");
            llOwnerSay("Copyright (C) 2004-2006, Strife Onizuka, http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule");

    }
    on_rez(integer a)
    {
        clear();
    }
    link_message(integer a, integer b, string c, key d)
    {
        if(b == 12)
        {
            b = llSubStringIndex(llDeleteSubString(c,0,0), llGetSubString(c,0,0));
            list    e = TightListParse(d);
            integer buttonmask =  (integer)llList2String(e,3);
            string  user       =  llList2String(e,0);
            list    buttons    =  llDeleteSubList(e,0,3);
            float   timeout    =  (float)llList2String(e,2);
            integer cat        =  (integer)llFrand(-2147483392.0) - 255;
            integer chan       =  (integer)llDeleteSubString(c, b + 1, 0);
            string  ans        =  llDeleteSubString(c,0,b);

            if(buttonmask & 0x100000)
                clear();
            else if(buttonmask & 0x200000)
            {//clean out other box's that went to this user.
                while(1 + (b = llSubStringIndex(users, user)))
                {
                    if(llList2Integer(mask,b/=36)&0x20000)
                        llMessageLinked(llList2Integer(prim,b), llList2Integer(intchan,b),
                                        llList2String(answer,b), "|-2||"+llGetSubString(users,b * 36,(b * 36) + 35)+"||-1");
                    remove(b);
                }
                if(time == [])
                    llSetTimerEvent(ticks = 0);
            }

            if(user == "" || user == NULL_KEY) //lazy check
                user = llGetOwner();
            if(!llGetAgentInfo(user))
            {// target not in the sim
                if(buttonmask & 0x40000)
                    llMessageLinked(a, chan, ans, "|-3||"+user+"||0");
                jump end1;//instead of a return, too many local variables to clear off the stack.
            }
            while(1+llListFindList(chans,[cat]))
                --cat;

            b = llListen(cat,"",user,"");
            llDialog(user, llList2String(e,1), buttons, cat);
            if(chan != 12)
            {//loopback catch
                if(buttons == []) // so we can match the ok button
                    buttons = ["OK"];
                if(buttonmask & ((1<<llGetListLength(buttons)) - 1))
                { //we checked the mask to see if we should expect any values back
                    chans   +=  cat;
                    handles +=  b;
                    if(timeout < 5.0 || timeout > 7200)
                        timeout = 5.0;
                    time    +=  (ticks + (timeout / 5.0));
                    users   +=  user;
                    answer  +=  ans;
                    button  +=  (llGetSubString(d,0,0) + llDumpList2String(buttons, llGetSubString(d,0,0)));
                    prim    +=  a;
                    mask    +=  buttonmask;
                    intchan +=  chan;
                    llSetTimerEvent(5.0);
                    jump end2;//instead of a return, too many local variables to clear off the stack.
                }
            }
            llListenRemove(b);
            if(buttonmask & 0x80000)
                llMessageLinked(a, chan, ans, "|-4||"+(string)user+"||0");
        }
        @end1;@end2;
    }
    listen(integer a, string b, key c, string d)
    {
        if(a+1 && llGetSubString(users,36 * a=llListFindList(chans,[a]),(a * 36) + 35) == c)
        {//it's one of our listens
            integer f = llListFindList(TightListParse(llList2String(button,a)),[d]);
            if(f+1)
            {//we matched a button
                if(llList2Integer(mask,a)&(1<<f))
                {
                    list ret = [f, d, c, b,(llList2Float(time,a) - ticks) * 5.0];
                    if(llSubStringIndex(d = (string)ret,b = "|") + 1)
                    {
                        f = -37;
                        b = "|\\/?!@#$%^&*()_=:;~{}[]<>`',\n\" qQxXzZ";
                        do;while(1+llSubStringIndex(d,llGetSubString(b,f,f)) && ++f);
                        b = llGetSubString(b,f,f);
                    }
                    d = "";
                    llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
                                    llList2String(answer,a), (b + llDumpList2String(ret, b)));
                }
                remove(a);
                if(time == [])
                    llSetTimerEvent(ticks = 0);
            }
        }
    }
    timer()
    {
        ++ticks;
        integer a = llGetListLength(time);
        float c;
        key d;
        while(a)
        {
            if(((c = llList2Float(time,a)) <= ticks) || !llGetAgentInfo(d = llGetSubString(users,--a * 36,(a * 36) + 35)))
            {
                if(llList2Integer(mask,a)&0x10000)
                    llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
                            llList2String(answer,a), "|-1||"+(string)d+"||"+(string)((ticks - c) * 5.0));

                remove(a);
            }
        }
        if(time == [])
            llSetTimerEvent(ticks = 0);
    }
}

ESL Version:
//////////////////////////////////////////////////////////////////////////////////////
//
//    Dialog Module
//    Version 9.3 Release
//    Copyright (C) 2004-2006, Strife Onizuka
//    http://home.comcast.net/~mailerdaemon/
//    http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule
//
//    This library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Lesser General Public License
//    as published by the Free Software Foundation; either
//    version 2.1 of the License, or (at your option) any later version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with this library; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//////////////////////////////////////////////////////////////////////////////////////

#ifndef DIALOGCOMM
    #define DIALOGCOMM    12
#endif
#ifdef USESCRIPTNAME
integer DialogComm = DIALOGCOMM;
#elif !defined DialogComm
#define DialogComm DIALOGCOMM
#endif

#ifndef TICKSIZE
    #define TICKSIZE 5.0
#endif
#ifndef DEFAULTTIMEOUT
    #define DEFAULTTIMEOUT TICKSIZE
#endif
//////////////////////////////////////////////////////////////////////////////////////
//Don't change anything else unless you *really* need to.
#define ZERO_INTEGER 0
list handles;
list time;
list chans;
string users;//save memory
list answer;
list button;
list prim;
list mask;
list intchan;

integer ticks;

remove(integer a)
{
    llListenRemove(llList2Integer(handles,a));
    handles = llDeleteSubList(handles,a,a);
    time    = llDeleteSubList(time,   a,a);
    chans   = llDeleteSubList(chans,  a,a);
    answer  = llDeleteSubList(answer, a,a);
    button  = llDeleteSubList(button, a,a);
    prim    = llDeleteSubList(prim,   a,a);
    mask    = llDeleteSubList(mask,   a,a);
    intchan = llDeleteSubList(intchan,a,a);
    users   = llDeleteSubString(users,a*=36,a+35);
}

#define wipe() llSetTimerEvent(ticks = ZERO_INTEGER)
#define quickdump(a, b)  (b + llDumpList2String(a, b))
#ifndef quickdump
string quickdump(list a, string b){  return b + llDumpList2String(a, b); }
#endif

list TightListParse(string a) {
    string b = llGetSubString(a,ZERO_INTEGER,ZERO_INTEGER);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,ZERO_INTEGER,ZERO_INTEGER), [a=b],[]);
}

clear()
{
    integer a = llGetListLength(handles);
    while(a)
    {
        llListenRemove(llList2Integer(handles,--a));
        if(llList2Integer(mask,a)&0x20000)
            llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
                    llList2String(answer,a), "|-2||"+llGetSubString(users,a * 36,(a * 36) + 35)+"||-1");
    }
    wipe();
}

default
{
    state_entry()
    {
        #ifdef USESCRIPTNAME
        if((DialogComm += (integer)llList2String(llParseString2List(llGetScriptName(), [" "],[]), -1)) == DialogComm)
        {
        #endif
            llOwnerSay("Dialog Module, Version 9.2, Released Under the GNU Lesser General Public License");
            llOwnerSay("Copyright (C) 2004-2006, Strife Onizuka, http://secondlife.com/badgeo/wakka.php?wakka=LibraryDialogModule");
        #ifdef USESCRIPTNAME
        }
        #endif
    }
    on_rez(integer a)
    {
        clear();
    }
    link_message(integer a, integer b, string c, key d)
    {
        if(b == DialogComm)
        {
            b = llSubStringIndex(llDeleteSubString(c,ZERO_INTEGER,ZERO_INTEGER), llGetSubString(c,ZERO_INTEGER,ZERO_INTEGER));
            list    e = TightListParse(d);
            integer buttonmask =  (integer)llList2String(e,3);
            string  user       =  llList2String(e,ZERO_INTEGER);
            list    buttons    =  llDeleteSubList(e,ZERO_INTEGER,3);
            float   timeout    =  (float)llList2String(e,2);
            integer cat        =  (integer)llFrand(-2147483392.0) - 255;
            integer chan       =  (integer)llDeleteSubString(c, b + 1, ZERO_INTEGER);
            string  ans        =  llDeleteSubString(c,ZERO_INTEGER,b);

            if(buttonmask & 0x100000)
                clear();
            else if(buttonmask & 0x200000)
            {//clean out other box's that went to this user.
                while(1 + (b = llSubStringIndex(users, user)))
                {
                    if(llList2Integer(mask,b/=36)&0x20000)
                        llMessageLinked(llList2Integer(prim,b), llList2Integer(intchan,b),
                                        llList2String(answer,b), "|-2||"+llGetSubString(users,b * 36,(b * 36) + 35)+"||-1");
                    remove(b);
                }
                if(time == [])
                    wipe();
            }

            if(user == "" || user == NULL_KEY) //lazy check
                user = llGetOwner();
            if(!llGetAgentInfo(user))
            {// target not in the sim
                if(buttonmask & 0x40000)
                    llMessageLinked(a, chan, ans, "|-3||"+user+"||0");
                jump end1;//instead of a return, too many local variables to clear off the stack.
            }
            while(1+llListFindList(chans,[cat]))
                --cat;

            b = llListen(cat,"",user,"");
            llDialog(user, llList2String(e,1), buttons, cat);
            if(chan != DialogComm)
            {//loopback catch
                if(buttons == []) // so we can match the ok button
                    buttons = ["OK"];
                if(buttonmask & ((1<<llGetListLength(buttons)) - 1))
                { //we checked the mask to see if we should expect any values back
                    chans   +=  cat;
                    handles +=  b;
                    if(timeout < 5.0 || timeout > 7200)
                        timeout = DEFAULTTIMEOUT;
                    time    +=  (ticks + (timeout / TICKSIZE));
                    users   +=  user;
                    answer  +=  ans;
                    button  +=  quickdump(buttons,llGetSubString(d,ZERO_INTEGER,ZERO_INTEGER));
                    prim    +=  a;
                    mask    +=  buttonmask;
                    intchan +=  chan;
                    llSetTimerEvent(TICKSIZE);
                    jump end2;//instead of a return, too many local variables to clear off the stack.
                }
            }
            llListenRemove(b);
            if(buttonmask & 0x80000)
                llMessageLinked(a, chan, ans, "|-4||"+(string)user+"||0");
        }
        @end1;@end2;
    }
    listen(integer a, string b, key c, string d)
    {
        if(a+1 && llGetSubString(users,36 * a=llListFindList(chans,[a]),(a * 36) + 35) == c)
        {//it's one of our listens
            integer f = llListFindList(TightListParse(llList2String(button,a)),[d]);
            if(f+1)
            {//we matched a button
                if(llList2Integer(mask,a)&(1<<f))
                {
                    list ret = [f, d, c, b,(llList2Float(time,a) - ticks) * TICKSIZE];
                    if(llSubStringIndex(d = (string)ret,b = "|") + 1)
                    {
                        f = -37;
                        b = "|\\/?!@#$%^&*()_=:;~{}[]<>`',\n\" qQxXzZ";
                        do;while(1+llSubStringIndex(d,llGetSubString(b,f,f)) && ++f);
                        b = llGetSubString(b,f,f);
                    }
                    d = "";
                    llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
                                    llList2String(answer,a), quickdump(ret, b));
                }
                remove(a);
                if(time == [])
                    wipe();
            }
        }
    }
    timer()
    {
        ++ticks;
        integer a = llGetListLength(time);
        float c;
        key d;
        while(a)
        {
            if(((c = llList2Float(time,a)) <= ticks) || !llGetAgentInfo(d = llGetSubString(users,--a * 36,(a * 36) + 35)))
            {
                if(llList2Integer(mask,a)&0x10000)
                    llMessageLinked(llList2Integer(prim,a), llList2Integer(intchan,a),
                            llList2String(answer,a), "|-1||"+(string)d+"||"+(string)((ticks - c) * TICKSIZE));

                remove(a);
            }
        }
        if(time == [])
            wipe();
    }
}
ScriptLibrary | LibraryInventoryMenuModule


LibraryDoor

// deluxe door script by Ezhar Fairlight
// features: one prim, no building skills required, automatic closing, workaround for rotation drift,
// doesn't mess up when moved, adjustable direction (inwards/outwards) and sound volume, HHGG quotes!
// updated for SL 1.5
// 1.6 2007-02-22 Now also supports use as part of a link set (local rotation)

// just rez a cube primitive and put this script inside - it will shape and texture itself into a door

// ********** SETTINGS HERE ************
float       TIMER       = 30.0;      // automatically close the door after this many seconds,
                                    // set to 0 to disable automatic closing

integer     DIRECTION   = 1;       // direction door opens in. Either 1 (outwards) or -1 (inwards);

float       VOLUME      = 0.8;      // sound volume, 1.0 loudest, 0.0 to disable sound
// ********** END OF SETTINGS **********

key         SOUND_OPEN  = "cb340647-9680-dd5e-49c0-86edfa01b3ac";
key         SOUND_CLOSE = "e7ff1054-003d-d134-66be-207573f2b535";

vector      gPos;      // door position (objects move a tiny amount
                        // away from their position each time they are rotated,
                        // thus we need to workaround this by resetting
                        // the position after rotating)

door(integer open) {
    if (open) {
        llTriggerSound(SOUND_OPEN, VOLUME);
        llSetLocalRot(llEuler2Rot(<0, 0, -DIRECTION * PI_BY_TWO>) * llGetLocalRot());
    } else { // close
        llSetLocalRot(llEuler2Rot(<0, 0, DIRECTION * PI_BY_TWO>) * llGetLocalRot());
        llTriggerSound(SOUND_CLOSE, VOLUME);
    }
}

default {   // first time startup
    state_entry() {
        if (llGetTexture(0) == "89556747-24cb-43ed-920b-47caed15465f") { // is default texture, set it up
            llSetPos(llGetPos() + <0, 0, 3.325 / 2 - 0.25>);
            llSetPrimitiveParams([PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0.375, 0.875, 0>, 0.0, ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
                PRIM_SIZE, <0.2, 4, 3.325>,
                PRIM_TEXTURE, 0, "086c7e6b-bdd6-7388-f146-f3d1b353ed15", <2.000000, 0.060000, 0.000000>, <0.500015, 0.469985, 0.000000>, 1.570840,
                PRIM_TEXTURE, 1, "086c7e6b-bdd6-7388-f146-f3d1b353ed15", <-2.000000, 1.000000, 0.000000>, <0.500015, 0.000000, 0.000000>, 0.000000,
                PRIM_TEXTURE, 2, "086c7e6b-bdd6-7388-f146-f3d1b353ed15", <0.100000, 1.000000, 0.000000>, <0.599994, 0.000000, 0.000000>, 0.000000,
                PRIM_TEXTURE, 3, "086c7e6b-bdd6-7388-f146-f3d1b353ed15", <2.000000, 1.000000, 0.000000>, <0.500015, 0.000000, 0.000000>, 0.000000,
                PRIM_TEXTURE, 4, "086c7e6b-bdd6-7388-f146-f3d1b353ed15", <2.000000, 0.080000, 0.000000>, <0.500015, 0.550005, 0.000000>, 1.570840,
                PRIM_TEXTURE, 5, "086c7e6b-bdd6-7388-f146-f3d1b353ed15", <0.100000, 1.000000, 0.000000>, <0.449995, 0.000000, 0.000000>, 0.000000,
                PRIM_TEXTURE, 6, "086c7e6b-bdd6-7388-f146-f3d1b353ed15", <0.100000, 1.000000, 0.000000>, <0.449995, 0.000000, 0.000000>, 0.000000]
            );
            llSetObjectName("Door Deluxe");
            llSetObjectDesc("Door Deluxe (for SL 1.6) - free script available at http://www.lslwiki.net/lslwiki/wakka.php?wakka=LibraryDoor");
            llSay(0, "Thank you for making a simple door very happy.");
        }
        gPos = llGetPos();  // remember where we're supposed to be
        door(TRUE);
        state closed;
    }
}

state closed {  // door is closed
    on_rez(integer start_param) {
        gPos = llGetPos();
    }

    state_entry() {
        door(FALSE);
    }

    touch_start(integer total_number) {
        state open;
    }

    moving_end() {  // done moving me around, store new position
        gPos = llGetPos();
    }
}

state open {    // door is open
    on_rez(integer start_param) {
        gPos = llGetPos();
        state closed;
    }

    state_entry() {
        llSetTimerEvent(TIMER);
        llSetPos(gPos); // rotation drift workaround
        door(TRUE);
    }

    touch_start(integer num) {
        state closed;
    }

    timer() { // auto-close
        state closed;
    }

    moving_start() { // close when being moved
        state closed;
    }

    state_exit() {
        llSetTimerEvent(0);
    }
}

ScriptLibrary


LibraryDumpPrimParameters

DumpPrimParameters

This script dumps prim params to a string, which can then be used to recreate settings in code. It also tries to substitute values with constants.

The script only deals with a subset of available params, so please add to this code whenever you implement more of them.

// Playahead DumpPrimParams - dump current object prim parameters to LSL list string
// This code is free to copy and modify as long as this header remains intact.
// It would also be prudent to update this script with additions and bug fixes.

string vectorToString( vector val )
{
    if( val == ZERO_VECTOR )
    {
        return "ZERO_VECTOR";
    }
    else
    {
        string dump = "<"+floatToString( val.x )+", "+floatToString( val.y )+", "+floatToString( val.z )+">";

        return dump;
    }
}

string floatToString( float val )
{
    integer round = llFloor( val );
    if( val == (float) round )
    {
        return (string)round + ".0";
    }
    else
    {
        return (string) val;
    }
}

string parseListToCSV( list l )
{
    string result;
    integer c = llGetListLength( l );
    integer last = c - 1;
    integer i;

    for( i=0; i < c; i++ )
    {
        string strval;

        integer entryType = llGetListEntryType( l, i );

        if( entryType == TYPE_FLOAT )
        {
            strval = floatToString( llList2Float( l, i ) );
        }
        else
        if( entryType == TYPE_INTEGER )
        {
            integer val = llList2Integer( l, i );
            strval = (string) val;
        }
        else
        if( entryType == TYPE_KEY )
        {
            key val = llList2Key( l, i );
            if( val == NULL_KEY )
            {
                strval = "NULL_KEY";
            }
            else
            {
                strval = (string) val;
            }
        }
        else
        if( entryType == TYPE_ROTATION )
        {
            rotation val = llList2Rot( l, i );
            if( val == ZERO_ROTATION )
            {
                strval = "ZERO_ROTATION";
            }
            else
            {
                strval = (string) val;
            }
        }
        else
        if( entryType == TYPE_STRING )
        {
            string val = llList2String( l, i );
            strval = "\"" + val + "\"";
        }
        else
        if( entryType == TYPE_VECTOR )
        {
            strval = vectorToString( llList2Vector( l, i ) );
        }

        result += strval;

        if( i != last )
        {
            result += ", ";
        }
    }

    return result;
}

string AddParam( integer getId, string setName )
{
    string dump;

    list params = llGetPrimitiveParams( [ getId ] );

    dump = setName + ", " + parseListToCSV( params );

    return dump;
}

string AddTypeParam( )
{
    list types = [ "PRIM_TYPE_BOX", "PRIM_TYPE_CYLINDER", "PRIM_TYPE_PRISM",  "PRIM_TYPE_SPHERE", "PRIM_TYPE_TORUS", "PRIM_TYPE_TUBE", "PRIM_TYPE_RING"  ];

    string dump;

    list params = llGetPrimitiveParams( [ PRIM_TYPE ] );

    integer type = llList2Integer( params, 0 );

    params = llDeleteSubList( params, 0, 0 );

    dump = "PRIM_TYPE, " + llList2String( types, type ) + ", " + parseListToCSV( params );

    return dump;
}

string AddColorParam( )
{
    string dump;

    list side0 = llGetPrimitiveParams( [ PRIM_COLOR, 0 ] );
    vector color0 = llList2Vector( side0, 0 );
    float alpha0 = llList2Float( side0, 1 );

    integer sides = llGetNumberOfSides();

    integer all_sides_same = TRUE;

    integer i;
    for( i = 1; i < sides; i++ )
    {
        list side = llGetPrimitiveParams( [ PRIM_COLOR, i ] );

        vector color = llList2Vector( side, 0 );
        float alpha = llList2Float( side, 1 );

        if( color == color0 && alpha == alpha0 )
        {
        }
        else
        {
            all_sides_same = FALSE;
            i=sides;
        }
    }

    if( all_sides_same )
    {
        dump = "PRIM_COLOR, ALL_SIDES, "+vectorToString( color0 )+", "+floatToString( alpha0 );
    }
    else
    {
        // Different colors on faces.
        integer last = sides - 1;
        for( i = 0; i < sides; i++ )
        {
            list side = llGetPrimitiveParams( [ PRIM_COLOR, i ] );

            vector color = llList2Vector( side, 0 );
            float alpha = llList2Float( side, 1 );

            dump += "PRIM_COLOR, "+(string)i+", "+vectorToString( color )+", "+floatToString( alpha );
            if( i != last )
            {
                dump +=", ";
            }
        }
    }

    return dump;
}
string AddTextureParam( )
{
    string dump;

    list texture0 = llGetPrimitiveParams( [ PRIM_TEXTURE, 0 ] );

    string uuid0 = llList2String( texture0, 0 );
    vector repeats0 = llList2Vector( texture0, 1 );
    vector offsets0 = llList2Vector( texture0, 2 );
    float rot0 = llList2Float( texture0, 3 );

    integer sides = llGetNumberOfSides();

    integer all_sides_same = TRUE;

    integer i;
    for( i = 1; i < sides; i++ )
    {
        list side = llGetPrimitiveParams( [ PRIM_TEXTURE, i ] );

        string uuid = llList2String( side, 0 );
        vector repeats = llList2Vector( side, 1 );
        vector offsets = llList2Vector( side, 2 );
        float rot = llList2Float( side, 3 );

        if( uuid == uuid0 && repeats == repeats0 && offsets == offsets0 && rot == rot0 )
        {
        }
        else
        {
            all_sides_same = FALSE;
            i=sides;
        }
    }

    if( all_sides_same )
    {
        dump += "PRIM_TEXTURE, ALL_SIDES, \""+uuid0+"\", "+vectorToString( repeats0 )+", "+vectorToString( offsets0 )+", "+floatToString( rot0 );
    }
    else
    {
        // Different colors on faces.
        integer last = sides - 1;
        for( i = 0; i < sides; i++ )
        {
            list side = llGetPrimitiveParams( [ PRIM_TEXTURE, i ] );

            string uuid = llList2String( side, 0 );
            vector repeats = llList2Vector( side, 1 );
            vector offsets = llList2Vector( side, 2 );
            float rot = llList2Float( side, 3 );

            dump += "PRIM_TEXTURE, "+(string)i+", \""+uuid+"\", "+vectorToString( repeats )+", "+vectorToString( offsets )+", "+floatToString( rot );

            if( i != last )
            {
                dump +=", ";
            }
        }
    }

    return dump;
}

list GetParamDumpList( )
{
    list paramDumpList;

    paramDumpList += AddTypeParam( );
    paramDumpList += AddParam( PRIM_SIZE, "PRIM_SIZE" );
    paramDumpList += AddParam( PRIM_ROTATION, "PRIM_ROTATION" );
    paramDumpList += AddColorParam( );
    paramDumpList += AddTextureParam( );

    return paramDumpList;
}

string GetParamDump( )
{
    string dump;
    list paramDumpList = GetParamDumpList();

    dump = llList2CSV( paramDumpList );

    dump = "[ " + dump + "]";

    return dump;
}

default
{
    state_entry()
    {
        string dump = GetParamDump();

        llOwnerSay( dump );
    }
}


LibraryElizaChatbot

Eliza Chatbot

ELIZA is a famous 1966 computer program by Joseph Weizenbaum
ELIZA is a parody of a Rogerian therapist
ELIZA rephrases many of the patient's statements as questions and posing them to the patient.

Transcript of a sample conversation:

You: hi
Eliza Doolittle: how do you do--please state your problem.
You: i don't have a problem
Eliza Doolittle: does that trouble you?
You: no
Eliza Doolittle: you are being a bit negative.
You: does that trouble you?
Eliza Doolittle: we were discussing you-- not me.
You: you have the problem.
Eliza Doolittle: oh, i have the problem
You: yes
Eliza Doolittle: i see.

Chatbot.Eliza.lsl
// ELIZA is a famous 1966 computer program by Joseph Weizenbaum
// ELIZA is a parody of a Rogerian therapist
// ELIZA rephrases many of the patient's statements as questions and posing them to the patient.

// This LSL version was originally ported by Dedric Mauriac
// This LSL version is a port from a BASIC implementation written by John Schugg in January 1985.

// Data Setup:
// Keywords and patterns are contained in a seperate notecard
// a keyword set appears on a line delimited with semi-colon and prefixed with number of responses
// each responses is written on its own line
// if more then one response follows a set of keywords, a random response will be chosen
// the last keyword in the notecard will always be used for responses that are not understood
// blank lines should only appear before keywords or after the last response.
// blank lines are optional and only used for easier management of data

// how long to wait before responding and listening for next user input
float delay = 7.5;

// name of notecard containing patterns and responses
string replyNote = "Chatbot.Eliza.txt";

// stated when end-user repeats themselves
string repeatReply = "please do not repeat yourself";

// greeting
string introduction = "Have any problems?  Let me help you.";

// don't listen to objects?
integer ignoreObjects = TRUE;

list keywords; // recognized keywords
list matchStart; // start position in notecard for responses to each keyword
list matchCount; // number of responses for each keyword
integer lastLine;

// used to rephrase input into responses
list conjugations = [
    "are", "am",
    "were", "was",
    "you", "i",
    "your", "my",
    "ive", "youve",
    "im", "youre",
    "you", "me"
];

// number of lines in reply notecard
integer replyLines;

// id of request for notecard line count
key replyCountId;

// current line number being requested from notecard
integer replyLine;

// id of request for text in notecard for initialization
key replyLineId;

// characters recognized from user input
string recognized = "abcdefghijklmnopqrstuvwxyz ";

// id of listener for user input
integer listener;

// id of request for text in notecard as a response to input
key responseId;

string said; // what the user said
string matched; // keyword/phrase the program understood

integer processing = FALSE; // state if program still processing last input

string say; // response to user input

initialize()
{
    lastLine = -1;
    replyLine = 0;
    replyLines = -1;
    showProgress();
    keywords = [];
    matchCount = [];
    matchStart = [];
    replyCountId = llGetNumberOfNotecardLines(replyNote);
}
string conjugateWord(string word)
{
    // find word to replace
    integer i = llListFindList(conjugations, [word]);

    // not found
    if(i == -1) return word;

    // word found.  opposites in pairs.
    if(i % 2 == 0) return llList2String(conjugations, i + 1);
    return llList2String(conjugations, i - 1);
}
string processConjugates()
{
    // rephrase what user said after the matched keyword.

    integer i = llSubStringIndex(said, matched);
    string text = llGetSubString(said, i + llStringLength(matched), -1);
    list words = llParseString2List(text, [" "], []);
    text = "";

    integer n = llGetListLength(words);

    for(i = 0; i < n; i++)
        text += " " + conjugateWord(llList2String(words, i));

    return text;
}
processResponse(string message)
{
    // format response
    message = llToLower(message);

    // if wildcard in response, rephrase what user said
    integer i = llSubStringIndex(message, "*");
    if(i != -1)
    {
        message = llDeleteSubString(message, i, i);
        message = llInsertString(message, i, processConjugates());
    }

    // prepare to respond
    say = message;
    llSetTimerEvent(delay);
}
respondTo(integer keywordIndex)
{

    // determine what keyword to response to
    matched = llList2String(keywords, keywordIndex);

    // determine what to respond with
    integer start = llList2Integer(matchStart, keywordIndex);
    integer count = llList2Integer(matchCount, keywordIndex);
    integer line = start + llFloor(llFrand(count));

    // prevent repeat response
    while(line == lastLine && count != 1)
        line = start + llFloor(llFrand(count));
    lastLine = line;

    // request text for chosen response
    responseId = llGetNotecardLine(replyNote, line);
}
processMessage(string message)
{
    // don't process if we are still working on the last message
    if(processing) return;
    processing = TRUE;

    message = formatMessage(message);

    // do not bother with repeat input
    if(said == message)
    {
        say = repeatReply;
        llSetTimerEvent(delay);
        return;
    }

    // remember what user said last
    said = message;

    // itterate through keywords
    integer n = llGetListLength(keywords);
    integer i;
    for(i = 0; i < n; i++)
    {
        // padd keyword with spaces
        string keyword = " " + llList2String(keywords, i) + " ";

        // if keyword in user-input, respond to it.
        if(llSubStringIndex(message, keyword) != -1)
        {
            respondTo(i);
            return;
        }
    }

    // since keyword not found, response with last keywords replies (not understood)
    respondTo(n - 1);
}
string formatMessage(string message)
{
    // remove punctuation and change everything to lowercase

    string message = llToLower(message);
    string format = " ";
    integer i;
    integer n = llStringLength(message);
    for(i = 0; i < n; i++)
    {
        string char = llGetSubString(message, i, i);
        if(llSubStringIndex(recognized, char) != -1)
            format += char;
    }
    return format + " ";
}
showProgress()
{
    // determine how much is done
    integer percent = (integer)(((float)replyLine / (float)replyLines) * 100);

    // build a text based progress bar - 59% [|||||......]
    string progress = (string)percent + "%\n[";
    integer i = 0;
    for(i = 0; i < 100; i+= 3)
        if(i <= percent) progress += "|";
        else progress += ".";
    progress += "]";

    llSetText("Initializing\n" + progress, <1,1,1>, 1);
}
readReplies()
{
    // at end of notecard?
    if(replyLine >= replyLines)
    {
        startSession();
        return;
    }

    showProgress();

    // read next line
    replyLineId = llGetNotecardLine(replyNote, replyLine++);
}
initializeReply(string data)
{
    data = llToLower(data);

    // is this a pattern?
    if(llSubStringIndex(data, ";") != -1)
    {
        list patterns = llParseString2List(data, [";"], []);
        integer count = llList2Integer(patterns, 0);
        keywords += llDeleteSubList(patterns, 0, 0);
        replyLine++;
        setMatchStart();
        setMatchLength(count);
        replyLine += count;
    }

    readReplies();
}
setMatchStart()
{
    // set starting index for all keywords that do not yet have it set
    integer count = llGetListLength(keywords);
    integer i = llGetListLength(matchStart);
    for(; i < count; i++)
        matchStart += [replyLine - 1];
}
setMatchLength(integer length)
{
    // determine number of replies for keyword set
    integer count = llGetListLength(keywords);
    integer i = llGetListLength(matchCount);
    for(; i < count; i++)
        matchCount += [length];
}
startSession()
{
    // start listening
    llSetText("", ZERO_VECTOR, 0);
    if(listener != 0) llListenRemove(listener);
    listener = llListen(PUBLIC_CHANNEL, "", NULL_KEY, "");
    llSay(0, introduction);
}
default
{
    state_entry()
    {
        initialize();
    }
    on_rez(integer start_param)
    {
        initialize();
    }
    changed(integer change)
    {
        if(change && CHANGED_INVENTORY) initialize();
    }
    dataserver(key queryId, string data)
    {
        // finding reply count
        if(queryId == replyCountId)
        {
            replyLines = (integer)data;
            readReplies();
            return;
        }

        // initializing keywords/replies
        if(queryId == replyLineId)
        {
            initializeReply(data);
            return;
        }

        // retrieving response template
        if(queryId == responseId)
        {
            processResponse(data);
            return;
        }

    }
    listen(integer channel, string name, key id, string message)
    {
        if(ignoreObjects && llGetOwnerKey(id) != id) return;

        processMessage(message);
    }
    touch_start(integer count)
    {
        llSay(0, introduction);
    }
    timer()
    {
        // say what we previously decided to say
        llSay(PUBLIC_CHANNEL, say);
        llSetTimerEvent(0);

        // listen for next input
        processing = FALSE;
    }
}

Chatbot.Eliza.txt
3;CAN YOU
DON'T YOU BELIEVE THAT I CAN*
PERHAPS YOU WOULD LIKE TO BE ABLE TO*
YOU WANT ME TO BE ABLE TO*

2;CAN I
PERHAPS YOU DON'T WANT TO*
DO YOU WANT TO BE ABLE TO*

4;YOU ARE;YOURE
WHAT MAKES YOU THINK I AM*
DOES IT PLEASE YOU TO BELIEVE I AM*
PERHAPS YOU WOULD LIKE TO BE*
DO YOU SOMETIMES WISH YOU WERE*

4;I DONT
DON'T YOU REALLY*
WHY DON'T YOU*
DO YOU WISH TO BE ABLE TO*
DOES THAT TROUBLE YOU?

3;I FEEL
TELL ME MORE ABOUT SUCH FEELINGS.
DO YOU OFTEN FEEL*
DO YOU ENJOY FEELING*

3;WHY DONT YOU
DO YOU REALLY BELIEVE I DON'T*
PERHAPS IN GOOD TIME I WILL*
DO YOU WANT ME TO*

2;WHY CANT I
DO YOU THINK YOU SHOULD BE ABLE TO*
WHY CAN'T YOU*

3;ARE YOU
WHY ARE YOU INTERESTED IN WHETHER OR NOT I AM*
WOULD YOU PREFER IF I WERE NOT*
PERHAPS IN YOUR FANTASIES I AM*

3;I CANT
HOW DO YOU KNOW YOU CAN'T*
HAVE YOU TRIED?
PERHAPS YOU CAN NOW*

4;I AM;IM
DID YOU COME TO ME BECAUSE YOU ARE*
HOW LONG HAVE YOU BEEN*
DO YOU BELIEVE IT IS NORMAL TO BE*
DO YOU ENJOY BEING*

3;YOU
WE WERE DISCUSSING YOU-- NOT ME.
OH, I*
YOU'RE NOT REALLY TALKING ABOUT ME, ARE YOU?

5;I WANT
WHY DO YOU WANT*
WHAT IF YOU NEVER GOT*
I SOMETIMES ALSO WANT*
WHAT WOULD IT MEAN TO YOU IF YOU GOT*
SUPPOSE YOU SOON GOT*

9;WHAT;HOW;WHO;WHERE;WHEN;WHY
WHY DO YOU ASK?
DOES THAT QUESTION INTEREST YOU?
WHAT ANSWER WOULD PLEASE YOU THE MOST?
WHAT DO YOU THINK?
ARE SUCH QUESTIONS ON YOUR MIND OFTEN?
WHAT IS IT THAT YOU REALLY WANT TO KNOW?
HAVE YOU ASKED ANYONE ELSE?
HAVE YOU ASKED SUCH QUESTIONS BEFORE?
WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?

2;NAME
NAMES DON'T INTEREST ME.
I DON'T CARE ABOUT NAMES-- PLEASE GO ON.

4;CAUSE
IS THAT THE REAL REASON?
DON'T ANY OTHER REASONS COME TO MIND?
DOES THAT REASON EXPLAIN ANY THING ELSE?
WHAT OTHER REASONS MIGHT THERE BE?

4;SORRY
PLEASE DON'T APOLOGIZE.
APOLOGIES ARE NOT NECESSARY.
WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?
DON'T BE SO DEFENSIVE!

4;DREAM
WHAT DOES THAT DREAM SUGGEST TO YOU?
DO YOU DREAM OFTEN?
WHAT PERSONS APPEAR IN YOUR DREAMS?
ARE YOU DISTURBED BY YOUR DREAMS?

1;HELLO;HI
HOW DO YOU DO--PLEASE STATE YOUR PROBLEM.

5;MAYBE
YOU DON'T SEEM QUITE CERTAIN.
WHY THE UNCERTAIN TONE?
CAN'T YOU BE MORE POSITIVE?
YOU AREN'T SURE?
DON'T YOU KNOW?

5;NO
ARE YOU SAYING NO JUST TO BE NEGATIVE?
YOU ARE BEING A BIT NEGATIVE.
WHY NOT?
ARE YOU SURE?
WHY NO?

2;YOUR
WHY ARE YOU CONCERNED ABOUT MY*
WHAT ABOUT YOUR OWN*

4;ALWAYS
CAN YOU THINK OF A SPECIFIC EXAMPLE?
WHEN?
WHAT ARE YOU THINKING OF?
REALLY, ALWAYS?

3;THINK
DO YOU REALLY THINK SO?
BUT YOU ARE NOT SURE YOU*
DO YOU DOUBT YOU*

7;ALIKE
IN WHAT WAY?
WHAT RESEMBLANCE DO YOU SEE?
WHAT DOES THE SIMILARITY SUGGEST TO YOU?
WHAT OTHER CONNECTIONS DO YOU SEE?
COULD THERE REALLY BE SOME CONNECTION?
HOW?
YOU SEEM QUITE POSITIVE.

3;YES
ARE YOU SURE?
I SEE.
I UNDERSTAND.

6;FRIEND
WHY DO YOU BRING UP THE TOPIC OF FRIENDS?
DO YOUR FRIENDS WORRY YOU?
DO YOUR FRIENDS PICK ON YOU?
ARE YOU SURE YOU HAVE ANY FRIENDS?
DO YOU IMPOSE ON YOUR FRIENDS?
PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU?

7;COMPUTER
DO COMPUTERS WORRY YOU?
ARE YOU TALKING ABOUT ME IN PARTICULAR?
ARE YOU FRIGHTENED BY MACHINES?
WHY DO YOU MENTION COMPUTERS?
WHAT DO YOU THINK MACHINES HAVE TO DO WITH YOUR PROBLEM?
DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?
WHAT IS IT ABOUT MACHINES THAT WORRIES YOU?

5;CAR
OH, DO YOU LIKE CARS?
MY FAVORITE CAR IS A LAMBORGINI COUNTACH. WHAT IS YOUR FAVORITE CAR?
MY FAVORITE CAR COMPANY IS FERRARI.  WHAT IS YOURS?
DO YOU LIKE PORSCHES?
DO YOU LIKE PORSCHE TURBO CARRERAS?

6;NOKEYFOUND
SAY, DO YOU HAVE ANY PSYCHOLOGICAL PROBLEMS?
WHAT DOES THAT SUGGEST TO YOU?
I SEE.
I'M NOT SURE I UNDERSTAND YOU FULLY.
COME COME ELUCIDATE YOUR THOUGHTS.
CAN YOU ELABORATE ON THAT?

See Linden AIML
ScriptLibrary Chatbots


LibraryEmailModule

Email Module

This module provides a wrapper for LL's email functionality. It allows scripts to receive email events without declaring their own timer and send emails without llEmail's 20 second delay.

// Copyright (c) 2006 Francisco V. Saldana (Christopher Omega)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// This module is responsible for coordinating outbound
// and inbound email. It uses a timer that calls llGetNextEmail
// every 5 seconds, invoking receivedEmailData if new mail arrives.
// Invoking sendEmailData or sendObjectEmail causes the module to
// rez a temporary-on-rez worker object that does the actual llEmail call.

// In order to preserve the integrity of the sent data, a seperater
// (declared below as a global constant) is prepended the message before it is sent.
// This is so programs can easily discard the additional data Linden Lab's email
// server prepends to the message passed to llEmail.

// The suffix added to an object's UUID to send an email
// to it via llEmail.
string OBJECT_EMAIL_SUFFIX = "@lsl.secondlife.com";

// Specifies the amount of time between object email polls.
float EMAIL_POLL_INTERVAL = 5.0;

// Seperator between crap LL prepends to llEmail-sent messages and the real message.
string REAL_DATA_SEPERATOR = "#_|#|_#";

// Name of llEmail-calling worker object.
string EMAIL_OBJECT_NAME = "email";

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

// Events triggered by this module:
m_receivedEmailData(string timestamp, string senderAddress, string senderSubject, string senderMessage) {
    callMethod(0, "receivedEmailData", [timestamp, senderAddress, senderSubject, senderMessage]);
}

m_moduleReset(string moduleName) {
    callMethod(0, "moduleReset", [moduleName]);
}

// Methods called:
m_chat(integer channel, string message) {
    callMethod(0, "chat", [channel, message]);
}

sendEmail(string address, string subject, string message) {
    if (llGetInventoryType(EMAIL_OBJECT_NAME) != INVENTORY_NONE) {
        integer chan = (integer) llFrand(10394) + 1;
        // Scripts in the email object do the actual sending.
        llRezObject(EMAIL_OBJECT_NAME, llGetPos(), ZERO_VECTOR, ZERO_ROTATION, chan);
        m_chat(chan, listToString([address, subject, REAL_DATA_SEPERATOR + message]));
    } else {
        // Unable to delegate llEmail call.
        llEmail(address, subject, message);
    }
}

// Global variables:
    string this;

default {
    state_entry() {
        this = llGetScriptName();
        m_moduleReset(this);
        if (llGetInventoryType(EMAIL_OBJECT_NAME) == INVENTORY_NONE
                || llGetInventoryType("ChatCodec") == INVENTORY_NONE) {
            llOwnerSay("Warning: Rapid llEmail optimization unavailable,"
                + "missing ChatCodec module or email object.");
        }
        state main;
    }
}

state main {
    state_entry() {
        llSetTimerEvent(EMAIL_POLL_INTERVAL);
    }

    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "sendEmailData") {
            list paramList = stringToList(parameters);
            // Method signature:
            // sendEmailData(string address, string subject, string message);
            string address = llList2String(paramList, 0);
            string subject = llList2String(paramList, 1);
            string message = llList2String(paramList, 2);
            sendEmail(address, subject, message);
        } else if (methodName == "sendObjectEmail") {
            list paramList = stringToList(parameters);
            // Method signature:
            // sendObjectEmail(key objectKey, string subject, string message)
            key    objectKey = (key) llList2String(paramList, 0);
            string subject   = llList2String(paramList, 1);
            string message   = llList2String(paramList, 2);
            sendEmail((string)objectKey + OBJECT_EMAIL_SUFFIX, subject, message);
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(callId, methodName, [TRUE]);
        }
    }

    timer() {
        llGetNextEmail("", "");
    }

    email(string timestamp, string senderAddress, string senderSubject,
            string senderMessage, integer numQueued) {
        llSetTimerEvent(0);
        if (llSubStringIndex(senderMessage, REAL_DATA_SEPERATOR) != -1)
            senderMessage = llDumpList2String(llDeleteSubList(llParseStringKeepNulls(senderMessage, [REAL_DATA_SEPERATOR], []), 0, 0), REAL_DATA_SEPERATOR);
        m_receivedEmailData(timestamp, senderAddress, senderSubject, senderMessage);

        if (numQueued > 0) {
            llGetNextEmail("", "");
        } else {
            llSetTimerEvent(EMAIL_POLL_INTERVAL);
        }
    }
}

To use this module, copy and paste the above into a script and name it "EmailModule". The script requires ChatCodec in the same object's inventory. To send emails, an object named "email" must be in the same object's inventory as EmailModule. The object named "email" must also contain ChatCodec and the script below:

// Copyright (c) 2006 Francisco V. Saldana (Christopher Omega)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Deligates llEmail-calling duties, so EmailModule doesn't need to incur the 20-second delay.
// Requires ChatCodec.

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

m_addChatHandle(string moduleName, integer channel) {
    callMethod(0, "addChatHandle", [moduleName, channel]);
}

m_moduleReset(string moduleName) {
    callMethod(0, "moduleName", []);
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        m_moduleReset(this);
    }
    on_rez(integer param) {
        if (param != 0) {
            llSetPrimitiveParams([PRIM_TEMP_ON_REZ, TRUE]);
            m_addChatHandle(this, param);
        }
    }
    link_message(integer sender, integer call, string params, key methodName) {
        if (methodName == "receivedChatData") {
            list paramList = stringToList(params);
            // Method sig:
            // receivedChatData(integer channel, string name, key id, string message)
            integer channel = (integer) llList2String(paramList, 0);
            string  name    = llList2String(paramList, 1);
            key     id      = (key) llList2String(paramList, 2);
            string  message = llList2String(paramList, 3);
            if (channel == llGetStartParameter()) {
                list paramList = stringToList(message);
                llEmail(llList2String(paramList, 0), llList2String(paramList, 1), llList2String(paramList, 2));
                llDie();
            }
        }
    }
}

Here's an example script that utilizes this module:
// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

// Replacement for llEmail:
m_sendEmailData(string address, string subject, string message) {
    callMethod(0, "sendEmailData", [address, subject, message]);
}

// Handy wrapper that makes object<->object emails simpler:
m_sendObjectEmail(key uuid, string subject, string message) {
    callMethod(0, "sendObjectEmail", [uuid, subject, message]);
}

default {
    state_entry() {
        m_sendEmailData("foo.bar@fubar.com", "BOOM!", "Happy April Fools Day!");
        m_sendObjectEmail("a003e27c-1623-ae7d-3bdb-19e26d4986e2", "explode", "confetti");
    }
    link_message(integer sender, integer callIdent, string params, key methodName) {
        if (methodName == "receivedEmailData") {
            list paramList = stringToList(params);
            string timestamp = llList2String(paramList, 0);
            string from = llList2String(paramList, 1);
            string subject = llList2String(paramList, 2);
            string message = llList2String(paramList, 3);
            // email event code goes here.
            if (subject == "damn you" && from == "foo.bar@fubar.com") {
                m_sendEmailData("foo.bar@fubar.com", ":-P", "haha");
            }
        }
    }
}

Changelog:
  • ChristopherOmega, 4/6/06: Use of llGetInventoryKey for "inventory item exists" check changed to llGetInventoryType.


LibraryEmailParse

default {
    state_entry() {
        llEmail((string)llGetKey() + "@lsl.secondlife.com", "Test!", "This (is) a test message.\n This is test message line two."); // Send email to self.
        llSetTimerEvent(2.5); // Poll for emails. (Yes, that is a retarded way on an event-based system!)
    }

    timer() {
        llGetNextEmail("", ""); // Check for email with any sender address and subject.
    }

    email(string time, string address, string subj, string message, integer num_left) {
        string object_name;
        string region_name;
        float region_corner_x;
        float region_corner_y;
        vector local_position;
        string message_body;

        // Parse email to list
        list email_parts = llParseString2List(message,[],["\n"," (",")",", ","Object-Name: ","Region: "]);
        list format_parts = ["Object-Name: ","x","\n","Region: ","x"," (","X",", ","X",")","\n","Local-Position:"," (","X",", ","X",", ","X",")","\n","\n"];
        // Determine if email is properly formatted.
        integer error = FALSE;
        if (llGetListLength(email_parts)>20) {
            integer n;
            for (n=0;n<21;n++) {
                string f = llList2String(format_parts,n);
                string e = llList2String(email_parts,n);
                if ((f!="x")&&(f!="X")) {
                    if (f!=e) error = TRUE;
                }
            }
        }
        else {
            error = TRUE;
        }

        if (error) {
            object_name = "unknown";
            region_name = "unknown";
            region_corner_x = -1.0;
            region_corner_y = -1.0;
            local_position = ZERO_VECTOR;
            message_body = message;
        }
        else {
            object_name = llList2String(email_parts,1);
            region_name = llList2String(email_parts,4);
            region_corner_x = (float)llList2String(email_parts,6);
            region_corner_y = (float)llList2String(email_parts,8);
            local_position = <(float)llList2String(email_parts,13),
                                (float)llList2String(email_parts,15),
                                (float)llList2String(email_parts,17)>;
            message_body = llDumpList2String(llDeleteSubList(email_parts, 0, 20),"");
        }

        llSay(0, "You got mail! " + (string)num_left + " more mails.");
        llSay(0, "Unix Time: " + (string)time);
        llSay(0, "Address: " + (string)address);
        llSay(0, "Subject: " + (string)subj);
        llSay(0, "Object_Name: "+(string)object_name);
        llSay(0, "Region_Name: "+(string)region_name);
        llSay(0, "Region_Corner_x: "+(string)region_corner_x);
        llSay(0, "Region_Corner_y: "+(string)region_corner_y);
        llSay(0, "Local_Position: "+(string)local_position);
        llSay(0, "Message_Body: "+(string)message_body);
    }
}


LibraryEventLottery

Event Lottery Script

This is a simple script for randomly awarding money to event attendies. Everyone who wishes to enter simply clicks the object the script is on. To find a winner the owner of the object says 'Find Winner'. This can be repeated as often as needed to find as many winners as possible. After a name has 'won' it is removed from the pool. This is a simple script.

As a note, when I wrote this script I had little to no faith in llListFindList working, and so built my own, which as Ezhar pointed out, is probably not the optimal method at this point.
list names;
integer i;
integer j;
integer count;
string name;

integer find(string name)
{
    for (i=0;i<count;i++)
        if (llList2String(names,i) == name)
            return i;
    return -1;
}

default
{
    state_entry()
    {
        llListen(0,"",llGetOwner(),"Find Winner");
        count = 0;
    }

    touch_start(integer total_number)
    {
        for (j=0;j<total_number;j++)
        {
            if (find(llDetectedName(j)) == -1)
            {
                name = llDetectedName(j);
                names += name;
                llSay(0,name + " has been entered.");
                count++;
            }
        }
    }

    listen(integer chan, string name, key id, string mes)
    {
        names = llListRandomize(names,1);
        i = llFloor(llFrand(llGetListLength(names)));
        llWhisper(0,"And the Winner is " + llList2String(names,i) +
            "! There were " + (string)count + " participants.");
        names = llDeleteSubList(names,i,i);
    }
}
ScriptLibrary


LibraryFadeTexture

// Call this from an event handler.
// Currently only fades the top.
// Set kSide to ALL_SIDES to fade all sides.
integer kSide = 0;

fade_in_and_out()
{
    integer steps = 15;
    float base = 0.85;
    float delay = 1.0 / steps;
    integer i;

    // fade up
    for (i = 0; i < steps; i++)
    {
        llSetAlpha(1.0 - llPow(base,i), kSide);
        llSleep(delay);
    }

    llSetAlpha(1.0, kSide);
    llSleep(4);

    // fade out
    for (i = 0; i < steps; i++)
    {
        llSetAlpha(llPow(base,i), kSide);
        llSleep(delay);
    }

    llSetAlpha(0.0, kSide);
}
ScriptLibrary


LibraryFastFind

Fast Find # Of Lines in a Notecard

There is an LL function that does this now:
llGetNumberOfNotecardLines

This is a fast way to find the number of lines in a notecard. It is fast as is, and can be adjusted to be faster if you have an idea about the notecards you will be getting the size of. This is an implementation of a binary search.

// Made by Ama Omega
// Version 1.1

// Adjust these two values for your notecards:

// A Higher START_INC will be faster if
//   you have a large range of possible card lengths
// A Lower START_INC will be faster if
//   you have a small range of possible card lengths
// This needs to be less than START_LINE
integer START_INC = 10;

// This should be set to your average note card length.
integer START_LINE = 20;

// Below is the actual code.
integer inc;
integer line;
string name;
key request;

default
{
    state_entry()
    {
        inc = START_INC;
        line = START_LINE;
    }

    changed(integer total_number)
    {
        inc = START_INC;
        line = START_LINE;

        name = llGetInventoryName(INVENTORY_NOTECARD,0);
        request = llGetNotecardLine(name,line);
    }

    dataserver(key query, string data)
    {
        if (query == request)
        {
            request = "";

            if (data == EOF) // Too Far
            {
                if ( inc == 1 ) // We just went up 1 space.  We go up if a line was found.
                {
                    line++;
                    llWhisper(0, (string)line + " lines in notecard " + name);
                    inc = 10;
                    line = 10;
                }
                else if (inc < 0) // Were going down
                {
                    line += inc;  // Keep going down
                    request = llGetNotecardLine(name,line);
                }
                else if (inc > 0) // Were going up
                {
                    inc = - inc / 2; // Go back (down) half as much.
                    line += inc;
                    request = llGetNotecardLine(name,line);
                }
                else // Error!
                {
                    llWhisper(0, "inc == 0!");
                }
            }
            else
            {
                if ( inc == - 1 ) // We just went down 1 space.  We go down if EOF.
                {
                    line++;
                    if (data == "") llWhisper(0, "Notecard " + name + " is empty");
                    else llWhisper(0, (string)line + " lines in notecard " + name);
                    inc = START_INC;
                    line = START_LINE;
                }
                else if (inc > 0) // Were going up
                {
                    line += inc;  // Keep going up.
                    request = llGetNotecardLine(name,line);
                }
                else if (inc < 0) // Were going down
                {
                    inc = - inc / 2; // Go back (up) half as much.
                    line += inc;
                    request = llGetNotecardLine(name,line);
                }
                else // Error!
                {
                    llWhisper(0, "inc == 0!");
                }
            }
        }
    }
}

ScriptLibrary / Examples


LibraryFixRepeatsPerMeter

default
{
    state_entry()
    {
        // round position to grid
        float grid = 0.125;
        vector pos = llGetPos();
        pos.x = grid * llRound(pos.x / grid);
        pos.y = grid * llRound(pos.y / grid);
        pos.z = 27.750;
        llSetPos(pos);

        // round scale to twice grid
        grid *= 2.0;
        vector scale = llGetScale();
        scale.x = grid * llRound(scale.x / grid);
        if (scale.x == 0) { scale.x = grid; }
        scale.y = grid * llRound(scale.y / grid);
        if (scale.y == 0) { scale.y = grid; }
        scale.z = 3.0;
        llSetScale(scale);
        llSay(0, "scale " + (string)scale);

        // repeats per meter
        float r = 0.5;
        llScaleTexture(r*scale.x, r*scale.z, 1);
        llScaleTexture(r*scale.y, r*scale.z, 2);
        llScaleTexture(r*scale.x, r*scale.z, 3);
        llScaleTexture(r*scale.y, r*scale.z, 4);

        //string me = llGetInventoryName(INVENTORY_SCRIPT, 0);
        //llRemoveInventory(me);
        llRemoveInventory(llGetScriptName());
    }
}
ScriptLibrary


LibraryFlightAssist

One way to play with this script is to put it in a 10m X 0.01m X 0.01m blank-texture semi-transparent box and attach it to a convenient HUD attachment point.

The script comments contain highlights about its operation.

Enjoy!

//
// Flight Assist Example
//
// Highlights:
//    - altitude-based assist (lower altitude == less assist, higher == more)
//    - horizontal movement gets relatively less assist than vertical.
//    - HUD color indication of movement mode
//      - brown: not flying (i.e. earth)
//      - green: flying w/ assist
//      - white: hovering w/ assist
//      - black: assist disabled

// gAssistParams enables altitude-based flight assist.   Lower altitudes
// will get less momentum assist so that the client updating overhead
// is less likely to harm controllability.  Plus it's a pain to collide
// with a building at high speed in a damage-enabled area.
//
// see increaseMomentumAssist() for more info
//
list    gAssistParams = [ <1.1, 100, 200>, <1.25, 1000, 800>, <1.4, 10000, -1> ];
float   gInc = 0.1; // The increment to add to the momentum assist parameter on the next control key event
float   gMass;      // current mass (assuming it's constant; can imagine a variable mass assist tho :-) )
integer gMotor = FALSE; // Indicates whether momentum assistance is on
integer gAgentInfo; // A bitmask of the agent/avi current state
integer gReqKeys;  // A bitmask of the control keys required to operate
integer gReqPerms; // A bitmask of the required permissions to operate
key     gWearer;   // UUID of the avi who is wearing this attachment

float   gMomentumAssist = 0; // The current applied momentum assist
float increaseMomentumAssist(vector pos)
{
    integer iii;
    integer len;

    len = llGetListLength(gAssistParams);
    vector aParams;
    for (iii = 0; iii < len; iii++)
    {
        aParams = llList2Vector(gAssistParams, iii);
        if (pos.z < aParams.z)
        {
            jump exitLoop;
        }
    }
    @exitLoop;

    if (gMomentumAssist < aParams.y)
    {
        gInc *= aParams.x; // increase momentum assist exponentially
                           // param values closer to 1 result in slower growth
        gMomentumAssist += gInc;
    }
    else
    {
        gMomentumAssist = aParams.y;
    }

    return gMomentumAssist;
}

vector getForwardDir()
{
    vector ret;

    ret = <1,0,0>*llGetCameraRot(); // camera rotation leads forward direction; so use it to direct assist
    ret.z = 0;

    return llVecNorm(ret);
}

getPermissions()
{
    integer hasPerms = llGetPermissions();
    integer askPerms = (~hasPerms) & gReqPerms;
    if (hasPerms & PERMISSION_TAKE_CONTROLS)
    {
        llTakeControls(gReqKeys, TRUE, TRUE);
    }
    if (askPerms)
    {
        llRequestPermissions(gWearer, askPerms);
    }
}

gotControlInput(integer held)
{
    gAgentInfo = llGetAgentInfo(gWearer);

    if (!(held & gReqKeys))
    {
        if (gAgentInfo & AGENT_FLYING)
        {
            state hover;
        }
    }

    if (gAgentInfo & AGENT_FLYING)
    {
        if (held & gReqKeys)
        {
            vector p = llGetPos();
            vector dir;
            float assist;

            assist = increaseMomentumAssist(p);

            if (p.z > llGround(ZERO_VECTOR)+50.0)
            {
                llSetBuoyancy(1.0);
            }
            else
            {
                // For some reason, if you are below
                // llGround()+50.0 meters, you will
                // slowly rise to that height if you
                // llSetBuoyancy(1.0).  An avatar can maintain
                // hover below this height w/o assist; so
                // no buoyancy change.
                llSetBuoyancy(0.0);
            }

            if (held & CONTROL_FWD)
            {
                dir = getForwardDir();
                // flying too fast horizontally
                // typically makes the avatar
                // uncontrollable since lag is
                // high due to heavy updates;
                // Do a simple reduction of assist
                assist /= 3.0;
            }
            else if (held & CONTROL_BACK)
            {
                dir = -getForwardDir();
                assist /= 3.0;
            }
            else if (held & CONTROL_UP)
            {
                dir = <0,0,1>;
            }
            else if (held & CONTROL_DOWN)
            {
                dir = <0,0,-1>;
            }

            llPushObject(gWearer, assist*gMass*dir, ZERO_VECTOR, FALSE);

            gMotor = TRUE;
        }
    }
}

onAttach(key avatar)
{
    gWearer = avatar;
    if (gWearer != NULL_KEY)
    {
        getPermissions();
    }
}

default
{
    state_entry()
    {
        gReqKeys = CONTROL_FWD | CONTROL_UP | CONTROL_DOWN | CONTROL_BACK;
        gReqPerms = PERMISSION_TRACK_CAMERA|PERMISSION_TAKE_CONTROLS;
        gMass = llGetMass();
        llSetColor(<0,1,0>, ALL_SIDES);

        // Check if HUD is already attached
        gWearer = llGetOwner(); // for now
        if (llGetAttached() != 0)
        {
            getPermissions();
        }

        llSetTimerEvent(0.1);
    }

    on_rez(integer param)
    {
        llResetScript();
    }

    attach(key agent)
    {
        onAttach(agent);
    }

    run_time_permissions(integer perm)
    {
        if (perm & PERMISSION_TAKE_CONTROLS)
        {
            llTakeControls(gReqKeys, TRUE, TRUE);
        }
        else
        {
            llWhisper(0, "The flight assist will not operate properly");
        }

        if (!(perm & PERMISSION_TRACK_CAMERA))
        {
            llWhisper(0, "The flight assist will not operate properly");
        }
    }

    touch_start(integer num)
    {
        state disabled;
    }

    control(key owner, integer held, integer change)
    {
        gotControlInput(held);
    }

    timer()
    {
        gAgentInfo = llGetAgentInfo(gWearer);

        if (!(gAgentInfo & AGENT_FLYING))
        {
            state landed;
        }
    }
}

state hover
{
    state_entry()
    {
        llSetColor(<1,1,1>, ALL_SIDES);

        gMotor = FALSE;
        gMomentumAssist = 0;
        gInc   = 0.1;
        // adjust a little for lag
        llMoveToTarget(llGetPos(), 0.1);
        vector pos = llGetPos();
        // you can hover unassisted at ground level + 50 meters
        // in fact, if you set buoyancy to 1.0 there, you will slowly
        // rise to ground level + 50 meters
        if (llGround(ZERO_VECTOR)+50.0 < pos.z)
        {
            llSetBuoyancy(1.0);
        }
        else
        {
            llSetBuoyancy(0.0);
        }

        llTakeControls(gReqKeys, TRUE, TRUE);
    }

    on_rez(integer param)
    {
        llResetScript();
    }

    at_target(integer number, vector curPos, vector targPos)
    {
        llStopMoveToTarget();
    }

    control(key owner, integer held, integer change)
    {
        gotControlInput(held);

        state default;
    }

    touch_start(integer num)
    {
        state disabled;
    }

    state_exit()
    {
        llStopMoveToTarget();
    }
}

state landed
{
    state_entry()
    {
        llStopMoveToTarget();    // just in case
        llSetBuoyancy(0.0);
        llSetColor(<0.5,0.5,0.25>, ALL_SIDES);
        llSetTimerEvent(0.3);
    }

    on_rez(integer param)
    {
        llResetScript();
    }

    touch_start(integer num)
    {
        state disabled;
    }

    timer()
    {
        gAgentInfo = llGetAgentInfo(gWearer);

        if (gAgentInfo & AGENT_FLYING)
        {
            state default;
        }
    }
}

state disabled
{
    state_entry()
    {
        llSetBuoyancy(0.0);
        llStopMoveToTarget();    // just in case
        llSetColor(<0,0,0>, ALL_SIDES);
    }

    on_rez(integer param)
    {
        llResetScript();
    }

    touch_start(integer num)
    {
        state landed;
    }
}


LibraryFloat2Hex

Use to encode floats in hex notation, minimal overhead, does not introduce errors. No special decoder needed. Try it.

Use this instead of (string) when converting floats to strings.

LSL (float) typecast supports C99 hex floats. This is good because with hex you can store floats without the need for a special decoder (just use (float)) and since hex is a power of 2 format, floats can be stored without any loss or rounding errors.

A similar function (also by me) Float to Scientific Notation, works much the same way (except it uses base 10). The trouble is it has to emulates higher precision math, in a scripting language this is a bad solution. Because of the base conversion using logs would introduce a huge accumulated error (read as: floats suck). Resulting in the need to do the shifting with a while loop. This wasn't good enough, even with 32 bits small numbers would still be corrupted by the shifting. An integer and a float (or in one rewrite two integers) were used, one to store the integer portion, and the other the float. This worked, but was slow as all get out for large and small numbers (2 seconds). Finaly some optical approches were used to do the conversion which sped up large numbers. While accurate, the function was slow and used alot of memory.

This function is much faster, it requires about 900 instructions with little deviation from that number. In the sceme of things it's not too costly (on a not too lagged sim that is about 0.12 seconds). There isn't much that can be done to reduce the number of instructions executed, unless LL wants to give us an llInteger2HexString or an llFloat2HexString.

UPDATE:
There was a small bug that would cause the script to crash on numbers that were greater then 0x1.FFFFF8p127. Please update to fix the script crash issue. As a result of the fix, it will be just a bit slower.

string hexc="0123456789ABCDEF";//faster

string Float2Hex(float input)
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
    if((integer)input != input)//LL screwed up hex integers support in rotation & vector string typecasting
    {//this also keeps zero from hanging the zero stripper.
        float unsigned = llFabs(input);//logs don't work on negatives.
        integer exponent = llFloor(llLog(unsigned) / 0.69314718055994530941723212145818);//floor(log2(b))
        if(exponent > 127) exponent = 127;//catch fatal rounding error in exponent.
        integer mantissa = (integer)((unsigned / (float)("0x1p"+(string)exponent)) * 0x1000000);//shift up into integer range
        while(!(mantissa & 0x1))
        {//strip extra zeros off before converting or they break "p"
            mantissa = mantissa >> 1;
            exponent = -~exponent;//++c;
        }
        string str = "p" + (string)(exponent - 24);
        do
            str = llGetSubString(hexc,15&mantissa,15&mantissa) + str;
        while(mantissa = mantissa >> 4);
        if(input < 0)
            return "-0x" + str;
        return "0x" + str;
    }//integers pack well so anything that qualifies as an integer we dump as such, supports netative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}

Usage:
string a = Float2Hex(100.000000); // a == 100
string b = Float2Hex(14353.344727); // b == 0xE04561p-10
float c = (float)a;//c == 100.000000
float d = (float)b;//d == 14353.344727

Helper Functions:
string Rot2Hex(rotation a)
{
    return "<"+Float2Hex(a.x)+","+Float2Hex(a.y)+","+Float2Hex(a.z)+","+Float2Hex(a.s)+">";
}

string Vec2Hex(vector a)
{
    return "<"+Float2Hex(a.x)+","+Float2Hex(a.y)+","+Float2Hex(a.z)+">";
}

string DumpList2String(list input, string seperator)// for csv use ", " as the seperator.
{
    integer b = (input != []);
    string c;
    string d;
    integer e;
    if(b)
    {
        @top;
        if((e = llGetListEntryType(input,--b)) == TYPE_FLOAT)
            d=Float2Hex(llList2Float(input,b));
        else if(e == TYPE_ROTATION)
            d=Rot2Hex(llList2Rot(input,b));
        else if(e == TYPE_VECTOR)
            d=Vec2Hex(llList2Vector(input,b));
        else
            d=llList2String(input,b);
        if(b)
        {
            c = d + (c=seperator) + c;
            jump top;
        }
    }
    return (c=d) + c;
}

How it works:
string hexc="0123456789ABCDEF";//faster

string Float2Hex(float a)
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
    //If it's zero, the zero stripper will hang, lets avoid that
    if(a)
    {//we need to find the exponent, but to do that we need to take the log of the number (or run a while loop)
        float b = llFabs(a);//logs don't work on negatives.
        string f = "p";//This is a trade off, slightly slower (only slightly) for a couple bytes savings (10)
        //To get the exponent we need to round the integer portion down or the log2(b)
        //  LSL doesn't have log2, so we use one of the laws of logs to get it from the natural log
        integer c = llFloor(llLog(b) / 0.69314718055994530941723212145818);//floor(log2(b))
        //There is a rounding error in the exponent that causes it to round up, this usualy isn't a problem as the
        //  exponent only is only used to shift the number. It becomes a problem when dealing with float max as
        //  it will round up to 128; which will crash llPow.
        if(c > 127) c = 127;//catch fatal rounding error in exponent.
        //Q: why not do (b * llPow(2,-c))? A: because the range of floats are different at the top and bottom
        //  at the top it's 2^127 at the bottom it's 2^-149; and if you tried to do this otherwise,
        //  llPow would crash with an overflow. Then we multiply it by 2 ^ 24, floats only use 23 bits for the mantisa,
        //  we cannot add it into c because we could overflow llPow; we correct the value of c later.
        // Instead of using llPow(2.0, c) we are using (float)("0x1p"+(string)c), it's 10% faster and 16 bytes lighter.
        integer d = (integer)((b / (float)("0x1p"+(string)c)) * 0x1000000);//shift up into integer range
        while(!(d & 0xf))
        {//strip extra zeros off before converting or they break "p"
            //a catch22 of sorts, you cannot know exactly how far to shift it till after you have shifted it
            d = d >> 4;
            c+=4; //for each zero stripped we must adjust the exponent
        }
        do//convert to hex
            f = llGetSubString(hexc,15&d,15&d) + f;
        while(d = d >> 4);
        if(a < 0)//final formating & c adjustment (from making it an integer)
            return "-0x" + f +(string)(c - 24);
        return "0x" + f +(string)(c - 24);
    }//no point in this getting special formating.
    return "0";//zero would hang the zero stripper.
}

This version is perfect, it is slightly slower. It properly handles the exponent bug. It's included for curiosities sake (and a bunch of work went into it and i didn't want to throw it away).
string Float2Hex(float a)
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
    if(a)
    {
        float b = llFabs(a);
        integer c = llFloor(llLog(b) / 0.69314718055994530941723212145818);//floor(log2(b))
        string f = "";
        if(c > 127) c = 127;
        else if(c < -126) c = -126;
        else c -= ((float)("0x1p"+(string)c) > b);
        integer d = ((integer)(b / (float)("0x.000002p"+(string)c)) & 0x7FFFFF) << 1;
        integer m = 6;
        while(!(d & 0xf))
        {//strip extra zeros off before converting or they break "p"
            d = d >> 4;
            --m;
        }
        do
        {
            f = llGetSubString(hexc, 0xf & d, 0xf & d) + f;
            d = d >> 4;
        }while(--m);
        if(a < 0)
            return "-0x"+(string)(b > 1.1754943508222875079687365372222e-38)+"."+f +"p"+(string)(c);
        return "0x"+(string)(b > 1.1754943508222875079687365372222e-38)+"."+f +"p"+(string)(c);
    }
    return "0";//zero would hang the zero stripper.
}


LibraryFppMain

Creating a flying pet.

Successfully simulating a flying creature is a challenge in any language. This worked example will take you through everything required to script a small physical object that will fly around in a 'realistic' manner. The emphasis is on creating an expandable example with a re-usable movement script.

I still have about 50% of these pages to complete. However the sections on the basic brain script and basic movement script are done.

Introduction : Introduction, Model limitations, Overall design.
Using Link Messages : Link Messages, Message design, data compression
Basic Brain : Basic design, Selecting a random movement target, putting it all together.
Basic Movement : Basic design, initialising the physical object, waiting for orders, moving, putting it all together. (85% complete)
Putting it all together : Getting the script to actually work. Tuning. (50% complete)
Improving the Brain : Avoiding the ground or sea, giving up. (10% complete)
Improving the Movement : Basic object avoidance (0% complete)
Returning home : Making your pet return to you (0% complete)
Adding random actions : Adding a separate script containing random actions (0% complete)
Adding a particle system : How to put all your particle effects into one reusable script (0% complete)
Adding voice control : Making your pet obey you. (0% complete)

FAQ: Add your questions here.

I have not been active in Second Life for sometime. Although I'm currently back, my interests have moved on and I will not be finishing this tutorial.

However, I have made a version of my flying pet available for free at http://slurl.com/secondlife/olivia/17/95/21/ . The pet moves, stays near its owner and has a couple of random actions. The code is open and free for you to use as you want. You may use the code in your own commercial products, but may not sell the flying pet as it stands. Look for the blue box.
Script Library | Examples


LibraryFractalGenerator

The following is a set of scripts for generating fractals (strictly speaking, fractal trees). I used variants of this script to build all of the fractals in my (2005) burning life build.

///////////////////////////////////////////
//////////Fractal Generator 1.0////////////
///////////////////////////////////////////
///////////////////by//////////////////////
///////////////////////////////////////////
/////////////Seifert Surface///////////////
/////////////2G!tGLf 2nLt9cG/////Aug 2005//
///////////////////////////////////////////

//Instructions for use:

//Rez a prim, mess around with it however you want. When you're done, make 3 copies of it.
//Put this script ("Fractal Generator 1.0") in one, which will be the base prim of the
//fractal. Put the script "Fractal Generator 1.0 Child" in another, and "Fractal Generator
//1.0 Rescaler" in the third. Take the "rescaler" prim, make sure it's name is "Object",
//and put it in the contents of the base prim (the one with this script). The scripts will
//build the fractal out of copies of this "rescaler" prim.

//Now we tell the scripts how we want the fractal to be built. Make some number of copies
//of the "child" prims (2 or 3 is a good number), and arrange them near or touching the
//"base" prim. Rotating them is fine, scaling them (down usually) is a good idea, but
//remember whenever you scale it to scale all three dimensions equally (use the grey boxes
//in the "Stretch" tool, not the red, green or blue). Rotating or moving the base prim is
//fine, but don't rescale it at this stage (the scripts rely on the "rescaler" prim to be
//an exact copy of the base prim).

//Now decide how many iterations you want the fractal to contain. 3 is a good number to
//start with. This script calculates beforehand how many prims the final fractal will
//contain, and will not build if it contains more than the "prim_limit" below. You can
//change this number (it is set in the first line of code below) to something bigger, but
//I take no responsibility if you end up crashing a sim! This is *not* a piece of self
//replicating code, but it will rez many prims relatively quickly. You have been warned.

//The formula for the number of prims in the final fractal, starting from b children and
//doing i iterations is:
// i+1                    if b == 1
// (b^(i+1) - 1)/(b - 1)  if b >= 1

//Having decided on the number of iterations you want, simply enter this number into the
//"Description" of the object (in the "General" tab of the extended editing controls).
//Finally, link all the child prims to the base prim, making sure that the base prim is
//the root of the link set. To do this, select the child prims first, add the base prim to
//the selection last (by holding down Shift and clicking on it), and then link (Ctrl-L, or
//selecting "Link" from the "Tools" menu).

//Now say "go" (the script will only listen to its owner), and the fractal will begin
//building (as long as the final fractal wouldn't contain too many prims). A good trick is
//to have the base and child prims (which are now linked) selected in edit mode, so that
//once the fractal is built, you can easily move the generator away. The generator will
//rez copies of the base and the child prims in their exact locations, so it can be
//difficult to select them.

//If you don't like how it turned out, simply edit the positions and scales of the child
//prims (you might want to unlink first and then relink again), and when you're happy, say
//"go" again, and it will generate the new fractal.

//Issues:

//It is impossible to rez a prim that is smaller than 0.01 metres, or larger than 10.0
//metres in any dimension. The script will detect when it tries to do so, and counts how
//many prims fail to rez because of this. It is also impossible to rez a prim further than
//around 10 metres distance from the rezzing object, which is the base prim. Unfortunately
//it doesn't seem to be exactly 10 metres, so I haven't implemented a similar check to see
//how many prims would be too far from the base prim to be rezzed. The code is in there,
//but commented out. Let me know if you work out something reliable. As is, attempting to
//rez a prim too far from the base will silently fail.

//If you modify the code, please keep my name with it, and make sure you don't crash a sim!
//Please do not sell this code, or anything with this code in it. Selling objects created
//with this code is ok. It would be nice if people would let me know what they're making
//with it :)

//   Seifert Surface
//   2G!tGLf 2nLt9cG

///////Start of code///////

integer prim_limit = 255; //wont build anything with more than this number of prims
integer iterations;  //number of levels to go down
integer branching;  //number of elts in each list

list posns;
list rotns;
list scales;

vector base_posn;

integer listen_control;
integer count_replies;      //for making sure that all child prims in the base have replied
integer bad_scales = 0;     //counting how many prims were too small or too big to rez
//integer bad_positions = 0;  //counting how many prims were too far away from the generator to rez
//this seems unreliable - according to the wiki rezzing at a distance of more than 10 meters fails silently,
//but it seems to be able to go a little further (11 meters?). So the distance checking code is here but commented out,
//you can experiment with it if you like. As is, when trying to rez prims too far away, it will just fail silently.

rez_children(vector pos, rotation rot, float scl, integer iters)
{
    integer branch = 0;
    for(branch = 0; branch < branching; branch++)
    {
        //llSay(0, "iter: "+(string)iters+" branch: "+(string)branch);
        vector check_scale = llGetScale()*llList2Float(scales, branch)*scl;
        if(check_scale.x < 0.01 || check_scale.x > 10.0 || check_scale.y < 0.01 || check_scale.y > 10.0 || check_scale.z < 0.01 || check_scale.z > 10.0)
        {
            bad_scales++;
        }
        else
        {
            //start of distance checking code, commented out due to unreliability
            //vector check_pos = pos + scl*(llList2Vector(posns, branch)*rot);
            //
            //if(llVecMag(check_pos) > 10)
            // {
            //     bad_positions++;
            //  }
            //  else
            //  {
            //end of dist checking code
                llRezObject("Object", base_posn + pos + scl*(llList2Vector(posns, branch)*rot), //position to rez
                <0,0,0>,   //given vel... ignore
                llList2Rot(rotns, branch)*rot, //rotation to rez
                llRound(1000000.0 * llList2Float(scales, branch)*scl)  //scaling, including workaround passing integer
                );
            // }   //(from dist checking code)
        }

        //llSleep(1.0);  //if you want to slow it down to see what its doing
        if(iters > 1)
        {
        rez_children((pos + scl*llList2Vector(posns, branch)*rot), llList2Rot(rotns, branch)*rot, llList2Float(scales, branch)*scl, iters - 1);
        }
    }
}

default
{
    state_entry()
    {
        key owner = llGetOwner();  //will only listen to its owner
        listen_control = llListen(0, "", owner, "");
    }

    on_rez(integer number)
    {
        llResetScript();
    }

    listen(integer channel, string name, key id, string message)
    {
        if(message == "go")  //say "go" to make it start
        {
            llSay(0, "Recording data...");
            state Recording;
        }
    }

    state_exit()
    {
        llListenRemove(listen_control);
    }
}

state Recording
{
   state_entry()
   {
        branching = llGetNumberOfPrims() - 1; //dont count the root prim
        if(branching <= 0)
        {
            llSay(0, "Link child prims first!");
            state default;
        }
        //llSay(0, "branching: "+(string)branching);
        //check how many prims there will be... (assuming none too small/big/far away)
        iterations = (integer)llGetObjectDesc();  //put desired number of iterations in the object description
        if(iterations <= 0)
        {
            llSay(0, "Number of iterations (enter it into the object description) must be a positive integer!");
            state default;
        }
        //llSay(0, "iterations: "+(string)iterations);
        integer num_prims;
        if(branching == 1)
        {
           num_prims = iterations + 1;
        }
        else
        {
        float fi = (float)iterations;
        float fb = (float)branching;
        float temp1 = llPow(fb,(fi + 1)) - 1.0;  //some math here...
        float temp2 = (fb - 1.0);
        num_prims = llRound(temp1/temp2);
        }
        llSay(0, "Final fractal will contain prims: "+(string)num_prims);
        if(num_prims > prim_limit)
        {
            llSay(0, "Too many prims!");
            state default;
        }

        count_replies = 0; //going to need all of the child prims to reply

        posns = [];
        rotns = [];
        scales = [];
        integer branch = 0;
        for(branch = 0; branch < branching; branch++)
        {  //reset lists to correct lengths
        posns = llListInsertList(posns, [ZERO_VECTOR], branch);
        rotns = llListInsertList(rotns, [ZERO_ROTATION], branch);
        scales = llListInsertList(scales, [1.0], branch);
        }

        llMessageLinked(LINK_ALL_OTHERS, 0, "return", NULL_KEY);  //ask all child prims to report their parameters
   }

   link_message(integer sender_num, integer num, string str, key id)
   {
        count_replies++;
        integer branch = sender_num - 2;
        llSay(0, "Replied, child prim: "+(string)branch);

        list reply = llParseString2List(str, ["|"], []);  //reply is of form <position>|<rotation>|scale

        vector temp_pos = (vector)llList2String(reply, 0);
        rotation temp_rot = (rotation)llList2String(reply, 1);

        vector temp = llGetScale();    //only measures scaling in x, but doesnt matter which: child prims should
        float tempx = temp.x;          //be scaled in all coords equally for code to work properly
        float temp_scale = (float)llList2String(reply, 2)/tempx;  //stored scale is relative to root prim scale

        //llSay(0, (string)temp_pos+(string)temp_rot+(string)temp_sca  le);

        posns = llListReplaceList(posns, [temp_pos], branch, branch);
        rotns = llListReplaceList(rotns, [temp_rot], branch, branch);
        scales = llListReplaceList(scales, [temp_scale], branch, branch);

        if(count_replies >= branching)
        {
           //llSay(0, "posns: "+llDumpList2String(posns, "|"));
           //llSay(0, "rotns: "+llDumpList2String(rotns, "|"));
           //llSay(0, "scales: "+llDumpList2String(scales, "|"));
           state Building;
        }
    }
}

state Building
{
    state_entry()
    {
        //llSay(0, "Starting...depth: "+(string)iterations)+" branching: "+(string)branching);
        base_posn = llGetPos();
        bad_scales = 0;
        //bad_positions = 0;

        llRezObject("Object", base_posn, <0,0,0>, llGetRot(),1); //rez copy of the base
        rez_children(<0,0,0>, llGetRot(), 1.0, iterations);      //yay recursion!!
        llSay(0, "Done! "+(string)bad_scales+" prims had sizes too large or too small.");
        //llSay(0, "(string)bad_positions+" had positions too far from base prim.");
        state default;
    }
}

///////////////////////////////////////////
///////Fractal Generator 1.0 Child/////////
///////////////////////////////////////////
///////////////////by//////////////////////
///////////////////////////////////////////
/////////////Seifert Surface///////////////
/////////////2G!tGLf 2nLt9cG/////Aug 2005//
///////////////////////////////////////////

default
{
    state_entry()
    {
    }
    link_message(integer sender_num, integer num, string str, key id)
    {
        vector pos = llGetLocalPos();
        rotation rot = llGetLocalRot();
        vector temp = llGetScale();
        float scale = temp.x;  //everything assumes scaling happens on all 3 axes equally, so just pick one
        list data = [pos, rot, scale];
        string reply = llDumpList2String(data, "|");
        llMessageLinked(1, 0, reply, NULL_KEY);
    }
}

///////////////////////////////////////////
/////Fractal Generator 1.0 Rescaler////////
///////////////////////////////////////////
///////////////////by//////////////////////
///////////////////////////////////////////
/////////////Seifert Surface///////////////
/////////////2G!tGLf 2nLt9cG/////Aug 2005//
///////////////////////////////////////////

default
{
    state_entry()
    {
    }

    on_rez(integer number)
    {
        if(number != 0) //when rezzed by an avatar, don't do anything
        {
            float scale = (float)number/1000000.0;  //sneaky way to get a float in here using an integer...
            llSetScale(llGetScale()*scale);
            llRemoveInventory(llGetScriptName());   //kill the script when done
        }
    }
}


LibraryGeneralMenuEngine

General Menu Engine

This is a library that allows LSL programmers to easily create dialog-based objects (instead of the laggy listen-based ones). It communicates with link messages with your script (in the same prim). It is somewhat in the similar domain as DialogModule, but it's quite a bit different.
This is the first version of my script, so feel free to comment on it, and I might enhance it in the future!

The theory is that you need to 'define' your hierarchic menu system (in a "menudefs" notecard), then you can use just easy 'doMenu("THEMENUNAME");' calls to start a menu, and you get the result with a link message.

The library opens listens for 60 seconds maximum (timeout) or only as long as is needed (until user presses a choice), so it should generate as less lag as possible.

The menudefs notecard goes like this:
MENU DEFAULT
TEXT ~ Main Menu ~\nSelect a function
TOMENU SETTITLE Set title
OPTION Start vote
TOMENU CLITEM Clear Item
OPTIONASK Reset

Where:
* MENU <MENUID> -- defines the menu ID, that you can later pass to the doMenu() function, also returned in the results
* TEXT <textTextText> -- the text displayed in the menu
* TOMENU <MenuID> <ButtonText> -- the users sees 'ButtonText' on a button, upon pressing, s/he'll get to the menu with ID: MenuID. You can call that a submenu.
* OPTION <ButtonText> -- An option, upon press, the MenuID and the ButtonText gets returned
* OPTIONASK <ButtonText> -- Same as option, but will ask Yes/No before returning the result

The result, returned in the 'string' part of a linkmessage (number: LM_OPTIONDATA) looks like this:
* MENUID|ButtonText

A MenuEngine-enhanced script and object contains:
* MenuEngine script - the library, used with link messages
* menudefs - notecard defining the menus
* your custom script - using the library with link messages, and containing the 'interface' code for easiness

If you want to create a menu-enhanced object
* Have your script in the object, and copy & paste the interface code from the start of Menu demo included below
* Put my MenuEngine script in the object
* Create your menudefs notecard, and put it in the object (or update it)
* IMPORTANT: RESET the MenuEngine script - it will (re)read the menudefs, that takes a few seconds (5-10)

The inteface library provides these functions:
* resetMenu() -- resets and reloads the menu notecard
* doMenu( key user, string menuname ) -- show MENUNAME ID menu to user USER, and get the response with linkMessages
* readString( key user, string var, string prompt ) -- show a PROMPT to user and read a string from chat
* customMenu( key user, string menuname, string message, list buttons ) -- make a dialog for user USER, with a pseudo menuid MENUNAME, the dialog will say message and have BUTTONS for buttons

Also: another example for using this library: General Menu Engine Example 1

Issues:
* Concurrency: only one person can really use the dialogs at a time. The last popped-up dialog handling will clear the listen to the previous user. Most of the time, this is not really a problem, though, but be aware of it.

History:
1.0.2: custom menu function added

The "MenuEngine" script:
// Menu Engine - v 1.0.2
// by Zonax Delorean
// License: BSD, but please provide patches/bugfixes if you can!

integer LM_DOMENU   = 10001;
integer LM_OPTIONDATA = 10002;
integer LM_MENULOADED = 10003;
integer LM_RESETMENUSYSTEM = 10004;
integer LM_READSTRING = 10005;
integer LM_READSTRINGDATA = 10006;
integer LM_CUSTOMMENU   = 10007;

string confNoteName = "menudefs";
integer cLine;
key cqid;

integer MC_TEXT = 1;
integer MC_TOMENU = 2;
integer MC_OPTION = 3;
integer MC_OPTIONASK = 4;
integer MC_END = 999;
// ...

integer numMenus;
list menudata;
list menuOffsets;
integer MENUREC_SIZE = 4;

integer debug = FALSE;

emitMsgSender( integer num, string msg ) {
    if (debug) debugSay("Emit MSG: chn#"+(string)num+" : "+msg);
    llMessageLinked(llGetLinkNumber(),num,msg,cUser);
}
emitMsgGlobal( integer num ) {
    if (debug) debugSay("Emit MSG Global: chn#"+(string)num+" : ");
    llMessageLinked(llGetLinkNumber(),num,"",NULL_KEY);
}

debugSay( string st ) {
    llSay(0,st);
}

string clMenuID;
list clButtons;
list clActions;
string clMenuText;

// read data
startReadConfig() {
    numMenus = 0;
    menudata = [];
    menuOffsets = [];
    clMenuID = "";
    clButtons = [];
    clActions = [];
    clMenuText = "";

    cLine = 1;
    cqid = llGetNotecardLine( confNoteName, cLine - 1 ); // request first line
}

commitEntry() {
    // [ menuID, startOffset, startOffsetActions, endOffset ]
    integer sofs = llGetListLength(menudata);
    menuOffsets += [ clMenuID, sofs, sofs + 1 + llGetListLength(clButtons), sofs + 1 + llGetListLength(clButtons) + llGetListLength(clActions) ];
    menudata += [ clMenuText ];
    menudata += clButtons;
    menudata += clActions;

    clMenuID = "";
    clMenuText = "";
    clButtons = [];
    clActions = [];
}

readConfigLine( string data ) {
    if (data != EOF) {
        integer spos = llSubStringIndex(data," ");
        if (spos==-1) jump LreadNextLine; // not a line with space

        string cmd = llGetSubString( data, 0, spos - 1);
        data = llGetSubString( data, spos+1, -1);

        integer cmdid = llListFindList(["MENU","TEXT","TOMENU","OPTION","OPTIONASK"],[cmd]);
        if (cmdid==-1) jump LreadNextLine; // not a command

        if (cmdid==0) { // MENU
            if (clMenuID!="") commitEntry();
            clMenuID = data;
        }
        else if (cmdid==1) { // TEXT
            if (clMenuText!="") clMenuText += "\n";
            list tmpl = llParseString2List(data,["\\n"],[]);
            data = llDumpList2String(tmpl,"\n");
            clMenuText += data;
        }
        else if (cmdid==2) { // TOMENU <MenuID> <ButtonText> or TOMENU <MenuID>
            integer wpos = llSubStringIndex(data," ");
            string tomenuid = data;
            string btext = data;
            if (wpos>=0) {
                tomenuid = llGetSubString(data,0,wpos - 1);
                btext = llGetSubString(data,wpos+1,-1);
            }

            clButtons  += [ btext ];
            clActions  += [ MC_TOMENU, tomenuid ];
        }
        else if (cmdid==3) { // OPTION <ButtonText>
            clButtons  += [ data ];
            clActions  += [ MC_OPTION, data ];
        }
        else if (cmdid==4) { // OPTIONASK <ButtonText>
            clButtons  += [ data ];
            clActions  += [ MC_OPTIONASK, data ];
        }

@LreadNextLine; // jump point
        cLine++;
        cqid = llGetNotecardLine( confNoteName, cLine - 1 );
    } else {
        if (clMenuID!="") commitEntry();
        numMenus = llGetListLength(menuOffsets) / MENUREC_SIZE;
        configLoaded();
    }
}

//
// gets called when the config is done loading
//
configLoaded() {
    if (debug) {
        debugSay("Menu data loaded, mem: "+(string)llGetFreeMemory());
        llInstantMessage(llGetOwner(),"OFS: "+llDumpList2String(menuOffsets,","));
        llInstantMessage(llGetOwner(),"DATA: "+llDumpList2String(menudata,","));
    }

    // signal a message to the main prog
    emitMsgGlobal(LM_MENULOADED);
}

string cMenuName;
integer cMenuNum;
integer CHATBASECHANNEL = 100050;
integer cOfs; // current menu start offset
key cUser; // current user
integer nList;
integer isListening = FALSE;
float RESPONSE_TIMEOUT = 60.0;
list cButtons;
list cActions;
integer cMenuType;
integer MT_ASK = 2;
integer MT_NORMAL = 1;
integer MT_READSTRING = 3;
integer MT_CUSTOM = 4;
integer cSenderID;

clearListens() {
    if (isListening) {
        llListenRemove(nList);
        isListening = FALSE;
    }
}

integer getMenuNum( string menuid ) {
    integer c;
    for (c=0;c<numMenus;c++)
        if (llList2String(menuOffsets,c*MENUREC_SIZE) == menuid)
            return(c);
    return(-1);
}

// load a data for a menu and start a dialog
doMenu( key user,  string menuname ) {
    if (debug) debugSay("Do menu: "+menuname);
    integer mnum = getMenuNum(menuname);
    if ( mnum == -1 ) { // menu not found
        llSay(0,"Error: no such menu: "+menuname);
        return;
    }

    cOfs = llList2Integer( menuOffsets, mnum * MENUREC_SIZE + 1 );
    integer actOfs = llList2Integer( menuOffsets, mnum * MENUREC_SIZE + 2 );
    integer actEnd = llList2Integer( menuOffsets, mnum * MENUREC_SIZE + 3 );

    cMenuNum = mnum;
    cMenuName = menuname;
    cUser = user;
    cButtons = llList2List( menudata, cOfs+1, actOfs - 1 );
    cActions = llList2List( menudata, actOfs, actEnd );
    cMenuType = MT_NORMAL; // normal menu

    clearListens();
    nList = llListen( CHATBASECHANNEL + cMenuNum, "", user, "");
    isListening = TRUE;
    llDialog( user, llList2String( menudata, cOfs ), cButtons, CHATBASECHANNEL + cMenuNum );
    llSetTimerEvent(RESPONSE_TIMEOUT);
}

// param: <TEXT>
doAskMenu( key user, string param ) {
    cMenuType = MT_ASK;
    cButtons = [ "Yes", "No" ];
    cActions = [ param ];
    clearListens();
    nList = llListen( CHATBASECHANNEL + cMenuNum, "", user, "");
    isListening = TRUE;
    llDialog( user, param, cButtons, CHATBASECHANNEL + cMenuNum );
    llSetTimerEvent(RESPONSE_TIMEOUT);
}

////////////////////////////////////////////
default
{
    state_entry()
    {
        if (debug) llSay(0, "Loading menu... mem: "+(string)llGetFreeMemory());
        startReadConfig();
    }

    dataserver(key query_id, string data) {
        if (query_id==cqid) readConfigLine(data);
    }

    listen( integer channel, string name, key id, string message ) {
        llSetTimerEvent(0.0);
        clearListens();

        if (cMenuType==MT_ASK) {
            if (message=="Yes") {
                string param = llList2String( cActions, 0 ); // special case
                emitMsgSender(LM_OPTIONDATA,llDumpList2String([ cMenuName, param ],"|"));
            }
            return;
        }
        if (cMenuType==MT_CUSTOM) {
            emitMsgSender(LM_OPTIONDATA,llDumpList2String([ cMenuName, message ],"|"));
            return;
        }
        if (cMenuType==MT_READSTRING) {
            string param = llList2String( cActions, 0 ); // special case
            emitMsgSender(LM_READSTRINGDATA,llDumpList2String([ param, message ],"|"));
            return;
        }

        integer c;
        integer nb = llGetListLength(cButtons);
        integer fnd = -1;
        for (c=0;c<nb;c++) {
            if (llList2String(cButtons,c)==message) {
                fnd = c;
                jump doneSR;
            }
        }
        @doneSR;
        if (fnd>=0) {
            integer act = llList2Integer( cActions, fnd * 2 );
            string param = llList2String( cActions, fnd * 2 + 1 );
            if (debug) {
                debugSay("r: "+(string)act+" "+param);
            }
            if (act==MC_TOMENU) {
                string tomenu = param;
                integer spos = llSubStringIndex(tomenu," ");
                if (spos>=0) tomenu = llGetSubString(tomenu,0,spos - 1);

                doMenu(id,tomenu);
                return;
            }
            if (act==MC_OPTION) {
                emitMsgSender(LM_OPTIONDATA,llDumpList2String([ cMenuName, param ],"|"));
                return;
            }
            if (act==MC_OPTIONASK) {
                doAskMenu(id,param);
                return;
            }

        }
    }

    link_message(integer sender_num, integer num, string str, key id) {
        if (num==LM_DOMENU) {
            cSenderID = sender_num;
            doMenu(id,str);
            return;
        }
        if (num==LM_READSTRING) {
            cSenderID = sender_num;
            cMenuType = MT_READSTRING;

            string message = str;
            string retval  = str;
            integer spos = llSubStringIndex(str," ");
            if (spos>=0) {
                message = llGetSubString( str, spos+1, -1 );
                retval = llGetSubString( str, 0, spos - 1 );
            }
            cActions = [ retval ];
            nList = llListen( 0, "", id, ""); // listen from the user only
            isListening = TRUE;
            llSay(0,message);
            llSetTimerEvent(RESPONSE_TIMEOUT);
            return;
        }
        if (num==LM_CUSTOMMENU) {
            list par = llParseString2List(str,["~|~"],[]);

            if (llGetListLength(par)<2) {
                llSay(0,"Error: not enough parameters for custom menu");
                return;
            }

            cMenuName = llList2String(par,0);
            string message = llList2String(par,1);
            cUser = id;
            cButtons = llList2List( par, 2, llGetListLength(par) - 1 );
            cMenuType = MT_CUSTOM;

            clearListens();
            nList = llListen( CHATBASECHANNEL, "", cUser, "");
            isListening = TRUE;
            llDialog( cUser, message, cButtons, CHATBASECHANNEL );
            llSetTimerEvent(RESPONSE_TIMEOUT);
            return;
        }
        if (num==LM_RESETMENUSYSTEM) {
            llResetScript();
        }
    }

    timer() {
        llSetTimerEvent(0.0);
        clearListens();
    }
}

The "menudefs" notecard:
MENU DEFAULT
TEXT ~ Main Menu ~\nSelect a function
TOMENU SETTITLE Set title
OPTION Start vote
TOMENU CLITEM Clear Item
OPTIONASK Reset

MENU SETTITLE
TEXT Set title for which item?
OPTION 1
OPTION 2
OPTION 3

MENU CLITEM
TEXT Clear title and picture for which item?
OPTION 1
OPTION 2
OPTION 3

MENU INVOTE
TEXT Voting in progress...
OPTIONASK Clear votes
OPTIONASK Close voting

MENU VOTEDONE
TEXT Vote closed
OPTIONASK Restart voting
OPTION Tell results

A "MenuDemo" script:
// Menu demo program

//--- Menu library interface start-------------
integer LM_OPTIONDATA = 10002;
integer LM_MENULOADED = 10003;
integer LM_READSTRINGDATA = 10006;

resetMenu() {
    llMessageLinked(llGetLinkNumber(),10004,"",NULL_KEY); // 10004: LM_RESETMENUSYSTEM
}
doMenu( key user, string menuname ) {
    llMessageLinked(llGetLinkNumber(),10001,menuname,user); // 10001: LM_DOMENU
}
readString( key user, string var, string prompt ) {
    llMessageLinked(llGetLinkNumber(),10005,var+" "+prompt,user); // 10005: READSTRING
}
customMenu( key user, string menuname, string message, list buttons ) {
    llMessageLinked(llGetLinkNumber(),10007,llDumpList2String([ menuname, message ] + buttons,"~|~"),user);
}
//---Menu Library inteface end----------

default
{
    state_entry()
    {
        //resetMenu();
    }

    link_message(integer sender_num, integer num, string str, key id) {
        if (num==LM_OPTIONDATA) {
            llSay(0,"MenuOutput: "+str);
        }
        if (num==LM_MENULOADED) {
            llSay(0,"Menu has just been (re)loaded");
        }
        if (num==LM_READSTRINGDATA) {
            integer wpos = llSubStringIndex(str,"|");
            string varname = llGetSubString(str,0,wpos - 1);
            string response = llGetSubString(str,wpos+1,-1);
            llSay(0,"Variable: "+varname+" String read: "+response);
        }
    }

    touch_start(integer total_number)
    {
        doMenu(llDetectedKey(0),"DEFAULT");

        // other things you can try (uncomment it)
        //customMenu(llDetectedKey(0),"CUST1","Test custom menu",["Test A","Test B","Test C"]);
        //readString(llDetectedKey(0),"FAVCOLOR","Please enter your favorite color in chat:");
    }
}
script library | examples


LibraryGetDayOfWeek

Method to get the day of the week from a unix timestamp - llGetUnixTime. The timestamp returns the number of seconds elapsed beginning Thursday, January 1, 1970 UTC. This script first converts the seconds to hours, then adds the GMT offset (if desired), then converts the hours to days, and finally grabs the day of the week from a list.

// Gives day of the week
// DoteDote Edison

list weekdays = ["Thursday", "Friday", "Saturday", "Sunday", "Monday", "Tuesday", "Wednesday"];
integer offset = -4; // offset from UTC

default {
    state_entry() {
        //
    }
    touch_start(integer total_number) {
        integer hours = llGetUnixTime()/3600;
        integer days = (hours + offset)/24;
        integer day_of_week = days%7;
        llSay(0, "Today is " + llList2String(weekdays, day_of_week));
    }
}

And a function version:

string getDay(integer offset) {
    list weekdays = ["Thursday", "Friday", "Saturday", "Sunday", "Monday", "Tuesday", "Wednesday"];
    integer hours = llGetUnixTime()/3600;
    integer days = (hours + offset)/24;
    integer day_of_week = days%7;
    return llList2String(weekdays, day_of_week);
}

default {
    touch_start(integer total_number) {
        integer offset = -4; // offset from UTC
        llSay(0, "Today is " + getDay(offset) + ".");
    }
}

Alternate method at: Day of the Week by RichardSolvang
ScriptLibrary


LibraryGiveInventory

I've used this a lot for my packaged products. Iv'e always given it out with full perms, but it seems about time to share it with the wiki folks here as well. -- DedricMauriac

- When owner touches the object, a folder is given to them containing all copyable items in the contents excluding the script and any specified items.
- When a non-owner touches the object, they get an information notecard (if present), and a landmark (if present) as well as a message that they are not the owner
- When the creator modifies inventory, permissions of each item are sent to the owner. This helps me a lot to make sure I have permissions setup accordingly.

list exclude = []; // list of inventory to exclude from list
string prefix = ".: Dedric Mauriac :."; // prefix of folder given to owner with inventory
string packageSuffix = " (boxed)"; // suffix of package (usually to indicate it is boxed
string infoSuffix = ""; // only notecards with this suffix will be handed out as package information

string info; // information notecard to be given to anyone who is not the owner
string landmark; // store landmark to be given to anyone who is not the owner
list inventory; // inventory to be handed out to the end-user
string product; // name of the product

//Strife says: I optimized "-(1 + llStringLength(value))" to "~llStringLength(value)"
//also did some small code clean up etc. The 'endsWith' function works differently now.
//it will really only effect infoSuffix. Don't forget to set one ^^ (even if you don't use it).

init()
{
    info = landmark = "";

    product = productName();
    makeInventoryList();

    if (inventory == [])
        llSetText("", <0.0,0.0,0.0>, 0.0);
    else
        llSetText("Touch me to unpack\nyour new " + product + ".",<1.0,1.0,1.0>,1.0);

    checkInventoryPermissions();
}

makeInventoryList()
{
    inventory = [];
    integer i;
    integer n = llGetInventoryNumber(INVENTORY_ALL);
    for(i = 0; i < n; ++i)
    {
        string name = llGetInventoryName(INVENTORY_ALL, i);
        if(llGetInventoryPermMask(name, MASK_OWNER) & PERM_COPY)
            if(include(name))
            {
                inventory += [name];
                if(isInfo(name)) info = name;
                else if(isLandmark(name)) landmark = name;
            }
    }
}

string productName()
{
    string name = llGetObjectName();
    if(endsWith(name, packageSuffix))
        name = llGetSubString(name, 0, ~llStringLength(packageSuffix));
    return name;
}

checkInventoryPermissions()
{
    if(llGetOwner() == llGetCreator())
    {
        llOwnerSay(permissionString(llGetObjectPermMask(MASK_NEXT)));
        integer count = llGetInventoryNumber(INVENTORY_ALL);
        integer i;
        for(i = 0; i < count; ++i)
            checkPermissions(llGetInventoryName(INVENTORY_ALL, i));
    }
}

integer include(string name)
{
    if(name != llGetScriptName())
        return (llListFindList(exclude, [name]) == -1);
    return FALSE;
}

integer isLandmark(string name)
{
    if(llGetInventoryType(name) == INVENTORY_LANDMARK)
        if(llGetInventoryPermMask(name, MASK_OWNER) & PERM_TRANSFER)
            return TRUE;
    return FALSE;
}

integer isInfo(string name)
{
    if(llGetInventoryType(name) == INVENTORY_NOTECARD)
        if(llGetInventoryPermMask(name, MASK_OWNER) & PERM_TRANSFER)
            return endsWith(name, infoSuffix);
    return FALSE;
}

integer endsWith(string text, string value)
{
    return llDeleteSubString(text, 0, ~llStringLength(value)) == value;
}

checkPermissions(string name)
{
    integer mask = llGetInventoryPermMask(name, MASK_NEXT);
    llOwnerSay(name + ": " + permissionString(mask));
}

string permissionString(integer mask)
{
    list perms = [];
    if(mask & PERM_COPY) perms += "copy";
    if(mask & PERM_MODIFY) perms += "modify";
    if(mask & PERM_TRANSFER) perms += "transfer";
    if(perms == []) perms = ["limited"];
    return llList2CSV(perms);
}

default
{
    state_entry()
    {
        init();
    }
    on_rez(integer start_param)
    {
        init();
    }
    touch_start(integer total_number)
    {
        integer i = 0;
        for(i = 0; i < total_number; ++i)
        if(llDetectedKey(i) == llGetOwner())
        {
            if(llGetListLength(inventory))
            {
                llGiveInventoryList(llGetOwner(), prefix + " " + product, inventory);
                llOwnerSay("Your items are now in your inventory in a folder called " + prefix + " " + product + ".");
            }
        }
        else
        {
            llOwnerSay("Only the owner can unpack me.");
            if(info) llGiveInventory(llDetectedKey(i), info);
            if(landmark) llGiveInventory(llDetectedKey(i), landmark);
        }
    }
    changed(integer change)
    {
        if(change & CHANGED_INVENTORY) init();
    }
}

ScriptLibrary


LibraryGoGame

This is my first (and perhaps only) large scale LSL project. I don't have time to do anything more with it, so I'm going to release it into the wild for others to use, adapt, play with as the like. I only ask that noone sells the original as posted here, as that would be a bit greedy.

So, what is it? A fully functional Go board, which allows two players to play a game of Go and score up at the end. It uses various tricks, such as XYText style textures for the pieces on the board, and a squidgy double-click square selector device thing to keep the number of prims required to a minimum.

There are several components and scripts, so it's all a bit complex to put together, and it's not as well commented as perhaps it should be, but I'll do my best to write some useful instructions.

The board looks very basic. My talents do not lie in making things look pretty. All textures are linked to ones I've created. It would be trivial to put together some nicer textures and change the relevent keys. The code does the basic setting up of size/shape/colour of all parts of the game.

A large number of link messages are used to glue all the various bits and pieces together. I didn't document this as well as I should, and it's all a bit random as things which grow whilst coded tend to be. For my own benefit I eventually put together a little file documenting very briefly each message, so I'll include that here too.

As to the rules of the game of Go, there are plenty of websites out there that can explain it better than I can. However, I will say this, it is an extremely interesting and deep game that you can enjoy quickly, but study for years and not come close to mastering.

First up, GoButtonScript

// GoButtonScript
// Jonathan Shaftoe
// A simple button used on the Go board, can be one of Pass, Done, Info, Resign or Reset
// Drop into a basic prim, call it GoButton, then store in your inventory for later.

key gPlayer; // Current player whose turn it is.
float gSize; // scaling constant.
integer gType; // which type of button we are. 1 - pass, 2 - done, 3 - reset, 4 - resign, 5 - info

set_size(string str) {
    gSize = (float)str;
    llSetScale(<0.2 * gSize, 0.1 * gSize, 0.01 * gSize>);
}

default {
    state_entry() {
        llSetPrimitiveParams([
            PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
            PRIM_SIZE, <0.2, 0.1, 0.01>,
            PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>,  <0, 0, 0>, 0.0,
            PRIM_COLOR, ALL_SIDES, <0.8, 0.6, 0.5>, 1.0
        ]);
        llSitTarget(ZERO_VECTOR, ZERO_ROTATION);
        llSetObjectName("GoButton");
    }

    on_rez(integer start_param) {
        if (start_param > 0) {
            gType = start_param;
            if (gType == 1) {
                llSetTouchText("Pass");
//                llSetText("Pass", <0, 0, 0>, 1.0);
                llSetObjectDesc("Go Game Pass Button.");
                llSetTexture("f8919345-d4ab-613f-c594-5cdafcf6f8a1", 0);
            } else if (gType == 2) {
                llSetTouchText("Done");
//                llSetText("Done", <0, 0, 0>, 1.0);
                llSetObjectDesc("Go Game Done Button.");
                llSetTexture("9db6cc17-c2bc-04e9-bc5e-659e0b20d82f", 0);
            } else if (gType == 3) {
                llSetTouchText("Reset");
//                llSetText("Reset", <0, 0, 0>, 1.0);
                llSetObjectDesc("Go Game Reset Button");
                llSetTexture("619570be-ce9c-11ff-3f8b-9ebab0f3c759", 0);
            } else if (gType == 4) {
                llSetTouchText("Resign");
//                llSetText("Resign", <0, 0, 0>, 1.0);
                llSetObjectDesc("Go Game Resign Button");
                llSetTexture("5ad73a1c-060c-3fba-5662-584ddfcdc115", 0);
            } else if (gType == 5) {
                llSetTouchText("Info");
//                llSetText("Info", <0, 0, 0>, 1.0);
                llSetObjectDesc("Go Game Info Button");
                llSetTexture("d0b9c9de-a289-01df-734a-10a8201841ad", 0);
            }
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }

    touch_start(integer num) {
        if (gType == 1) {
            if (llDetectedKey(0) == gPlayer) {
                llMessageLinked(LINK_ROOT, 10, "", "");
            }
        } else if (gType == 2) {
            if (llDetectedKey(0) == gPlayer) {
                llMessageLinked(LINK_ROOT, 11, "", "");
            }
        } else if (gType == 3) {
            llMessageLinked(LINK_ROOT, 12, "", llDetectedKey(0));
        } else if (gType == 4) {
            llMessageLinked(LINK_ROOT, 13, "", llDetectedKey(0));
        } else if (gType == 5) {
            llMessageLinked(LINK_ROOT, 14, "", llDetectedKey(0));
        }
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 1) {
            set_size(str);
        } else if (num == 0) {
            gPlayer = id;
        }
    }
}

Next up, GoJoinButtonScript

// GoJoinButtonScript
// Jonathan Shaftoe
// A simple button used for players to join the Go game, and also to start the game.
// Gets disabled and hidden once game is in progress.
// Drop into a simple prim, call it GoJoinButton, then store in your inventory for later.

integer gColour; // what colour are we, 1 - black, 2 - white
float gSize;     // scaling constant
string gPlayAs;  // Play as Black or Play as White
vector gPlayAsCol; // Actual colour, black or white.

set_size() {
    llSetScale(<0.2 * gSize, 0.2 * gSize, 0.1 * gSize>);
}

default {
    state_entry() {
        llSetPrimitiveParams([
            PRIM_TYPE, PRIM_TYPE_SPHERE, PRIM_HOLE_DEFAULT, <0, 0.5, 0>, 0.0, ZERO_VECTOR, <0, 1, 0>,
            PRIM_SIZE, <0.4, 0.4, 0.1>,
            PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>,  <0, 0, 0>, 0.0,
            PRIM_COLOR, ALL_SIDES, <0.5, 0.5, 0.5>, 1.0
        ]);
        llSetTouchText("Join Game");
        llSetObjectDesc("Go Game Join Button.");
        llSetObjectName("GoJoinButton");
        llSitTarget(ZERO_VECTOR, ZERO_ROTATION);
    }

    on_rez(integer start_param) {
        if (start_param > 0) {
            gColour = start_param;
            if (gColour == 1) {
                llSetColor(<0, 0, 0>, ALL_SIDES);
                gPlayAs = "Play as Black";
                gPlayAsCol = <0, 0, 0>;
            } else if (gColour == 2) {
                llSetColor(<1, 1, 1>, ALL_SIDES);
                gPlayAs = "Play as White";
                gPlayAsCol = <1, 1, 1>;
            }
        }
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 1) {
            gSize = (float)str;
            set_size();
            state enabled;
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

state enabled {
    state_entry() {
        llSetAlpha(1.0, ALL_SIDES);
        llSetText(gPlayAs, gPlayAsCol, 1.0);
        set_size();
    }

    touch_start(integer num) {
        llMessageLinked(LINK_ROOT, gColour, "", llDetectedKey(0));
        state disabled;
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

state disabled {
    state_entry() {
        llSetAlpha(0.0, ALL_SIDES);
        llSetScale(<0.01, 0.01, 0.01>);
        llSetText("", <0, 0, 0>, 1.0);
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 999) {
            state enabled;
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

That's the simple stuff done. Now things get more complicated, you need to create two grids, one of 'tiles' (prim textures to display the pieces on the board), and one of 'sensors' (to detect clicks when specifying where you want to play).

First off sensors, here's the GoSensorScript

// GoSensorScript
// Jonathan Shaftoe
// Script for each individual Go Sensor, used to detect mouse clicks to allow the player to select
// where they want to play. Uses a two click mechanism to reduce prim usage, a grid of these sensors
// moves over the board. For example, using a 19x19 board, 20 sensors in a 4x5 grid are used to allow
// the player to select the square they want to play in. To start with, the 4x5 grid covers the
// whole board, each sensor covering 20 squares. When one is clicked on, the sensors rearrange to
// just be over the 20 squares that were under the sensor clicked, then allowing the player to
// select an individual square. It's hard to explain, but when you see it working it'll be much
// more sense.
// Create a single prim, call it GoSensor, then drop this script in. Then see GoSensorGridScript.

integer gIndex;  // which sensor in the grid am I
float gSize;     // global scaling factor

integer gXSensors; // how many sensors across in grid to cover board
integer gYSensors; // how many sensors up/down in grid to cover board

integer gXSquares; // how many squares on the board across covered by a sensor
integer gYSquares; // how many squares on the board up/down covered by a sensor

integer gGameSize; // size of game board, will be 9, 13 or 19

integer gXPosBase; // x coordinate of sensor in grid
integer gYPosBase; // y coordinate of sensor in grid

integer gXPos;  // x position of sensor in grid
integer gYPos;  // y position of sensor in grid

integer gZoomPosX; // x coordinate when zoomed in (after first click by player)
integer gZoomPosY; // y coordinate when zoomed in

float gSquareSize; // Size of an individual square on the board.

key gPlayer;  // player whose turn it currently is.

hide() {
    llSetScale(<0.01, 0.01, 0.01>);
    llSetPos(<0, 0, - 0.01>);
}

default {
    state_entry() {
        llSetPrimitiveParams([
            PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
            PRIM_SIZE, <0.5, 0.5, 0.5>,
            PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>,  <0, 0, 0>, 0.0,
            PRIM_COLOR, ALL_SIDES, <0, 0, 1>, 0.25
        ]);
        llSetObjectName("GoSensor");
        llSetTouchText("Play here");
        llSetObjectDesc("Go Game sensor - click once to select region to play in, then again for exact position.");
        llSitTarget(ZERO_VECTOR, ZERO_ROTATION);
    }

    on_rez(integer start_param) {
        gIndex = start_param;
        state ready;
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

// Do setup, hide sensor and wait for message
state ready {
    state_entry() {
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 3) {
            hide();
            list params = llParseString2List(str, [","], []);
            gGameSize = llList2Integer(params, 0);
            gXSensors = llList2Integer(params, 1);
            gYSensors = llList2Integer(params, 2);
            gSize = llList2Float(params, 3);

            gXSquares = llCeil((float)gGameSize / gXSensors);
            gYSquares = llCeil((float)gGameSize / gYSensors);

            gXPosBase = (gIndex % gXSensors);
            gYPosBase = (gIndex / gXSensors);

            if ((gXPosBase + gYPosBase) % 2 == 0) { // Alternating colours for checkerboard effect
                llSetColor(<1, 0, 0>, ALL_SIDES);
            } else {
                llSetColor(<0, 1, 0>, ALL_SIDES);
            }

            gXPos = gXPosBase * gXSquares;
            gYPos = gYPosBase * gYSquares;

            if (gGameSize - gXPos < gXSquares) {
                gXSquares = gGameSize - gXPos;
            }
            if (gGameSize - gYPos < gYSquares) {
                gYSquares = gGameSize - gYPos;
            }
            gSquareSize = gSize / gGameSize;
        } else if (num == 0) {
            gPlayer = id;
            state zoom1;
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

// Move to first position, sensor grid covering whole board, wait for player click
state zoom1 {
    state_entry() {
        if (gXPosBase >= gXSensors || gYPosBase >= gYSensors) {
            hide();
        } else {
            float width = gSquareSize * gXSquares;
            float height = gSquareSize * gYSquares;
            llSetScale(<width, height, 0.01 * gSize>);
            llSetPos(<(gXPos + (gXSquares / 2.0)) * gSquareSize - gSize / 2 , (gYPos + (gYSquares / 2.0)) * gSquareSize - gSize / 2, 0.02 * gSize + .02>);
        }
    }

    touch_start(integer num) {
        if (llDetectedKey(0) == gPlayer) {
            llMessageLinked(LINK_ALL_CHILDREN, 5, (string)gXPos + "," + (string)gYPos, "");
        } else {
//            llWhisper(0, "It's not your turn, " + llKey2Name(llDetectedKey(0)));
        }
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 5) {
            list params = llParseString2List(str, [","], []);
            gZoomPosX = llList2Integer(params, 0);
            gZoomPosY = llList2Integer(params, 1);
            state zoom2;
        }
        else if (num == 0) {
            gPlayer = id;
        } else if (num == 105 || num == 999) {
            hide();
            state ready;
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

// move to zoomed in grid, each sensor covering one square on the board, wait for click.
state zoom2 {
    state_entry() {
        if (gXPosBase >= gXSensors || gYPosBase >= gYSensors || gZoomPosX + gYPosBase >= gGameSize || gZoomPosY + gXPosBase >= gGameSize) {
            hide();
        } else {
            llSetScale(<gSquareSize, gSquareSize, 0.01 * gSize>);
            llSetPos(<(gZoomPosX + gYPosBase + 0.5) * gSquareSize - gSize / 2, (gZoomPosY + gXPosBase + 0.5) * gSquareSize - gSize / 2, 0.02 * gSize + .02>);
        }
    }

    touch_start(integer num) {
        if (gZoomPosX + gYPosBase < gGameSize && gZoomPosY + gXPosBase < gGameSize) {
            if (llDetectedKey(0) == gPlayer) {
                llMessageLinked(LINK_ALL_CHILDREN, 20, "", "");
                integer x = gZoomPosX + gYPosBase;
                integer y = gZoomPosY + gXPosBase;
                llMessageLinked(LINK_ROOT, 4, (string)x + "," + (string)y, "");

                state waiting;
            } else {
//                llWhisper(0, "It's not your turn, " + llKey2Name(llDetectedKey(0)));
            }
        }
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 0) {
            gPlayer = id;
            state zoom1;
        } else if (num == 20) {
            state waiting;
        } else if (num == 105 || num == 999) {
            hide();
            state ready;
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

// Move has been attempted, disable clicks and wait to see what to do next.
state waiting {
    link_message(integer sender, integer num, string str, key id) {
        if (num == 0) {
            gPlayer = id;
            state zoom1;
        } else if (num == 21) {
            state zoom2;
        } else if (num == 105 || num == 999) {
            hide();
            state ready;
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

And here's the GoSensorGridScript

// GoSensorGridScript
// Jonathan Shaftoe
// Creates the sensor grid by rezzing 19 sensors (to make 20, including the one we're in) and
// giving each one a unique index. Script removes itself once done. Will take a few seconds
// to run, as rezzing has a built in delay. Requires link permission as all the created
// sensors are linked together.
// To use, rez a previously created GoSensor (see GoSensorScript), then put a GoSensor
// in that GoSensor's inventory, then drop this script in. Wait until it has done its stuff,
// then take the resulting GoSensorGrid into your inventory for later.

// If you only wanted to support smaller game boards and use less prims, you could change
// totalSensors below to the relevent number. For a 9x9 board, you would only need 9
// sensors, so make totalSensors 8 - for a 13x13 board, you need 16 sensors, so make
// totalSensors 15.

integer gNumSensors = 0; // how many have we made
integer gCountRez = 0; // how many are still waiting to be linked.

default
{
    state_entry()
    {
        llSetObjectName("GoSensorGrid");
        llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
    }

    run_time_permissions(integer perms) {
        state start;
    }
}

state start {
    state_entry() {
        llWhisper(0, "Creating sensors, please wait ...");
        // 19x19 board needs 4x5 sensors, so 19 plus the one we're in to start with..
        integer totalSensors = 19;
        while (gNumSensors < totalSensors) {
            llRezObject("GoSensor", llGetPos(), ZERO_VECTOR, ZERO_ROTATION, gNumSensors + 1);
            gNumSensors++;
            gCountRez++;
        }
    }

    object_rez(key id) {
        llCreateLink(id, TRUE);
        gCountRez--;
        if (gCountRez == 0) {
            llRemoveInventory(llGetScriptName());
        } else if (gCountRez % 4 == 0) {
            llWhisper(0, "... " + (string)gCountRez + " more sensors to make ...");
        }
    }

}

Now for the tiles to display textures, first GoTileScript

// GoTileScript
// Jonathan Shaftoe
// A tile prim for displaying textures on the board to show where pieces are. Each tile is a squashed
// flattened cube, to create 3 square faces in a row, like XYText. Each face displays a texture which
// shows 4 game pieces. Using rotation and flipping, just 20 textures are required to display all
// 81 possible states of 4 squares on the board being either empty, black or white.
// The tile prims are made into a tile grid, in the same way as the sensors.
// Create a single prim, drop this script and the GoTileFaceScript in, then take the resulting
// GoTile object into your inventory and see GoTileGridScript

integer gGameSize; // size of game board, 9, 13 or 19
integer gIndex;  // which tile in the grid are we
float gSize;     // scaling factor

integer TILEWIDTH = 6;  // absolute values for making us the right shape
integer TILEHEIGHT = 2;

integer gRotated = 0; // are we one of the rotated tiles used down the edge to conserve prim count

integer gXPosBase;  // Our logical coordinates within the grid
integer gYPosBase;

integer gXPos;  // Our actual physical coordinates
integer gYPos;

float gSquareSize; // Size of an individual square on the board.

integer gFace0Modified = 0; // has one of the three faces been modified and need re-texturing
integer gFace1Modified = 0;
integer gFace2Modified = 0;

list gStates = []; // The state of this tile. Each face is a string in the list, each
                   // string is 4 characters of 0 - blank, 1 - black, 2 - white
list gStatesBackup = []; // backup copy of game state used for undoing changes during endgame
integer gGotBackup = 0;  // do we have a backup.
string  TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";

add_turn(integer face, integer x, integer y, string colour) {
    string current = llList2String(gStates, face);
//    llWhisper(0, "Got add turn, face: " + (string) face + " coords " + (string)x + ", " + (string)y + " and current state: " + current + ". Index is: " + (string)gIndex);
    string newstr;
    integer pos = (y * 2) + x;
    if (pos == 0) {
        newstr = colour + llGetSubString(current, 1, 3);
    } else if (pos == 3) {
        newstr = llGetSubString(current, 0, 2) + colour;
    } else {
        newstr = llGetSubString(current, 0, pos - 1) + colour + llGetSubString(current, pos + 1, 3);
    }
//    llWhisper(0, "New state is: " + newstr);
    gStates = llListInsertList(llDeleteSubList(gStates, face, face), [newstr], face);
}

show_face(integer face) {
    string statestr = llList2String(gStates, face);
    llMessageLinked(llGetLinkNumber(), 301, statestr, (string)(face + 10 * gRotated));
}

default {
    state_entry() {
        llSetPrimitiveParams([
            PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <0.333333, 1, 0>, ZERO_VECTOR,
            PRIM_SIZE, <1.5, 0.5, 0.01>,
            PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>,  <0, 0, 0>, 0.0,
            PRIM_COLOR, ALL_SIDES, <1, 1, 1>, 1.0
        ]);
        llSetObjectName("GoTile");
        llSetTouchText("");
        llSetObjectDesc("Go Game board tile");
        llSitTarget(ZERO_VECTOR, ZERO_ROTATION);

    }

    on_rez(integer start_param) {
        gIndex = start_param;
//        llSetText((string)gIndex, <0, 0, 0>, 1.0);
        state ready;
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

state ready {
    link_message(integer sender, integer num, string str, key id) {
        if (num == 3) {
            list params = llParseString2List(str, [","], []);
            gGameSize = llList2Integer(params, 0);
            gSize = llList2Float(params, 3);

            integer vertical_strips = 0;
            integer xTiles = gGameSize / TILEWIDTH;
            integer yTiles = gGameSize / TILEHEIGHT;
            if (yTiles * TILEHEIGHT < gGameSize) {
                yTiles++;
            }
            if (xTiles * TILEWIDTH + 2 < gGameSize) {
                xTiles++;
            } else {
                vertical_strips = xTiles + 1;
            }
            integer extra_index = gIndex - (xTiles * yTiles);
            if (extra_index >= vertical_strips) {
                // hide us away, not needed
                llSetScale(<0.01, 0.01, 0.01>);
                llSetPos(<0, 0, -.01>);
                llSetText("", <0,0,0>, 1.0);
                gXPos = -10;
                gYPos = -10;
            } else {
                if (extra_index >= 0 && extra_index < vertical_strips) {
                    gXPosBase = xTiles;
                    gYPosBase = extra_index * (TILEWIDTH / TILEHEIGHT);
                    gRotated = 1;
                } else {
                    gXPosBase = (gIndex % xTiles);
                    gYPosBase = (gIndex / xTiles);
                }

                gXPos = gXPosBase * TILEWIDTH;
                gYPos = gYPosBase * TILEHEIGHT;

                gSquareSize = gSize / gGameSize;
                llSetTexture(TRANSPARENT, ALL_SIDES);
                llSetScale(<gSquareSize * TILEWIDTH, gSquareSize * TILEHEIGHT, 0.01>);
                if (gRotated == 1) {
                    llSetPos(<(gXPos + (TILEHEIGHT / 2.0)) * gSquareSize - gSize / 2, (gYPos + (TILEWIDTH /     2.0)) * gSquareSize - gSize / 2, 0.005 * gSize + .005>);
                    llSetLocalRot(llEuler2Rot(<0, 0, PI_BY_TWO>));
                } else {
                    llSetPos(<(gXPos + (TILEWIDTH / 2.0)) * gSquareSize - gSize / 2, (gYPos + (TILEHEIGHT / 2.0)) * gSquareSize - gSize / 2, 0.01 * gSize + .001>);
                }
                gStates = ["0000", "0000", "0000"];
            }
        } else if (num == 201) {
            list params = llParseString2List(str, [","], []);
            integer x;
            integer y;
            string colour;
            x = llList2Integer(params, 0);
            y = llList2Integer(params, 1);
            colour = llList2String(params, 2);
            if (x < gXPos || y < gYPos) {
                return;
            }
            integer face;
            integer facex;
            integer facey;
            if (gRotated == 0) {
                facey = y - gYPos;
                if (facey > 1) {
                    return;
                }
                face = (x - gXPos) / 2;
                facex = (x - gXPos) % 2;
            } else {
                facex = x - gXPos;
                if (facex > 1) {
                    return;
                }
                face = (y - gYPos) / 2;
                facey = (y - gYPos) % 2;
            }
            if (face > 2) {
                return;
            }
            // need to save states in case we have to undo the changes - needed when marking groups for endgame
            if (id == "1" && gGotBackup == 0) {
                gGotBackup = 1;
                gStatesBackup = gStates;
            }
            add_turn(face, facex, facey, colour);
            if (colour != "0") {
                show_face(face);
            } else {
                if (face == 0) {
                   gFace0Modified = 1;
                } else if (face == 1) {
                    gFace1Modified = 1;
                } else if (face == 2) {
                    gFace2Modified = 1;
                }
            }
        } else if (num == 202) {
            if (gFace0Modified == 1) {
                show_face(0);
                gFace0Modified = 0;
            }
            if (gFace1Modified == 1) {
                show_face(1);
                gFace1Modified = 0;
            }
            if (gFace2Modified == 1) {
                show_face(2);
                gFace2Modified = 0;
            }
        } else if (num == 15) {
            if (gGotBackup == 1) {
                gStates = gStatesBackup;
                gStatesBackup = [];
                gGotBackup = 0;
                show_face(0);
                show_face(1);
                show_face(2);
            }
        } else if (num == 203) {
            gGotBackup = 0;
            gStatesBackup = [];
        } else if (num == 999) {
            gGotBackup = 0;
            gStatesBackup = [];
            gStates = ["0000", "0000", "0000"];
            show_face(0);
            show_face(1);
            show_face(2);
            gFace0Modified = 0;
            gFace1Modified = 0;
            gFace2Modified = 0;
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK)
            if (llGetLinkNumber() == 0)
                llDie(); // so i die
    }
}

Due to script length restrictions, it wouldn't all fit in one, so there's another script required here, GoTileFaceScript

// GoTileFaceScript
// Jonathan Shaftoe
// Texture-handling side of functionality required by GoTiles. Wouldn't all fit in one
// script due to script size limits. Also has problems with runtime size, hence the way
// the lists are joined together in bits. Contains a mapping from all of the possible
// game states of a 2x2 grid of squares, each of which being 0 - empty 1 - black or
// 2 - white, to the texture to be used to display it, and with what rotation/flipping.
// Also, each of the three faces on a tile has a different rotation to apply to make
// things all be the right way around.
// Some heavy borrowing from XYText in here - thank you!
// For usage, see GoTileScript

// List of 20 textures used to display board pieces.
list gTextureIndex = ["701917a8-d614-471f-13dd-5f4644e36e3c",
                        "b51f6506-2156-a8ea-15f5-172d80add211",
                        "d48ab9c0-c49d-e009-a893-84bd7326de84",
                        "3823e129-e09f-b0fb-8717-90bc88e8ef46",
                        "0f3113bb-39c2-e0b9-475e-00e59afd85b0",
                        "bc806a54-3b0e-8f96-c105-d9da31160be5",
                        "58b6e53a-8b13-726f-24e7-ec94798bc9c4",
                        "760aa9bf-c25f-6880-2292-0c3c56421985",
                        "633464b2-2d1b-b050-2af9-f6dd2427465f",
                        "9f71ccc5-63e1-f5f0-6407-43577019f408",
                        "ad62bcb5-a607-21da-66eb-15548aad6b0a",
                        "d56bad91-3d05-e90e-080f-31224b353c42",
                        "00e8a3c3-996e-ed81-5e37-a86b099a9be7",
                        "5b206598-0c5d-352f-14e2-d18b520919a6",
                        "cf839003-eaf4-4af3-abe9-5d8bf3f28f0a",
                        "ab6e5d0b-16b9-3aee-b898-8c532c5e691c",
                        "e05ef3e7-f2a3-4b8c-f4ea-86d8b00a1c20",
                        "b5014c6e-d0b6-c6c7-6c25-d532fe08476f",
                        "15c19f1d-ee3f-22ba-541c-c44c62ea75c7",
                        "3f563f39-1c4d-297a-3956-cc174a9209e6",
                        "6879b0ca-a295-dd20-1e08-cc60da377a1a"];

// Will contain complete list, put together from parts below. Striped list of 4 items,
// first is game state represented as a string, second is index into texture list for
// which texture to use, third is mirroring/flipping required of texture, fourth is
// rotation.
list gStateList = [];

// Index of prim faces for each of the three faces we're using to display textures
list gFaceIndex = [4, 0, 2];
// hack for apparent bug, can't just negate PI_BY_TWO.
float MINUS_PI_BY_TWO = -1.5707963267948966192313216916398;
// rotation needed for each of the three faces.
list gFaceRotations = [PI_BY_TWO, 0, MINUS_PI_BY_TWO];

// parts used to create gStateList, see boave.
list part1 =    ["0000", 0, <1, 1, 0>, 0.0,
                "1000", 1, <1, -1, 0>, 0.0,
                "0100", 1, <-1, -1, 0>, 0.0,
                "0010", 1, <1, 1, 0>, 0.0,
                "0001", 1, <-1, 1, 0>, 0.0,
                "2000", 2, <1, -1, 0>, 0.0,
                "0200", 2, <-1, -1, 0>, 0.0,
                "0020", 2, <1, 1, 0>, 0.0,
                "0002", 2, <-1, 1, 0>, 0.0,
                "1200", 3, <1, -1, 0>, 0.0,
                "2100", 3, <-1, -1, 0>, 0.0,
                "0012", 3, <1, 1, 0>, 0.0,
                "0021", 3, <-1, 1, 0>, 0.0,
                "1020", 3, <1, 1, 0>, PI_BY_TWO,
                "2010", 3, <-1, 1, 0>, PI_BY_TWO];
list part2 = [
                "0102", 3, <1, -1, 0>, PI_BY_TWO,
                "0201", 3, <-1, -1, 0>, PI_BY_TWO,
                "1002", 4, <1, -1, 0>, 0.0,
                "0120", 4, <-1, -1, 0>, 0.0,
                "2001", 4, <-1, 1, 0>, 0.0,
                "0210", 4, <1, 1, 0>, 0.0,
                "1100", 5, <1, -1, 0>, 0.0,
                "0011", 5, <1, 1, 0>, 0.0,
                "1010", 5, <1, 1, 0>, PI_BY_TWO,
                "0101", 5, <1, -1, 0>, PI_BY_TWO,
                "1001", 6, <-1, 1, 0>, 0.0,
                "0110", 6, <1, 1, 0>, 0.0,
                "2200", 7, <1, -1, 0>, 0.0,
                "0022", 7, <1, 1, 0>, 0.0,
                "2020", 7, <1, 1, 0>, PI_BY_TWO];
list part3 = [
                "0202", 7, <1, -1, 0>, PI_BY_TWO,
                "2002", 8, <-1, 1, 0>, 0.0,
                "0220", 8, <1, 1, 0>, 0.0,
                "1110", 9, <-1, -1, 0>, 0.0,
                "1101", 9, <1, -1, 0>, 0.0,
                "1011", 9, <-1, 1, 0>, 0.0,
                "0111", 9, <1, 1, 0>, 0.0,
                "0211", 10, <1, 1, 0>, 0.0,
                "2011", 10, <-1, 1, 0>, 0.0,
                "1102", 10, <1, -1, 0>, 0.0,
                "1120", 10, <-1, -1, 0>, 0.0,
                "1012", 10, <1, 1, 0>, PI_BY_TWO,
                "1210", 10, <-1, 1, 0>, PI_BY_TWO,
                "0121", 10, <1, -1, 0>, PI_BY_TWO,
                "2101", 10, <-1, -1, 0>, PI_BY_TWO];
list part4 = [
                "0112", 11, <1, 1, 0>, 0.0,
                "1201", 11, <1, -1, 0>, 0.0,
                "1021", 11, <-1, 1, 0>, 0.0,
                "2110", 11, <-1, -1, 0>, 0.0,
                "0122", 12, <1, 1, 0>, 0.0,
                "2201", 12, <1, -1, 0>, 0.0,
                "1022", 12, <-1, 1, 0>, 0.0,
                "2210", 12, <-1, -1, 0>, 0.0,
                "2021", 12, <1, 1, 0>, PI_BY_TWO,
                "2120", 12, <-1, 1, 0>, PI_BY_TWO,
                "0212", 12, <1, -1, 0>, PI_BY_TWO,
                "1202", 12, <-1, -1, 0>, PI_BY_TWO,
                "0221", 13, <1, 1, 0>, 0.0,
                "2102", 13, <1, -1, 0>, 0.0,
                "2012", 13, <-1, 1, 0>, 0.0];
list part5 = [
                "1220", 13, <-1, -1, 0>, 0.0,
                "2220", 14, <-1, -1, 0>, 0.0,
                "2202", 14, <1, -1, 0>, 0.0,
                "2022", 14, <-1, 1, 0>, 0.0,
                "0222", 14, <1, 1, 0>, 0.0,
                "1111", 15, <1, 1, 0>, 0.0,
                "1121", 16, <1, 1, 0>, 0.0,
                "2111", 16, <1, -1, 0>, 0.0,
                "1112", 16, <-1, 1, 0>, 0.0,
                "1211", 16, <-1, -1, 0>, 0.0,
                "1122", 17, <1, 1, 0>, 0.0,
                "2211", 17, <1, -1, 0>, 0.0,
                "2121", 17, <1, 1, 0>, PI_BY_TWO,
                "1212", 17, <1, -1, 0>, PI_BY_TWO];
list part6 = [
                "1221", 18, <1, 1, 0>, 0.0,
                "2112", 18, <-1, 1, 0>, 0.0,
                "1222", 19, <1, 1, 0>, 0.0,
                "2212", 19, <1, -1, 0>, 0.0,
                "2122", 19, <-1, 1, 0>, 0.0,
                "2221", 19, <-1, -1, 0>, 0.0,
                "2222", 20, <1, 1, 0>, 0.0];
                // 2111

default {
    state_entry() {
        gStateList = part1 + part2 + part3 + part4 + part5 + part6;
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 301) {
            integer face = (integer)((string)id) % 10;
            integer rotated = (integer)((string)id) / 10;
            integer realface = llList2Integer(gFaceIndex, face);
            string mystate;
            if (rotated == 1) {
                // need to horizontally flip the state we're looking for if we're part of a rotated tile.
                mystate = llGetSubString(str, 1, 1) + llGetSubString(str, 3, 3) + llGetSubString(str, 0, 0) + llGetSubString(str, 2, 2);
            } else {
                mystate = str;
            }
            integer pos = llListFindList(gStateList, [mystate]);
            if (pos == -1) {
                llWhisper(0, "Failed to find state for: " + mystate);
                return;
            }
            integer texture = llList2Integer(gStateList, pos + 1);
            key realtexture = llList2Key(gTextureIndex, texture);
            llSetPrimitiveParams([PRIM_TEXTURE, realface, realtexture, llList2Vector(gStateList, pos + 2), <0, 0, 0>, llList2Float(gStateList, pos + 3) + llList2Float(gFaceRotations, face)]);
        }
    }
}

As with the sensors, we need a utility script to create the grid, GoTileGridScript.

// GoTileGridScript
// Jonathan Shaftoe
// Utility script to create a linked grid of GoTile prims, each with a unique index.
// To use, rez a previously created GoTile from your inventory (see GoTileScript), then
// put a GoTile into the inventory of the one you rezzed, then put this script into it.
// It will spend a while rezing and linking all the GoTiles required, then once finished
// the script will delete itself from the object, now called GoTileGrid, which you take
// into your inventory and save for later.

// By default, this creates 33 sensors to make a total of 34, needed to cover a 19x19 board.
// If you only want to support smaller board sizes, you can reduce the totalTiles variable
// below to reduce the prim usage. For example, a 9x9 board just uses 10 tiles, so you can
// set totalTiles to 9.

integer gNumTiles = 0; // how many have we created
integer gCountRez = 0; // how many are waiting to be rezzed and linked.

default
{
    state_entry()
    {
        llSetObjectName("GoTileGrid");
        llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
    }

    run_time_permissions(integer perms) {
        state start;
    }
}

state start {
    state_entry() {
        llWhisper(0, "Creating tiles, please wait ...");
        // 19x19 board needs 34, 6x2 sensors, minus the one we're in to start with
        integer totalTiles = 33;
//        integer totalTiles = 9; // just 9x9 for testing
        while (gNumTiles < totalTiles) {
            llRezObject("GoTile", llGetPos(), ZERO_VECTOR, ZERO_ROTATION, gNumTiles + 1);
            gNumTiles++;
            gCountRez++;
        }
    }

    object_rez(key id) {
        llCreateLink(id, TRUE);
        gCountRez--;
        if (gCountRez == 0) {
            llRemoveInventory(llGetScriptName());
        } else if (gCountRez % 4 == 0) {
            llWhisper(0, "... " + (string)gCountRez + " more tiles to make ...");
        }
    }

}

At this point, I seem to have hit the limit of wiki page size, so please carry on to Second page


LibraryInstantMessageAnyone

Examples ScriptLibrary AnorcaCalamari BlindWanderer
This script can IM anyone in the W-HAT key database and on the grid you're on.
It can also give any inventory it has to people on command.

Channel | Parameters | Description | Example
40 | Avatar Name | Gets the users key from W-Hat database | /40 Anorca Calamari
50 | Message | Sends the message to the last avatar queried by the script | /50 Hello Hello
60 | Item | Sends an item from the objects inventory to the last avatar queried by the script | /60 InstantMessage

//Original Script Created by AnorcaCalamari
//Modified by BlindWanderer
//BlindWanderer added lag reducing features,Inventory check,non cap sensitive inventory and a few other things.
//Please do not sell this script except as a part of a larger creation. You may modify it.
//You can give this script away for free.
key requestid;
string resident;
key target;
key owner;
string owner_name;

string InventoryType(string name, integer type)
{//finds an item in a case insensitive fashion of the given type and returns its true name.
    integer a = llGetInventoryType(name);
    if(!~a)//a == INVENTORY_NONE
    {
        string b;
        a = llGetInventoryNumber(type);
        name = llToLower(name);
        while(a)
        {//(a = ~-a) is equivalent to --a, but runs faster.
            if(llToLower(b = llGetInventoryName(type, a = ~-a)) == name)
            {//we found a match ^^
                return b;
            }
        }
    }
    else if((a == type) ^ (!~type))//when type == INVENTORY_ALL, invert the result.
    {//the reason for this mess is that INVENTORY_NONE == INVENTORY_ALL
        return name;
    }
    return "";
}

default
{
    state_entry()
    {
        owner = llGetOwner();
        owner_name = llKey2Name(owner);//never changes.
        if(owner_name == "")
            requestid = llRequestAgentData(owner, DATA_NAME);//this should never need to be called but it's here just in case.
        llListen(40, "", owner, "");
        llListen(50, "", owner, "");
        llListen(60, "", owner, "");
    }

    listen(integer channel, string name, key id, string message)
    {
        if(channel == 40)
        {
            if((key)message)//check to see if message is a valid key.
            {//if it is, skip the lookup.
                target = message;
                resident = "";
            }
            else
            {//look up the name.
                list names = llList2List(llParseString2List(message, [" "], []), 0, 1);
                resident = llDumpList2String(names, " ");
                requestid = llHTTPRequest("http://w-hat.com/name2key?name=" + llDumpList2String(names, "+"), [HTTP_METHOD, "GET"], "");
                target = "";
            }
        }
        else if(target)
        {//make sure 'target' is a valid key.
            if(channel == 50)
            {
                llInstantMessage(target, owner_name + ": " + message);
            }
            else if(channel == 60)
            {
                name = InventoryType(message, INVENTORY_ALL);
                if(name)
                {//make sure name is not null.
                    llGiveInventory(target, name);
                }
                else
                {//if name is null, then the item could not be found.
                    llOwnerSay("Item \"" + message + "\" not found in inventory");
                }
            }
        }
        else
        {//since it wasn't channel 40, and target is invalid, it must be channel 50 or 60
            llOwnerSay("Invalid user, please lookup a user with \"/40 User Name\"");
        }
    }

    http_response(key request_id, integer status, list metadata, string body)
    {
        if (request_id == requestid)
        {
            integer i = llSubStringIndex(body,resident);
            if ( i != -1 )
            {
                i += llStringLength(resident) + 1;//length of name plus a space.
                target = llGetSubString(body, i, i + 35);
                if(target)
                {//test the key to make sure it is valid
                    llOwnerSay("Headset set to: \"" + resident + "\" at key: " + (string)target);
                    return;
                }//if it is not valid fall through to the error message.
            }
            llSay(0,"No resident named \"" + resident + "\" found in the w-hat name2key database");
        }
        else
        {
            llSay(0, (string)status + " error");
        }
    }

    on_rez(integer rez_code)
    {
        if(llGetOwner() != owner)
        {
            llResetScript();
        }
    }

    dataserver(key id, string data)
    {
        if(id == requestid)
        {
            owner_name = data;
        }
    }
}

Original script by Anorca Calamari
Full names required.
Cap sensitive.
key requestid;
string resident;

string target;
default
{
    state_entry()
    {
        llListen(40,"",llGetOwner(),"");
        llListen(50,"",llGetOwner(),"");
        llListen(60,"",llGetOwner(),"");
    }

    listen(integer channel,string name,key id,string message)
    {
        if(channel==40)
        {
            list names = llParseString2List(message,[" "],[]);
            resident = llDumpList2String(names," ");
            requestid = llHTTPRequest("http://w-hat.com/name2key?name="+llDumpList2String(names,"+"),[HTTP_METHOD,"GET"],"");
            target=message;
        }
        if(channel==50)
        {
            llInstantMessage((key)target,llKey2Name(llGetOwner())+":"+message);
        }
        if(channel==60)
        {
            llGiveInventory(target,message);
        }
    }

   http_response(key request_id, integer status, list metadata, string body)
    {
        if (request_id == requestid)
        {
            integer i = llSubStringIndex(body,resident);
            if ( i != -1 )
            {
            string avatarkey=llGetSubString(body,i,i+llStringLength(resident)+36);
            llOwnerSay("Headset set to: "+target+" at key: "+avatarkey);
            avatarkey=llGetSubString(avatarkey,llStringLength(avatarkey) - 36,llStringLength(avatarkey));
target=avatarkey;

            }
            else
                llSay(0,"No resident named \""+resident+"\" found in the w-hat name2key database");
        } else
            llSay(0,(string)status+" error");
    }
}


LibraryKeknehvParticles

Keknehv's Particle Script v1.2

This script was designed for a decent balance of efficiency and usability. It is good if you know what you wish to accomplish, and avoids added complexity of some of the other wrappers. The actual function is 46 lines long.

If you look closely, you'll see that all this script really is is an llParticleSystem with all the parameters, commented.

// Keknehv's Particle Script v1.2
// 1.0 -- 5/30/05
// 1.1 -- 6/17/05
// 1.2 -- 9/22/05 (Forgot PSYS_SRC_MAX_AGE)

//     This script may be used in anything you choose, including and not limited to commercial products.
//     Just copy the MakeParticles() function; it will function without any other variables in a different script
//         ( You can, of course, rename MakeParticles() to something else, such as StartFlames() )

//    This script is basically an llParticleSystem() call with comments and formatting. Change any of the values
//    that are listed second to change that portion. Also, it is equipped with a touch-activated off button,
//    for when your particles go haywire and cause everyone to start yelling at you.

//  Contact Keknehv Psaltery if you have questions or comments.

MakeParticles()                //This is the function that actually starts the particle system.
{
    llParticleSystem([                   //KPSv1.0
        PSYS_PART_FLAGS , 0 //Comment out any of the following masks to deactivate them
    //| PSYS_PART_BOUNCE_MASK           //Bounce on object's z-axis
    //| PSYS_PART_WIND_MASK             //Particles are moved by wind
    | PSYS_PART_INTERP_COLOR_MASK       //Colors fade from start to end
    | PSYS_PART_INTERP_SCALE_MASK       //Scale fades from beginning to end
    | PSYS_PART_FOLLOW_SRC_MASK         //Particles follow the emitter
    | PSYS_PART_FOLLOW_VELOCITY_MASK    //Particles are created at the velocity of the emitter
    //| PSYS_PART_TARGET_POS_MASK       //Particles follow the target
    | PSYS_PART_EMISSIVE_MASK           //Particles are self-lit (glow)
    //| PSYS_PART_TARGET_LINEAR_MASK    //Undocumented--Sends particles in straight line?
    ,

    //PSYS_SRC_TARGET_KEY , NULL_KEY,   //Key of the target for the particles to head towards
                                                //This one is particularly finicky, so be careful.
    //Choose one of these as a pattern:
    //PSYS_SRC_PATTERN_DROP                 Particles start at emitter with no velocity
    //PSYS_SRC_PATTERN_EXPLODE              Particles explode from the emitter
    //PSYS_SRC_PATTERN_ANGLE                Particles are emitted in a 2-D angle
    //PSYS_SRC_PATTERN_ANGLE_CONE           Particles are emitted in a 3-D cone
    //PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY     Particles are emitted everywhere except for a 3-D cone

    PSYS_SRC_PATTERN,           PSYS_SRC_PATTERN_ANGLE_CONE

    ,PSYS_SRC_TEXTURE,           ""                 //UUID of the desired particle texture, or inventory name
    ,PSYS_SRC_MAX_AGE,           0.0                //Time, in seconds, for particles to be emitted. 0 = forever
    ,PSYS_PART_MAX_AGE,          1.0                //Lifetime, in seconds, that a particle lasts
    ,PSYS_SRC_BURST_RATE,        0.02               //How long, in seconds, between each emission
    ,PSYS_SRC_BURST_PART_COUNT,  2                  //Number of particles per emission
    ,PSYS_SRC_BURST_RADIUS,      5.0                //Radius of emission
    ,PSYS_SRC_BURST_SPEED_MIN,   5.5                //Minimum speed of an emitted particle
    ,PSYS_SRC_BURST_SPEED_MAX,   6.0                //Maximum speed of an emitted particle
    ,PSYS_SRC_ACCEL,             <0.0,0.0,-0.8>     //Acceleration of particles each second
    ,PSYS_PART_START_COLOR,      <0.0,0.0,1.0>      //Starting RGB color
    ,PSYS_PART_END_COLOR,        <0.6,0.6,1.0>      //Ending RGB color, if INTERP_COLOR_MASK is on
    ,PSYS_PART_START_ALPHA,      0.9                //Starting transparency, 1 is opaque, 0 is transparent.
    ,PSYS_PART_END_ALPHA,        0.0                //Ending transparency
    ,PSYS_PART_START_SCALE,      <2.4,2.4,0.0>      //Starting particle size
    ,PSYS_PART_END_SCALE,        <1.3,2.3,0.0>      //Ending particle size, if INTERP_SCALE_MASK is on
    ,PSYS_SRC_ANGLE_BEGIN,       PI                 //Inner angle for ANGLE patterns
    ,PSYS_SRC_ANGLE_END,         PI                 //Outer angle for ANGLE patterns
    ,PSYS_SRC_OMEGA,             <0.0,0.0,0.0>       //Rotation of ANGLE patterns, similar to llTargetOmega()
            ]);
}

default
{
    state_entry()
    {
        MakeParticles();                //Start making particles
    }

    touch_start( integer num )            //Turn particles off when touched
    {
        state off;        //Switch to the off state
    }
}

state off
{
    state_entry()
    {
        llParticleSystem([]);        //Stop making particles
    }

    touch_start( integer num )        //Turn particles back on when touched
    {
        state default;
    }
}

ScriptLibrary


LibraryKeyboardScript

Ok, so here's my first addition to the library. Feedback welcome. bugs? Poor choice of implementation? Contact me in world.
Yes, I know it's commenty, but the comments aren't for you 733T 5cr!pt3r5 - it's to help newer people navigate, explain what's what - you don't need this script, anyway. XD

You can drop this in a rectangular prim and it should work.

Thanks to Strife for pointers/cleanup.

Max

//Max Case's Free Keyboard Script 1.0  04/18/2005
//Put it in a prim, attach it to yourself (or not) and watch as a keyboard magically appears whenever you type.
//Feel free to reuse, etc.

//Please don't just resell this script. That would be lame, since I am giving it to you for free.
//naturally, if you build something nice off of it, sell that.
//Or make a nice texture you would like to sell with keyboard script, that's ok. Just leave script open to all to see.
//Feedback/Rates/Donations/Hate Mail always welcome. Don't be shy.
//leave in the comments. they don't affect performance, and they are meant to help other beginners learn about LSL.
// - Max
//25.03.2007 This was written before things like CHANGED_OWNER, and before I knew what I  was doing :) Dusting it off.

integer keyboard_status; //Used to track if the keyboard is visible or not.
key kbTexture = "eef62334-e85e-b8f5-0717-19056bdbafde";
integer box_face = 0; //What side to show the keyboard. 0 means Top

initialize() {
    llSay(0, "Welcome to Max's Symple Keyboard.");     //You can change the welcome messages here.
    llSay(0, "initializing...");
    llSetTexture(kbTexture, box_face);        //Setting the Keyboard Texture
    llSetTextureAnim(ANIM_ON | LOOP, box_face, 4, 1, 0, 0, 4); //Set the typing animation

    llSetAlpha(0.0, ALL_SIDES);            //Hide the keyboard.

    llSetTimerEvent(.5);         //honestly you could make this shorter, but what would be the point? .5 seems to work nice.
    keyboard_status = FALSE;    //Keep track of the keyboards visibility.
}

default {
    state_entry() {
       initialize();
    }

    changed(integer change) {
        if (change & CHANGED_OWNER) {
            llResetScript();
        }
    }

    timer() {
        // are we typing? Well? Are we??? thanks to Strife for rewrite here.
        integer are_we_typing = llGetAgentInfo(llGetOwner()) & AGENT_TYPING;

        if (keyboard_status != are_we_typing ) { //keyboard_status changed since last checked?
            llSetAlpha(!keyboard_status, box_face);//flip the keyboard_status
            keyboard_status = are_we_typing;//save the current keyboard_status.
        }
    }
}

ScriptLibrary


LibraryLinkNumsScript

I made a function like this for my Flying Tako sailboat so that I didn't have to worry about linking the various parts in just the right order, or have to remember which part was link #14 (or which link number the sail was). If all your child prims have unique names (like "mast" or "door"), then you can use this function to automatically get the link numbers for them and assign them to variables in your script. This makes using llMessageLinked much easier - for example, you could just use a new variable DOOR to send a link message to the door on your vehicle. Or use the DOOR variable to change the color of your door in llSetLinkColor.

integer PRIM1;
integer PRIM2;
integer PRIM3;

getLinkNums() {
    integer i;
    integer linkcount=llGetNumberOfPrims();
    for (i=1;i<=linkcount;++i) {
        string str=llGetLinkName(i);
        if (str=="prim1") PRIM1=i;
        if (str=="prim2") PRIM2=i;
        if (str=="prim3") PRIM3=i;
    }
}

default {

    state_entry() {
        getLinkNums();
        llMessageLinked(PRIM1,0,"Test","");
        llMessageLinked(PRIM2,0,"Test","");
        llMessageLinked(PRIM3,0,"Test","");
    }

}


LibraryListenTexture

This script, when placed into an object, will listen for "text *", where * is the key of the texture to use.
It can get annoying if you place it into a lot of prims, so don't use it a lot.

//Listening Texturer script by Aldar Rayner
default
{
    state_entry()
    {
        //Listen to my owner.
        llListen(0,"",llGetOwner(),"");
    }

    listen(integer chan, string name, key id, string msg)
    {
        //Figure out what the key is and use it.
        list parsed = llParseString2List(msg,[" "],[]);
        string text = llList2String(parsed,0);
        string texture = llList2String(parsed,1);
        //Edit the word text in quotes to change the trigger keyword.
        if(text == "text")
        {
            llSetPrimitiveParams([PRIM_TEXTURE,ALL_SIDES,(key)texture,<1,1,0>,<0,0,0>,0.0]);
        }
    }
}
Script Library | Examples | ClientAssetKeys


LibraryMilliTime

A collection of time utility functions, to try and make sub-second and timezone handling easier.

First a timezone-aware cousin of llGetGMTclock:
// By Christopher Wolfe (SL name "Minsk Oud").
// This script is in the public domain.

//
// A version of llGetGMTclock() with timezone support, using the
// offset in seconds. Some useful offsets:
//
// EST = -5 * 3600 = -18000
// PST = -8 * 3600 = -28800
//
integer GetTZclock(integer offset)
{
    if (offset < 0) offset += 86400;
    integer time = (integer) llGetGMTclock() + offset;
    if (time >= 86400) time -= 86400;
    return time;
}

The remainder of these functions deal with time of day as *milliseconds* since midnight. As this format would overflow every ~24 days, it must not be used to represent date or elapsed time information (unless you are Microsoft and like your servers crashing).

// By Christopher Wolfe (SL name "Minsk Oud").
// This script is in the public domain.

// Gets the number of milliseconds since midnight UTC.
integer GetGMTmsclock()
{
    string stamp = llGetTimestamp();
    return
        (integer) llGetSubString(stamp, 11, 12) * 3600000 +
        (integer) llGetSubString(stamp, 14, 15) * 60000 +
        llRound((float) llGetSubString(stamp, 17, -2) * 1000.0);
}

// Gets the number of milliseconds since midnight in the specified
// timezone, using the offset in milliseconds. Some useful offsets:
//
// EST = -5 * 3600000 = -18000000
// PST = -8 * 3600000 = -28800000
//
integer GetTZmsclock(integer offset)
{
    if (offset < 0) offset += 86400000;
    integer time = (integer) GetGMTmsclock() + offset;
    if (time >= 86400000) time -= 86400000;
    return time;
}

//
// Formats a number of milliseconds since midnight as human readable
// time of day.
//
// time:     Number of milliseconds since midnight. Must be greater than
//           or equal to zero and strictly less than 86400000.
//
// military: If TRUE, output in 24-hour (or military) time.
//           If FALSE, output in 12-hour time with AM/PM indicator.
//
// second_decimals: Number of decimal digits to display for seconds.
//           Zero will remove the decimal point, greater than three
//           is useless.
//
string FormatTime(integer time, integer military, integer second_decimals)
{
    // Uncomment this to ensure time is within the correct range
    //
    // while (time < 0) time += 86400000;
    // while (time >= 86400000) time -= 86400000;
    //

    integer hour = time / 3600000; time -= hour * 3600000;
    integer minute = time / 60000; time -= minute * 60000;
    integer second = time / 1000; time -= second * 1000;

    string ampm;
    if (military) {
        ampm = "";
    }
    else {
        // 12 noon until 11:59:59 (just before midnight) are "PM"
        if (hour >= 12) ampm = " PM";
        // 12 midnight until 11:59:59 (just before noon) are "AM"
        else ampm = " AM";

        if (hour == 0) hour = 12;
        else if (hour > 12) hour -= 12;
    }

    // Only pad the hour field if military time is specified

    string shour;
    if (military && hour < 10) shour = "0" + (string) hour; else shour = (string) hour;

    // Pad the minute and second fields in either time format

    string sminute;
    if (minute < 10) sminute = "0" + (string) minute; else sminute = (string) minute;

    string ssecond;
    if (second < 10) ssecond = "0" + (string) second; else ssecond = (string) second;

    string decimals;
    if (second_decimals > 0) {
        // Construct the decimals string with all interesting digits to the
        // left of the decimal point. This automatically provides zero padding
        // unless the actual decimal value is zero.
        decimals = (string) llFloor(time * llPow(10, second_decimals - 3));

        // If the decimal value was exactly zero, additional padding will need
        // to be added.
        while (llStringLength(decimals) < second_decimals) decimals += "0";

        // Prepend a decimal point.
        decimals = "." + decimals;
    }
    else {
        decimals = "";
    }

    return shour + ":" + sminute + ":" + ssecond + decimals + ampm;
}


LibraryMitzFader

The MitzFader

This script will allow you to fade an object to a specific alpha between 0 and 100.
The transition effect is fairly smooth, and can be adjusted to your liking.
The script is written so that a chat command controls the action, but this can be changed to allow for other types of control, such as a Dialog Box, touch command, ect.

// Fader by Mitzpatrick Fitzsimmons

// Place this script inside the object you wish to "fade"

//---------------------------------------------------------
//                   NOTES
// You can control (somewhat) how fast or slow your fader works by editing the float v variable
// which is set as (i * 0.01) by default.
// Change the 0.01 value to your liking (HINT: 0.02 will fade 2x faster than 0.01)
// You can also change the fade and fadeset values too...just remember to change your float too!

// SCRIPT

integer fadeset = 0;
integer fade = 100;

fade_up()
{
        float i;
        for (i =fadeset; i < fade; i++)
        {
            float v = i * 0.01;
            llSetAlpha(v, ALL_SIDES);
          }
}

fade_down()
{
        float i;
        for (i =fadeset; i > fade; i--)
        {
            float v = i * 0.01;
            llSetAlpha(v, ALL_SIDES);
          }
}

default
{
    state_entry()
    {
        llOwnerSay("Type 'fade #' where # is between 0 and 100");
        llListen(0,"",llGetOwner(),"");
        llListen(1,"",llGetOwner(),"");
    }

    on_rez(integer num)
    {
        llResetScript();
    }

    listen(integer number, string name, key id, string message)
    {
        if(llGetSubString(message, 0, 4) == "fade ")
        {
            fadeset = fade;
            fade = (integer) llGetSubString(message, 5, -1);
            if(fadeset < fade)
            {
                fade_up();
            }else{
                fade_down();
            }
            fadeset = fade;
        }
   }

}


LibraryMultiChannelListener

Examples ScriptLibrary AnorcaCalamari
This script listens on a set of 50 channels. If you set integer lm to 1 then its listening on channels 1 through 50. 2 would be 2 through 51. 51 would be 51 through 100.
It is not only limited to sets like 1-50 and 51-100 you can have lm=3 and itll be 3-52.

Integer lm | Channel Set
1 | 1-50
51 | 51-100
101 | 101-150
And so forth.

//Please do not sell this script. You may give it away or modify it but please do not sell it
//except as part of a larger creation.

integer lm=1;
default
{
    state_entry()
    {
        llListen(lm,"","","");
        llListen(lm+1,"","","");
        llListen(lm+2,"","","");
        llListen(lm+3,"","","");
        llListen(lm+4,"","","");
        llListen(lm+5,"","","");
        llListen(lm+6,"","","");
        llListen(lm+7,"","","");
        llListen(lm+8,"","","");
        llListen(lm+9,"","","");
        llListen(lm+10,"","","");
        llListen(lm+11,"","","");
        llListen(lm+12,"","","");
        llListen(lm+13,"","","");
        llListen(lm+14,"","","");
        llListen(lm+15,"","","");
        llListen(lm+16,"","","");
        llListen(lm+17,"","","");
        llListen(lm+18,"","","");
        llListen(lm+19,"","","");
        llListen(lm+20,"","","");
        llListen(lm+21,"","","");
        llListen(lm+22,"","","");
        llListen(lm+23,"","","");
        llListen(lm+24,"","","");
        llListen(lm+25,"","","");
        llListen(lm+26,"","","");
        llListen(lm+27,"","","");
        llListen(lm+28,"","","");
        llListen(lm+29,"","","");
        llListen(lm+30,"","","");
        llListen(lm+31,"","","");
        llListen(lm+32,"","","");
        llListen(lm+33,"","","");
        llListen(lm+34,"","","");
        llListen(lm+35,"","","");
        llListen(lm+36,"","","");
        llListen(lm+37,"","","");
        llListen(lm+38,"","","");
        llListen(lm+39,"","","");
        llListen(lm+40,"","","");
        llListen(lm+41,"","","");
        llListen(lm+42,"","","");
        llListen(lm+43,"","","");
        llListen(lm+44,"","","");
        llListen(lm+45,"","","");
        llListen(lm+46,"","","");
        llListen(lm+47,"","","");
        llListen(lm+48,"","","");
        llListen(lm+49,"","","");
    }

    listen(integer channel,string name,key id,string message)
    {
        llOwnerSay(message + " said on channel "+ (string)channel);
    }
}

Listens on channels 1 through 50.


LibraryMultidimensionalArray

This script allows you to store data in lists as you would in a C++ or Java multidimensional array. It is a bit more flexible than those language's arrays, since it allows you to dynamically resize each dimension; You don't have to define the size of the array before you use it. If you ever ran into the "Runtime Error: Lists cannot contain lists" message and really needed a list within another, this is what you can use.

For example, say you had a chessboard with each square a different color. Your script can refer to each color using a two-dimensional array; one number for the row another for the column of the particular color.
default {
    state_entry() {
        integer SQUARES_ON_SIDE = 12;
        // Randomly assign colors to each square:
        list colorArray;
        integer row;
        integer col;
        for (row = 0; row < SQUARES_ON_SIDE; ++row) {
            for (col = 0; col < SQUARES_ON_SIDE; ++col) {
                vector color = <llFrand(1), llFrand(1), llFrand(1)>;
                colorArray = setArray(colorArray, [row, col], [color]);
            }
        }

        // Get the color of the square at row 1, column 2:
        vector squareColor = (vector) llList2String(getArray(colorArray, [0, 1]), 0); // Note that arrays, like LSL's lists, are 0-based; they start counting at 0, not 1.
        // ...

        // Get the colors of all the squares in row 4:
        list rowColors = getArray(colorArray, [3]);
        // ...
    }
}

Getting an element at index [1, 5, 2, 9], in an array "A" translates to:
Get list at index 1 in A, call this B,
Get list at index 5 in B, call this C,
Get list at index 2 in C, call this D,
Return element at index 9 in D.

And here are the functions :)
// Generates a random string of length len
// from the characters passed to it.
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len) {
        integer val;
        do {
            val = (integer)llFrand(60) + 20;
        } while (val == firstChar || val == lastChar);
        ret += llUnescapeURL("%" + (string)val);
    }
    return ret + llUnescapeURL("%"+(string)lastChar);
}

integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomSeperator(SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

list setListElement(list dest, list src, integer index) {
    if (src == []) {
        return llListReplaceList(src, [""], index, index);
    } else {
        integer len = llGetListLength(dest);
        for (; len < index; ++len) {
            dest += "";
        }
        return llListReplaceList(dest, src, index, index + llGetListLength(src) - 1);
    }
}

list getArray(list array, list index){
    if (index == [])
        return array;
    integer numIndicies = llGetListLength(index);
    list src = array;
    integer i;
    for (i = 0; i < numIndicies - 1; ++i) {
        integer listIndex = llList2Integer(index, i);
        string element = llList2String(src, listIndex);
        if (llGetSubString(element, 0, 0) == "l")
            element = llDeleteSubString(element, 0, 0);
        src = parseStringKeepNulls(element);
    }
    string element = llList2String(src, llList2Integer(index, -1));
    if (llGetSubString(element, 0, 0) == "l") {
        // Caller is retreiving a dimension.
        return parseStringKeepNulls(llDeleteSubString(element, 0, 0));
    } else {
        return [element];
    }
}

// To set an element, we need to extract
// each list starting from where exactly the
// new data will be. In a set of russian dolls, this
// would be equivelant to grabbing the littlest one
// first. then gradually adding back on the layers over it.
// I think its easiest to do this recursively.
list setArray(list array, list index, list data) {
    string element;
    if (llGetListLength(data) > 1) {
        // Data is a new dimension:
        element = "l" + dumpList2String(data);
    } else {
        // Data is a single element
        element = llList2String(data, 0);
    }
    if (llGetListLength(index) > 1) {
        // index is in the form [a,b,c,d]
        // here, we grab the list that d is in.
        list containerIndex = llDeleteSubList(index, -1, -1);
        list dest = getArray(array, containerIndex);
        integer listIndex = llList2Integer(index, -1); // Grab d
        dest = setListElement(dest, [element], listIndex); // replace the element in d's list.
        // Make sure the recursion treats the container like a list, not an element.
        if (llGetListLength(dest) == 1)
            dest += "";
        return setArray(array, containerIndex, dest);
    } else {
        return setListElement(array, [element], llList2Integer(index, 0));
    }
}

Indicies are referred to like this: [1, 2, 3, 4], instead of the standard C++/Java style [1][2][3][4]. It saves the script parsing work :)

Creative Commons License
This work is licensed under a Creative Commons Public Domain License.


LibraryMultiTitler

//say "/1 mynameis text" to set the title and "/1mynameis setcolor colornameorvector" to set the color
// with mynameis being "title1" you would say "/1title1 bananas" to set the title to "bananas"
// for the color you could say "/1title1 setcolor <255,255,0>" or since yellow is in the presets you culd say "/1title1 setcolor yellow"
string mynameis = "title1"; //caps doesn't matter, to use more than one titler at once put this script on another prim and change its name
list colors = [ "red",      255,    0,      0,
                "green",    0,      255,    0,
                "blue" ,    0,      0,      255,
                "orange" ,  255,    128,    0,
                "pink" ,    255,    0 ,     128 ,
                "purple" ,  255 ,   0 ,     255,
                "yellow",   255,    255,    0,
                "cyan",     0,      255,    255 ,
                "white",    255,    255,    255
                ];

//////////////////////////////////////
/////end of customization section/////
//////////////////////////////////////

string text;
vector color = <1,1,1>;
setColor(string command)
{
     color = <1,1,1>;
    list bits = llParseString2List(command, ["setcolor"],[]);
    integer index = llListFindList(colors,llCSV2List(llList2String(bits,1)));
    if ( index > -1)
    {
        list thiscolor = llList2List(colors,index+1, index+4);
        color = <llList2Float(thiscolor,0), llList2Float(thiscolor,1), llList2Float(thiscolor,2)>/255;
    }
    else
    {
        list thiscolor = llParseString2List(command, ["<", ","],[]);
        color = <llList2Float(thiscolor,1), llList2Float(thiscolor,2), llList2Float(thiscolor,3)>/255;
    }

    llSetText(text,color, 1);
}

setText(string command)
{

   list bits = llParseString2List(command, [" "],[]);
   text = llDumpList2String(llList2List(bits,1, llGetListLength(bits)), " ");
   text = llDumpList2String(llParseString2List(text, ["\\n"],[]), "\n");
   llSetText(text,color, 1);
}

clearText()
{
    text = "";
    llSetText(text,color, 1);
}

integer lishan;

default
{
    state_entry()
    {
       lishan = llListen(1,"",llGetOwner(),"");
    }
    changed (integer change)
    {
        if (change & CHANGED_OWNER)
        {
            llListenRemove(lishan);
            lishan = llListen(1,"",llGetOwner(),"");
        }
    }

    listen(integer chan, string name, key id, string msg)
    {
        list messie = llParseString2List(msg, [" "],[]);
        if (llToLower(llList2String(messie,0)) == llToLower(mynameis))
        {
            //list breakdown = llParseString2List(
            if (llToLower(llList2String(messie,1)) == "setcolor")
            {
                setColor(msg);
            }
            else if(llToLower(llList2String(messie,1)) == "off")
            {
                clearText();
            }
            else
            {
                setText(msg);
            }
        }
    }

}
ScriptLibrary


LibraryNoticeGiver

Notice Giver

If you ever want to communicate a message to everyone in your parcel, you can use this script to artificially form a notice for everyone in range.

NOTE- Think of it like a linden sending a notice to everyone, its like that, only its sim-limited
///////////Created by Joker Opus, December///////////
default
{
    touch_start(integer num_detected)
    {
        integer i;
        llSensorRepeat("",NULL_KEY,AGENT,512,2*PI,0.001);//This scans on touch
    }
    sensor(integer count)
    {
        integer count = 50;
        integer i;
        for(i=0;i<count;i++)
        {
            llDialog(llDetectedKey(i), "Hi all, I am testing this!", ["Ok"], 12345); //This is dialog, it looks like a notice, and it says "Hi all, I am testing this!" and it has an [ok] for a button, indicating the notice look. and the OK gets rid of the pop up.
        }
    }
}

Up-to-date version... Seems to me like the above version would send many messages...

///////////Created by Joker Opus, December///////////
default
{
    touch_start(integer num_detected)
    {
        llSensor("", NULL_KEY, AGENT, 96, PI); //This scans on touch
    }
    sensor(integer total_number)
    {
        integer current_agent;
        for (current_agent = 0; current_agent < total_number; current_agent++) llDialog(llDetectedKey(current_agent), "Your message here", ["Ok"], -12345); //This is dialog, it looks like a notice, and it says your message and it has an [Ok] for a button, indicating the notice look. and the Ok gets rid of the pop up.
    }
}

ScriptLibrary


LibraryNumRs

NumRs is an alternative to XyText for numbers, scripted by NornAlbion. Using only one texture, it obviously loads faster, however it is a a maximum of 3 numbers per prim. It could be made to be 6, but there was no need when it was scripted.

It uses the XyText prim setup script.
To get it to display numbers, llMessageLinked it with a string consisting of three or more numbers. Any characters after the first three are truncated. The number and key of the linked message are not important.

NumRs script:
list horizontals = [0.410,-0.490,-0.390,-0.290,-0.190,-0.090,0.010,0.110,0.210,0.310];
vector repeats = <0.100,0.5,0>;
vector normoffset = <0.310,0.250,0>;
key texture = "42e6610b-3ad8-c81a-9701-ba7186a2ea7e";
// 4 = left
// 0 = middle
// 2 = right
default
{
    state_entry()
    {
        llSetPrimitiveParams([
        PRIM_TEXTURE,4,texture,repeats,normoffset,PI_BY_TWO,
        PRIM_TEXTURE,0,texture,repeats,normoffset,0,
        PRIM_TEXTURE,2,texture,repeats,normoffset,-PI_BY_TWO
        ]);
    }
    link_message(integer sender, integer num, string mesg, key id) {
            integer num1; integer num2; integer num3;
            integer thenum = (integer)mesg;
            if(thenum < 10) {
                num2 = 0;
                num3 = (integer)llGetSubString(mesg,0,0);
                num1 = 0;
            } else if(thenum < 100) {
                num2 = (integer)llGetSubString(mesg,0,0);
                num3 = (integer)llGetSubString(mesg,1,1);
                num1 = 0;
            } else  {
                num2 = (integer)llGetSubString(mesg,1,1);
                num3 = (integer)llGetSubString(mesg,2,2);
                num1 = (integer)llGetSubString(mesg,0,0);
            }
            llSetPrimitiveParams([
            PRIM_TEXTURE,4,texture,repeats,<llList2Float(horizontals,num1),normoffset.y,0>,PI_BY_TWO,
            PRIM_TEXTURE,0,texture,repeats,<llList2Float(horizontals,num2),normoffset.y,0>,0,
            PRIM_TEXTURE,2,texture,repeats,<llList2Float(horizontals,num3),normoffset.y,0>,-PI_BY_TWO
            ]);
    }
}


LibraryParametricSurfaceRezzer

The following is a set of scripts for rezzing surfaces from arbitrary parametric equations.

Pictures: 1, 2, 3.

//Parametric Surface Rezzer
//by Seifert Surface
//Nov 2006

//////PARAMETERS//////

//Example surface, WARNING: rezzes 200 prims

float scale = 1.0;     //scales the surface in all directions. If this is 2.0 the surface will be twice as large as if it is 1.0
float depth = 0.01;  //thickness of triangles rezzed

float min_u = -4.0;
float max_u = 4.0;
integer steps_u = 10;

float min_v = -4.0;
float max_v = 4.0;
integer steps_v = 10;

vector f(float u, float v)   //the parametric function
{
    return <u, v, 2.0 * llSin(u) * llSin(v)>;
}

//////END OF PARAMETERS//////

//Description:

//This collection of scripts rezzes a surface defined by a parametric function. The surface is made out of
//triangles, one prim per triangle. There will be at most 2*steps_u*steps_v triangles in the final surface
//(it is possible to have fewer triangles as the script does not rez degenerate triangles). To change the
//surface rezzed, edit the PARAMETERS section above.

//Do whatever you want with this script. I'll be annoyed if someone tries to sell it to newbies, but I think the
//chances of that are slim!

//Instructions:

//There are three scripts. The scripts "surface_rezzer" (this script), and "triangle_detailer" go in the
//prim that will do the building. Another script, "triangle_rezzee" goes inside another prim, which must be
//called "triangle".  The prim "triangle" goes in the inventory of the builder prim.

//To build the surface, touch the builder prim. The surface will be built centered on the location of the
//builder prim when touched. Be careful when rezzing surfaces near the ground, as the builder can get stuck
//trying to move to a position to rez something when that position is underground.

//Once the surface is rezzed, the following commands can be shouted on channel 347:

//"die" - Deletes all rezzed triangles (in range of your shout).
//"clear" - removes scripts from all rezzed triangles (in range of your shout).

//If you prefer, you can have the triangles remove their scripts as soon as they are in position, there is
//a commented-out line in the triangle rezzee script which can be uncommented to do this.

//Have fun!

// Seifert Surface
// 2G!tGLf 2nLt9cG

integer comm_channel = -61347732;
integer rez_counter;
vector origin;
float delta = 0.01;

rez_triangle(vector a, vector b, vector c)
{
    a = origin + scale * a;
    b = origin + scale * b;
    c = origin + scale * c;
    //check for 2 vertices in the same place
    if(llVecDist(a,b) < delta || llVecDist(b,c) < delta || llVecDist(c,a) < delta)
    {  //if so, don't try to rez the triangle
        return;
    }
    float cosA = (b - a) * (c - a);
    float cosB = (c - b) * (a - b);
    float cosC = (a - c) * (b - c);  //signs -ve means obtuse angle
    if(cosA < 0.0)  //so the angle at a is obtuse, meaning the opposite edge must be the base of the prim
    {
        triangle(a, b, c, TRUE);
    }
    else if(cosB < 0.0)
    {
        triangle(b, c, a, TRUE);
    }
    else if(cosC < 0.0)
    {
        triangle(c, a, b, TRUE);
    }
    else //all acute angles... so we can choose which way to base the prim, so as to minimise the
    {        //error introduced by the shear value having a resolution of only 0.01
        float error1 = triangle(a,b,c,FALSE);
        float error2 = triangle(b,c,a,FALSE);
        float error3 = triangle(c,a,b,FALSE);
        if(error1 < error2)
        {
            if(error1 < error3)
            {
                triangle(a,b,c,TRUE);
            }
            else
            {
                triangle(c,a,b,TRUE);
            }
        }
        else
        {
            if(error2 < error3)
            {
                triangle(b,c,a,TRUE);
            }
            else
            {
                triangle(c,a,b,TRUE);
            }
        }
    }
}

float triangle(vector a, vector b, vector c, integer rez)
{
    float width = llVecDist(b, c);
    vector left = llVecNorm(b - c);
    vector fwd = llVecNorm(left % (a - c));
    vector up = fwd % left;
    float height = (a - c) * up;
    float y_shear = 0.5 - ((b-a) * left) / width;

    if(rez)
    {
        vector  center = 0.5 * ( (b+c) + (height * up)  );
        vector scale = <depth, width, height>;
        rotation rot = llAxes2Rot(fwd, left, up);
        llMessageLinked(LINK_THIS, comm_channel + rez_counter, (string)scale + "|" + (string)y_shear, NULL_KEY);

        while(llVecDist(llGetPos(), center) > 9.5)
        {
            llSetPos(center);
        }
        llRezObject("triangle", center, ZERO_VECTOR, rot, comm_channel + rez_counter);
        rez_counter += 1;
        return -1.0;
    }
    else
    {
        y_shear = (float)llRound(y_shear * 100.0) / 100.0;
        return llVecDist(a, 0.5 * (b + c) + height * up + y_shear * width * left);
        //error between where the vertex of the triangle should be and would be...
    }
}

rez_surface()
{
    integer i;
    integer j;
    float scale_u = (max_u - min_u) / (float)steps_u;
    float scale_v =  (max_v - min_v) / (float)steps_v;
    for(i=0; i<steps_u; i+=1)
    {
        for(j=0; j<steps_u; j+=1)
        {
            vector A = f(min_u + scale_u * i, min_v + scale_v * j);
            vector B =  f(min_u + scale_u * (i + 1), min_v + scale_v * j);
            vector C =  f(min_u + scale_u * i , min_v + scale_v * (j + 1));
            vector D =  f(min_u + scale_u * (i + 1) , min_v + scale_v * (j + 1));
            rez_triangle(A, B, C);
            rez_triangle(B, D, C);
            //llSleep(0.2); //in some very laggy situations this may be necessary to add, shouldn't be necessary
            //only use if the triangle_detailer gets backed up on its list.
        }
    }
}

default
{
    touch_start(integer num)
    {
        if(llDetectedKey(0) == llGetOwner())
        {
            origin = llGetPos();
            rez_surface();
            while(llVecDist(llGetPos(), origin) > 9.5) //go back to the start
            {
                llSetPos(origin);
            }
            llSetPos(origin);
            llOwnerSay("Done!");
        }
    }
}

//Triangle Detailer
//by Seifert Surface
//Nov 2006

integer comm_channel2 = 61347732;
list rezzees;
list tri_data;

default
{
    state_entry()
    {
        llListen(comm_channel2, "", NULL_KEY, "");
    }
    link_message(integer sender, integer num, string str, key id)
    {
        rezzees += [num];
        tri_data += [str];
        if(llGetListLength(rezzees) > 10)
        {
            llSay(DEBUG_CHANNEL, "Detailer list getting long...");
        }
    }
    listen(integer chan, string name, key id, string msg)
    {
        //llOwnerSay((string)llGetFreeMemory() + " " + (string)llGetListLength(rezzees));  //in case you want to see if the detailer is getting overloaded
        integer who = llListFindList(rezzees, [(integer)msg]);
        if(who != -1)
        {
            llShout((integer)msg, llList2String(tri_data, who));
            rezzees = llDeleteSubList(rezzees, who, who);
            tri_data = llDeleteSubList(tri_data, who, who);
        }
    }

}

//Triangle Rezzee
//by Seifert Surface
//Nov 2006

integer comm_channel = -61347732;
integer comm_channel2 = 61347732;
integer me;
integer listen_control;
integer times_to_ask = 10;

default
{
    state_entry()
    {
        llListen(347, "", "", "");
    }
    on_rez(integer param)
    {
        if(param != 0)
        {
            me = param;
            llSetObjectName((string)(me - comm_channel));
            listen_control = llListen(me, "", "", "");
            llShout(comm_channel2, (string)me);
            llSetTimerEvent(5.0);
        }
    }
    listen(integer chan, string name, key id, string msg)
    {
        if(msg == "die")
        {
            llDie();
        }
        else if(msg == "clear")
        {
            llRemoveInventory(llGetScriptName());
        }
        else if(chan == me)
        {
            list data = llParseStringKeepNulls(msg, ["|"], []);
            vector scale = (vector)llList2String(data, 0);
            if(scale.x < 0.01)
            {
                scale.x = 0.01;
            }
            if(scale.y < 0.01)
            {
                scale.y = 0.01;
            }
            if(scale.z < 0.01)
            {
                scale.z = 0.01;
            }
            float y_shear = (float)llList2String(data, 1);
            llSetPrimitiveParams([PRIM_TYPE, PRIM_TYPE_BOX, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 0.0, 0.0>, <0.0, y_shear, 0.0>, PRIM_SIZE, scale]);
            //llRemoveInventory(llGetScriptName());   //if you want to remove scripts as soon as possible, uncomment this.
            llListenRemove(listen_control);
            llSetTimerEvent(0.0);
        }
    }
    timer()
    {
        llShout(comm_channel2, (string)me);
        times_to_ask -= 1;
        if(times_to_ask <= 0)
        {
            llSetTimerEvent(0.0);
            llSay(DEBUG_CHANNEL, "no reply after 10 tries");
        }
    }
}
Here are some example parametric equations to plug in. The number of prims used can be reduced by lowering the steps_u and steps_v variables, though the surfaces will not look as good. It is also a good idea to lower the scale number as well, to avoid prims trying (and failing) to scale to above 10m along any dimension. The parametric equations are from: http://vmm.math.uci.edu/3D-XplorMath/Surface/gallery.html
//Sphere, with a slight twist
//224 prims
float depth = 0.01;
float scale = 3.0;

float min_u = 0.0;
float max_u = TWO_PI;
integer steps_u = 16;

float min_v = 0.0;
float max_v = PI;
integer steps_v = 8;

vector f(float u, float v)
{
    return <llSin(u + 0.5 * v) * llSin(v), llCos(u + 0.5 * v) * llSin(v), llCos(v)>;
}
//Klein Bottle
//800 prims, large (rez in a sandbox)
float depth = 0.01;
float scale = 5.0;

float min_u = 0.0;
float max_u = TWO_PI;
integer steps_u = 20;

float min_v = 0.0;
float max_v = TWO_PI;
integer steps_v = 20;

float a = 2.0;
vector f(float u, float v)
{
    float temp = (a + llCos(u * 0.5) * llSin(v) - llSin(u*0.5)*llSin(2*v));
    return < temp * llCos(u), temp * llSin(u), llSin(0.5*u)*llSin(v) + llCos(0.5*u) * llSin(2*v)>;
}
//Dini's Surface
//800 prims, large (rez in a sandbox)
float depth = 0.01;
float scale = 10.0;

float min_u = -5.0;
float max_u = 5.0;
integer steps_u = 20;

float min_v = -3.14159;
float max_v = PI;
integer steps_v = 20;

vector f(float u, float v)
{
    float psi = 1.2; //this can be from 0 to PI
    float sinpsi = llSin(psi);
    float cospsi = llCos(psi);
    float g = (u - cospsi * v) / sinpsi;
    float s = llPow(2.7182818, g);
    float r = (2 * sinpsi) / (s + 1 / s);
    float t = r * (s - 1 / s) * 0.5;

    return <u - t, r * llCos(v), r * llSin(v)>;
}
//Boy's surface
//1250 prims, large
float depth = 0.01;
float scale = 25.0;

float min_u = 0.0;
float max_u = PI;
integer steps_u = 25;

float min_v = 0.0;
float max_v = PI;
integer steps_v = 25;

float E = 2.718281828;

float Exp(float x)
{
    return llPow(E, x);
}
float Cosh(float x)
{
    return 0.5 * (Exp(x) + Exp(-x));
}
float Sinh(float x)
{
    return 0.5 * (Exp(x) - Exp(-x));
}

vector f(float u, float v)
{
    float x = llCos(u) * llSin(v);
    float y = llSin(u) * llSin(v);
    float z = llCos(v);
    float ef = 0.5 * ( (2 * x*x - y*y - z*z) + 2*y*z*(y*y - z*z) + z*x*(x*x - z*z) + x*y*(y*y - x*x) );
    float g = llSqrt(3.0) * 0.5 * ( (y*y - z*z) + z*x*(z*z - x*x) + x*y*(y*y - x*x) );
    float h = (x+y+z) * ( llPow(x+y+z, 3.0) + 4 * (y - x)*(z - y)*(x - z)  );
    return <ef,g,h * 0.125>;
}


LibraryParticleGarden

What does it do?
- It shows a particle picture of what ever texture that is in the object randomly.
- The flowers grow slowly
- It is visible from all angles like all particles

How do you use it?
- Rez an Object
- Change the ROTATION X=90, Y=0, Z=0 (Under the OBJECT tab for the object)
- You put this script into an object (Click on New Script in the object's contents and copy the code below to this object and save)
- Put any other flowers, gnomes, tree textures you want inside it.
- Click to start / stop

float MAX_RADIUS = 2.5;
float RADIUS_INTERVAL = 0.5;

// Modified values
integer IS_ON = FALSE;
float RADIUS = 2;
string TEXTURE = "f42bcc2b-20c8-9df7-cf1c-69566e377fd8";

garden() {
    llParticleSystem([
        PSYS_PART_FLAGS, 0 | PSYS_PART_EMISSIVE_MASK |
        PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK,
        PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_ANGLE_CONE,

        // Texture / Size / Alpha / Color
        PSYS_SRC_TEXTURE, TEXTURE,
        PSYS_PART_START_SCALE,<0.2000, 0.2000, 0.0000>,
        PSYS_PART_END_SCALE,<0.5000, 0.5000, 0.0000>,
        PSYS_PART_START_ALPHA,0.000000,
        PSYS_PART_END_ALPHA,1.000000,
        PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
        PSYS_PART_END_COLOR, <1.0,1.0,1.0>,

        // Flow
        PSYS_PART_MAX_AGE,1000.0000,
        PSYS_SRC_BURST_RATE,1.000000,
        PSYS_SRC_BURST_PART_COUNT,1,
        PSYS_SRC_MAX_AGE,0.000000,

        // Rez position
        PSYS_SRC_BURST_RADIUS,RADIUS,
        PSYS_SRC_INNERANGLE,1.550000,
        PSYS_SRC_OUTERANGLE,1.550000,
        PSYS_SRC_OMEGA,<0.00000, 0.00000, 4>,
        PSYS_SRC_BURST_SPEED_MIN,0.000000,
        PSYS_SRC_BURST_SPEED_MAX,0.000000
    ]);
}

stop() {
    llParticleSystem([]);
}

default {
    state_entry() {
        if(IS_ON) {
            llSetTimerEvent(RADIUS_INTERVAL);
            garden();
        } else {
            stop();
        }
    }

    touch_start(integer num_detected) {
        if(IS_ON) {
            llSetTimerEvent(0.0);
            stop();
            llWhisper(0, "Garden has stopped");
        } else {
            llSetTimerEvent(RADIUS_INTERVAL);
            garden();
            llWhisper(0, "Garden has started");
        }
        IS_ON = !IS_ON;
    }

    timer() {
        integer max_inventory = llGetInventoryNumber(INVENTORY_TEXTURE);
        if(max_inventory > 0) {
            TEXTURE = llGetInventoryName(INVENTORY_TEXTURE, (integer)llFrand(max_inventory));
        }
        RADIUS = llFrand(MAX_RADIUS);
        garden();
    }
}

Oh I do give a 3 part class on this. And the classes are FREE! Come on down to http://slurl.com/secondlife/Eson/29/155/351/. I'll try to repeat it once a week, but then again if you are reading Wiki sites I should learn from you! My plan is to create a new class every week.

Hope I didn't talk to much. And remember me when you make your millions.... lol


LibraryParticleSignboard

Howdy everyone,

What is it?
It is a signboard made out of particles, I've done 120 characters from a single prim before!

How do I use it?
REZ a BOX, create and script and put the following code in:
You can use channel 99 to change the text from the sign. Note that particles are slightly unreliable and it depends on the SIM, how many people are there, the weather in your country, etc.... But in a low load SIM it almost always appears for me. Do let me know if you find any improvements!

// Default text
string TEXT = "I @ ASL!";

// Interface between letters
float LETTER_INTERVAL = 1.2;

// Modified values
integer IS_ON = TRUE;
string CUR_TEXT = "";
integer LISTEN_HANDLE;

// Characters
list CHARACTERS = [
    // A - Z
    "05fb8c9a-2ace-8c64-6688-9a08f535e87b",
    "c68b3416-1b02-76f4-8295-5170f2a430fc",
    "ab55d18b-f220-7dad-2036-9dbe6d9d24ae",
    "bb14e17f-48e2-b0de-2d3a-f3fc919d3455",
    "81d319dc-08a1-70ff-7d08-d2d5e8521d90",
    "993b9e32-77cd-17c6-b9d5-aa0950eeeedc",
    "4bbbec6c-26c7-b28c-8e2b-b95fd6b4c73e",
    "5c52c7da-7443-8d03-1493-94a167981f74",
    "7bfad861-8374-84fb-f71f-2a58fb6dd64a",
    "e79f9242-17d9-1377-f8b1-59812df58def",
    "6121cbf7-45dd-81d1-5983-bb4da8bb6cbd",
    "b9c2b4f6-2dd2-f05e-2ebe-8291a1c8208c",
    "e4c16f2e-5589-4dab-d658-2d8f8e78e212",
    "3dd9c930-88cb-80cd-04df-a146429166ba",
    "aa61ecc4-59c6-d390-b0e2-770abd463037",
    "702072b1-a14a-c1c2-7e96-ec69044989ef",
    "ed6b8c52-2540-df1e-2fb1-5c2d74d52951",
    "50ceb1e2-bf65-43d1-14b9-f7c6baab856f",
    "d1684b98-25c3-6d6d-9517-cc5b15264a5d",
    "d2daf814-6aa7-7a59-54b2-3110bf0d9dee",
    "367def79-30ef-5de7-cade-55fb1973dec4",
    "02e99a90-31c8-b50b-8b16-2c182d67a2d4",
    "722b77a3-8513-9537-c0cb-00978c85745e",
    "ea2aa90d-4a98-8d3b-3815-4505113a5a90",
    "ebe59dea-4a23-d415-1503-9c789c68d0f5",
    "9fbdeb14-7814-2c21-ab27-ee1867e349a3",

    // Special characters
    "701917a8-d614-471f-13dd-5f4644e36e3c",  // Space
    "d3f39664-ec05-92f0-1b45-db3acfe276e3",  // Period
    "3ff82ed4-c521-dc69-b1ad-5d70f4b89da1",  // Question
    "eaa3ec1a-d071-285e-7058-14ce838e8e25",  // Semicolon
    "c0eeefcc-fa2c-0ff9-c9b9-babc365643ea",  // Exclamation
    "8ccd81a4-ac36-7618-d7b4-b40fc2a548f0",  // Comma
    "f81176b3-32b8-86bf-3613-a9f89895c1ad",  // Colon

    // Numbers
    "1494e996-a91b-f770-bd10-ad788642d859",
    "f545e486-2a2e-730d-845a-cbe9d4bcb9fd",
    "16a84092-421f-3225-bb36-4071b55fab2d",
    "4b0a62c4-65f4-932e-d440-7fe3cf5a1540",
    "e0e7eacd-e956-14e6-df65-3bc3b7d4e679",
    "6f579c89-bf1f-71d7-854d-b08341edf51e",
    "a5063c9b-377c-1244-62b4-7ce5d1dbfafe",
    "b055ba6a-d2ff-d67d-066c-ccb8a9e8300d",
    "42b58e86-e83c-bc1b-4d73-ab3603d00d98",
    "872560f2-d964-ab22-d558-1607366666c1",

    // Zara's
    "fcdac14d-6128-ce47-66bc-a3d0d27f6d3d", // Hearts

    // ADD yours here.
    // Note that if you add more you need to put a comma in
    // but leave the last line with out a comma
    ""
];

string CHARACTER_INDEX = "ABCDEFGHIJKLMNOPQRSTUVWXYZ .?;!,:0123456789@";

letter(string char) {
    string TEXTURE = llList2Key(CHARACTERS, llSubStringIndex(CHARACTER_INDEX, llToUpper(char)));

    llParticleSystem([
        PSYS_PART_FLAGS, 0 | PSYS_PART_EMISSIVE_MASK |
        PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK,
        PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_ANGLE_CONE,

        // Texture / Size / Alpha / Color
        PSYS_SRC_TEXTURE, TEXTURE,
        PSYS_PART_START_SCALE,<0.2000, 0.2000, 0.0000>,
        PSYS_PART_END_SCALE,<0.3000, 0.3000, 0.0000>,
        PSYS_PART_START_ALPHA,0.000000,
        PSYS_PART_END_ALPHA,1.000000,
        PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
        PSYS_PART_END_COLOR, <1.0,1.0,1.0>,

        // Flow
        PSYS_PART_MAX_AGE,10.0,
        PSYS_SRC_BURST_RATE,1.200000,
        PSYS_SRC_BURST_PART_COUNT,1,
        PSYS_SRC_MAX_AGE,0.000000,

        // Rez position
        PSYS_SRC_BURST_RADIUS,0.0,
        PSYS_SRC_INNERANGLE,0.0000,
        PSYS_SRC_OUTERANGLE,0.0000,
        PSYS_SRC_OMEGA,<0.00000, 0.00000, 0.00000>,
        PSYS_SRC_ACCEL, <0,0,0.0>,
        PSYS_SRC_BURST_SPEED_MIN,0.250000,
        PSYS_SRC_BURST_SPEED_MAX,0.250000
    ]);
}

stop() {
    llParticleSystem([]);
}

default {
    state_entry() {
        if(IS_ON) {
            llSetTimerEvent(LETTER_INTERVAL);
            LISTEN_HANDLE = llListen(99, "", llGetOwner(), "");
        }
    }

    touch_start(integer num_detected) {
        if(IS_ON) {
            stop();
            llSetTimerEvent(0.0);
            //llListenControl(LISTEN_HANDLE, FALSE);
            llWhisper(0, "Signboard has stopped");

        } else {
            llWhisper(0, "Signboard has started");
            //LISTEN_HANDLE = llListen(99, "", llGetOwner(), "");
            llSetTimerEvent(LETTER_INTERVAL);
        }
        IS_ON = !IS_ON;
    }

    listen( integer channel, string name, key id, string message) {
        TEXT = message;
        CUR_TEXT = message;
        llOwnerSay("Setting text:" + message);
    }

    timer() {
        if(CUR_TEXT == "") {
            CUR_TEXT = TEXT;
        }
        string char = llGetSubString(CUR_TEXT, 0, 0);
        letter(char);
        if(llStringLength(CUR_TEXT) == 1) {
            if(CUR_TEXT != " ") {
                CUR_TEXT = " ";
            } else {
                CUR_TEXT = "";
            }
        } else {
            CUR_TEXT = llGetSubString(CUR_TEXT, 1, -1);
        }
    }
}

Oh I do give a class on this. And the classes are FREE! Come on down to ASL. I'll try to repeat it once a week. Although my plan is to create a new class every week.

Hope I didn't talk to much. And remember me when you make your millions.... lol


LibraryPasswall

This script will allow you to set up a solid wall that lets certain people pass through it (just replace "my name" etc with the names of those you wish to have access)

// access_list is the list of every person you wish to be able to trigger the wall phasing out
list access_list = ["my name", "my friend's name", "my other friend's name"];

// phase_length is the length of time in seconds to hold phantom state
float phase_length = 1.0;

default
{
    state_entry()
    {
        // Set status to not phantom on start just in case it got stuck as phantom
        llSetStatus(STATUS_PHANTOM, FALSE);
    }

    collision(integer num_detected)
    {
        // check to see if the person who bumped into the wall is on the access list
        if(llListFindList(access_list, (list)llKey2Name(llDetectedKey(0))) != -1)
        {
            // if the above is true, then phase out the wall to allow passage by applying a phantom state to the prim
            llSetStatus(STATUS_PHANTOM, TRUE);
            // set the timer to activate phase_length (set at the top of the script) seconds later
            llSetTimerEvent(phase_length);
        }
    }

    timer()
    {
        // when the timer goes off, the wall is made solid again by removing the phantom state.
        llSetStatus(STATUS_PHANTOM, FALSE);
    }
}


LibraryPolygonFormer

Polygon Former script

This little function will form a polygon/triangle between the three input vectors.

form_triangle(vector cor1,vector cor2,vector cor3)
{
    ///////////////////////////////////
    //Open source polygon script     /
    //Made by Jontte Gremlin        /
    //DO NOT REMOVE THIS TAG       /
    ///////////////////////////////
    //
    //This script will rotate and scale the prim so that it forms a triangle between the 3 corners
    ///////
    if(llVecDist(cor2,cor3)>llVecDist(cor1,cor2)&&llVecDist(cor2,cor3)>llVecDist(cor1,cor3))
    {
        vector tempvec = cor3;
        cor3=cor1;
        cor1=tempvec;
    }
    if(llVecDist(cor1,cor3)>llVecDist(cor1,cor2)&&llVecDist(cor1,cor3)>llVecDist(cor2,cor3))
    {
        vector tempvec = cor3;
        cor3=cor2;
        cor2=tempvec;
    }
    //These parts are needed because the shear cant be less than -0.5 or more than 0.5, so it needs to rotate the corners before doing anything.

    float height=llSin(llRot2Angle(llRotBetween(cor3-cor1,cor2-cor1)));//calculate the height of the polygon
    height*=llVecDist(cor1,cor3);
    float shear=llCos(llRot2Angle(llRotBetween(cor3-cor1,cor2-cor1)));//shear calculations
    shear*=llVecDist(cor1,cor3);
    shear=shear/llVecDist(cor1,cor2);
    shear-=0.5;
    vector percent=(cor1+(cor2-cor1)*(shear+0.5));//or3 on a line segment from cor1 to cor2
    vector normal = llVecNorm(llVecNorm(cor2-cor1) % llVecNorm(cor3-cor1));//triangles normal (vector pointing outside of it)
    rotation rot=llAxes2Rot(normal,llVecNorm(cor2-cor1),llVecNorm(cor3-percent));//rotation calculations
    vector size=<0.01,llVecDist(cor1,cor2),height>;//size calculations, 0.01 is how thick the triangle will be
    vector pos=(cor1+cor2)/2+<0,0,height/2>*rot;//position

    if(size.x<0.01)size.x=0.01;//PRIM_SIZE will be ignored if one of these is less than 0.01
    if(size.y<0.01)size.y=0.01;
    if(size.z<0.01)size.z=0.01;

    while(llVecDist(llGetPos(),pos)>5)llSetPos(pos);//move closer position ;). llSetPrimitiveParams will do the rest //you could change this to warpPos
    llSetPrimitiveParams([PRIM_TYPE,PRIM_TYPE_BOX,0,<0,1,0>,0.0,<0,0,0>,<1,0,0>,<0,shear,0>,PRIM_ROTATION,rot,PRIM_POSITION,pos,PRIM_SIZE,size]);
    //and finally use the variables calculated earlier
}
default
{
    state_entry()
    {
        vector position = llGetPos();

        form_triangle(position + <1,0,0>, position + <-1,0,0>, position + <0,0,1>);
    }
}

I've made a dome rezzer and a wavefront obj 3D model loader using it, here are some snapshots:
Snapshot1
Snapshot2
Snapshot3
Snapshot4 -From Milkshape 3D


LibraryPoseBall

As you can see, I just did some minor edits. I added an llListen on channel 1,
but this is because I wanted certain behaviour from it. What I wanted was to have it
listen for the hide/show commands regardless of the presence of the notecard. It now does so.

I also changed what the configuration notecard's name is, I found "Pose ball - CONFIGURATION"
way too long and prone to typos; it also seemed to be too much of windowsy (tm) name to be
using spaces in it.

Here it is... please go ahead and improve on it, I'm no 'master coder', in any language, so be my guest. Oh, also corrected the typos in some of the comments, may have missed some.

// Pose Ball script, Revision 4.3.3

// decrappified by Strife Onizuka - December 11th (2006)

// revised by SimonRaven Chippewa
// fixed parsing of llListen handler

// Notecard configuration enabled, switchable
// link_message/touch_start/llListen support, sit_text, floating text, alpha.
// Version 4.3 fixes link_set hide/show and permission sensing issues.

// By CrystalShard Foo.
// Work started - October 10th (2004).
// Last compile - November 7th (2004).

// Version 4.3.1 mods by Strife Onizuka
// Modified to fix logic holes in permissions code when used on multi-sittarget objects.
// Work started - Febuary 14th 2005.
// Last compile - Febuary 14th 2005.

// This script will let you choose if you want to turn the ball visible on
// CLICK, or by using a SHOW/HIDE voice command.
// You can also set the offset and the title of the ball, as well
// as the sit button text - all with a notecard.

// This script is free and comes without support. Dont contact me.
// Ask a local geek for help if it gets messy.

// ** This script is NOT FOR SALE **
// You can use it in commercial products as long as you give this script to anyone who asks for it.
// You can use this source, distribute it and modify it freely, but leave the credits intact!
// (You can add your own name to the list, of course. Like, "Modified by John Doe")

// Last Modified: 2006/12/09
// Modification Author: SimonRaven Chippewa

vector ROTATION = <0,0,0>; //Euler in degrees (like the edit box)

string TITLE = "Chill";            //This text will appear in the floating title above the ball
string ANIMATION = "";             //Put the name of the pose/animation here
vector offset = <0,0,0.5>;    // You can play with these numbers to adjust
                            // how far the person sits from the ball. ( <X,Y,Z> )
integer use_voice = TRUE;

string  gNotecard = "poseBall.conf";
integer gLine = 0;

integer listenHandle;
integer masterswitch = TRUE;
integer visible = TRUE;
float base_alpha = 1.0;
key avatar;
key trigger;

key dataserver_key = "";
rotation rot;
integer lowest = 0x7FFFFFFF;
integer me;
string animation;

show()
{
    visible = TRUE;
    llSetText(TITLE, <1.0,1.0,1.0>,1.0);
    llSetAlpha(base_alpha, ALL_SIDES);
}

hide()
{
    visible = FALSE;
    llSetText("", <1.0,1.0,1.0>,1.0);
    llSetAlpha(0.0, ALL_SIDES);
}

init()
{
    if(llGetInventoryType(ANIMATION) != INVENTORY_ANIMATION)
    {
        if(llGetInventoryNumber(INVENTORY_ANIMATION) == 0)  // Make sure we actually got something to pose with.
        {
            llWhisper(0,"Error: No animation found. Cannot pose.");
            animation = "sit";
        }
        else
            animation = llGetInventoryName(INVENTORY_ANIMATION, 0);
    }
    else
        animation = ANIMATION;

    if(llGetInventoryType(gNotecard) == INVENTORY_NOTECARD)
    {
        dataserver_key = llGetNotecardLine(gNotecard, gLine = 0);
    }
    else //If we are here no configuration notecard was found... lets use the defaults.
    {
        llSetSitText(TITLE);
        if(visible)
            show();
        else
            hide();
    }
}

default
{
    state_entry()
    {
        llSetText("Starting up", <1.0,1.0,1.0>, 1.0);
        rot = llEuler2Rot(ROTATION * DEG_TO_RAD);
        llSitTarget(offset, rot);
        if(use_voice)
            listenHandle = llListen(1, "", "", "");
        me = llGetLinkNumber();
        init();
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        if(num == 99)
        {
            if(str == "hide")
            {
                masterswitch = FALSE;
                hide();
            }
            else if(str == "show")
            {
                masterswitch = TRUE;
                if(llKey2Name(trigger) == "")
                    show();
            }
            if(use_voice)
            {
                if(lowest > sender_num)
                {//only adjust if it is lower then lowest.
                    lowest = sender_num;
                    if(sender_num < llGetLinkNumber())
                    {
                        llListenRemove(listenHandle);
                        listenHandle = 0;
                    }
                    else if(!listenHandle)
                    {
                        listenHandle = llListen(1, "", "", "");
                        llMessageLinked(LINK_ALL_OTHERS, num, "", id);
                    }
                }
            }
        }
    }

    touch_start(integer detected)
    {
        if(use_voice)
            llWhisper(0, "/me "+llDetectedName(0)+", say '/1 hide' to hide me, or '/1 show' to make me show. Or just right-click and sit on me to use me.");
        else
        {
            if(visible)
                hide();
            else if(llKey2Name(trigger) == "")
                show();
            llMessageLinked(LINK_ALL_OTHERS, 99, llList2String(["hide","show"], visible), "");
        }
    }

    changed(integer change)
    {
        if(change & CHANGED_LINK)
        {
            avatar = llAvatarOnSitTarget();
            if(llKey2Name(avatar))
            {
                if(trigger != avatar)
                    llRequestPermissions(avatar, PERMISSION_TRIGGER_ANIMATION);
                hide();
            }
            else if(trigger)
            {
                if(trigger == llGetPermissionsKey())//right user?
                    if(llGetPermissions() & PERMISSION_TRIGGER_ANIMATION)//got permissions?
                        if(llKey2Name(trigger)) //user in the sim? modern permision system makes this last check unnecessary.
                            llStopAnimation(animation);
                if(masterswitch)
                    show();
                trigger = "";
            }
            else if(use_voice)
            {
                change = llGetLinkNumber();
                if(change != me)
                {
                    if(me < change)
                    {
                        lowest += change - me;
                        if(listenHandle)
                            llMessageLinked(LINK_ALL_OTHERS, 99, "", "");
                    }
                    else
                    {
                        if(!listenHandle)
                            listenHandle = llListen(1, "", "", "");
                        if(change)//intended to reduce packet storm on large linksets.
                            llMessageLinked(LINK_ALL_CHILDREN, 99, "", "");
                    }
                    me = change;
                }
            }
        }
        if(change & CHANGED_INVENTORY)
        {
            llSetText("Reloading configuration...",<1.0,1.0,1.0>,1.0);
            init();
        }
    }

    run_time_permissions(integer perm)
    {
        avatar = llAvatarOnSitTarget();
        if(perm & PERMISSION_TRIGGER_ANIMATION && llKey2Name(avatar) != "" && avatar == llGetPermissionsKey())
        {
            trigger = avatar;
            llStopAnimation("sit");
            llStartAnimation(animation);
            if(visible == TRUE)
                base_alpha = llGetAlpha(ALL_SIDES);
            hide();
        }
    }

    listen(integer channel, string name, key id, string message)
    {
        if(llStringLength(message) == 4)
        {
            message = llToLower(message);
            if(message == "show")
            {
                masterswitch = TRUE;
                if(llKey2Name(trigger) == "")
                    show();
            }
            else if(message == "hide")
            {
                masterswitch = FALSE;
                hide();
            }
            else
                return;
            llMessageLinked(LINK_ALL_OTHERS, 99, message, "");
        }
    }

    dataserver(key queryid, string data)
    {
        if(queryid == dataserver_key)
        {
            if(data != EOF)
            {
                integer command = llListFindList([";","title", "offset","rotation","voice","sit_button"], [llToLower(llList2String(llParseString2List(data, [" "], [":",";"]),0))]);
                if(command > 0)
                {
                    data = llDeleteSubString(data, 0, llSubStringIndex(data,":"));
                    if(command == 1)//title
                    {
                        if(llGetSubString(data, 0, 0) != " ")
                            TITLE = data;
                        else
                            TITLE = llDeleteSubString(data, 0, 0);
                        llSetSitText(TITLE);
                        if(visible && masterswitch)
                            show();
                    }
                    else if(command == 2)//offset
                    {
                        offset = (vector)data;
                        if(offset); else
                        {
                            if((llSubStringIndex(data, "<") & 0x7FFFFFFF) < llSubStringIndex(data, ">"))//could be a valid vector
                                offset = <0.0, 0.0, 0.01>;
                            else
                            {//definately isn't a valid vector
                                llSay(0,"Error: The numbers in the offset value lack the '<' and '>' signs. (Should be something like <3,1,6> )");
                                offset = <0.0, 0.0, 0.5>;
                            }
                        }
                        rot = llEuler2Rot(ROTATION * DEG_TO_RAD);
                        llSitTarget(offset, rot);
                    }
                    else if(command == 3)//rotation
                    {
                        ROTATION = (vector)data;
                        rot = (rotation)data;
                        if(<rot.x,rot.y,rot.z> != ROTATION)//allows us to use raw rotations & vectors at the same time.
                            rot = llEuler2Rot(ROTATION * DEG_TO_RAD);
                        llSitTarget(offset, rot);
                    }
                    else if(command == 4)//voice
                    {
                        if(llGetSubString(data, 0, 0) == " ")
                            data = llDeleteSubString(data, 0, 0);
                        use_voice = (llListFindList(["yes", "true", "on", "lag"], [llToLower(data)]) >= 0);
                        if(use_voice)
                        {
                            if(!listenHandle)
                                listenHandle = llListen(1, "", "", "");
                        }
                        else
                        {
                            llListenRemove(listenHandle);
                            listenHandle = 0;
                        }
                    }
                    else if(command == 5)//sit_button
                    {
                        if(llGetSubString(data, 0, 0) != " ")
                            llSetSitText(data);
                        else
                            llSetSitText(llDeleteSubString(data, 0, 0));
                    }
                }
                ++gLine;
                dataserver_key = llGetNotecardLine(gNotecard, gLine);
            }
            else
            {
                if(visible)
                    show();
                else
                    hide();
            }
        }
    }
}

===

Go back to ScriptLibrary


LibraryPositionHUD

Moves a HUD to a sane position no matter which attachment point is used. This only works if you put the script in the root prim, and the root prim is about the same size as the HUD.

default
{

    attach(key id)
    {
        if (id==NULL_KEY)  //being detached
            return;

        vector scale=llGetScale();
        integer point=llGetAttached();
        if ((point==31)||(point==35)) //center
        {
            llSetPos(<0,0,0>);
        }
        else
        if (point==32) //top right
        {
            llSetPos(<0,scale.y,-scale.z>);
        }
        else
        if (point==33) // top
        {
            llSetPos(<0,0,-scale.z>);
        }
        else
        if (point==34) //top left
        {
            llSetPos(<0,-scale.y,-scale.z>);
        }
        else
        if (point==36) //bottom left
        {
            llSetPos(<0,-scale.y,scale.z>);
        }
        else
        if (point==37) //bottom center
        {
            llSetPos(<0,0,scale.z>);
        }
        else
        if (point==38) //bottom right
        {
            llSetPos(<0,scale.y,scale.z>);
        }

    }
}


LibraryPrecisionTime

Precision Timer

I spent a few hours testing a few things; here is the best I managed for precision timing in that period. There are certain to be improvements that can be made, but here we go!

Please ignore my SLOP.

Also, note that this script is not "Ready To Use" but rather is a demonstration of how to make a precision timer, along with code to analyze how precise it is.

Run it, and after a few minutes click on it to get statistics. "Drift" means "how far off from the target" in seconds.

A final WARNING: This MAY cause sim performance to suffer and bleed. But it shouldn't be too horrible. And it can be improved, I'm just yawning now. :)

vector vecTime()
{
    vector tsp;
    list tsv;

    tsv = llParseString2List( llGetTimestamp(), ["T", ":", "Z"], [] );

    tsp.x = llList2Integer(tsv, 1) - 8;
    if( tsp.x < 0 ) {
        tsp.x += 24;
    }
    tsp.y = llList2Integer(tsv, 2);
    tsp.z = llList2Float(tsv, 3);

    return tsp;
}

vector fixTime( vector inTime )
{
    vector oTime = inTime;

    while( oTime.z >= 60.0 ) {
        oTime.z -= 60.0;
        oTime.y++;
    }
    while( oTime.y >= 60.0 ) {
        oTime.y -= 60.0;
        oTime.z++;
    }
    return oTime;
}

float timeDiffNow( vector vTarget )
{
    vector vTime = vecTime();
    return (vTarget.x - vTime.x)*3600.0 + (vTarget.y - vTime.y) * 60.0 + (vTarget.z - vTime.z);
}

// included for reference for those of you who might not know the breakdown of llGetTimestamp
float timeDiff( vector vMore, vector vLess )
{
    float hours = vMore.x - vLess.x;
    float minutes = vMore.y - vLess.y;
    float seconds = vMore.z - vLess.z;

    float tics = hours * 3600.0 + minutes * 60.0 + seconds;

//    llOwnerSay("Diff between " + (string)vMore + " and " + (string)vLess );
//    llOwnerSay("Diff returned approx. " + (string)( (integer)tics ) + " seconds.");
    return tics;
}

vector timeTarget;

// the actual timer setter
precTimer( float secdiff )
{
    vector timeNow;
    vector timeNext;
    integer i;

    timeTarget = timeNow = vecTime();
    timeTarget.z += secdiff;
    timeTarget = fixTime(timeTarget);
    llSetTimerEvent(secdiff - 0.5);
//    llOwnerSay("Start: " + (string)timeNow + ", Finish: " + (string)timeTarget);
}

list lDiffs;

default
{
    state_entry()
    {
        lDiffs = [];
        precTimer(2.0);
    }

    timer()
    {
//ETA to time to execute:
        float timeleft = timeDiffNow(timeTarget);// - llList2Float( llParseString2List( llGetTimestamp(), [":", "Z"], [] ), 2 );;

//Begin precision countdown
        llResetTime();
        while( timeleft > 0.01 ) {
            timeleft -= llGetAndResetTime();
        }
        if( timeleft < 0 ) { // this is purely for statistical reasons.
            timeleft = llFabs(timeleft);
        }
        lDiffs = (lDiffs=[]) + lDiffs + [timeleft];
        precTimer(2.0);
    }

    touch_start(integer nd)
    {
        float min;
        float max;
        float avg;
        float total;
        float cur;
        integer i;
        integer len;
        llSetTimerEvent(0.0);
        llOwnerSay("Calculating min,max,avg.");

        len = llGetListLength(lDiffs);
        total = 0.0;
        min = 1.0;
        max = 0.0;
        for( i = 0; i < len; i++ ) {
            cur = llList2Float(lDiffs, i);
            total += cur;
            if( cur < min ) {
                min = cur;
            }
            if( cur > max ) {
                max = cur;
            }
        }
        avg = total/(float)len;
        llOwnerSay("Total drift: " + (string)total + ", timers run: " + (string)len);
        llOwnerSay("Avg   drift: " + (string)avg);
        llOwnerSay("Min   drift: " + (string)min);
        llOwnerSay("Max   drift: " + (string)max);
        llOwnerSay("Starting another run.");
        llResetScript();
    }
}

Example output:

[21:46] Object: Total drift: 1.045955, timers run: 162
[21:46] Object: Avg drift: 0.006457
[21:46] Object: Min drift: 0.000021
[21:46] Object: Max drift: 0.026995

For reference, here's my short (didn't need to be very long lol) analysis of llSetTimerEvent()'s drift.

[21:54] Object: Total drift: 1.163673, timers run: 26
[21:54] Object: Avg drift: 0.044757
[21:54] Object: Min drift: 0.013641
[21:54] Object: Max drift: 0.082230
[21:54] Object: Starting another run.

That's right... which do you want, a drift of a 20th of a second, or of 150th of a second?

ScriptLibrary


LibraryPrimTorture

Prim Torture

Q: What is prim torture?
A: Prim torture is the art of using shape changes to produce shapes that are otherwise impossible.

Q: How does prim torture work?
A: All prims share the same attributes, even if the attributes can't be directly modified (in the prim's current shape) they are still responsible for the shape of the prim. By using multiple shape changes these otherwise hidden attributes can be set.

The trouble with torturing prims through scripts is that it requires constantly changing complex llSetPrimitiveParams calls. There are 13 different shape changes that can be done, with about 8 different syntaxes total. To complicate things, SL has restrictions on some shape changes that will result in the prim being deleted instead of the change going through. Each of the 13 shape changes results in restrictions being applied to the attributes; each with it's own unique restrictions.

Simple easy to use prim torture function, not the most efficiant but gets the job done with minimal hassle.

SetPrimType(integer version, integer type,
            integer holeshape, vector cut, float hollow, vector twist, vector topsize, vector topshear,
            vector advancedcut, vector taper, float revolutions, float radiusoffset, float skew)
{
    if(1 == version )
    {//legacy PRIM_TYPE interface, still active for backwards compatability. Introduced in 1.3, phased out in 1.5.
        if(3 == type)//PRIM_TYPE_SPHERE
            llSetPrimitiveParams([version, type, cut, hollow, advancedcut]);
        else if(type >= 0 && type <= 2)//PRIM_TYPE_BOX,PRIM_TYPE_CYLINDER,PRIM_TYPE_PRISM
            llSetPrimitiveParams([version, type, cut, hollow, twist.y, topsize, topshear]);
        else if(4 == type)//PRIM_TYPE_TORUS
            llSetPrimitiveParams([version, type, cut, hollow, twist.y, topsize.y, topshear, advancedcut]);
        else if(5 == type)//PRIM_TYPE_TUBE
            llSetPrimitiveParams([version, type, cut, hollow, twist.y, topshear.x]);
//        else if(PRIM_TYPE_RING == p) ring shape not arround when this interface was implemented
    }
    else if(version == 9)
    {//new PRIM_TYPE interface. introduced in 1.5, various revisions have added new restrictions
        if(3 == type)//PRIM_TYPE_SPHERE
            llSetPrimitiveParams([version, type, holeshape, cut, hollow, twist, advancedcut]);
        else if(type >= 0 && type <= 2)//PRIM_TYPE_BOX,PRIM_TYPE_CYLINDER,PRIM_TYPE_PRISM
            llSetPrimitiveParams([version, type, holeshape, cut, hollow, twist, topsize, topshear]);
        else if(type >= 4 && type <= 6)//PRIM_TYPE_TORUS,PRIM_TYPE_TUBE,PRIM_TYPE_RING
            llSetPrimitiveParams([version, type, holeshape, cut, hollow, twist, topsize, topshear, advancedcut, taper, revolutions, radiusoffset, skew]);
    }

}

Chained Prim Torture Script

This allows for easy chaining of prim torture commands. If you do alot of prim torture this will save some script time.

SetPrimTypeChained(list versions, list types,
            integer holeshape, vector cut, float hollow, vector twist, vector topsize, vector topshear,
            vector advancedcut, vector taper, float revolutions, float radiusoffset, float skew)
{
    integer version;
    integer type = -llGetListLength(types);
    integer len = -llGetListLength(versions);
    if(type > len) len = type;
    for(;len;++len)
    {
        if(1 == (version = llList2Integer(versions,len)))
        {//legacy PRIM_TYPE interface, still active for backwards compatability. Introduced in 1.3, phased out in 1.5.
            if(3 == (type = llList2Integer(types,len)))//PRIM_TYPE_SPHERE
                llSetPrimitiveParams([version, type, cut, hollow, advancedcut]);
            else if(type >= 0 && type <= 2)//PRIM_TYPE_BOX,PRIM_TYPE_CYLINDER,PRIM_TYPE_PRISM
                llSetPrimitiveParams([version, type, cut, hollow, twist.y, topsize, topshear]);
            else if(4 == type)//PRIM_TYPE_TORUS
                llSetPrimitiveParams([version, type, cut, hollow, twist.y, topsize.y, topshear, advancedcut]);
            else if(5 == type)//PRIM_TYPE_TUBE
                llSetPrimitiveParams([version, type, cut, hollow, twist.y, topshear.x]);
    //        else if(PRIM_TYPE_RING == p) ring shape not arround when this interface was implemented
        }
        else if(version == 9)
        {//new PRIM_TYPE interface. introduced in 1.5, various revisions have added new restrictions
            if(3 == (type = llList2Integer(types,len)))//PRIM_TYPE_SPHERE
                llSetPrimitiveParams([version, type, holeshape, cut, hollow, twist, advancedcut]);
            else if(type >= 0 && type <= 2)//PRIM_TYPE_BOX,PRIM_TYPE_CYLINDER,PRIM_TYPE_PRISM
                llSetPrimitiveParams([version, type, holeshape, cut, hollow, twist, topsize, topshear]);
            else if(type >= 4 && type <= 6)//PRIM_TYPE_TORUS,PRIM_TYPE_TUBE,PRIM_TYPE_RING
                llSetPrimitiveParams([version, type, holeshape, cut, hollow, twist, topsize, topshear, advancedcut, taper, revolutions, radiusoffset, skew]);
        }
    }
}

One of my favorite chains is
SetPrimTypeChained([9,1],[PRIM_TYPE_RING,PRIM_TYPE_SPHERE],PRIM_HOLE_DEFAULT,
        <0.0, 1.0, 0>,0.95,<0.0, 0.5, 0>,<0.0, 0.5, 0>,<0.0, 0.0, 0>,<0.47, 0.53, 0>,<0.0, 0.0, 0>,1,0,0);

Updatable Prim Torture Script

To help minimize prim deletion from happening, the script passes safe values when it knows they will later be overwritten.

Script that could be updated via a linked message to support new formats.
Definitely overkill.

Changes Since 1.0:
  • changed the default values
  • Added commented out jump to avoid new return code. If you don't want the extra return data just uncomment the line "jump end;"
  • Added nice return from the function.
  • Changed default values for non-forced attributes.
  • Changed the values of all flags.
  • New feature: Making a flag negative forces it to use the specified values and not use the non-forced attribute defaults. It does the exact same thing as if you had called the function twice with the negative flag being the last flag of the first call.
  • Made Script into a function
  • Changed default values for non-forced attributes.

//----------------------------------------------//
//    Universal Prim Torture Script 1.2.2.1     //
//    Copyright (C) 2005  Strife Onizuka        //
//    http://home.comcast.net/~mailerdaemon/    //
//----------------------------------------------//
//
// To Use:
// Change the values in the state_entry
//
//--------------------------------------------------------------------//
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public License
//  as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330,
//  Boston, MA  02111-1307  USA
//
//  Any changes to this please post to:
//  http://secondlife.com/badgeo/wakka.php?wakka=LibraryPrimTorture
//
//--------------------------------------------------------------------//
//
//How to update if LL revises PRIM_TYPE or release new shapes:
//1. Add new PRIM_TYPE definition if it is needed (in init and below).
//2. Create TAGS for unique variables (variables that don't overlap with
//      others).
//3. Add new shapes, and unique variables.
//4. Add new overlapping code to Force Value (in state_entry) if
//      variables unevenly overlap.
//5. Adjust and add variables to llSetPrimitiveParams in state_entry.
//6. Update comments.
//
//--------------------------------------------------------------------//

integer status;    //set in state_entry with flags listed below.

//these need to be unique to the PRIM_TYPE value and shape.
//flag numbers need to be greater then zero

//PRIM_TYPE == 1
integer PRIM_TYPE_a_BOX         =    1;
integer PRIM_TYPE_a_CYLINDER    =    2;
integer PRIM_TYPE_a_PRISM       =    3;
integer PRIM_TYPE_a_SPHERE      =    4;
integer PRIM_TYPE_a_TORUS       =    5;
integer PRIM_TYPE_a_TUBE        =    6;

//PRIM_TYPE == 9
integer PRIM_TYPE_b_BOX         =    7;
integer PRIM_TYPE_b_CYLINDER    =    8;
integer PRIM_TYPE_b_PRISM       =    9;
integer PRIM_TYPE_b_SPHERE      =    10;
integer PRIM_TYPE_b_TORUS       =    11;
integer PRIM_TYPE_b_TUBE        =    12;
integer PRIM_TYPE_b_RING        =    13;

//bit fields for type definitions
//if these are changed then forceValue conditionals will need changing.

integer HOLESHAPE       =    0x1;       //i 0  old shapes abuse
integer CUT             =    0x2;       //v 1
integer HOLLOW          =    0x4;       //f 2
integer TWIST           =    0x8;       //v 3  twist - new shapes
integer TWISTY          =    0x10;      //f 4  twist - old shapes
integer TOPSIZE         =    0x20;      //v 5  holesize - new shapes
integer TOPSIZEY        =    0x40;      //f 6  holesize - old torus
integer TOPSHEAR        =    0x80;      //v 7
integer TOPSHEARX       =    0x100;     //f 8  holesize - old tube
integer ADVANCEDCUT     =    0x200;     //v 9
integer TAPER           =    0x400;     //v 10 new shapes
integer REVOLUTIONS     =    0x800;     //f 11 new shapes
integer RADIUSOFFSET    =    0x1000;    //f 12 new shapes
integer SKEW            =    0x2000;    //f 13 new shapes
//integer AVAILABLE       =    0x4000;    //  14
//integer AVAILABLE       =    0x8000;    //  15
//integer AVAILABLE       =    0x10000;   //  16
//integer AVAILABLE       =    0x20000;   //  17
//integer AVAILABLE       =    0x40000;   //  18
//integer AVAILABLE       =    0x80000;   //  19

//PRIM_TYPE_BOX         0
//PRIM_TYPE_CYLINDER    1
//PRIM_TYPE_PRISM       2
//PRIM_TYPE_SPHERE      3
//PRIM_TYPE_TORUS       4
//PRIM_TYPE_TUBE        5
//PRIM_TYPE_RING        6

init()
{
    if(status&1) return;
    integer a = CUT | HOLLOW;
    integer b = HOLESHAPE | CUT | HOLLOW | TWIST;
    integer c = TOPSIZE | TOPSHEAR;
    integer d = ADVANCEDCUT | TAPER | REVOLUTIONS | RADIUSOFFSET | SKEW;

    addType(PRIM_TYPE_a_BOX,        1, PRIM_TYPE_BOX,
            TWISTY | c | a);
    addType(PRIM_TYPE_a_CYLINDER,   1, PRIM_TYPE_CYLINDER,
            TWISTY | c | a);
    addType(PRIM_TYPE_a_PRISM,      1, PRIM_TYPE_PRISM,
            TWISTY | c | a);
    addType(PRIM_TYPE_a_SPHERE,     1, PRIM_TYPE_SPHERE,
            ADVANCEDCUT | a);
    addType(PRIM_TYPE_a_TORUS,      1, PRIM_TYPE_TORUS,
            ADVANCEDCUT | TWISTY | TOPSIZEY | TOPSHEAR | a);
    addType(PRIM_TYPE_a_TUBE,       1, PRIM_TYPE_TUBE,
            TOPSHEARX | TWISTY | a);

    addType(PRIM_TYPE_b_BOX,        9, PRIM_TYPE_BOX,       c | b);
    addType(PRIM_TYPE_b_CYLINDER,   9, PRIM_TYPE_CYLINDER,  c | b);
    addType(PRIM_TYPE_b_PRISM,      9, PRIM_TYPE_PRISM,     c | b);
    addType(PRIM_TYPE_b_SPHERE,     9, PRIM_TYPE_SPHERE,
            ADVANCEDCUT | b);
    addType(PRIM_TYPE_b_TORUS,      9, PRIM_TYPE_TORUS,     d | c | b);
    addType(PRIM_TYPE_b_TUBE,       9, PRIM_TYPE_TUBE,      d | c | b);
    addType(PRIM_TYPE_b_RING,       9, PRIM_TYPE_RING,      d | c | b);
    status = status | 1;
}

list recType;
list recDef = [0];    //do not change,
//it allows the script to ignore bad types.

addType(integer fake, integer type, integer real, integer flag)
{
    integer c=llListFindList(recType,[fake]);
    flag = (flag & 0xFFFFF) | ((real&0xF)<<20) | ((type&0xFF)<<24);
    if(c+1)
        recDef=llListInsertList(llDeleteSubList(recDef,c,c),[flag],c);
    else
    {
        recDef=llListInsertList(recDef,[flag],llGetListLength(recType));
        recType+=fake;
    }
}

list test(integer m, integer n, integer t, list b)
{
    if(m & t) {
        t = ((n & t)!=0);
        return llList2List(b,t,t);
    }
    return [];
}

list PrimTorture(list flags, integer holeshape, vector cut,
             float hollow, vector twist, vector topsize,
             vector topshear, vector advancedcut, vector taper,
             float revolutions, float radiusoffset, float skew)
{
    init();
    integer p = llGetListLength(flags);
    integer m;
    integer n;
    integer r;
    list force;
    list k;
    while(p + 1)
    {
        n = n | (llList2Integer(recDef, llListFindList(recType,
                [llAbs(m)])) & 0xFFFFF);
        r = r | n;
        if(n & TWIST)
            n = n | TWISTY;
        if(n & TOPSIZE)
            n = n | TOPSIZEY;
        if(n & TOPSHEAR)
            n = n | TOPSHEARX;
        if(p--)
        {
            m = llList2Integer(flags, p);
            if(m<0)
                n = 0;
        }
        force = [n] + force;
    }
    //--------------------//
    p=-llGetListLength(flags);
    while(p)
    {
        //k = llList2Integer(flags,p);
        m = llList2Integer(recDef, llListFindList(recType,
            [llAbs(llList2Integer(flags, p))]));
        n = llList2Integer(force, p++);
        if(m)
        {
            k = [(m>>24)&0xFF, (m>>20)&0xF]
                +    test(m,n,HOLESHAPE,     [holeshape,    0       ])
                +    test(m,n,CUT,           [cut,          <0,1,0> ])
                +    test(m,n,HOLLOW,        [hollow,       0.5     ])
                +    test(m,n,TWIST,         [twist,        <0,0,0> ])
                +    test(m,n,TWISTY,        [twist.y,      0.0     ])
                +    test(m,n,TOPSIZE,       [topsize,      <1,0,0> ])
                +    test(m,n,TOPSIZEY,      [topsize.y,    0.0     ])
                +    test(m,n,TOPSHEAR,      [topshear,     <0,0,0> ])
                +    test(m,n,TOPSHEARX,     [topshear.x,   0.0     ])
                +    test(m,n,ADVANCEDCUT,   [advancedcut,  <0,1,0> ])
                +    test(m,n,TAPER,         [taper,        <0,0,0> ])
                +    test(m,n,REVOLUTIONS,   [revolutions,  1.0     ])
                +    test(m,n,RADIUSOFFSET,  [radiusoffset, 0.0     ])
                +    test(m,n,SKEW,          [skew,         0.0     ])
//                +    test(m,n,AVAILABLE,     [,        ])
                    ;
//            llWhisper(0,llList2CSV(k));
            llSetPrimitiveParams(k);
        }
    }
    k = [r];
//    jump end;
    if(r & HOLESHAPE)
        k+=["Holeshape = "  +  llList2String(
                ["PRIM_HOLE_DEFAULT", "PRIM_HOLE_SQUARE",
                "PRIM_HOLE_CIRCLE",  "PRIM_HOLE_TRIANGLE",
                "PRIM_HOLE_DEFAULT"],
                llListFindList([PRIM_HOLE_DEFAULT, PRIM_HOLE_SQUARE,
                PRIM_HOLE_CIRCLE, PRIM_HOLE_TRIANGLE],[holeshape]))];
    if(r & CUT)
        k+=["Cut = "            +   clean(cut)];
    if(r & HOLLOW)
        k+=["Hollow  = "        +   strip(hollow)];
    if(r & TWIST)
        k+=["Twist = "          +   clean(twist)];
    else if(r & TWISTY)
        k+=["Twist.y = "        +   strip(twist.y)];
    if(r & TOPSIZE)
        k+=["Topsize = "        +   clean(topsize)];
    else if(r & TOPSIZEY)
        k+=["Topsize.y = "      +   strip(topsize.y)];
    if(r & TOPSHEAR)
        k+=["Topshear = "       +   clean(topshear)];
    else if(r & TOPSHEARX)
        k+=["Topshear.x = "     +   strip(topshear.x)];
    if(r & ADVANCEDCUT)
        k+=["Advanced Cut = "   +   clean(advancedcut)];
    if(r & TAPER)
        k+=["Taper = "          +   clean(taper)];
    if(r & REVOLUTIONS)
        k+=["Revolutions = "    +   strip(revolutions)];
    if(r & RADIUSOFFSET)
        k+=["Radius Offset = "  +   strip(radiusoffset)];
    if(r & SKEW)
        k+=["Skew = "           +   strip(skew)];
    @end;
    return k;
}

string strip(float a)
{
    string b = (string)a;
    integer c = 5;
    while(c-- && llGetSubString(b,-1,-1)=="0")
        b = llGetSubString(b,0,-2);
    return b;
}

string clean(vector a)
{
    return "<"+llList2CSV([strip(a.x),strip(a.y)])+">";
}

default
{
    state_entry()
    {
        //Old Flags
        // PRIM_TYPE_a_BOX
        // PRIM_TYPE_a_CYLINDER
        // PRIM_TYPE_a_PRISM
        // PRIM_TYPE_a_SPHERE
        // PRIM_TYPE_a_TORUS
        // PRIM_TYPE_a_TUBE

        //New Flags
        // PRIM_TYPE_b_BOX
        // PRIM_TYPE_b_CYLINDER
        // PRIM_TYPE_b_PRISM
        // PRIM_TYPE_b_SPHERE
        // PRIM_TYPE_b_TORUS
        // PRIM_TYPE_b_TUBE
        // PRIM_TYPE_b_RING

        list    flags  =   [PRIM_TYPE_b_TORUS,
//                            -PRIM_TYPE_a_CYLINDER,
                            PRIM_TYPE_a_SPHERE];

        integer holeshape       =    PRIM_HOLE_DEFAULT;
        vector  cut             =    <0.0, 1.0, 0>;
        float   hollow          =    0.95;
        vector  twist           =    <0.0, 0.5, 0>;
        vector  topsize         =    <0.0, 0.5, 0>;
        vector  topshear        =    <0.0, 0.0, 0>;
        vector  advancedcut     =    <0.47, 0.53, 0>;
        vector  taper           =    <0.0, 0.0, 0>;
        float   revolutions     =    1;
        float   radiusoffset    =    0;
        float   skew            =    0;

        // PRIM_HOLE_DEFAULT
        // PRIM_HOLE_SQUARE
        // PRIM_HOLE_CIRCLE
        // PRIM_HOLE_TRIANGLE

        llSay(0,"running");
        list a = PrimTorture(flags, holeshape, cut, hollow,
                    twist, topsize, topshear, advancedcut,
                    taper, revolutions, radiusoffset, skew);
        string b = llDumpList2String(llDeleteSubList(a,0,0),"\n");
        llSetObjectDesc(b);
        llSetText(b,<1,1,1>,1);
    }
}

Restrictions

Shape | Version | Type | Hole Shape |  | Cut |  |  |  | Hollow |  | Twist |  |  |  | Taper / Holesize Top Size |  |  |  | Top Shear |  |  |  | Advanced Cut |  |  |  | Taper |  |  |  | Revolutions |  | Radius Delta* |  | Skew
 |  |  |  |  | Begin |  | End |  |  |  | Begin |  | End |  | X |  | Y |  | X |  | Y |  | Begin |  | End |  | X |  | Y
 |  |  | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max | Min | Max
Box | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.95 |  |  | -0.5 | 0.5 | 0 | 1 | 0 | 1 | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 0 | 1 | 1 |  |  |  |  |  |  |  |  |  |
Cylinder | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.95 |  |  | -0.5 | 0.5 | 0 | 1 | 0 | 1 | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 0 | 1 | 1 |  |  |  |  |  |  |  |  |  |
Prism | 1 | 2 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.95 |  |  | -0.5 | 0.5 | 0 | 1 | 0 | 1 | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 0 | 1 | 1 |  |  |  |  |  |  |  |  |  |
Sphere | 1 | 3 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.95 |  |  |  |  |  |  |  |  |  |  |  |  | 0 | 1 | 0 | 1 |  |  |  |  |  |  |  |  |  |
Torus | 1 | 4 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.95 |  |  | -0.5 | 0.5 |  |  | 0 | 0.5 | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 1 | 0 | 1 |  |  |  |  |  |  |  |  |  |
Tube | 1 | 5 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.95 |  |  | -0.5 | 0.5 |  |  |  |  | 0.05 | 0.45 |  |  | 0 | 0 | 1 | 1 |  |  |  |  |  |  |  |  |  |
Box | 9 | 0 | 0 | 48 | 0 | 1 | 0 | 1 | 0 | 0.95 | -0.5 | 0.5 | -0.5 | 0.5 | (0,-1) | (2,1) | (0,-1) | (2,1) | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0
Cylinder | 9 | 1 | 0 | 48 | 0 | 1 | 0 | 1 | 0 | 0.95 | -0.5 | 0.5 | -0.5 | 0.5 | (0,-1) | (2,1) | (0,-1) | (2,1) | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0
Prism | 9 | 2 | 0 | 48 | 0 | 1 | 0 | 1 | 0 | 0.95 | -0.5 | 0.5 | -0.5 | 0.5 | (0,-1) | (2,1) | (0,-1) | (2,1) | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0
Sphere | 9 | 3 | 0 | 48 | 0 | 1 | 0 | 1 | 0 | 0.95 | -1 | 1 | -1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0
Torus | 9 | 4 | 0 | 48 | 0 | 1 | 0 | 1 | 0 | 0.95 | -1 | 1 | -1 | 1 | 0 | 1 | 0 | 0.5 | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 1 | 0 | 1 | -1 | 1 | -1 | 1 | 1 | 4 | -1 | 1 | -1 | 1
Tube | 9 | 5 | 0 | 48 | 0 | 1 | 0 | 1 | 0 | 0.95 | -1 | 1 | -1 | 1 | 0 | 1 | 0 | 0.5 | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 1 | 0 | 1 | -1 | 1 | -1 | 1 | 1 | 4 | -1 | 1 | -1 | 1
Ring | 9 | 6 | 0 | 48 | 0 | 1 | 0 | 1 | 0 | 0.95 | -1 | 1 | -1 | 1 | 0 | 1 | 0 | 0.5 | -0.5 | 0.5 | -0.5 | 0.5 | 0 | 1 | 0 | 1 | -1 | 1 | -1 | 1 | 1 | 4 | -1 | 1 | -1 | 1

  • Values in parentheses: The first value is the script range, the second value is the client range. To convert between the two do (1.0 - value)
  • If values are supplied in the table that is the range the value is forced to be within when that shape change is preformed by llSetPrimitiveParams.
  • Blue boxes are attributes supplied to llSetPrimitiveParams to perform the shape change.
  • If ranges are absent in the table that indicates the attribute is not checked.
  • Client-based shape changes are governed by different restrictions (usually more restrictive).
  • Version is the value of PRIM_TYPE.
  • Type is represent the PRIM_TYPE_* constants.
  • Hole Shape only uses bits 4, 5, 6 & 7; bits 0->3 are ignored; valid values range from 0->63 inclusive. [PRIM_HOLE_DEFAULT, 0x0], [PRIM_HOLE_CIRCLE, 0x10], [PRIM_HOLE_SQUARE, 0x20], [PRIM_HOLE_TRIANGLE, 0x30].
  • Radius Delta's range is dependent on Taper Y. Radius Delta must have the same sign as Taper Y.
  • Taper / Holesize is a different attribute then Taper
  • Skew's range is affected by Hole Size X and Taper.

As you can see by this table, old-style spheres rock the Casbah.

Script Library | Examples


LibraryProductUpdateServer

Product Update Server

Sorry if this is kind of badly written, I wrote it quickly in notepad, and I have yet to compile it yet. Please improve it further, or fix anything I did wrong!

Edited and Updated to allow compiling, by CryasTokhes
Server
//Comet Aero
//12-11-06
//Reviewed and updated to compile by: Cryas Tokhes (19.06.07)
//This script is under the protection of the
//DNSOIWKY License
//A.K.A. The "Do Not Sell Or I Will Kill You" License*
string productGiveName = "gGadget 1.1";//Object name inside of the object to give if it is out of date.
string password = "password";//Password to protect from people stealing your hard work!!
string product = "gGadget";//The product series name, this is in place so you can handle multiple products per prim ^.^
integer latestVersion = 1;//This is the latest version of your product.

integer updateNotify = FALSE;//Want the server to email you when a product has been updated?
integer sendOldMessage = FALSE;//Want the server to message user that their product is out of date.
integer sendCurrentMessage = FALSE;//Want the server to message user stating that their product is up to date.
integer sendNewMessage = TRUE;//Want the server to message user that THEY have a newer product version then the server.
integer sendNewProduct = FALSE;//Want the server to RECEIVE the newer product from the user.
string emailAddress = "name@isp.com";//Your email address.

string currentMessage = "Your product is up to date.";
string overMessage = "Your product is newer then what is currently available. Sending to server...";
string oldMessage = "Your product is out of date. Sending new version...";
//====================================================================================================
//===Subject Format: password|product|version=========================================================
//===Message Format: UserKey==========================================================================
//==========================================WARNING===================================================
//===========There MUSNT be any spaces in between the "|" and all of them MUST match EXACTLY!!========
//====================================================================================================

default {
    state_entry() {
        llSetTimerEvent(2.5);//Poll for emails.
        llSetObjectDesc((string)llGetKey());//You will need the key when setting up you client
        llWhisper(0, (string)llGetKey());
    }

    timer() {
        llGetNextEmail("","");
        if(llGetFreeMemory() < 100){
            llInstantMessage(llGetOwner(), "/me is running low on available memory, script is resetting in 5 seconds.");
            llSleep(5);//There is nothing really to store in the runtime, so if were running
            //low on memory, just notify the owner and reset.
            llResetScript();
        }
    }

    email(string time, string address, string subj, string message, integer num_left) {
        list messageList = llParseString2List(subj, ["|"], []);//Parses the subject into the parts by "|"
        string inputPassword = llList2String(messageList,0);//Get the first parced part.
        string inputProduct = llList2String(messageList,1);//Get the second parsed part.
        string inputVersionString = llList2String(messageList,2);//Get the THIRD parsed part.
        integer inputVersion = (integer)inputVersionString;//Just thought it would be better if we did it this way :)
        key inputUser = llDeleteSubString(message, 0, llSubStringIndex(message, "\n\n") + 1);

        //===========================================================================================
        if(inputPassword == password && inputProduct == product){
            if(inputVersion < latestVersion){//If its older then the newest version...
                if(sendOldMessage) llInstantMessage(inputUser, oldMessage);//If you want to send a message, then do!
                llGiveInventory(inputUser,productGiveName);//Send newest product
                if(updateNotify) llEmail(emailAddress, product + " has been updated.", product + " has been updated.");//Sends YOU a notification.
            }
            else if (inputVersion == latestVersion){//If it is the newest version...
                if(sendCurrentMessage) llInstantMessage(inputUser, currentMessage);//If you want to send a message, then do!
            }
            else if(inputVersion > latestVersion){//If its NEWER then the newest versiion..............
                if(sendNewMessage) llInstantMessage(inputUser, overMessage);//If you want to send a message, then do.
                if(sendNewProduct) llGiveInventory(inputUser,productGiveName);//If you want to give them YOUR newest version, then do.
            }
        }
    }
}
//*Comet Aero does not support the killing of Kittens, humans or goldfish.

Client
//Comet Aero
//12-11-06
//Reviewed and updated to compile by: Cryas Tokhes (19.06.07)
//This script is under the protection of the
//DNSOIWKY License
//A.K.A. The "Do Not Sell Or I Will Kill You" License*
//
//==========================================WARNING===================================================
//=======The password, productName and serverKey MUST match EXACTLY=======================================
string serverKey = "1b685a6d-fec9-4f2a-9c3f-53be0ae25164";//What the server whispers when you put the script in the server or reset it.
string productName = "gGadget";//The product name you set in your script.
string password = "password";//The password you set in your server.
integer currentVersion = 1;//The current version of the product.

default {
    on_rez(integer param){
    llEmail(serverKey + "@lsl.secondlife.com", password + "|" + productName + (string)currentVersion, (string)llGetOwner());
    //Send the server the request.
    llSay(0, "Thank you," + llKey2Name(llGetOwner()) + ", we are currently checking to see if there is a product update available.");
    }
}
//Comet Aero does not support the killing of Kittens, humans or goldfish.


LibraryPseudoRandomGenerator

Pseudo-Random Number Generator

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.

One of the big problems in LSL is communication security. It is next to impossible to keep communcations secure if they are on a fixed channel. For this reason, I have writen this function that can be used to generate pseudo-random numbers. They aren't really random, but are difficult to predict without the original algorithm. Using this function to secure your script systems does not compromise them because the range of inputs is so large that it would be impossible to calculate all the solutions in a timely fashion.

The idea is to use a slice of the MD5 hash of a string and convert that to an integer. MD5 does a nice job of shaking up the bits. If this is used for channel hopping (like in the example) it would be very difficult to gather enough information to crunch the numbers to start predicting the channels.

Function:
integer pseudo_random(string text, integer nonce, integer start, integer end)
{//(c)(cc-by) Strife Onizuka, http://creativecommons.org/licenses/by/3.0/
    return (integer)("0x"+llGetSubString(llMD5String(text, nonce), start, end));
}

integer pseudo_random_float_a(string text, integer nonce, integer start, integer end, float max)
{//(c)(cc-by) Strife Onizuka, http://creativecommons.org/licenses/by/3.0/
    if(text = llGetSubString(llMD5String(text, nonce), start, end))
        max *= (float)("0x"+text) /
            (float)("0x"+llGetSubString("ffffffffffffffffffffffffffffffff", start, end));
    return max * !(text == "");
}//0 <= result <= max

integer pseudo_random_float_b(string text, integer nonce, integer start, integer end, float max)
{//(c)(cc-by) Strife Onizuka, http://creativecommons.org/licenses/by/3.0/
    return max * (float)("0x"+text+"p-"+(string)(llStringLength(text = llGetSubString(llMD5String(text, nonce), start, end))<<2));
}//0 <= result < max

integer pseudo_random_float_c(string text, integer nonce, integer start, integer end, float max)
{//(c)(cc-by) Strife Onizuka, http://creativecommons.org/licenses/by/3.0/
    return iuf((integer)("0x"+llGetSubString(llMD5String(text, nonce), start, end)) & 0x3FFFFFFF) * max;
}//0 <= result < max

float iuf(integer a)
{//union integer to float
    return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.

Example Listen:
integer pseudo_random(string text, integer nonce, integer start, integer end)
{//(c)(cc-by) Strife Onizuka, http://creativecommons.org/licenses/by/3.0/
    return (integer)("0x"+llGetSubString(llMD5String(text, nonce), start, end));
}

list handles;
integer last;
//Used in the generation of the hash, best to make this at least 64 characters.
string feed = "How much wood would a wood chuck chuck if a wood chuck could chuck wood?";

default
{
    state_entry()
    {
        integer now = 30 * ((integer)llGetGMTclock() / 30);
        integer count = 0;
        while(count < 2)//number of channels to hold open at any one time.
        {//if you have problems with missed message do to a lagged sim increase the number
            handles += llListen(pseudo_random(feed, (last = now), 0, 7), "", "", "");
            now += 30;
            ++count
        }
        llSetTimerEvent(10); //this is so if there is a nasty time dilation
    }
    state_exit()
    {
        handles = [];
    }
    timer()
    {
        integer next = 30 * ((integer)llGetGMTclock() / 30) + 30;
        if(next > last)
        {
            last = next;
            llListenRemove(llList2Integer(handles, 0));
            handles = llList2List(handles, 1, -1) +
                [llListen(next = pseudo_random(feed, next, 0, 7), "", "", "")];
//            llOwnerSay(llList2CSV([last, next]));
        }
    }
    listen(integer a, string b, key c, string d)
    {
        b = llGetTimestamp();
        float e = (float)llGetSubString(b,17,-1) - (float)llGetSubString(d,17,-1);
        if(e < 0.0)
            e+=60.0;
        llOwnerSay((string)e);
    }
}

Example speak:
integer pseudo_random(string text, integer nonce, integer start, integer end)
{//(c)(cc-by) Strife Onizuka, http://creativecommons.org/licenses/by/3.0/
    return (integer)("0x"+llGetSubString(llMD5String(text, nonce), start, end));
}

string feed = "How much wood would a wood chuck chuck if a wood chuck could chuck wood?";

default
{
    state_entry()
    {
    }

    touch_start(integer next)
    {
        string time = llGetTimestamp();
        next = pseudo_random(feed, 30 * ((integer)llGetGMTclock() / 30), 0, 7);
        llSay(next, time);
//        llOwnerSay((string)next);
    }
}

Functions of interest:
integer Key2Integer(string input, integer start, integer end)
{
    return (integer)("0x"+llGetSubString(
            (string)llParseString2List(input,["-"],[]),
        start, end));
}

integer timestamp_to_unixtime(string timestamp) {
    integer year    = (integer) llGetSubString(timestamp,  0,  3);
    integer month   = (integer) llGetSubString(timestamp,  5,  6);

    return
    (
        (year - 1970) * 365
        + (year - 1969) / 4 - (year - 2001) / 100 + (year - 2001) / 400
        + llList2Integer(
            [ 0, -1,  30, 58, 89, 119, 150, 180,  211, 242, 272, 303, 333 ],
            month )
        + (integer) llGetSubString(timestamp,  8,  9)
        + (month>2) * (!(year % 4) - !(year % 100) + !(year % 400))
    )
    * 86400
    + (integer) llGetSubString(timestamp, 11, 12) * 3600
    + (integer) llGetSubString(timestamp, 14, 15) * 60
    + (integer) llGetSubString(timestamp, 17, 18);
}


LibraryPublicBillboard

PublicBillboard

Okay this script makes a public texture prim that anybody can drag a texture into it and it will cycle through all of the textures inside of its invontory. So enjoy! =^.^=

////////////////////////////////////////////////////////////////////////////
///////////////////// Creator: Chasingred3 Ixtab ///////////////////////////
////////////////////////////////////////////////////////////////////////////

// This script makes a "projector" like prim, anybody can ctrl drag a
// texture into it and it will cycle through the textures, making a public Billboard.

// PLEASE dont try to sell this to some n00b or I will have to hunt you down and kill you, thank you! =^.^=

RandomTexture ()
{
    llSetTexture (llGetInventoryName (INVENTORY_TEXTURE, (integer) llFrand (llGetInventoryNumber (INVENTORY_TEXTURE))), ALL_SIDES);
}
default
{
    state_entry ()
    {
        llAllowInventoryDrop (TRUE);
        llSetTimerEvent (60.0);
        RandomTexture ();
    }
    timer ()
    {
        RandomTexture ();
    }
    touch_start (integer touches)
    {
        RandomTexture ();
    }
    changed (integer Change)
    {
        if (Change & (CHANGED_ALLOWED_DROP | CHANGED_INVENTORY))
            RandomTexture ();
    }
}

Textures | llFrand |


LibraryRadar

Radar

Drop this script in a prim and set it on the ground or wear it on your HUD.

//Sable Till - Radar/scannar script.
//You can get a copy of the license this script is under at http://www.gnu.org/copyleft/gpl.html
//Copyright (C) 2006 Sable Till

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

string status="none";
list people;
integer maxScanDistance=96;
vector color = <0,1,1>;
integer maxPeople = 8;
integer scanType = AGENT;
integer scanFreq=1;
integer scanDistance=10;

integer count(string name) {
    integer i = llListFindList(people, [name]);
    if(i ==-1){
        people+=[name, 0];
        return 0;
    } else {
        integer count = llList2Integer(people, i+1);
        people=llListReplaceList(people, [count+scanFreq], i+1, i+1);
        return count;
    }
}

//calculate time strings with proper units that are sensibly rounded
string time(integer cnt) {
    if(cnt>3600) {
        return (string)(cnt/3600)+"hr " + (string)((cnt%3600)/60) +"min";
    }else {
       if(cnt>60) {
            return (string)(cnt/60)+"min";
        } else {
            return (string)cnt+"s";
        }
    }
}

//I'm pretty sure there's a better way to do this but I'm trying to calculate the angle between
//North and the target so I can work out which direction it is in.
float getAngle(vector me, vector target) {
    float hyp = llVecDist(me, target);
    float yDiff = target.y-me.y;
    float xDiff = target.x-me.x;
    float angle = llSin(yDiff/hyp);
    if(xDiff>0 && yDiff>0) {
        return angle*RAD_TO_DEG;
    }
    if(xDiff>0 && yDiff<0) {
        return 90-angle*RAD_TO_DEG;
    }
    if(xDiff<0 && yDiff>0) {
        return angle*RAD_TO_DEG+270;
    }
    if(xDiff<0 && yDiff<0) {
        return angle*RAD_TO_DEG + 270;
    }
    return angle*RAD_TO_DEG;
}

default
{
    state_entry()
    {
        llSensorRepeat("", "",scanType, scanDistance, PI, scanFreq);
        llSetTimerEvent(15);
    }

    sensor(integer num_detected) {
        people=[];
        string result;
        integer n=-1;
        integer distance=0;
        integer detDist;
        string name;

        vector pos = llGetPos();
        //get the dist, name and direction of everyone we just scanned.
        for(n=0;n<num_detected && n<maxPeople;++n) {
            vector detPos = llDetectedPos(n);
            detDist = (integer)llVecDist(pos, detPos);
            float angle = getAngle(llGetPos(), detPos);
            name = llKey2Name(llDetectedKey(n));
            if(detDist<96) {
               people+=detDist;
               people+=name;
               people+=angle;
            }
        }
        //sort the strided list
        people = llListSort(people, 3, TRUE);
        //construct settext
        num_detected = llGetListLength(people)/3;
        for(n=0;n<num_detected;++n) {
            detDist=llList2Integer(people, n*3);
            name = llList2String(people, n*3+1);
            float dir = llList2Float(people, n*3+2);
            if(detDist>20 && distance<=20) {
                result+="<- Chat Range Limit ->\n";
            }
            result+=name;
            if(detDist<20) {
                integer cnt = count(name);
                result+=" ["+time(cnt)+"]";
            }
            result+=" ["+(string)detDist+"m]";

            if(dir < 0 || dir > 360) {
                llOwnerSay("Error:"+(string)dir+":"+name);
            }
            //determine which compass direction they are in.
            if(dir <= 22.5) {
                result+=" N\n";
            } else {
            if(dir > 22.5 && dir <= 67.5) {
                result+=" NE\n";
            } else {
            if(dir > 67.5 && dir <= 112.5) {
                result+=" E\n";
            } else {
            if(dir > 112.5 && dir <= 157.5) {
                result+=" SE\n";
            } else {
            if(dir > 157.5 && dir <= 202.5) {
                result+=" S\n";
            } else {
            if(dir > 202.5 && dir <= 247.5) {
                result+=" SW\n";
            } else {
            if(dir > 247.5 && dir <= 292.5) {
                result+=" W\n";
            } else {
            if(dir > 292.5 && dir <= 337.5) {
                result+=" NW\n";
            } else {
            if(dir > 337.5 && dir < 360) {
                result+=" N\n";
            }
            }
            }

            }}}}}}

             distance=detDist;
        }

        //If we detected more (or the same number of) people as maxPeople then shrink down the scan distance to just
        //the distance to the furthest one. Otherwise increment it a bit in case there are people further out.
        if(num_detected>=maxPeople) {
            scanDistance=distance+10;
            llSensorRepeat("", "",scanType, scanDistance, PI, scanFreq);
        } else {
            if(scanDistance<maxScanDistance) {
                scanDistance+=10;
                llSensorRepeat("", "",scanType, scanDistance, PI, scanFreq);
            }
        }

        result+="\nStatus:" + status + ":" + (string)scanFreq + ":" + (string)scanDistance + ":" + (string)maxScanDistance;
        //adjust max people based on the length of result
        if(llStringLength(result)>254) {
            maxPeople--;
            llOwnerSay("Length is "+(string)llStringLength(result) +
                " Decrementing maxPeople to "+(string)maxPeople);
        } else {
            if(llStringLength(result)<200 && num_detected>maxPeople) {
                maxPeople++;
                llOwnerSay("Length is "+(string)llStringLength(result) +
                " Incrementing maxPeople to "+(string)maxPeople);
            }
        }
        llSetText(result, color, 1);
    }

    no_sensor() {
        llSetText("Status:"+status, color, 1);
        maxScanDistance+=10;
        llSensorRepeat("", "", scanType, maxScanDistance, PI, scanFreq);
    }

    //all we do here is check the sims fps and dilation and tone down our scanning if necessary.
    timer() {
        float fps = llGetRegionFPS();
        float timeDilation = llGetRegionTimeDilation();

        if(fps<35 || timeDilation <0.9) {
            maxScanDistance=32;
            if(scanDistance>maxScanDistance) {
                scanDistance=maxScanDistance;
            }
            scanFreq=240;
            status = "poor";
            llSetTimerEvent(240);
            color=<1,0,0>;
        } else
        {
        //sim is slightly lagged, we scan every 120seconds and to a max of 64metres
        if(fps<40 || timeDilation<0.95) {
            maxScanDistance=64;
            if(scanDistance>maxScanDistance) {
                scanDistance=maxScanDistance;
            }
            scanFreq=30;
            status = "ok";
            llSetTimerEvent(120);
            color=<1,1,0>;
        } else
        //sim is fine, we scan every second and to the max distance possible
        {
            maxScanDistance=96;
            if(scanDistance>maxScanDistance) {
                scanDistance=maxScanDistance;
            }
            scanFreq=1;
            llSetTimerEvent(60);
            status = "good";
            color=<0,1,1>;
        }}
        llSensorRepeat("", "", scanType, scanDistance, PI, scanFreq);
    }
}


LibraryRadioStation

Fill in the lists and then add this to a prim, deed the prim to the land group if there is one, and then click to change stations. Dont forget to add yourself to the authorized users!

list stations = [ ]; // add station URLs here between the [ ] in quotes seperated by commas "url1", "url2", "url3"....
list station_descriptions = [ ];  // add station descriptions here between the [ ] in quotes seperated by commas... they should be in the same order as the urls they describe, you can use "" for any of them if you do not wish to add a description.
list djs = [ ];  // add the names of those authorized to change stations here between the [ ] in quotes seperated by commas
integer current_station = -1;

default
{
    state_entry()
    {
        current_station = -1;
    }

    on_rez(integer start_param)
    {
        llResetScript();
    }

    touch_start(integer total_number)
    {
        if(llListFindList(djs, (list)llKey2Name(llDetectedKey(0))) != -1)
        {
            current_station++;
            if(current_station >= llGetListLength(stations))
                current_station = 0;
            llSetTimerEvent(3);
            llWhisper(0, "Now Playing Station " + (string)current_station + ": " + llList2String(station_descriptions, current_station));
        }
    }

    timer()
    {
        llSetParcelMusicURL(llList2String(stations, current_station));
        llSetTimerEvent(0);
    }
}


LibraryRandomHoverTextColor

This script generates a random colour every 0.5 seconds and sets the ?hovertext? of and object to it, the ?hovertext? itself is defined in the script.

//Created by Kopilo Hallard on 4th March 2007
// sets the ?hovertext? of an object to a random colour every 0.5 seconds
// Copyright (C) 2007  Kopilo Hallard

// text for the hover text
string TXT = "default text";  //Simply change ?default text? to what you wish the hovertext to be.

// *********** License Agreement Follows ***********************
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.

float MIN_GREY = 0.4; //sets the minimum darkness of the text

vector colour; //variable to keep the current colour value

//Method for changing the colour of the text
RandomColour() {
    //sets three random values between MIN_GREY and 1 to the colour variable
    colour = (<llFrand(1 - MIN_GREY) + MIN_GREY, llFrand(1 - MIN_GREY) + MIN_GREY, llFrand(1 - MIN_GREY) + MIN_GREY>);
}

default
{
    state_entry() {
        llSetTimerEvent(0.5);
    }
    timer() {
        RandomColour();
        llSetText(TXT,colour,1);
    }
}


LibraryRopeConstraint

Rope Constraint

This script creates a pseudo-realistic rope constraint between two objects. Put this script in two nearby physics objects; they should detect one another and become joined by a particle-based representation of a rope. Making one of the objects non-physical and suspending it in mid-air high enough should cause the other, physical object to swing under it. (In fact, it's much less dangerous this way.) This does not require the llPushObject function to be allowed.

// Pseudo-Realistic Rope Constraint by Comrade Podolsky
// Free for non-commercial use.
// If used commercially, if possible please notify me of what use you found for it. (Besides tether-ball.)

integer lockon = FALSE;
float rope_length = 5.0; // Obviously, the length of the simulated rope. (It will stretch slightly longer than this, though)
float dampening = 0.04; // This dampens a fraction of the object's velocity every 0.1 seconds, if the rope is stretched.
float bouncing = 0.4; // How much the object "bounces" back after stretching the rope to the limit of its length. This applies if the rope is stretched suddenly. 0.4 means it will bounce back with  40% of its original velocity. Setting this to below 0 will make the constraint act more stretchy than ropes normally do. Setting this to above 1 will make it unstable and dangerous.
float constant = 16.0; // The force constant of the rope. This applies when the rope is stretched slowly - in which case, it acts sort of like a spring.
key target;
vector ropecolor;
list rope_effect = [];

default
{
    state_entry()
    {
        llListen(12345,"","","TEST123");
        llSetTimerEvent(5.0);
    }

    timer() { if ( !lockon ){llWhisper(12345,"TEST123"); } } // Automatically locate "other" objects

    listen(integer chan, string name, key id, string msg)
    {
        if (!lockon)
        {
            lockon = TRUE;
            llWhisper(12345,"TEST123");
            llSetTimerEvent(0.0);
            target = id;
            llSensorRepeat("", target, SCRIPTED, 20.0, PI, 0.1); // This part may get rather resource-consuming. For slower-moving objects, and objects roped to something static, range may be decreased and delay may be increased.
            //Sensor range must be at least twice the length of the rope. Stretching the rope farther will cause it to act like it breaks, but it will restore itself if the objects are moved back together.

            rope_effect = // This is the particle effect representation of the rope. Change the colors, alphas, scales, and textures however you like. I can't say how it will affect the effect if you change anything else.
            [
                PSYS_PART_FLAGS,            PSYS_PART_FOLLOW_SRC_MASK | PSYS_PART_FOLLOW_VELOCITY_MASK | PSYS_PART_TARGET_LINEAR_MASK | PSYS_PART_TARGET_POS_MASK,

                PSYS_SRC_PATTERN,           PSYS_SRC_PATTERN_DROP,
                PSYS_SRC_TARGET_KEY,        target,

                PSYS_PART_START_COLOR,      ropecolor,
                PSYS_PART_END_COLOR,        ropecolor,
                PSYS_PART_START_ALPHA,      1.0,
                PSYS_PART_END_ALPHA,        1.0,
                PSYS_PART_START_SCALE,      <0.05,1.0,1.0>,
                PSYS_PART_END_SCALE,        <0.05,1.0,1.0>,
                PSYS_SRC_TEXTURE,           "",

                PSYS_PART_MAX_AGE,          0.5,
                PSYS_SRC_BURST_RATE,        0.001,
                PSYS_SRC_BURST_PART_COUNT,  1
            ];

            llParticleSystem(rope_effect);
        }
    }

    sensor(integer num_detected)
    {
        vector i_pos = llGetPos();
        vector u_pos = llDetectedPos(0);

        if (llVecMag(u_pos-i_pos)>rope_length)
        {
            llSetForce(constant*llGetMass()*llVecNorm(u_pos-i_pos)*(llVecMag(u_pos-i_pos) - rope_length),FALSE); // This is how an ideal spring would behave under these conditions. Without this statement, the rope would stretch slowly but indefinitely. Ropes do not work that way.
            llApplyImpulse( llGetMass() * llGetVel() * dampening * -1.0 , FALSE ); // This dampens the motion of the object, preventing buildup of excess kinetic energy through oscillation.
            vector wrongway = llVecNorm(i_pos - u_pos); // The direction in which the object should NOT be moving
            float wrongmag = ( llGetVel() - llDetectedVel(0) ) * wrongway; // The velocity with which the object is apparently moving, against the pull of the rope.
            if ( wrongmag > 0.0 ) { llApplyImpulse( llGetMass() * ( ( -1.0 - bouncing ) * ( wrongway * wrongmag ) ) , FALSE ); } // Stop the object from stretching too much farther than rope length.
        }
        else { llSetForce( ZERO_VECTOR, FALSE ); } // If the two objects are closer than rope length, the rope doesn't do anything.
    }

    no_sensor()
    {
        llSetForce( ZERO_VECTOR, FALSE );
    } // If the other object is nowhere to be seen, this object stops doing stuff.
}


LibraryRotationFunctions

Experimental Rotation Math Functions

While the default math library functions are good, they are in no way perfect and are subject to change (and fail due to changes). All math functions will lose precision especially interpolation for quaternions. Drift will occur with many conversions between Euler and quaternion. These functions will return different results from the base LSL functions, this is due to the fact that the LSL functions work differently.

EulerToRotFast is accurate for about 7 decimal places as compared to llEuler2Rot.
EulerToRot needs some work; it is supposed to give the more accurate answer of llEuler2Rot and EulerToRotFast
RotToEuler is currently a bit more accurate then llRot2Euler and more sensitive to drift.

WARNING about EulerToRotFast AND EulerToRot

have issues with gimble lock when y=|90| degrees and |x+z|>180

Converts an Euler to a Rotation. Should be equivalent to llEuler2Rot.

rotation EulerToRotFast(vector v)
{
    v/=2;
    float ax=llSin(v.x);
    float aw=llCos(v.x);
    float by=llSin(v.y);
    float bw=llCos(v.y);
    float cz=llSin(v.z);
    float cw=llCos(v.z);
    return <aw*by*cz + ax*bw*cw,
            aw*by*cw - ax*bw*cz,
            aw*bw*cz + ax*by*cw,
            aw*bw*cw - ax*by*cz>;
}

Converts an Euler to a Rotation. Error checks llEuler2Rot and returns the less bugged answer.

rotation EulerToRot(vector v)
{
    float err=0.00001;
    v/=2;
    float ax=llSin(v.x);
    float aw=llCos(v.x);
    float by=llSin(v.y);
    float bw=llCos(v.y);
    float cz=llSin(v.z);
    float cw=llCos(v.z);
    rotation a=<0.0, 0.0, cz, cw>*<0.0, by, 0.0, bw>*<ax, 0.0, 0.0, aw>;
    rotation b = <ax*bw*cw + aw*by*cz,
                  aw*by*cw - ax*bw*cz,
                  aw*bw*cz + ax*by*cw,
                  aw*bw*cw - ax*by*cz>;
    rotation c = a + b;//this is probably the only time you will see the addition
    rotation d = a - b;//and subtraction operators properly used on quaternions.
    if( (llFabs(c.x)>err && llFabs(d.x)>err) ||
        (llFabs(c.y)>err && llFabs(d.y)>err) ||
        (llFabs(c.z)>err && llFabs(d.z)>err) ||
        (llFabs(c.s)>err && llFabs(d.s)>err) )
        return b;
    return a;
}

Converts a Rotation to a Euler. Should be equivalent to llRot2Euler.

vector RotToEuler(rotation r)
{
    rotation t=<r.x*r.x,r.y*r.y,r.z*r.z,r.s*r.s>;
    float m=(t.x + t.y + t.z + t.s);
    if(m==0) return <0.0, 0.0, 0.0>;
    float n=2*(r.y*r.s + r.x*r.z);
    float p=m*m-n*n;
    if(p>0)
        return < llAtan2(2.0 * (r.x*r.s - r.y*r.z),(-t.x - t.y + t.z + t.s)),
                 llAtan2(n,llSqrt(p)), llAtan2(2.0 * (r.z*r.s - r.x*r.y),( t.x - t.y - t.z + t.s))>;
    else if(n>0)
        return < 0.0, PI_BY_TWO, llAtan2((r.z*r.s + r.x*r.y), 0.5 - t.x - t.z)>;
    else
        return < 0.0, -PI_BY_TWO, llAtan2((r.z*r.s + r.x*r.y), 0.5 - t.x - t.z)>;
}

While converting from Euler to Rotation this is mathematicly accurate this suffers from gimble lock (set y to PI_BY_TWO) expanding it and simplifying it removes the gimble lock. EulerToRotFast is the expanded and simplified form. Ironicly the expanded simplifed form isn't as accurate. EulerToRot compares the answer from both.

Multiplying Quaternions (rotations)
rotation MultiplyRot(rotation a, rotation b)
{
float x = a.s*b.x + a.x*b.s + a.y*b.z - a.z*b.y;
float y = a.s*b.y + a.y*b.s + a.z*b.x - a.x*b.z;
float z = a.s*b.z + a.z*b.s + a.x*b.y - a.y*b.x;
float w = a.s*b.s - a.x*b.x - a.y*b.y - a.z*b.z;
return <x,y,z,w>;
}

v/=2;
rotation k = <0.0, 0.0, llSin(v.z), llCos(v.z)> * <0.0, llSin(v.y), 0.0, llCos(v.y)> * <llSin(v.x), 0.0, 0.0, llCos(v.x)>;

can be worked out to be EulerToRotFast by using quaternions multiplication.

A Euler to rotation function that allows you to tell it the order to use. Using <1,2,3> should return about the same result as llEuler2Rot.
rotation Euler2Rot(vector in, vector order)
{//careful, will gimbal lock.
    in *= 0.5;
    list a = llListSort([
                order.x, <llSin(in.x), 0.0, 0.0, llCos(in.x)>,
                order.y, <0.0, llSin(in.y), 0.0, llCos(in.y)>,
                order.z, <0.0, 0.0, llSin(in.z), llCos(in.z)>],
                2, FALSE);
    in.x = 6;
    rotation b;
    do
        b = llList2Rot(a, ~(integer)(in.x -= 2)) * b;
    while(in.x);
    return b;
}

//Taken from http://forums.secondlife.com/showthread.php?p=536622
rotation slerp( rotation a, rotation b, float f ) {
    return a*llAxisAngle2Rot(llRot2Axis(b/a)*a, llAngleBetween(a, b)*f);
}

//Taken from http://forums.secondlife.com/showthread.php?p=536622
rotation slerp( rotation a, rotation b, float f ) {
    float angleBetween = llAngleBetween(a, b);

    if ( angleBetween > PI )
        angleBetween = angleBetween - TWO_PI;
    return a*llAxisAngle2Rot(llRot2Axis(b/a)*a, angleBetween*f);
}

Copyright status of functions:
All functions on this page are released into public domain and may have been based off other public domain works.

Disclamer:
I do not have a degree in higher mathematics. Please use this as reference, and do plenty of testing. - BW

rotation torot(vector raw)
{
    raw *= DEG_TO_RAD; //convert to radians
    rotation quat = llEuler2Rot(raw); //convert to quaternion
    return(quat);//return rotation
}

raw = Vector to be converted;

This simply returns the rotation value of raw. - Andrew Montagne

Not efficient, better to just inline llEuler2Rot(raw * DEG_TO_RAD) - BW

ScriptLibrary


LibraryRuntimeEnvironment

Runtime Environment

A script to detect changes in an objects runtime environment.

These conditions are detected:

player login (if the object is attached)
script reset (state_entry) on ground
script reset (state_entry) while worn
worn from inventory
worn from ground
dropped while attached
dropped from inventory

This is a toy script that could be usefull for specific applications if tweaked a bit. I got frustrated today trying to make heads or tails of what to do when different combinations of the on_rez(), state_entry(), attach() callbacks were called. I wanted a higher level abstraction of what was going on with the object so I could decide how to react.

I also didn't want my main script to be all uglified trying to figure this stuff out.

This script does the trick. By capturing the state of various indicators we can derive some more meaningfull ideas about what's going on with the script. And with a bit of tweaking, this script could let other scripts know when things changed so they could react appropriately without needing to worry about the low level stuff.

The name could be better, but I couldn't think of anything decent that quite captured this concept. If you have a better name be sure and let me know ;-)

Feel free to use or modify this script in your own objects for any purpose.

// runtime environment // ready jack // 13 Nov 2005 // v1.0

float delay = 3.0;
integer reset;
integer rezzed;
integer worn;
integer removed;

out(string message) {
    llWhisper(0, message);
    //llMessageLinked(LINK_SET, 0, "environment " + message, NULL_KEY);
}

default
{
    state_entry()
    {
        ++reset;
        if (llGetAttached()) ++worn;
        llSetTimerEvent(delay);
    }

    on_rez(integer param)
    {
        ++rezzed;
        llSetTimerEvent(delay);
    }

    attach(key who)
    {
        if (who != NULL_KEY) {
            ++worn;
        } else {
            ++removed;
        }
        llSetTimerEvent(delay);
    }

    timer()
    {
        llSetTimerEvent(0);

        if (reset && !worn)
                out("reset on ground");
        else if (reset && worn)
                out("reset while worn");
        else if (worn && rezzed && removed)
                out("worn from inventory");
        else if (worn && rezzed && !removed)
                out("login");
        else if (worn && !rezzed)
                out("worn from ground");
        else if (!worn && !rezzed && removed)
                out("dropped while attached");
        else if (!worn && rezzed)
                out("dropped from inventory");
        else out("unexpected runtime environment");

        worn = removed = rezzed = reset = 0;
    }
}

ScriptLibrary


LibrarySailboat

Scripts for the Flying Tako Sailboat (30AUG2005)

The Flying Tako 2.1 sailboat scripts are provided below (with the exception of the motor script). 2.1 includes an artificial wind function, which is located in the Sail script. This function only works with the SLSF Race Wind System in my Takos (you would need the channel and key, which I am not providing). With 2.0, I completely revamped the script setup. Instead of one script doing everything, I modularized things to some extent. There is now a "Controls" script that provides the interface to the boat and controls vehicle dynamics, a "Sail" script that calculates sail forces and updates the sail prim's rotation, a "Motor" script that performs the back calculations for the outboard motor, and finally a "Color" script that contains functions for changing hull color, setting the sail transparency, displaying ID, etc. At the bottom of the page is a script that controls the sail prim, and a script that makes the streaming particles for the wind indicator. The boat has a few other miscellaneous scripts for setting sit targets which I won't include.

If anyone is interested in making their own line of boats using these scripts or variants thereof, please IM me in-world.

Eloise Pasteur has written up some FlyingTakoTips, which are also provided as a notecard with the retail version.

Steps for making your own sailboat:
1) You will need to make the following prims:
a. Any kind of prim to put the main script in. This prim will be the center of rotation, and will need to be the last object selected when linking all your objects together. In the Flying Tako, the parent prim is the box in the center of the floor. The main script assumes the x-axis of the parent prim is toward the bow, the y-axis is to port, and the z-axis is straight up.
b. A sail prim (see notes below sail script). In fact, a sail is not required, but then you really wouldn't have a sailboat, would you?
c. Various boat parts, none of which is required for the script to work as long as you remove their respective link messages from the main script.
2) Drop the Controls, Sail, and Colors scripts
3) If you look in the scripts, you will notice that the getLinkNums() function looks for linked objects with specific names (e.g. "windindicator"). You will have to name the linked objects those exact names for the getLinkNums() function to work. I added this function so I wouldn't have to keep track of which part had which link number, and it does away with the need to link objects in a specific order (except for the root prim, of course).
4) You will need to provide your own "raised" and "furled" sail textures, or you can do away with the llSetTexture calls in the sail script.
5) If you are having problems getting your own boat to work, I suggest you strip out everything from the script that involves link messages. None of the other linked prims send information back to the root.

The new "Controls" script (30AUG2005):

//Flying Tako Controls Script
//by Kanker Greenacre
//July 2005

//version settings
integer raceVersion=TRUE; integer practiceVersion=FALSE; integer motorVersion=FALSE;
//integer practiceVersion=TRUE; integer raceVersion=FALSE; integer motorVersion=FALSE;
//integer motorVersion=TRUE; integer practiceVersion=FALSE; integer raceVersion=FALSE;
string boatName="Flying Tako ";
string versionNumber="2.1";

//miscellaneous
key owner;//boat owner
key avatar;//person sitting at the helm
key kanker="7abbf31e-d601-4521-ae52-f7457d6e0012";
integer ownerVersion=TRUE;
integer SAIL_UP=FALSE;
integer permSet=FALSE;
integer HUD_ON=TRUE;
string idStr;
integer racing=FALSE;
integer numTouches=0;
integer sailing=TRUE;
integer motoring=FALSE;
list dataList;
integer phantom=FALSE;

//script module flags
integer CONTROLS_MODULE=1;
integer SAIL_MODULE=2;
integer MOTOR_MODULE=3;
integer COLORIZER_MODULE=4;

//reused math variables
vector eulerRot;
vector currEuler;
rotation quatRot;
rotation currRot;
vector fwdVec;
vector upVec;
vector leftVec;

//linear motion variables
float currSpeed;
vector groundSpeed=ZERO_VECTOR;
float spdFactor=0.0;
float leeway;
float rotSpeed=0.7;
float rotDelta;
vector eulerTurnLeft;
vector eulerTurnRight;
rotation quatTurnLeft=ZERO_ROTATION;
rotation quatTurnRight=ZERO_ROTATION;
float rotTweak=0.8;         //scales boat turning rate (80% for now)
float maxSpeedMotor=10.0;

//heeling variables
float heelAngle;
float heelTorque;
float heelAdd;

//environment
float seaLevel;
float windDir;              //we can set this in the practice version
float windSpeed;            //we can set this in the practice version

//linked parts
integer BOW;
integer HULL;
integer STERN;
integer SAIL;
integer WINDVANE;
integer PORTBENCH;
integer STARBOARDBENCH;
integer RUDDER;
integer TILLER;
integer CENTERBOARD;
integer FLOOR;
integer MAST;
integer TRANSOM;
integer REARFLOOR;
integer HUD;
integer NUMBER1;
integer NUMBER2;
integer PROP;

//wind algorithm parameters
float rate=1;
float avgAng=45;
float avgSpd=3;
float halfArc=0.2;
float spdRng=1;

///////////////////////////////////////////////////////////////////////////////////
//not for public release:
integer codeKey=0;
integer pingChannel=0;
integer secretChannel;
integer todaysChannel() {
    return 0;
}
///////////////////////////////////////////////////////////////////////////////////

//set initial vehicle parameters
setVehicleParams() {
    llSetVehicleType         (VEHICLE_TYPE_BOAT);
    llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME,ZERO_ROTATION);
    llSetVehicleFlags        (VEHICLE_FLAG_NO_DEFLECTION_UP|VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT
                             |VEHICLE_FLAG_LIMIT_MOTOR_UP );
    //linear motion
    llSetVehicleVectorParam  (VEHICLE_LINEAR_FRICTION_TIMESCALE,<50.0,2.0,0.5>);;
    llSetVehicleVectorParam  (VEHICLE_LINEAR_MOTOR_DIRECTION,ZERO_VECTOR);
    llSetVehicleFloatParam   (VEHICLE_LINEAR_MOTOR_TIMESCALE,10.0);
    llSetVehicleFloatParam   (VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE,60);
    llSetVehicleFloatParam   (VEHICLE_LINEAR_DEFLECTION_EFFICIENCY,0.85);
    llSetVehicleFloatParam   (VEHICLE_LINEAR_DEFLECTION_TIMESCALE,1.0);
    //angular motion
    llSetVehicleVectorParam  (VEHICLE_ANGULAR_FRICTION_TIMESCALE,<5,0.1,0.1>);
    llSetVehicleVectorParam  (VEHICLE_ANGULAR_MOTOR_DIRECTION,ZERO_VECTOR);
    llSetVehicleFloatParam   (VEHICLE_ANGULAR_MOTOR_TIMESCALE,0.1);
    llSetVehicleFloatParam   (VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,3);
    llSetVehicleFloatParam   (VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY,1.0);
    llSetVehicleFloatParam   (VEHICLE_ANGULAR_DEFLECTION_TIMESCALE,1.0);
    //vertical attractor
    llSetVehicleFloatParam   (VEHICLE_VERTICAL_ATTRACTION_TIMESCALE,3.0);
    llSetVehicleFloatParam   (VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY,0.8);
    //banking
    llSetVehicleFloatParam   (VEHICLE_BANKING_EFFICIENCY,0.0);
    llSetVehicleFloatParam   (VEHICLE_BANKING_MIX,0.8);
    llSetVehicleFloatParam   (VEHICLE_BANKING_TIMESCALE,1);
    //vertical control
    llSetVehicleFloatParam   (VEHICLE_HOVER_HEIGHT,seaLevel);
    llSetVehicleFloatParam   (VEHICLE_HOVER_EFFICIENCY,2.0);
    llSetVehicleFloatParam   (VEHICLE_HOVER_TIMESCALE,1.0);
    llSetVehicleFloatParam   (VEHICLE_BUOYANCY,1.0);
}

setVehicleParamsMotor() {
    //vertical attractor
    llSetVehicleFloatParam   (VEHICLE_VERTICAL_ATTRACTION_TIMESCALE,1.0);
    llSetVehicleFloatParam   (VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY,0.8);
    //banking
    llSetVehicleFloatParam   (VEHICLE_BANKING_EFFICIENCY,1.0);
    llSetVehicleFloatParam   (VEHICLE_BANKING_MIX,1.0);
    llSetVehicleFloatParam   (VEHICLE_BANKING_TIMESCALE,1);
}

//automatically detect link nums for each named part
getLinkNums() {
    integer i;
    integer linkcount=llGetNumberOfPrims();
    for (i=1;i<=linkcount;++i) {
        string str=llGetLinkName(i);
        if (str=="bow") BOW=i;
        if (str=="hull") HULL=i;
        if (str=="stern") STERN=i;
        if (str=="sail") SAIL=i;
        if (str=="mast") MAST=i;
        if (str=="windindicator") WINDVANE=i;
        if (str=="floor") FLOOR=i;
        if (str=="transom") TRANSOM=i;
        if (str=="tiller") TILLER=i;
        if (str=="rudder") RUDDER=i;
        if (str=="centerboard") CENTERBOARD=i;
        if (str=="starboardbench") STARBOARDBENCH=i;
        if (str=="portbench") PORTBENCH=i;
        if (str=="rearfloor") REARFLOOR=i;
        if (str=="hud") HUD=i;
        if (str=="number1") NUMBER1=i;
        if (str=="number2") NUMBER2=i;
        if (str=="prop") PROP=i;
    }
}

//set camera position for third person view
setCamera() {
    llSetCameraEyeOffset(<-11,0,11>);
    llSetCameraAtOffset(<3,0,3.5>);
}

//figure out where to put boat when it is rezzed
setInitialPosition() {
    vector pos=llGetPos();
    float groundHeight=llGround(ZERO_VECTOR);
    float waterHeight = llWater(ZERO_VECTOR);
    seaLevel=llWater(ZERO_VECTOR);
    upright();
    if (llGetRegionName()=="Gray" && owner==kanker)
    //if (FALSE)
    {
        rotation initRot=llEuler2Rot(<0,0,225*DEG_TO_RAD>);
        //llSetRot(ZERO_ROTATION);
        vector initPos;
        if (raceVersion) initPos=<36.934,142.546,20.1>;//starting position at my dock
        if (practiceVersion) initPos=<40.046,139.434,20.1>;//starting position at my dock - practice version
        if (motorVersion) initPos=<33.728,145.8,20.1>;
        //initPos=<21,150,20.1>;//test position out in the water
        if (llVecDist(llGetPos(),initPos)<2.0) {
            llSetRot(initRot);
            while (llVecDist(llGetPos(),initPos)>.001) llSetPos(initPos);
        }
        else {
            //if over water, set boat height to sealevel + 0.12m
            if (groundHeight<=waterHeight) {
                pos.z=waterHeight+0.12;
                while (llVecDist(llGetPos(),pos)>.001) llSetPos(pos);
            }
        }
    }
    else {
        //if over water, set boat height to sealevel + 0.12m;
        if (groundHeight <= waterHeight) {
            pos.z = waterHeight + 0.12;
            while (llVecDist(llGetPos(),pos)>.001) llSetPos(pos);
        }
    }
}

//set sit target for helmsperson
setSitTarget() {
    llSetSitText("Sit");
    rotation avRot=llEuler2Rot(<0,0,PI_BY_TWO>);
    llSitTarget(<-1.4,-0.45,0.7>,avRot);
    llSetText("",ZERO_VECTOR,1.0);
}

//force boat upright
upright() {
    currRot=llGetRot();
    currEuler=llRot2Euler(currRot);
    leftVec=llRot2Left(currRot);
    heelAngle=llAsin(leftVec.z);
    eulerRot=<-heelAngle,0,0>;
    quatRot=llEuler2Rot(eulerRot);
    llRotLookAt(quatRot*currRot,0.2,0.2);
}

//not sure if i can use this yet
moor() {
    llMessageLinked(LINK_THIS,SAIL_MODULE,"moor",NULL_KEY);
    llMessageLinked(LINK_THIS,MOTOR_MODULE,"off",NULL_KEY);
    llOwnerSay("Mooring.");
    if (permSet) llStopAnimation("helm");
    upright();
    llReleaseControls();
    llSetStatus(STATUS_PHYSICS,FALSE);
    llSetTimerEvent(0);
    currSpeed=0;
    //llResetScript();
}

//reset stuff
startup() {
    owner=llGetOwner();
    llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Z | STATUS_ROTATE_Y,TRUE);
    llSetStatus(STATUS_PHYSICS,FALSE);
    llSetStatus(STATUS_PHANTOM,FALSE);
    llSetStatus(STATUS_BLOCK_GRAB,TRUE);
    llSetTimerEvent(0);
    setInitialPosition();
    setVehicleParams();
    setSitTarget();
    getLinkNums();
    llMessageLinked(HUD,1000,"",NULL_KEY);      //reset HUD
    llMessageLinked(SAIL,1000,"",NULL_KEY);     //reset sail
    llMessageLinked(WINDVANE,1000,"",NULL_KEY); //turn off windindicator
    setCamera();
    currSpeed=0;
    if (practiceVersion) {
        windDir=45*DEG_TO_RAD;
        windSpeed=3;
        llSetLinkColor(MAST,<0.3,0.3,0.3>,ALL_SIDES);
        string tmp=boatName+versionNumber+" Touring";
        llSetObjectName(tmp);
        if (owner==kanker) llMessageLinked(LINK_THIS,COLORIZER_MODULE,"color lemon",NULL_KEY);
    }
    if (raceVersion) {
        idStr=llGetObjectDesc();
        string tmp=boatName+versionNumber+" Racing";
        if (idStr!="(No Description)" & idStr!="") {
            tmp+=" #"+idStr;
            llSetObjectName(tmp);
        }
        else llSetObjectName(tmp);
        llSetLinkColor(MAST,<0.9,0.9,0.9>,ALL_SIDES);
        if (owner==kanker) llMessageLinked(LINK_THIS,COLORIZER_MODULE,"color <0.2,0.6,0.7>",NULL_KEY);
    }
    if (motorVersion) {
        string tmp="Motor Tako "+versionNumber;
        llSetObjectName(tmp);
        llSetLinkColor(MAST,<1.0,1.0,1.0>,ALL_SIDES);
        if (owner==kanker) llMessageLinked(LINK_THIS,COLORIZER_MODULE,"color green",NULL_KEY);
        //llSetLinkColor(MAST,<0.35,0.5,0.6>,ALL_SIDES);
    }
    llMessageLinked(LINK_THIS,SAIL_MODULE,"reset",NULL_KEY);
    llMessageLinked(LINK_THIS,MOTOR_MODULE,"reset",NULL_KEY);
    llMessageLinked(LINK_THIS,COLORIZER_MODULE,"reset",NULL_KEY);
    llListen(0,"",owner,"");
    if (raceVersion) llListen(pingChannel,"",NULL_KEY,"");
    llOwnerSay("Ready.");
    //llOwnerSay("free memory: "+(string)llGetFreeMemory());
}

//////////////////////////////////////////////////////////////////////////////////////////////
//state default///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////

default {

    state_entry() {
        startup();
        llSetStatus(STATUS_BLOCK_GRAB,TRUE);
    }

    on_rez(integer param) {
        //reset();
        llResetScript();
    }

    changed(integer change) {
        avatar=llAvatarOnSitTarget();
        if (change & CHANGED_LINK) {
            if (avatar==NULL_KEY) {
                if (!(llGetAgentInfo(owner) & AGENT_ON_OBJECT)) {
                    llMessageLinked(WINDVANE,1000,"",NULL_KEY);//turn off windindicator
                    if (SAIL_UP) {
                        llOwnerSay("Mainsail lowered.");
                        llMessageLinked(LINK_THIS,SAIL_MODULE,"changed",NULL_KEY);
                    }
                    if (permSet) llStopAnimation("helm");
                    if (permSet) llReleaseControls();
                    permSet=FALSE;
                    llResetScript();
                }
           }
           else {
                if (ownerVersion && avatar!=owner) llWhisper(0,"Only the owner can operate this boat.");
                else if ((llGetAgentInfo(owner) & AGENT_ON_OBJECT)) {
                    //llMessageLinked(WINDVANE,4,"",NULL_KEY);//turn on windindicator
                    llWhisper(0,"Say 'notecard' for instructions or 'raise' to start sailing.");
                    if (llAvatarOnSitTarget()==owner) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION);
                }
            }
        }
    }

    listen(integer channel, string name, key id, string msg) {
        if (channel==0) {
            if (owner==id & llAvatarOnSitTarget()==owner) {
                if (llGetAgentInfo(owner) & AGENT_ON_OBJECT) {
                    if (llGetSubString(msg,0,4)=="sheet") {
                        llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                    }
                    else if (msg=="raise") {
                        sailing=TRUE;
                        motoring=FALSE;
                        if (!permSet) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION);
                        permSet=TRUE;
                        llStartAnimation("helm");
                        llSetStatus(STATUS_PHYSICS,TRUE);
                        llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                        llMessageLinked(LINK_THIS,MOTOR_MODULE,"off",NULL_KEY);
                        //if (practiceVersion) llMessageLinked(WINDVANE,2000+llRound(windDir),"",NULL_KEY);
                        //else llMessageLinked(WINDVANE,4,"",NULL_KEY);

                    }
                    else if (msg=="lower") llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                    else if (llGetSubString(msg,0,4)=="color") llMessageLinked(LINK_THIS,COLORIZER_MODULE,msg,NULL_KEY);
                    else if (msg=="moor") {
                        moor();
                        llResetScript();
                    }
                    else if (llGetSubString(msg,0,3)=="vane") {
                        if (llGetSubString(msg,5,-1)=="off") llMessageLinked(WINDVANE,1000,"",NULL_KEY);
                        else if (llGetSubString(msg,5,-1)=="on") {
                            if (practiceVersion) llMessageLinked(WINDVANE,2000+llRound(windDir),"",NULL_KEY);
                            else llMessageLinked(WINDVANE,4,"",NULL_KEY);
                        }
                    }
                    else if (llGetSubString(msg,0,3)=="anim") {
                        if (llGetSubString(msg,5,-1)=="off") {
                            if (permSet) llStopAnimation("helm");
                        }
                        else if (llGetSubString(msg,5,-1)=="on") {
                            if (permSet) llStartAnimation("helm");
                        }
                    }
                    else if (llGetSubString(msg,0,7)=="wind dir") {
                        if (!raceVersion) {
                            windDir=(float)llGetSubString(msg,8,-1);
                            string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate;
                            llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY);
                        }
                    }
                    else if (llGetSubString(msg,0,7)=="wind spd") {
                        if (!raceVersion) {
                            windSpeed=(float)llGetSubString(msg,8,-1);
                            string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate;
                            llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY);
                        }
                    }
                    else if (llGetSubString(msg,0,4)=="dir+-") {
                        if (!raceVersion) {
                            halfArc=(integer)llGetSubString(msg,5,-1);
                            string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate;
                            llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY);
                        }
                    }
                    else if (llGetSubString(msg,0,4)=="spd+-") {
                        if (!raceVersion) {
                            spdRng=(float)llGetSubString(msg,5,-1);
                            string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate;
                            llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY);
                        }
                    }
                    else if (llGetSubString(msg,0,3)=="rate") {
                        if (!raceVersion) {
                            rate=(float)llGetSubString(msg,4,-1);
                            string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate;
                            llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY);
                        }
                    }
                    else if (llGetSubString(msg,0,1)=="id") {
                        if (raceVersion) {
                            llMessageLinked(LINK_THIS,COLORIZER_MODULE,msg,NULL_KEY);
                            if (llGetSubString(msg,3,-1)!="off") {
                                idStr=llGetSubString(msg,3,-1);
                                string tmp=boatName+" #"+idStr;
                                llSetObjectName(tmp);
                                llSetObjectDesc(idStr);
                            }
                        }
                    }
                    else if (msg=="hud") {
                        if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                        else llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY);
                    }
                    else if (llGetSubString(msg,0,4)=="alpha") llMessageLinked(LINK_THIS,COLORIZER_MODULE,msg,NULL_KEY);
                    else if (msg=="motor") {
                        if (motorVersion) {
                            if (!permSet) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION);
                            permSet=TRUE;
                            llStartAnimation("helm");
                            llSetStatus(STATUS_PHYSICS,TRUE);
                            llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                            llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY);
                            motoring=TRUE;
                            sailing=FALSE;
                            llMessageLinked(WINDVANE,1000,"",NULL_KEY); //turn off wind indicator
                            setVehicleParamsMotor();
                            llOwnerSay("Motoring");
                        }
                        else llOwnerSay("No motor!");
                    }
                    else if (msg=="off") {
                        if (motorVersion) {
                            sailing=TRUE;
                            motoring=FALSE;
                            setVehicleParams();
                            if (practiceVersion) llMessageLinked(WINDVANE,2000+llRound(windDir),"",NULL_KEY);
                            else llMessageLinked(WINDVANE,4,"",NULL_KEY);
                            llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                            llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY);
                        }
                    }
                    else if (msg=="knots") {
                        llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                        llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY);
                    }
                    if (msg=="register") {
                        if (raceVersion) {
                            integer chan=todaysChannel();
                            llShout(pingChannel,"ping");
                            llSleep(2.0);
                            string regPacket=idStr+","+(string)llGetOwner();
                            llShout(chan,regPacket);
                        }
                        else {
                            llSay(0,"Your boat is not race legal and cannot be registered.");
                        }
                    }
                    else if (msg=="pp") {
                        if (!raceVersion) {
                            if (!phantom) {
                                llOwnerSay("phantom ON");
                                llSetStatus(STATUS_PHANTOM,TRUE);
                                phantom=TRUE;
                            }
                            else {
                                llOwnerSay("phantom OFF");
                                llSetStatus(STATUS_PHANTOM,FALSE);
                                phantom=FALSE;
                            }
                        }
                    }
                    else if (msg=="practice") {
                        if (!raceVersion) llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY);
                    }
                }
            }
            if (msg=="notecard") {
                llGiveInventory(id,"Sailing the Tako");
                llGiveInventory(id,"Instructions");
            }
        }
        else if (channel==pingChannel) {
            secretChannel=todaysChannel();
            llListen(secretChannel,"",NULL_KEY,"");
        }
        else if (channel==secretChannel) {
            string str="windparams "+msg;
            llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY);
        }
    }

    run_time_permissions(integer perms) {
        if (perms & (PERMISSION_TAKE_CONTROLS)) {
            llTakeControls(CONTROL_RIGHT | CONTROL_LEFT | CONTROL_ROT_RIGHT |
            CONTROL_ROT_LEFT | CONTROL_FWD | CONTROL_BACK | CONTROL_DOWN | CONTROL_UP,TRUE,FALSE);
            permSet=TRUE;
        }
    }

    control(key id, integer held, integer change) {
        //turning controls
        if ( (change & held & CONTROL_LEFT) || (held & CONTROL_LEFT) || (change & held & CONTROL_ROT_LEFT) || (held & CONTROL_ROT_LEFT) ) {
            if (sailing) llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<rotSpeed/2.0,0.0,rotSpeed>);
            else llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<-rotSpeed,0.0,rotSpeed/1.5>);
        }
        else if ( (change & held & CONTROL_RIGHT) || (held & CONTROL_RIGHT) || (change & held & CONTROL_ROT_RIGHT) || (held & CONTROL_ROT_RIGHT) ) {
            if (sailing) llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<-rotSpeed/2.0,0.0,-rotSpeed>);
            else llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<rotSpeed,0.0,-rotSpeed/1.5>);
        }
        else if ( (change & ~held & CONTROL_LEFT) || (change & ~held & CONTROL_ROT_LEFT) ) {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<0.0,0.0,0.0>);
        }
        else if ( (change & ~held & CONTROL_RIGHT) || (change & ~held & CONTROL_ROT_RIGHT) ) {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<0.0,0.0,0.0>);
        }
        //sail/throttle controls
        if ( (held & CONTROL_FWD) && (held & CONTROL_UP) ) {
            if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"fwd_big",NULL_KEY);
            else {
                currSpeed+=0.3;
                if (currSpeed>maxSpeedMotor) currSpeed=maxSpeedMotor;
                llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,0,0>);
                llMessageLinked(LINK_THIS,MOTOR_MODULE,"fwd",NULL_KEY);
            }
        }
        else if ( (held & CONTROL_FWD) || (change & held & CONTROL_FWD) ) {
            if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"fwd_small",NULL_KEY);
            else {
                currSpeed+=0.3;
                if (currSpeed>maxSpeedMotor) currSpeed=maxSpeedMotor;
                llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,0,0>);
                llMessageLinked(LINK_THIS,MOTOR_MODULE,"fwd",NULL_KEY);
            }
        }
        else if ( (held & CONTROL_BACK) && (held & CONTROL_UP) ) {
            if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"back_big",NULL_KEY);
            else {
                currSpeed-=0.3;
                if (currSpeed<-2) currSpeed=-2;
                llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,0,0>);
                llMessageLinked(LINK_THIS,MOTOR_MODULE,"back",NULL_KEY);
            }
        }
        else if ( (held & CONTROL_BACK) || (change & held & CONTROL_BACK) ) {
            if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"back_small",NULL_KEY);
            else {
                currSpeed-=0.3;
                if (currSpeed<-2) currSpeed=-2;
                llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,0,0>);
                llMessageLinked(LINK_THIS,MOTOR_MODULE,"back",NULL_KEY);
            }
        }
        //motor to idle
        if ( (change & held & CONTROL_DOWN ) ) {
            if (motoring) {
                currSpeed=0.0;
                llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<0,0,0>);
                llMessageLinked(LINK_THIS,MOTOR_MODULE,"idle",NULL_KEY);
            }
        }
    }

    link_message(integer from,integer to,string str,key id) {
        if (to==CONTROLS_MODULE) {
            dataList=llCSV2List(str);
            currSpeed=llList2Float(dataList,0);
            leeway=llList2Float(dataList,1);
            rotSpeed=rotTweak*llList2Float(dataList,2);
            heelTorque=5*llList2Float(dataList,3);
            llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,leeway,0>);
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<heelTorque,0.0,0.0>);
        }
    }

}

The new "Sail" script (goes in the root prim):

//Flying Tako Sail Script
//by Kanker Greenacre
//July 2005

//version settings
integer raceVersion=TRUE; integer practiceVersion=FALSE; integer motorVersion=FALSE; integer olympicVersion=FALSE;
//integer practiceVersion=TRUE; integer raceVersion=FALSE; integer motorVersion=FALSE; integer olympicVersion=FALSE;
//integer olympicVersion=TRUE; integer motorVersion=FALSE; integer practiceVersion=FALSE; integer raceVersion=FALSE;
string boatName="Flying Tako ";
string versionNumber="2.1";

//script module flags
integer CONTROLS_MODULE=1;
integer SAIL_MODULE=2;
integer MOTOR_MODULE=3;
integer COLORIZER_MODULE=4;

//environment
vector wind;
float windAngle;
float absWindAngle;
float depth;
float seaLevel;

//reused math variables
vector eulerRot;
vector currEuler;
rotation quatRot;
rotation currRot;

//boat variables
float zRotAngle;
vector fwdVec;
vector upVec;
vector leftVec;
float compass;

//heeling variables
float heelAngle;
float heelTorque;
float heelAdd;

//linear motion variables
float currSpeed;
vector groundSpeed=ZERO_VECTOR;
float spdFactor=0.0;
float leeway;

//angular motion variables
float rotSpeed;
float rotDelta;
vector eulerTurnLeft;
vector eulerTurnRight;
rotation quatTurnLeft=ZERO_ROTATION;
rotation quatTurnRight=ZERO_ROTATION;

//sail variables
integer boomAngle;
integer currBoomAngle=0;
integer delta;
integer incr;
float optBoomAngle;
float trimFactor;

//performance constants
float timerFreq=1.5;        //timer frequency, seconds
float maxSpeed=10.0;        //maximum allowed speed
integer sheetAngle=5;       //initial sheet angle setting
integer ironsAngle=35;      //this is as close as the boat can sail to the wind
float slowingFactor=0.7;    //speed drops by this factor every timerFreq seconds if sailing into the wind
float leewayTweak=0.5;      //scales leeway (0=no leeway)
float speedTweak=1.0;       //scales speed
float rotTweak=5.0;         //scales boat turning rate
float heelTweak=0.5;        //scales amount of heeling
float maxWindSpeed=14.0;    //used for heeling calculation
float windDir;              //we can set this in the practice version
float windSpeed;            //we can set this in the practice version

//motor settings
integer fromMotoring=FALSE;
float maxSpeedMotor=10.0;
float throttle=0;

//miscellaneous
string dataString;
key owner;//boat owner
key avatar;//person sitting at the helm
key kanker="7abbf31e-d601-4521-ae52-f7457d6e0012";
integer ownerVersion=TRUE;
integer SAIL_UP=FALSE;
integer permSet=FALSE;
integer HUD_ON=TRUE;
string idStr;
integer racing=FALSE;
integer numTouches=0;
integer sailing=TRUE;
integer motoring=FALSE;
float mpsToKts=1.944;
float convert=1;
string units=" m/s";
integer showKnots=FALSE;
float time;
float offset;
float theta;
integer practice=FALSE;

//linked parts
integer BOW;
integer HULL;
integer STERN;
integer SAIL;
integer WINDVANE;
integer PORTBENCH;
integer STARBOARDBENCH;
integer RUDDER;
integer TILLER;
integer CENTERBOARD;
integer FLOOR;
integer MAST;
integer TRANSOM;
integer REARFLOOR;
integer HUD;
integer NUMBER1;
integer NUMBER2;
integer PROP;

//wind algorithm parameters
float angRate=10;
float avgAng=0;
float avgSpd=5;
float halfArc=0.2;
float spdRng=3;
integer raceWindOn=FALSE;

//prevailing wind algorithm
calcWindDir() {
    time=llGetTimeOfDay();
    theta=time/14400*TWO_PI*angRate;
    offset=llSin(theta)*llSin(theta*2)*llSin(theta*9)*llCos(theta*4);
    windDir=avgAng+halfArc*offset;
    llMessageLinked(WINDVANE,2000+llRound(windDir*RAD_TO_DEG),"",NULL_KEY);
}

calcWindSpd() {
    offset=llSin(theta)*llSin(theta*4)+llSin(theta*13)/3;
    windSpeed=avgSpd+spdRng*offset;
    if (windSpeed<0) windSpeed=0;
}

//calculate wind relative to boat
calcWindAngle() {
    if (raceWindOn) {
        calcWindDir();
        calcWindSpd();
    }
    else {
        wind=llWind(ZERO_VECTOR);
        windDir=llAtan2(-wind.y,-wind.x);//direction wind is blowing FROM
        windSpeed=llVecMag(wind);
    }
    currRot=llGetRot();
    currEuler=llRot2Euler(currRot);
    zRotAngle=currEuler.z;//boat heading
    leftVec=llRot2Left(currRot);
    windAngle=windDir-zRotAngle;
    while (windAngle>PI) windAngle-=TWO_PI;//bw -PI and PI
    while (windAngle<-PI) windAngle+=TWO_PI;//bw -PI and PI
}

//calculate heel angle based on wind and sail settings
calcHeelAngle() {
    heelAngle=llAsin(leftVec.z);
    if (SAIL_UP)
        if (llFabs(windAngle+boomAngle)>3*DEG_TO_RAD)
            heelTorque=SAIL_UP*llSin(windAngle)*llCos(heelAngle)*PI_BY_TWO*(windSpeed/maxWindSpeed)*llCos(boomAngle*DEG_TO_RAD)*heelTweak;
        else heelTorque=0;
    else heelTorque=0;
    heelAdd=heelTorque-heelAngle;
    eulerRot=<heelAdd,0,0>;
    quatRot=llEuler2Rot(eulerRot);
}

//calculate angle of sail (or boom) based on sheet setting and the wind
calcBoomDelta() {
    boomAngle=sheetAngle;
    if (boomAngle>llFabs(windAngle*RAD_TO_DEG)) boomAngle=llRound(llFabs(windAngle*RAD_TO_DEG));
    if (windAngle<0) boomAngle*=-1;
    delta=boomAngle-currBoomAngle;
    currBoomAngle=boomAngle;
    llMessageLinked(SAIL,delta,"",NULL_KEY);//tell sail to rotate by delta
}

//calculate boat speed
calcSpeed() {
    groundSpeed=llGetVel();
    absWindAngle=llFabs(windAngle);
    if (llFabs(absWindAngle*RAD_TO_DEG-llFabs(boomAngle))<10) trimFactor=0;
    else {
        optBoomAngle=0.5*absWindAngle*RAD_TO_DEG;
        trimFactor=(90.-llFabs(optBoomAngle-llFabs(boomAngle)))/90.;
    }
    if (absWindAngle<ironsAngle*DEG_TO_RAD) currSpeed*=slowingFactor;
    else {
        if (SAIL_UP) {
            currSpeed=speedTweak*(llCos(windAngle/2.)+0.5)*windSpeed*trimFactor;
            if (currSpeed>maxSpeed) currSpeed=maxSpeed;
        }
        else currSpeed*=0.8;
    }
}

//calculate leeway (lateral drift) due to wind
calcLeeway() {
    leeway=SAIL_UP*-llSin(windAngle)*llSin(heelAngle)*windSpeed*leewayTweak;
}

//calculate turning rate based on current speed
calcTurnRate() {
    spdFactor=llVecMag(groundSpeed)/(maxSpeed);
    //rotSpeed=0.4-(spdFactor)*0.4;
    rotSpeed=0.5+(spdFactor)/2.0;
}

//gets depth of water below boat
calcDepth() {
    depth=llWater(ZERO_VECTOR)-llGround(ZERO_VECTOR);
}

//update heads-up display (in 1st and 3rd person view)
updateHUD() {
    compass=PI_BY_TWO-zRotAngle;
    while (compass<0) compass+=TWO_PI;
    calcDepth();
    dataString ="Heading:          "     +(string)((integer)(compass*RAD_TO_DEG))+" deg\n";
    dataString+="Wind Angle:       "  +(string)((integer)(windAngle*RAD_TO_DEG))+" deg\n";
    dataString+="Wind Speed:   "  +llGetSubString((string)(windSpeed*convert),0,3)+units+"\n";
    dataString+="Ground Speed: "+llGetSubString((string)(llVecMag(groundSpeed*convert)),0,3)+units+"\n";
    dataString+="Depth:             "       +llGetSubString((string)depth,0,3)+" m\n";
    dataString+="Sail Angle:         "  +(string)(-boomAngle)+" deg\n";
    dataString+="Sheet Angle:      " +(string)sheetAngle+" deg\n\n\n\n";
    llSetText(dataString,ZERO_VECTOR,1.0);
    llMessageLinked(HUD,1001,dataString,NULL_KEY);//sends data to HUD prim above and behind the boat
}

//automatically detect link nums for each named part
getLinkNums() {
    integer i;
    integer linkcount=llGetNumberOfPrims();
    for (i=1;i<=linkcount;++i) {
        string str=llGetLinkName(i);
        if (str=="bow") BOW=i;
        if (str=="hull") HULL=i;
        if (str=="stern") STERN=i;
        if (str=="sail") SAIL=i;
        if (str=="mast") MAST=i;
        if (str=="windindicator") WINDVANE=i;
        if (str=="floor") FLOOR=i;
        if (str=="transom") TRANSOM=i;
        if (str=="tiller") TILLER=i;
        if (str=="rudder") RUDDER=i;
        if (str=="centerboard") CENTERBOARD=i;
        if (str=="starboardbench") STARBOARDBENCH=i;
        if (str=="portbench") PORTBENCH=i;
        if (str=="rearfloor") REARFLOOR=i;
        if (str=="hud") HUD=i;
        if (str=="number1") NUMBER1=i;
        if (str=="number2") NUMBER2=i;
        if (str=="prop") PROP=i;
    }
}

//raise sail: start timer
raiseSail() {
    SAIL_UP=TRUE;
    llOwnerSay("Raising mainsail.");
    llMessageLinked(SAIL,1002,"",NULL_KEY);
    llMessageLinked(WINDVANE,4,"",NULL_KEY);
    llSetTimerEvent(timerFreq);
}

//lower sail but leave physics on
lowerSail() {
    llOwnerSay("Lowering mainsail.");
    llMessageLinked(SAIL,1000,"",NULL_KEY);//lower sail w/ reset
    currBoomAngle=0;
    sheetAngle=5;
    SAIL_UP=FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//state default///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////

default {

    state_entry() {
        getLinkNums();
        llSetText("",ZERO_VECTOR,1.0);
        llMessageLinked(HUD,1001,"",NULL_KEY);
        avgAng=300*DEG_TO_RAD;
        halfArc=18*DEG_TO_RAD;
    }

    on_rez(integer param) {
        raceWindOn=FALSE;
    }

    link_message(integer from,integer to,string msg,key id) {
        if (from==LINK_ROOT && to==SAIL_MODULE) {
            if (msg=="fwd_small") {
                sheetAngle+=2;
                if (sheetAngle>90) sheetAngle=90;
            }
            else if (msg=="back_small") {
                sheetAngle-=2;
                if (sheetAngle<5) sheetAngle=5;
            }
            else if (msg=="fwd_big") {
                sheetAngle+=7;
                if (sheetAngle>90) sheetAngle=90;
            }
            else if (msg=="back_big") {
                sheetAngle-=7;
                if (sheetAngle<5) sheetAngle=5;
            }
            else if (llGetSubString(msg,0,4)=="sheet") {
                incr=(integer)llDeleteSubString(msg,0,4);
                sheetAngle+=incr;
                if (sheetAngle>90) sheetAngle=90;
            }
            else if (msg=="raise") raiseSail();
            else if (msg=="lower") lowerSail();
            else if (msg=="moor") {
                llSetTimerEvent(0);
                if (SAIL_UP) lowerSail();
            }
            else if (llGetSubString(msg,0,9)=="windparams") {
                if (!practiceVersion | practice) {
                    list in=llParseString2List(msg,[" "],[""]);
                    avgAng=llList2Integer(in,1)*DEG_TO_RAD;
                    avgSpd=llList2Integer(in,2);
                    if (llGetListLength(in)==6) {
                        halfArc=llList2Integer(in,3)*DEG_TO_RAD;
                        spdRng=llList2Float(in,4);
                        angRate=llList2Integer(in,5)*10.0;
                    }
                    raceWindOn=TRUE;
                }
            }
            else if (msg=="hud") {
                if (HUD_ON) {
                    HUD_ON=FALSE;
                    llSetText("",ZERO_VECTOR,1.0);
                    llMessageLinked(HUD,1001,"",NULL_KEY);
                }
                else HUD_ON=TRUE;
            }
            else if (msg=="motor") {
                if (SAIL_UP) lowerSail();
                llSetTimerEvent(0);
                motoring=TRUE;
                sailing=FALSE;
            }
            else if (msg=="off") {
                llSetTimerEvent(timerFreq);
                motoring=FALSE;
                sailing=TRUE;
            }
            else if (msg=="reset") llResetScript();
            else if (msg=="knots") {
                if (showKnots==FALSE) {
                    convert=mpsToKts;
                    units=" kts";
                    showKnots==TRUE;
                }
                else {
                    convert=1;
                    units=" m/s";
                    showKnots=FALSE;
                }
            }
            else if (msg=="practice") {
                if (practice) {
                    llOwnerSay("practice wind OFF");
                    practice=FALSE;
                    raceWindOn=FALSE;
                }
                else {
                    llOwnerSay("practice wind ON");
                    practice=TRUE;
                    raceWindOn=TRUE;
                }
            }
        }
    }

    timer() {
        calcWindAngle();
        if (SAIL_UP) calcBoomDelta();
        calcHeelAngle();
        calcSpeed();
        calcLeeway();
        calcTurnRate();
        if (HUD_ON) updateHUD();
        dataString=(string)currSpeed+","+(string)leeway+","+(string)rotSpeed+","+(string)heelTorque;
        llMessageLinked(LINK_THIS,CONTROLS_MODULE,dataString,NULL_KEY);
    }

}

The new "Colors" script (goes in root prim):

//Flying Tako Colors Script
//by Kanker Greenacre
//July 2005

//miscellaneous
string dataString;
key owner;//boat owner
key avatar;//person sitting at the helm
key kanker="7abbf31e-d601-4521-ae52-f7457d6e0012";
vector currentColor=<1,1,1>;

//script module flags
integer CONTROLS_MODULE=1;
integer SAIL_MODULE=2;
integer MOTOR_MODULE=3;
integer COLORIZER_MODULE=4;

//linked parts
integer BOW;
integer HULL;
integer STERN;
integer SAIL;
integer NUMBER1;
integer NUMBER2;

//select colors
list colorNames=["puke","red","baby blue","lemon","blah","hot hot pink","blue","gray",
                 "beige","schwarz","tangerine","orenji","eloise","white","trep","tan","green"];
list colorVectors=[<149,159,62>,<146,29,8>,<62,115,187>,<237,234,118>,<171,175,111>,<244,2,91>,<5,35,62>,<113,129,134>,
                   <226,182,138>,<0,0,0>,<232,92,23>,<237,115,17>,<204,0,204>,<255,255,255>,<63,95,127>,<128,64,0>,<84,126,2>];

//set hull color to one from the list
setColor(string color) {
    integer colorIndex=llListFindList(colorNames,[color]);
    vector rgb=llList2Vector(colorVectors,colorIndex)/256.;
    setColorVec(rgb);
}

//set hull color from input rgb vector
setColorVec(vector rgb) {
    currentColor=rgb;
    llSetLinkColor(BOW,rgb,ALL_SIDES);
    llSetLinkColor(HULL,rgb,ALL_SIDES);
    llSetLinkColor(STERN,rgb,ALL_SIDES);
    llSetLinkColor(NUMBER1,rgb,ALL_SIDES);
    llSetLinkColor(NUMBER2,rgb,ALL_SIDES);
}

//new random color for the hull
colorize() {
    vector color;
    color.x=(llFrand(256)/255.);
    color.y=llFrand(256)/255.;
    color.z=llFrand(256)/255.;
    setColorVec(color);
}

//automatically detect link nums for each named part
getLinkNums() {
    integer i;
    integer linkcount=llGetNumberOfPrims();
    for (i=1;i<=linkcount;++i) {
        string str=llGetLinkName(i);
        if (str=="bow") BOW=i;
        if (str=="hull") HULL=i;
        if (str=="stern") STERN=i;
        if (str=="sail") SAIL=i;
        if (str=="number1") NUMBER1=i;
        if (str=="number2") NUMBER2=i;
    }
}

default {

    state_entry() {
        getLinkNums();
    }

    on_rez(integer param) {
        llResetScript();
    }

    link_message(integer from,integer to,string msg,key id) {
        if (from==LINK_ROOT && to==COLORIZER_MODULE) {
            if (llGetSubString(msg,0,4)=="color") {
                if (llGetSubString(msg,6,-1)=="random") colorize();
                else if (llGetSubString(msg,6,6)=="<") setColorVec((vector)llGetSubString(msg,6,-1));
                else setColor(llGetSubString(msg,6,-1));
            }
            else if (llGetSubString(msg,0,4)=="alpha") {
                integer num=(integer)llGetSubString(msg,6,-1);
                num+=2000;
                llMessageLinked(SAIL,num,"",NULL_KEY);
            }
            else if (msg=="reset") llResetScript();
            else if (llGetSubString(msg,0,1)=="id") {
                if (llGetSubString(msg,3,-1)=="off") {
                    llMessageLinked(NUMBER1,1001,"",NULL_KEY);
                    llMessageLinked(NUMBER2,1001,"",NULL_KEY);
                }
                else {
                    string idStr=llGetSubString(msg,3,-1);
                    llMessageLinked(NUMBER1,1000,idStr,NULL_KEY);
                    llMessageLinked(NUMBER2,1000,idStr,NULL_KEY);
                    llSetLinkColor(NUMBER1,currentColor,ALL_SIDES);
                    llSetLinkColor(NUMBER2,currentColor,ALL_SIDES);
                }
            }
        }
    }

}

Here is the other sail script for rotating the sail and setting textures (goes in the sail prim):

rotation childRot;
rotation quatRot;
rotation initRot;
vector eulerRot;

integer i;
key defaultFurledKey="f859c1a2-8fa4-5a6d-aa8b-15b81ef303a4";
key defaultRaisedRightKey="4922b72e-6c1c-3808-930e-3531b7af07c4";
key defaultRaisedLeftKey="4922b72e-6c1c-3808-930e-3531b7af07c4";
key furledKey;
key raisedRightKey;
key raisedLeftKey;

integer foundFurled;
integer foundRaisedRight;
integer foundRaisedLeft;

lower() {
    llSetLocalRot(initRot);
    llSetTexture(furledKey,1);//left side
    llSetTexture(furledKey,3);//right side
}

raise () {
    llSetTexture(raisedLeftKey,1);
    llSetTexture(raisedRightKey,3);
}

reset() {
    llSetLocalRot(initRot);
    llSetAlpha(1.0,ALL_SIDES);
}

default {

    state_entry() {
        furledKey=defaultFurledKey;
        raisedRightKey=defaultRaisedRightKey;
        raisedLeftKey=defaultRaisedLeftKey;
        llSetObjectName("sail");
        initRot=llEuler2Rot(<0,0,PI_BY_TWO>);
        llSetLocalRot(initRot);
        llSetTexture("transparent",ALL_SIDES);
        llSetTexture(furledKey,1);//left side
        llSetTexture(furledKey,3);//right side
        llSetAlpha(1.0,ALL_SIDES);
        llSetStatus(STATUS_PHANTOM,FALSE);
    }

    on_rez(integer param) {
        llAllowInventoryDrop(TRUE);
    }

    link_message(integer sender,integer num,string str,key id) {
        if (num==1000) {//reset
            lower();
            reset();
        }
        else if (num==1001) {//lower without reset
            lower();
        }
        else if (num==1002) {//raise sail
            raise();
        }
        else if (num>1999) {//change alpha setting
            float alpha=(float)num;
            alpha-=2000;
            alpha/=100.;
            llSetAlpha(alpha,1);
            llSetAlpha(alpha,3);
        }
        else {//rotate sail, CCW is positive
            childRot=llGetLocalRot();
            eulerRot=<0,0,num*DEG_TO_RAD>;
            quatRot=llEuler2Rot(eulerRot);
            llSetLocalRot(quatRot*childRot);
        }
    }

    changed(integer change){
        if( (change & CHANGED_INVENTORY) | (change & CHANGED_ALLOWED_DROP) ) {
            integer numInvTex=llGetInventoryNumber(INVENTORY_TEXTURE);
            foundFurled=FALSE;
            foundRaisedRight=FALSE;
            foundRaisedLeft=FALSE;
            for (i=0;i<numInvTex;++i) {
                string name=llGetInventoryName(INVENTORY_TEXTURE,i);
                if (name=="sail_furled") {
                    furledKey=llGetInventoryKey(name);
                    foundFurled=TRUE;
                    llOwnerSay("New furled sail texture: "+(string)furledKey);
                    lower();
                }
                else if (name=="sail_raised_right") {
                    raisedRightKey=llGetInventoryKey(name);
                    foundRaisedRight=TRUE;
                    llOwnerSay("New raised sail texture: "+(string)raisedRightKey);
                    raise();
                }
                else if (name=="sail_raised_left") {
                    raisedLeftKey=llGetInventoryKey(name);
                    foundRaisedLeft=TRUE;
                    llOwnerSay("New raised sail texture: "+(string)raisedLeftKey);
                    raise();
                }
            }
            if (!foundFurled) furledKey=defaultFurledKey;
            if (!foundRaisedRight) raisedRightKey=defaultRaisedRightKey;
            if (!foundRaisedLeft) raisedLeftKey=defaultRaisedLeftKey;
        }
    }

}

A word or two about the "Flying Tako" sail (an Ezhar Fairlight-inspired design). The sail is a prim box with the following settings: size x: 0.01, size y: 7.0, size z: 7.0, cut begin: 0.37, cut end: 0.87, top size x: 1.0, top size y: 1.0. By cutting the box this way, we have created a flat shape with an axis of rotation along the side, much like Ezhar's Deluxe Door Script. This greatly simplifies script rotations, as you may have noticed in the sail script.

Wind indicator script (goes in ball at top of mast, or wherever you want to put it):

vector windDir;
list particleVars=[PSYS_PART_FLAGS,
            PSYS_PART_FOLLOW_SRC_MASK,
            PSYS_SRC_PATTERN,
            PSYS_SRC_PATTERN_DROP,
            PSYS_PART_MAX_AGE,3.0,
            PSYS_PART_START_COLOR,<1,1,1>,
            PSYS_PART_START_ALPHA,1.0,
            PSYS_PART_START_SCALE,<.3,.3,.3>,
            PSYS_SRC_BURST_RATE,0.1,
            PSYS_SRC_BURST_SPEED_MIN,0.5,
            PSYS_SRC_BURST_SPEED_MAX,0.5,
            PSYS_SRC_BURST_PART_COUNT,1];
list tmpVars;
integer fixed=FALSE;

setParticles() {
    windDir=llWind(<0,0,0>);
    windDir*=0.15;
    tmpVars=particleVars;
    tmpVars+=[PSYS_SRC_ACCEL,windDir];
    llParticleSystem(tmpVars);
}

default {
    state_entry() {
        llSetObjectName("windindicator");
        llParticleSystem([]);
        llSetTimerEvent(4.0);
    }

    timer() {
        setParticles();
    }

    link_message(integer sender_num, integer num, string str, key id) {
        if (num==1000) {
            llParticleSystem([]);
            llSetTimerEvent(0);
        }
        else if (num>1000) {
            llSetTimerEvent(0);
            num-=2000;
            float windAng=num*DEG_TO_RAD;
            windAng+=PI;
            windDir=<llCos(windAng),llSin(windAng),0>;
            windDir*=0.5;
            tmpVars=particleVars;
            tmpVars+=[PSYS_SRC_ACCEL,windDir];
            llParticleSystem(tmpVars);
            fixed=TRUE;
        }
        else {
            llSetTimerEvent((float)num);
            fixed=FALSE;
        }
    }
}


LibrarySelfUpgradingScript

Self Upgrading Script

In some of my projects, I've had a number of prims that each needed the same script. When changes needed to be made to the script, however, it became rather annoying to go through every object, remove the existing script, and copy in the new one.

One way to remedy the problem is with a "manager" object and scripts to manage the numerous prims that must be keep up-to-date, but personally I wasn't thrilled with that solution, especially when I was in the habit of regularly linking and unlinking all the prims. Instead, I wanted a solution where I could drop the new script from inventory onto each of the required prims and have them upgrade themselves (by removing the old version).

So here is the solution I wrote. This code is added to the script to be kept upgraded; it runs as soon as it is dropped on the target prim.

upgrade() {
    // Simple upgrade script; remove all other scripts with the same name as
    // myself.  ASSUMPTION: No spaces in the name (ie, the only space will be
    // between the core name and the auto-generated "version" number).

    // get my name
    string me   = llGetScriptName();  // full name of this script
    list parts  = llParseString2List(me, [" "], []);
    string name = llList2String(parts, 0);   // name without any trailing number

    // search all scripts in inventory for those starting with the same name
    integer n = llGetInventoryNumber(INVENTORY_SCRIPT);
    while (n-- > 0) {
        string item = llGetInventoryName(INVENTORY_SCRIPT, n);
        if (item != me && 0 == llSubStringIndex(item, name)) {
            // remove scripts with same name (except myself, of course)
            llRemoveInventory(item);
        }
    }
}

default {
    state_entry() {
        upgrade();
        // rest of your script's startup code
    }
}

It isn't entirely robust, but (with a few minor restrictions on script naming) it serves my purpose. And it is relatively small, so I don't have any serious worries of it getting in the way of memory limits. This may not be a solution for everyone, but you might find it useful with a few tweaks of your own.

-= Update from Jippen Faddoul=-
This one isn't quite as flexable, but it assumes (usually correctly) that you are not starting off with several copies of the same script in the prim, but just 1 copy of the script to be updated.
Its a bit cleaner to put in your code, and will use less memory, since it doesn't need to deal with lists at all.
upgrade() {
    //Get the name of the script
    string self = llGetScriptName();
    // Find out if the script ends in " 1"
    // This rarely happens unless the script was copied in. ~.^
    integer find = llSubStringIndex(self," 1");
    // IF, and only if we find that this ends in " 1", start the update code.
    if(find != -1) { llRemoveInventory(llDeleteSubString(self,find,-1)); }
}

default {
    state_entry() {
        upgrade();
        // The actual script starts here.
    }
}

-= Update from Cron Stardust=-
I found that the upgrade capabilities of these two cool scripts was sadly limited to requiring no spaces in the name, and/or only removing the un-numbered edition allowing only one upgrade before going back to requiring manual work. So I decided to mess with it myself: Self Upgrading Script Enhanced

Cron


LibrarySelfUpgradingScriptAlt

Self Upgrading Script Alt

This is an alternative to the script created by MarkovBrodsky. Instead of having to physically update every child-prim, this will let you update the child-prims from a single source. You need to move a script to each child prim to begin with, but after that updating the child scripts is just a matter of calling a function.

NOTE: The SCRIPT_TO_UPDATE variable must name a script that exists in the inventory of the root prim.

1. Move the following script to each child-prim to begin with, or include the llSetRemoteScriptAccessPin(SCRIPT_PIN) call in the script to be updated and copy that script to each child-prim initially.

// Simple pin configuration for allowing other scripts
    // to update the internal script

   // Use an arbitrary PIN
   integer SCRIPT_PIN = 7589273495872;

   default {
      state_entry() {
         llSetRemoteScriptAccessPin(SCRIPT_PIN);
      }
   }

2. Move the following script to the root prim. This script simply goes through each prim and updates the script.

// Simple update script to update the given script name
   // to each of the child-prims in a linked set

   integer SCRIPT_PIN = 7589273495872;
   string SCRIPT_TO_UPDATE = "updated_script";

   UpdatePrims() {
      integer num_prims = llGetNumberOfPrims();
      integer i;
      for (i = 2; i <= num_prims; i++) {
         key prim_key = llGetLinkKey (i);
         llOwnerSay("Updating prim #" + (string)(i));
         llRemoteLoadScriptPin (prim_key, SCRIPT_TO_UPDATE, SCRIPT_PIN, TRUE, 0);
      }
      llOwnerSay("Updated " + (string)(num_prims - 1) + " prims");
   }

   default {

      state_entry() {
         llListen (9, "", llGetOwner(), "update");
      }

      listen(integer channel, string name, key id, string message)
      {
         UpdatePrims();
      }

   }

3. Now, calling "/9 update" will update each prim in the link set. It's not going to be super-fast, because the llRemoteLoadScriptPin delays the script on each call, but it's a lot more convenient that having to drag updated scripts to each prim. You can delete the root prim script when you don't need it anymore.


LibrarySelfUpgradingScriptEnhanced

Self Upgrading Script

Thanks to MarkovBrodsky's Self Upgrading Script and JippenFaddoul's update to the idea, I was inspired to add to the flexibility and usefulness of their original works.

MarkovBrodsky's original code is quite good, and I based my own implementation upon it, but it has an admitted flaw: It requires that all the scripts that use it have no spaces within the name. For me this will not do. I like my space ;D

JippenFaddoul's update is also pretty good, but while it is nicely shortened and optimized, it can't handle being copied into the prim more than 2 times. This makes it unusable for my coding style. (Tweak, save, copy, test, repeat...)

So here's my edition to fix those shortcomings. (You may use this code freely and distribute it freely, but DO NOT even try to SELL it.)

// Self Upgrading Script by Cron Stardust based upon work by Markov Brodsky and Jippen Faddoul.  If this code is used, this header line MUST be kept.
upgrade() {
    //Get the name of the script
    string self = llGetScriptName();

    string basename = self;

    // If there is a space in the name, find out if it's a copy number and correct the basename.
    if (llSubStringIndex(self, " ") >= 0) {
        // Get the section of the string that would match this RegEx: /[ ][0-9]+$/
        integer start = 2; // If there IS a version tail it will have a minimum of 2 characters.
        string tail = llGetSubString(self, llStringLength(self) - start, -1);
        while (llGetSubString(tail, 0, 0) != " ") {
            start++;
            tail = llGetSubString(self, llStringLength(self) - start, -1);
        }

        // If the tail is a positive, non-zero number then it's a version code to be removed from the basename.
        if ((integer)tail > 0) {
            basename = llGetSubString(self, 0, -llStringLength(tail) - 1);
        }
    }

    // Remove all other like named scripts.
    integer n = llGetInventoryNumber(INVENTORY_SCRIPT);
    while (n-- > 0) {
        string item = llGetInventoryName(INVENTORY_SCRIPT, n);

        // Remove scripts with same name (except myself, of course)
        if (item != self && 0 == llSubStringIndex(item, basename)) {
            llRemoveInventory(item);
        }
    }
}

default {
    state_entry() {
        upgrade();
        // rest of your script's startup code
    }
}

As far as my testing was able to show, I was able to have spaces in my script names and I was even able to tack on a number at the end of my script for personal version tracking and had no troubles. I was able to copy the script into the same prim over and over again with only the latest ever staying.

Cron

Creative Commons License
This Work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.


LibrarySenRot

Functional Warper/Smooth Movement Demo

This is a warppos that is functional without using the llSetPrimitiveParams hack found in 'warppos'. As an alternative, it utilizes parallel calls to llSetPos().

There are two scripts involved. The first script controls the second script, in response to a touch. To change the rotation, you can rotate the root prim
of your object, or you can modify the openrotvec and closerotvec. Both will have some effect; you may need to play around a little to get it right.

The second script does all of the actual rotation, and there should be multiple copies of it inside your object - it's the "parallel" script. See below for details.

Here's script 1, MasterRotate:
float howlong = 2.0;
integer continuous = 0;

vector vecTime()
{
    vector tsp;
    list tsv;

    tsv = llParseString2List( llGetTimestamp(), ["T", ":", "Z"], [] );

    tsp.x = llList2Integer(tsv, 1) - 8;
    if( tsp.x < 0 ) {
        tsp.x += 24;
    }
    tsp.y = llList2Integer(tsv, 2);
    tsp.z = llList2Float(tsv, 3);

    return tsp;
}

// returns the rotation needed to get to a point.
rotation rotTo( rotation tgtrot )
{
    return llGetLocalRot() / tgtrot;
}

// sleek as shit:
smoothRot( vector vOrigin, float timetotake, rotation rotoffset, integer repeats )
{
    vector vTime = vecTime();
    vTime.z += 1.5;
    llMessageLinked( LINK_SET, 999,
        rot2str(llGetLocalRot())
        + "~" + vec2str(llRot2Axis(rotoffset))
        + "~" + (string)( llRot2Angle(rotoffset) / timetotake )
        + "~" + (string)timetotake
        + "~" + (string)repeats
        + "~" + vec2str(vOrigin)
        + "~" + vec2str(llGetLocalPos() - vOrigin)
        + "~" + vec2str(vTime)

        ,NULL_KEY );
}

string vec2str(vector v)
{
    return (string)v.x + "," + (string)v.y + "," + (string)v.z;
}
string rot2str(rotation r)
{
    return (string)r.x + "," + (string)r.y + "," + (string)r.z + "," + (string)r.s;
}

integer touchstate=0;

default
{
    state_entry()
    {
        llSay(0, "Smooth Rotation Tester Online.");
        if( continuous == 1 ) {
            llSetTimerEvent(10.0);
        }
    }

    timer()
    {
        if( touchstate == 0 ) {
            llTriggerSound("Door open", 0.8);
            smoothRot( <0,0,0>, howlong, llEuler2Rot( <0,PI_BY_TWO,0> ), (integer)howlong*4 );
            touchstate = 1;
        } else {
            llTriggerSound("Door close", 0.8);
            smoothRot( <0,0,0>, howlong, llEuler2Rot( <0,-PI_BY_TWO,0> ), (integer)howlong*4 );
            touchstate = 0;
        }
    }

    touch_start(integer total_number)
    {
        if( continuous == 0 ) {
            if( touchstate == 0 ) {
                smoothRot( <0,0,0>, howlong, llEuler2Rot( <PI_BY_TWO,0,0> ), (integer)howlong*4 );
                llSleep(1.0);
                llTriggerSound("Door open", 0.8);
                touchstate = 1;
                llSleep(4.0);
            } else {
                smoothRot( <0,0,0>, howlong, llEuler2Rot( <-PI_BY_TWO,0,0> ), (integer)howlong*4 );
                llSleep(1.0);
                llTriggerSound("Door close", 0.8);
                touchstate = 0;
                llSleep(4.0);
            }
        }
    }
}

Put that script into an object.

This second script is our parallel script. We will want multiple copies of it in each prim that is going to be rotating.

The names of the scripts must be the same except with a space and a number after them, for instance:

SmoothRotate 1
SmoothRotate 2
SmoothRotate 3
... etc

I'd put at least 4 scripts in, you can use as many as you like though. Play around and see what works well for your object. You may have to reset the scripts when you add more or remove some, or rotation will get jumpy. You shouldn't need to change anything in this script!

SmoothRotate:
vector vecTime()
{
    vector tsp;
    list tsv;

    tsv = llParseString2List( llGetTimestamp(), ["T", ":", "Z"], [] );

    tsp.x = llList2Integer(tsv, 1) - 8;
    if( tsp.x < 0 ) {
        tsp.x += 24;
    }
    tsp.y = llList2Integer(tsv, 2);
    tsp.z = llList2Float(tsv, 3);

    return tsp;
}

float timeDiffNow( vector vTarget )
{
    vector vTime = vecTime();
    return (vTarget.x - vTime.x)*3600.0 + (vTarget.y - vTime.y) * 60.0 + (vTarget.z - vTime.z);
}

vector str2vec(string s)
{
    list l;
    l = llParseString2List( s, [","], [] );
    return < llList2Float(l, 0), llList2Float(l, 1), llList2Float(l, 2) >;
}

rotation str2rot(string s)
{
    list l;
    l = llParseString2List(s, [","], []);
    return < llList2Float(l, 0), llList2Float(l, 1), llList2Float(l, 2), llList2Float(l, 3) >;
}

integer scriptid;
integer scriptcount;
countScripts()
{
    integer invcount = llGetInventoryNumber(INVENTORY_SCRIPT);
    integer i;
    string myfirstname = llList2String( llParseString2List(llGetScriptName(), [" "], []), 0 );
    string ofirstname;

    scriptcount=0;
    for( i = 0 ; i < invcount; ++i ) {
        ofirstname = llList2String( llParseString2List(llGetInventoryName(INVENTORY_SCRIPT, i), [" "], []), 0 );
        if( ofirstname == myfirstname ) {
            scriptcount++;
        }
    }
}

default {
    state_entry()
    {
        scriptid = llList2Integer( llParseString2List(llGetScriptName(), [" "], []), 1 );
        countScripts();
    }
    timer()
    {
    }
    link_message( integer sp, integer num, string str, key id )
    {
        if( num == 999 ) {
            list lMsg = llParseString2List(str, ["~"], []);
            rotation rStartRot;
            float fAnglesPerSec;
            vector vAxis;
            vector vStartTime;
            float fTimeToTake;
            integer repeats;
            integer count;
            vector basicoffset = llGetLocalPos();
            float secs;

            rStartRot = str2rot( llList2String(lMsg, 0) );
            vAxis = str2vec( llList2String(lMsg, 1) );
            fAnglesPerSec = llList2Float(lMsg,2);
            fTimeToTake = llList2Float(lMsg,3);
            repeats = llList2Integer(lMsg,4);
            vector vOrigin = str2vec( llList2String(lMsg,5) );
            vector vArmVector = llGetLocalPos() - vOrigin; // str2vec( llList2String(lMsg,6) ); //llGetLocalPos() - vOrigin;
            vStartTime = str2vec( llList2String(lMsg,7) );
            secs = 0.0;

            // Offset time.
            float timeperhit = fTimeToTake/(float)repeats;
            float myoffset = ( timeperhit / (float)scriptcount ) * (float)scriptid;
//            llOwnerSay((string)scriptid + ", offset = " + (string)myoffset + ", timeperhit: " + (string)timeperhit);
//            llOwnerSay("Scriptcount = " + (string)scriptcount);
            for( count = 0; count < repeats && secs < fTimeToTake; count++ ) {
                secs = -timeDiffNow( vStartTime );
                // should we be moving yet?
                while( secs < (float)count*timeperhit + myoffset ) {
                    secs = -timeDiffNow( vStartTime );
                }
//                llOwnerSay((string)scriptid + " and count = " + (string)count + " at " + (string)secs + ", minimum " + (string)(count*timeperhit+myoffset) );
                if( secs > fTimeToTake ) {
                    secs = fTimeToTake;
                }

                rotation rAmount = llAxisAngle2Rot( vAxis, fAnglesPerSec*secs );
//                llSetLocalRot(rTarget);
                llSetPrimitiveParams( [ PRIM_POSITION, vOrigin + vArmVector*rAmount,
                                         PRIM_ROTATION, rStartRot * rAmount / llGetRootRotation() ] );
            }
        }
    }
}

Back to your regularly scheduled programming: ScriptLibrary

<b>Update: fixed the problem in the comment below about the first script not compiling.


LibrarySenWarp

Functional Warper/Smooth Movement Demo

This is a warppos that is functional without using the llSetPrimitiveParams hack found in 'warppos'. As an alternative, it utilizes parallel calls to llSetPos().

There are two scripts involved; here is the first script, which is the 'master control' - you will probably want to change this a bit to suit your particular needs.

vector warppoint;
key av;

vector vecTime()
{
    vector tsp;
    list tsv;

    tsv = llParseString2List( llGetTimestamp(), ["T", ":", "Z"], [] );

    tsp.x = llList2Integer(tsv, 1) - 8;
    if( tsp.x < 0 ) {
        tsp.x += 24;
    }
    tsp.y = llList2Integer(tsv, 2);
    tsp.z = llList2Float(tsv, 3);

    return tsp;
}

vector fixTime( vector inTime )
{
    vector oTime = inTime;

    while( oTime.z >= 60.0 ) {
        oTime.z -= 60.0;
        oTime.y++;
    }
    while( oTime.y >= 60.0 ) {
        oTime.y -= 60.0;
        oTime.z++;
    }
    return oTime;
}

string vec2str(vector v)
{
    return (string)v.x + "," + (string)v.y + "," + (string)v.z;
}
vector str2vec(string s)
{
    list l;
    l = llParseString2List( s, [","], [] );
    return < llList2Float(l, 0), llList2Float(l, 1), llList2Float(l, 2) >;
}

smoothMove( float timetilexec, float timetotake, vector moveoff, integer repeats )
{
    vector persecond = moveoff / timetotake;
    vector vStart = vecTime();
    vStart.z += timetilexec;
    vStart = fixTime(vStart);

    llMessageLinked( LINK_THIS, 999, vec2str(llGetPos()) + "~" + vec2str(vStart) + "~" + vec2str(persecond) + "~" + (string)timetotake + "~" + (string)repeats, NULL_KEY );
}

senWarp( vector d )
{
    smoothMove( 1.0, 1.0, d-llGetPos(), 4 );
    llSleep(3.0); // this sleep is needed for llDie() calls to not be earlieeeeee
}

integer listkey;

default
{
    state_entry()
    {
        llSitTarget( <0,0,1>, ZERO_ROTATION );
    }

    // Rez communications example - standard rezzer/rezzed configuration, sp is a random negative number
    on_rez(integer sp)
    {
        llSitTarget( <0,0,0.1>, ZERO_ROTATION );
        if( sp != 0 ) {
            listkey = llListen(sp, "", NULL_KEY, "");
            llSay(sp, "okay");
        }
    }

    listen( integer ch, string nam, key id, string msg )
    {
        warppoint = str2vec(msg);
        llListenRemove(listkey); // no need to keep listening.
    }

    // touch_start
    // DEMO MODE - jump to 200 meters, then down to ground level
    touch_start( integer nd )
    {
        if( llDetectedKey(0) == llGetOwner() ) {
            warppoint = llGetPos();
            if( warppoint.z > llGround( warppoint ) + 30 ) {
                warppoint.z = llGround( warppoint ) + 1.0;
            } else {
                warppoint.z = llGround( warppoint ) + 200.0;
            }
        }
    }

    run_time_permissions( integer whocares )
    {
        llStopAnimation("sit"); // doesn't work for some reason?
        warpPos(warppoint);
        llUnSit(av); // maybe not necessary, with llDie() call
//      llDie(); // uncomment this line for oneshot teleporters
        // otherwise you'll want to call warpPos(startpos)
    }

    changed( integer change )
    {
        if( change & CHANGED_LINK ) {
            av = llAvatarOnSitTarget();
            llSleep(0.5);
            if( av != NULL_KEY ) {
                llRequestPermissions( av, PERMISSION_TRIGGER_ANIMATION );
            }
        }
    }
}

Put that script into an object. Then take 10 copies of this second script and put them all in the same object, same primitive, just one prim is fine. An example contents list:

MasterMover
SmoothMoveX 1
SmoothMoveX 2
SmoothMoveX 3
SmoothMoveX 4
SmoothMoveX 5
SmoothMoveX 6
SmoothMoveX 7
SmoothMoveX 8
SmoothMoveX 9
SmoothMoveX 10

Note that you can use as many of the 'SmoothMove' scripts as you want, but 10 is what I have tested with. The SmoothMove script (included below) will count the number of scripts available to it and act accordingly, so you can adjust to your particular needs. You will also want to change the MasterMove script's senWarp() function to accurately account for the number of repetitions needed per second. I would suggest that with 10 scripts approximately 4 repetitions per second are needed (this is per script btw); test to your delight. This shit is totally wicked, I'm loving it all over the rotation and movement spectrum.

vector vecTime()
{
    vector tsp;
    list tsv;

    tsv = llParseString2List( llGetTimestamp(), ["T", ":", "Z"], [] );

    tsp.x = llList2Integer(tsv, 1) - 8;
    if( tsp.x < 0 ) {
        tsp.x += 24;
    }
    tsp.y = llList2Integer(tsv, 2);
    tsp.z = llList2Float(tsv, 3);

    return tsp;
}

float timeDiffNow( vector vTarget )
{
    vector vTime = vecTime();
    return (vTarget.x - vTime.x)*3600.0 + (vTarget.y - vTime.y) * 60.0 + (vTarget.z - vTime.z);
}

vector str2vec(string s)
{
    list l;
    l = llParseString2List( s, [","], [] );
    return < llList2Float(l, 0), llList2Float(l, 1), llList2Float(l, 2) >;
}

rotation str2rot(string s)
{
    list l;
    l = llParseString2List(s, [","], []);
    return < llList2Float(l, 0), llList2Float(l, 1), llList2Float(l, 2), llList2Float(l, 3) >;
}

integer scriptid;
integer scriptcount;
countScripts()
{
    integer invcount = llGetInventoryNumber(INVENTORY_SCRIPT);
    integer i;
    string myfirstname = llList2String( llParseString2List(llGetScriptName(), [" "], []), 0 );
    string ofirstname;

    scriptcount=0;
    for( i = 0 ; i < invcount; ++i ) {
        ofirstname = llList2String( llParseString2List(llGetInventoryName(INVENTORY_SCRIPT, i), [" "], []), 0 );
        if( ofirstname == myfirstname ) {
            scriptcount++;
        }
    }
}

default {
    state_entry()
    {
        scriptid = llList2Integer( llParseString2List(llGetScriptName(), [" "], []), 1 );
        countScripts();
    }
    link_message( integer sp, integer num, string str, key id )
    {
        if( num == 999 ) {
            list lMsg = llParseString2List(str, ["~"], []);
            vector vStartPos = str2vec( llList2String(lMsg,0) );
            vector vStartTime = str2vec( llList2String(lMsg,1) );
            vector vDistPerSec = str2vec( llList2String(lMsg,2) );
            float fTimeToTake = llList2Float(lMsg,3);
            integer repeats = llList2Integer(lMsg,4);
            float onecycle = fTimeToTake/(float)repeats;
            integer count;
            float secs;

            // Offset time.
            float myoffset = ( onecycle / (float)scriptcount ) * (float)scriptid;

            for( count = 0; count < repeats && secs < fTimeToTake; count++ ) {
                secs = -timeDiffNow( vStartTime );
                // should we be moving yet?
                while( secs < (float)count*onecycle + myoffset ) {
                    secs = -timeDiffNow( vStartTime );
                }

                if( secs > fTimeToTake ) {
                    secs = fTimeToTake;
                }

                llSetPos(vStartPos + ( vDistPerSec * secs ));
            }
        }
    }
}

Back to your regularly scheduled programming: ScriptLibrary


LibrarySerialize

Library providing two functions (currently...I plan to implement selective unserialization shortly) for providing storage and transport-safe data list serialization/unserialization.

These functions are derived from ReadyJack 's and EzharFairlight 's previous implementations. (See ExampleListConversion). It incorporates suggestions made by BlindWanderer (Strife) regarding the usage of Float2Hex when serializing float values. You will need to get his code, which can be found at LibraryFloat2Hex.

It also deviates from other implementations by using a 2-stage delimiter. Each list item is completely accessible after the first stage unserialization, which means you don't have to seek through the list if you only want and item at a specific index. Some of you may hate this. I like it.

I'm also working on a PHP library that provides classes that represent all the lsl data types. Included with this is a list object which has the same serialization and unserialization code, which becomes pretty handy now that we have HTTP available. I'll be making the library available soon, and I'll throw a link up here.

Strife's Thoughts:
This script has good bones, my main concern is with using the CSV functions, CSV has a tendancy of eating whitespace. I will post modified versions below that should solve that (and will be better optimized). My tests have shown it to be about 14% faster. Also I have writen a slightly faster Float2Hex.

"Note:
After looking at Strife's code, I dropped mine from the page. His is definitely better, and I don't want to cause confusion over which implementation should be used. I'll be updating my PHP stuff to be compatible with the slightly different serialization format. -- ShadowGretzky"

It's slightly different? I designed and tested the functions to be interchangable. Whats changed? -- BW aka Strife
Ohhhh with the CSV, the LSL CSV functions don't need the whitespace so "a,b" and "a, b" parse to the same thing; CSV is good for everything but keys & strings (keys and strings are really the same data-type). -- BW

"Correct. I was referring more to the changes I need to make on the PHP side of things, where I was expecting the whitespace. :) In any event, I'm happy with the changes you've made. Thanks again for your help. -- ShadowGretzky"

// lib_serializer.lsl
//
// This code will run a bit more slowly than RJ's list_2_string() and
// string_to_list(), but a) they'll be float-safe and b) it'll be
// compatible with my upcoming SecondLife PHP library. Lists suck anyway.
//
// I use 2-space tabwidths....sue me. ;)
//
// props to: Ezhar Fairlight, Strife Onizuka, Ready Jack
// apologies to: Ezhar Fairlight, Strife Onizuka, Ready Jack
//
// Requires: Float2Hex() http://secondlife.com/badgeo/wakka.php?wakka=LibraryFloat2Hex
// #include lib_floats.lsl

string SERIALIZER_DELIMITER = "$!#"; // Purposely different from RJ's delimiter to discourage cross-usage

// Serializes list into a storable, transportable form
// I use comma-separated values to store each variable's properties
// (type and value).  I use SERIALIZER_DELIMITER to separate one variable
// from the next.  That way, it's possible to unserialize a specific list
// position without having to process an entire stream (given the variable
// number of list indexes based on type)
string serializeList(list l) {
    integer i = (l != []);//This is a hack, it gets list lenght.
    if(i)
    {
        string serialized_data = "";
        integer type = 0;
        string result;
        {@loop;
            // this custom loop is about as fast as a while loop.
            // we build the string backwords for memory reasons.
            // My kingdom for select statements....

            if (TYPE_FLOAT == (type = llGetListEntryType(l, (i=~-i))))

                // floats get extra love
                result = Float2Hex(llList2Float(l, i));
            else if (TYPE_VECTOR == type) {
                vector v = llList2Vector(l, i);
                result = Float2Hex(v.x) + "," + Float2Hex(v.y) + "," + Float2Hex(v.z);
            } else  if (TYPE_ROTATION == type) {
                rotation r = llList2Rot(l, i);
                result = Float2Hex(r.x) + "," + Float2Hex(r.y) + "," + Float2Hex(r.z) + "," + Float2Hex(r.s);
            } else //if ((TYPE_INTEGER == type) || (TYPE_STRING ==  type) || (TYPE_KEY == type))
                result = llList2String(l, i);// integers, strings and keys required no voodoo

            if(i)
            {
                //This came to me after reverse engeneering LSL bytecode, the realization that LSL memory management sucks.
                serialized_data = SERIALIZER_DELIMITER + (string)type + (serialized_data = result = ",") + result + serialized_data;
                jump loop;
            }
        }
        return (string)type + (serialized_data = result = ",") + result + serialized_data;
    }
    return "";
}

list unserializeList(string serialized_data) {
    // TODO: add some checking in-case we encounter a poorly formed serialization
    //       consider using the same mem-packing list pushing technique used above
    //       (want to run performace tests first)
    list result = [];
    list t;
    list l = llParseStringKeepNulls(serialized_data, [SERIALIZER_DELIMITER], []);

    string item;
    integer i = (l != []);//This is a hack, it gets list lenght.
    integer type = 0;
    do
    {
        if((type = (integer)(item = llList2String(l, (i=~-i)))))
        {//Little error checking (also takes care of null strings).
            integer p = llSubStringIndex(item, ",");
            item = llDeleteSubString(item, 0, p);
            // How about those switch statements, Lindens???
            if (TYPE_INTEGER == type)
                t = [(integer)item];
            else if (TYPE_FLOAT == type)
                t = [(float)item];
            else if (TYPE_STRING == type)
                t = [item];
            else if (TYPE_KEY == type)
                t = [(key)item];
            else
            {
                if (TYPE_ROTATION ^ type)// if (TYPE_VECTOR == type)
                    t = [(vector)("<" + item + ">")];
                else// if (TYPE_ROTATION == type)
                    t = [(rotation)("<" + item + ">")];
            }
            //when dealing with very long lists it might be advantagous to use the commented out line instead.
            //result = [result = t] + result;
            result = t + result;

        }
    }while(i);
    return result;
}


LibrarySetTextOnce

I'm sick of people leaving their llSetText() scritps on objects just because they were too lazy to (or didn't know they could) remove them after the text was set. Plus, I'm always to lazy to remove my llSetText scripts from my objects. :)

This script will immediatly listen for the next thing spoken by the owner, then set the text to what was said, with the color based on which channel, then delete itself. I couldn't think of a faster way to do this. Simply drop the script on an object, say "/3 This is Red" and you're done.

I know this could be a lot more, but I like to keep things simple.

default
{
    state_entry()
    {
        llOwnerSay("Speak the text you want to hover over me");
        llOwnerSay("Preceed with /1 for White");
        llOwnerSay("Preceed with /2 for Black");
        llOwnerSay("Preceed with /3 for Red");
        llOwnerSay("Preceed with /4 for Green");
        llOwnerSay("Preceed with /5 for Blue");
        llOwnerSay("A single dash (-) will clear text");

        llListen(0, "", llGetOwner(), "");
        llListen(1, "", llGetOwner(), "");
        llListen(2, "", llGetOwner(), "");
        llListen(3, "", llGetOwner(), "");
        llListen(4, "", llGetOwner(), "");
        llListen(5, "", llGetOwner(), "");

    }

    listen( integer channel, string name, key id, string message )
    {
        vector vColor = <0.5, 0.5, 0.5>;
        if (channel==1) {
            vColor = <1,1,1>;
        }else
        if (channel==2) {
            vColor = <0,0,0>;
        }else
        if (channel==3) {
            vColor = <1,0,0>;
        }else
        if (channel==4) {
            vColor = <0,1,0>;
        }else
        if (channel==5) {
            vColor = <0,0,1>;
        }
        if (message=="-") {
            llSetText("", <0,0,0>, 1);
        }else{
            llSetText(message, vColor, 1);
        }
        llRemoveInventory(llGetScriptName()); // delete this script
    }
}

ScriptLibrary


LibrarySimpleCypher

Really this is just 2 functions (and a bunch of globals) for use in another script. The listen event will take a coma delimited password and string (myPass,myString) and both encode and decode the string with the password. It is merely for testing. Likely to be slow for long strings. This isn't truely secure, any decent computer, or someone with a little spare time and some context, will probably be able to decipher any message in short order.

Also see llXorBase64Strings for a different method of encrypting data.
This is what happens when I pull old scripts out of my inventory - yup, that function will do a better job than this, and was made well after I wrote this script. - AO

// Ama's Simple Cypher v. 0.3

// Feel free to reorganize either of these.  Infact, doing so is recommended.
// Just remember - these must match whatever strings are in the partner script you are communicating with.
string ASCII = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890`-=~!@#$%^&*()_+[]\\{}|;':\",./<>? ";
string CODE = " ABCDEFGHIJKLMNOPQRSTUVWXYZadbcefghijklmnopqrstuvwxyz1234567890`-=~!@#$%^&*()_+[]\\{}|;':\",./<>? ";
string PWD;
integer asciiLength;  // asciiLength = llStringLength(ASCII); in state_entry of default
integer passLength;
string ret;
integer i;
integer bit;
integer size;
list coms;

string encode(string mes, string pwd)
{
    ret = "";
    size = llStringLength(mes);
    passLength = llStringLength(pwd);
    for (i=0;i<size;i++)
    {
        bit = (llSubStringIndex(ASCII,llGetSubString(mes,i,i)) + (integer)llGetSubString(pwd,i%passLength,i%passLength) ) % asciiLength;
        ret +=  llGetSubString(CODE,bit,bit);
    }
    return ret;
}

string decode(string mes, string pwd)
{
    ret = "";
    size = llStringLength(mes);
    passLength = llStringLength(pwd);
    for (i=0;i<size;i++)
    {
        bit = (llSubStringIndex(CODE,llGetSubString(mes,i,i)) - (integer)llGetSubString(pwd,i%passLength,i%passLength) ) % asciiLength;
        ret +=  llGetSubString(ASCII,bit,bit);
    }
    return ret;
}

default
{
    state_entry()
    {
        asciiLength = llStringLength(ASCII);
        llListen(0,"",llGetOwner(),"");
    }

    listen(integer chan, string name, key id, string mes)
    {
        coms = llCSV2List(mes);
        llWhisper(0,encode(llList2String(coms,1),llList2String(coms,0)) + "," + decode(llList2String(coms,1),llList2String(coms,0)));
    }
}

ScriptLibrary / Examples


LibrarySimpleHoverTextRemovalOnSit

Simple Disappearing HoverText

This is a simple script that adds hovertext to the prim that it is in.
When an Avatar sits on the prim, it removes the hovertext.
Note, the prim MUST have a llSitTarget defined for this to work.

string Text = "Sit here\n \n \n \n";
vector TextColor = <1.0,1.0,1.0>;
vector TextAlpha = 1.0;
integer hidden;

default
{
    state_entry()
    {
        llSetText( Text, TextColor, TextAlpha  );
        hidden = FALSE;
    }

    changed( integer change )
    {
        if( change & CHANGED_LINK )
        {
            integer temp = llAvatarOnSitTarget() != NULL_KEY;
            if( temp != hidden )
            {
                if(temp)
                {
                    llSetText( "", ZERO_VECTOR, 0 );
                } else
                {
                    llSetText( Text, TextColor, TextAlpha  );
                }
                hidden = temp;
            }
            llSleep(0.5);
        }
    }
}
You might ask, "why keeping track of if the text is hidden?" When you link multiple prims with sit targets together any time someone stands up every script in the linked object has a changed event triggered (where applicable). If each script did not keep track of this data, ever script in the object would cause a full update for prim they are in. Full updates contribute to lag.

Here is a different way of doing it.

string sHoverText = "Sit here\n \n \n \n";

ShowHover()
{
    //llOwnerSay( "Showing" );
    llSetText( sHoverText, <1.0,1.0,1.0>, 1.0 );
    //add a slight delay to avoid sending too many messages to the prim
    llSleep( 0.5 );
}

HideHover()
{
    //llOwnerSay( "Hiding" );
    llSetText( "", <1.0,1.0,1.0>, 0.0 );
    //add a slight delay to avoid sending too many messages to the prim
    llSleep( 0.5 );
}

default
{
    state_entry()
    {
        ShowHover();
    }

    changed( integer iChange )
    {
    key kAgent = NULL_KEY;

        if( iChange & CHANGED_LINK )
        {
            kAgent = llAvatarOnSitTarget();
            //llOwnerSay( "kAgent=" + (string)kAgent );
            if( kAgent == NULL_KEY )
                ShowHover();
            else
                HideHover();
        }
    }
}}

Script Library | Examples


LibrarySLPrint

SLPrint

This is a helper function that works similar to printf. It takes two variables, the string and a list of the data. The recognized substrings ("\%","%s","%i","%f.","%f","%h") are replaced with the data from the list in corresponding order.

%s is a string. For example slprint("hi %s",["bob"]) will return the string "hi bob".
%i is integer form. For example slprint("%i",[123]) will return the string "123".
%f is a float. For example slprint("%f",[1.456]) will return the string "1.45600".
%f.# will convert the float to having # of decimal places. For example slprint("%f.2",[1.4567]) will return the string "1.45".
%h will display an integer in hexidecimal form. For example slprint("%h",[1023]) will return the string "3ff".
%H will display an integer in hexidecimal form. For example slprint("%H",[1023]) will return the string "3FF".
\% can be used to bypass conversion. For example slprint("\\%h",[1023]) will return the string "%h". note: within a script you must use a double backslash
%n a spacer token not inserted in the final string, it is skipped.

A more complicated example:
llWhisper(0, slprint("Script '%s' has %i bytes free (%f.2%).",[llGetScriptName(),llGetFreeMemory(),100 * llGetFreeMemory() / (float)(16*1024)]));
Results in:
Object whispers: Script 'New Script' has 14486 bytes free (88.36%).

This script is not particuarly efficient. If you have optimizations please make them. - Ama Omega

string slprint(string mes, list data)
{
    list parsed = llParseString2List(mes,[],["\\%","%s","%i","%f.","%f","%h","%n","%H"]);
    integer i;
    integer j;
    integer max = llGetListLength(parsed);
    mes = ""; //we use this for the output
    string token;
    for (j=i=0;i<max;i++)
    {
        token = llList2String(parsed,i);

        if ("%s" == token)
            mes += llList2String(data,j++);
        else if ("%i" == token)
            mes += (string)((integer)llList2String(data,j++));
        else if ("%f" == token)
            mes += (string)((float)llList2String(data,j++));
        else if ("%f." == token)
        {
            // The next token contains the number of places for the float
            string nextToken = llList2String(parsed, i); //we parse the next token too
            integer t = (integer)nextToken;
            token = (string)((float)llList2String(data,j++));
            if(!llSubStringIndex(nextToken,(string)t))//must be in the zero possition
            {//we made sure of the values location and now we are printing it out the answer and the next token.
                token = llGetSubString(token,0,t + llSubStringIndex(token,".")) +
                            llDeleteSubString(nextToken, 0, llStringLength((string)t) - 1);
                ++i;
            }
            mes += token;
        }
        else if ("%h" == token)
            mes += int2hex((integer)llList2String(data,j++), "0123456789abcdef");
        else if ("%H" == token)
            mes += int2hex((integer)llList2String(data,j++), "0123456789ABCDEF");
        else if ("\\%" == token)
            mes += "%";
        else if ("%n" != token) // we don't parse "%n"
            mes += token;
    }
    return token;
}

string int2hex(integer h, string s)
{
    string ret;
    integer g = 32; //this is to keep it from going on forever when given a negative number
    do
    {
        ret = llGetSubString(s, h & 15, h & 15) + ret;
        h = h >> 4;
    }while(h && --g);
    return ret;
}

ScriptLibrary | Examples


LibraryStridedLists

Strided List Functions

These functions are made for working with strided lists. They are pretty self explanitory so not much commenting is needed. These will give you a good head start in working with your own strided lists.

Strided Lists are lists within lists. Since there is no way to make a multi-dimentional array or have lists contain other lists directly, we use what are called "Strided Lists". This would allow you to keep multiple pieces of data on each of several objects or avatars. For example, if you wanted an answering machine to store messages from people, and wanted to store their name, thier message, and the time they left it, you could use three seperate lists: listA = [Name1, Name2, Name3]; listB = [Message1, Message2, Messag3]; listC = [Time1, Time2, Time3] or you could use a strided list: listA = [Name1, Message1, Time1, Name2, Message2, Time2, Name3, Message3, Time3].

A stride within that list would be one set of parameters, or 1 Name, 1 Message and 1 Time. the Stride Length is how many elements make up a single stride. In the case of the above list, the stride length is 3.

Note: Most people stay away from strided lists due partly to complexity (which I hope to correct here) and partly due to memory usage. While I have yet to test this myself, it seems to be popular concensus that working with strided lists use more memory than working with multiple regular lists. I leave the use of the following up to your discretion.

Examples:
Note: In all of the examples below, the last parameter is the number of elements per stride. in all of the below cases, this number is 3.
Note: The functions that modify the source list now return the original unaltered list on failure.

integer fncStrideCount(list source, integer stride)
Returns the number of strides found within a list.
Source is the list to operate on, and stride is the length of each stride within the list.
list Source = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];

integer Result = fncStrideCount(Source, 3);
// Returns 3

list fncFindStride(list source, list item, integer stride)
Returns the stride number and element number of an element within a stride, within a list. (zero indexed)
Source is the list to search, item is a single element list containing the item to search for, and stride is the length of each stride.
list Source = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];
list Item = ["B3"];

list Result = fncFindStride(Source, Item, 3);
// Returns [1, 2].
// The first number is which stride Item is in.
// The second is which element of the stride is Item.

// If item is not found, it will return [-1, -1]

list fncDeleteStride(list source, integer index, integer stride)
Deletes a stride from a list.
Source is the list to operate on, index is which stride to delete (zero indexed), and stride is the length of each stride.
list Source = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];

list Result = fncDeleteStride(Source, 1, 3);

// Returns ["A1", "A2", "A3", "C1", "C2", "C3"]
// On Failure, it returns original list

list fncGetStride(list source, integer index, integer stride)
Returns a single stride from the list.
Source is the list to retrieve from, index is which stride to return (zero indexed), and stride is the length of each stride.
list Source = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];

list Result = fncGetStride(Source, 1, 3);

// Returns ["B1", "B2", "B3"]
// On failure returns an empty list

list fncReplaceStride(list source, list replacement, integer index, integer stride)
Replaces an entire stride within a list, and returns new list.
Source is the list to operate on, replacement is a list to replace with (Must be an entire stride), index is which stride to replace, and stride is the length of each stride.
list Source = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];
list Replace = ["D1", "D2", "D3"]; // Note it's a single and complete stride

list Result = fncReplacestride(Source, Replace, 1, 3);

// Returns ["A1", "A2", "A3", "D1", "D2", "D3", "C1", "C2", "C3"]
// On failure returns original list

list fncGetElement(list source, integer index, integer subindex, integer stride)
Returns a single element from within a stride, within a list.
Source is the list to retrieve from, index is which stride to retrieve from, subindex is which element to retrieve, and stride is the length of each stride.
list Source = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];

list Result = fncGetElement(Source, 2, 1, 3);
// Returns ["C2"]
// On failure returns an empty list

list fncReplaceElement(list source, list item, integer index, integer subindex, integer stride)
Replaces a single element within a stride within a list, and returns the new list.
Source is the list to operate on, item is a single element list containing the item to replace with, index is which stride to operate on, subindex is which element in the stride to replace, and stride is the length of each stride.
list Source = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];
list Replace = ["D3"]; // Note it's a single item

list Result = fncReplaceElement(Source, Replace, 1, 2, 3);

// Returns ["A1", "A2", "A3", "B1", "B2", "D3", "C1", "C2", "C3"]
// On failure returns original list

And here are the functions
// Strided Functions For working with Strided Lists.
// By Aakanaar LaSalle

// the intStride parameter is the length of the strides within the list
// the intIndex is which stride we're working with.
// the intSubIndex is which element of the stride we're working with.

// Returns number of Strides in a List
integer fncStrideCount(list lstSource, integer intStride)
{
  return llGetListLength(lstSource) / intStride;
}

// Find a Stride within a List (returns stride index, and item subindex)
list fncFindStride(list lstSource, list lstItem, integer intStride)
{
  integer intListIndex = llListFindList(lstSource, lstItem);

  if (intListIndex == -1) { return [-1, -1]; }

  integer intStrideIndex = intListIndex / intStride;
  integer intSubIndex = intListIndex % intStride;

  return [intStrideIndex, intSubIndex];
}

// Deletes a Stride from a List
list fncDeleteStride(list lstSource, integer intIndex, integer intStride)
{
  integer intNumStrides = fncStrideCount(lstSource, intStride);

  if (intNumStrides != 0 && intIndex < intNumStrides)
  {
    integer intOffset = intIndex * intStride;
    return llDeleteSubList(lstSource, intOffset, intOffset + (intStride - 1));
  }
  return lstSource;
}

// Returns a Stride from a List
list fncGetStride(list lstSource, integer intIndex, integer intStride)
{
  integer intNumStrides = fncStrideCount(lstSource, intStride);

  if (intNumStrides != 0 && intIndex < intNumStrides)
  {
    integer intOffset = intIndex * intStride;
    return llList2List(lstSource, intOffset, intOffset + (intStride - 1));
  }
  return [];
}

// Replace a Stride in a List
list fncReplaceStride(list lstSource, list lstStride, integer intIndex, integer intStride)
{
  integer intNumStrides = fncStrideCount(lstSource, intStride);

  if (llGetListLength(lstStride) != intStride) { return lstSource; }

  if (intNumStrides != 0 && intIndex < intNumStrides)
  {
    integer intOffset = intIndex * intStride;
    return llListReplaceList(lstSource, lstStride, intOffset, intOffset + (intStride - 1));
  }
  return lstSource;
}

// Retrieve a single element from a Stride within a List
list fncGetElement(list lstSource, integer intIndex, integer intSubIndex, integer intStride)
{
  if (intSubIndex >= intStride) { return []; }

  integer intNumStrides = fncStrideCount(lstSource, intStride);

  if (intNumStrides != 0 && intIndex < intNumStrides)
  {
    integer intOffset = (intIndex * intStride) + intSubIndex;
    return llList2List(lstSource, intOffset, intOffset);
  }
  return [];
}

// Update a single item in a Stride within a List
list fncReplaceElement(list lstSource, list lstItem, integer intIndex, integer intSubIndex, integer intStride)
{
  integer intNumStrides = fncStrideCount(lstSource, intStride);

  if (llGetListLength(lstItem) != 1) { return lstSource; }

  if (intNumStrides != 0 && intIndex < intNumStrides)
  {
    integer intOffset = (intIndex * intStride) + intSubIndex;
    return llListReplaceList(lstSource, lstItem, intOffset, intOffset);
  }
  return lstSource;
}
HomePage | ScriptLibrary


LibrarySwarm

Reproduced from this SL Forum thread with permission of the Author, Apotheus Silverman.

This implements the widely-available swarm algorithm in SL. A good way to use this is to have an object that rezzes a new swarming object every N seconds and set the swarming object to die after X seconds. These coupled with the force and friction values you set ensure your swarm will never get out of hand.

This works absolutely awesome for creating flocks of birds and schools of fish.

Edit: A couple months' worth of testing and optimization goes a long way :-) This new version has a lot of bug fixes and better overall behavior.

Edit 10/14/2004: Added optional schooling behavior so the swarm wanders realistically.

// Swarm script
// by Apotheus Silverman
// This script is my implementation of the well-known swarm algorithm
// which can be found in numerous open-source programs.
// Due to the specifics of the SL environment, I have strayed from some
// of the traditional rules slightly. Regardless, the end effect is
// indistiguishable from the original algorithm.

// Configurable parameters

// Determines whether or not to enable STATUS_SANDBOX.
integer sandbox = FALSE;

// Timer length
float timer_length = 0.5;

// Die after this many seconds
integer kill_time = 300;

// Enables or disables schooling behavior
integer school = TRUE;

// Schooling comm channel
integer school_comm_channel = 9284;

// Schooling behavior update interval (should be a multiple of timer_length)
float school_update_interval = 2.0;

// How much force to apply with each impulse
float force_modifier = 0.7;

// How much force to apply when repulsed by another like me
float repulse_force_modifier = 0.86;

// How much friction to use on a scale from 0 to 1.
// Note that friction takes effect each timer cycle, so the lower the timer length,
// the more the friction you specify here will take effect, thereby increasing actual
// friction applied.
float friction = 0.45;

// How much to modify the rotation strength. Higher numbers produce greater strength
// Note that if the modifier is too small, the object may not rotate at all.
float rotation_strength_modifier = 2.8;

// How much to modify rotation damping. Higher numbers produce slower rotation.
float rotation_damping_modifier = 5000000.0;

// Does this object "swim" in air or water?
// 2 = air
// 1 = water
// 0 = both
integer flight_mode = 1;

// Maximum distance from spawn point
float max_distance = 15.0;

// How far away to scan for others like me
float sensor_distance = 30.0;

// *** Don't change anything below unless you *really* know what you're doing ***

float mass;
vector spawn_location;
float school_timer = 0.0;
vector school_modifier = <0,0,0>;

// Update rotation function
do_rotation(vector mypos, vector myvel) {
    llLookAt(mypos + myvel, mass * rotation_strength_modifier, mass * rotation_damping_modifier);
}

// Collision function
collide(vector loc) {
    vector mypos = llGetPos();
    // Apply repulse force
    vector impulse = llVecNorm(mypos - loc);
    llApplyImpulse(impulse * repulse_force_modifier * mass, FALSE);
//llSay(0, "collide() - impulse " + (string)impulse + " applied.");
    // Update rotation
    do_rotation(mypos, llGetVel());
}

// This function is called whether the sensor senses anything or not
sensor_any() {
    // Die after reaching kill_time
    if (kill_time != 0 && llGetTime() >= kill_time) {
        llDie();
    }

    // Get my velocity
    vector myvel = llGetVel();

    // Apply friction
    llApplyImpulse(-(myvel * friction * mass), FALSE);

    // Schooling behavior
    if (school && llGetTime() - school_timer > school_update_interval) {
        llSay(school_comm_channel, (string)myvel);
        school_timer = llGetTime();
    }

    // Get my position
    vector mypos = llGetPos();

    // Check for air/water breach
    if (flight_mode == 1) {
        // water
        if (mypos.z >= llWater(mypos) - llVecMag(llGetScale())) {
//llSay(0, "collide() called due to air/water breach.");
            collide(<mypos.x, mypos.y, mypos.z + 0.3>);
        }
    } else if (flight_mode == 2) {
        // air
        if (mypos.z <= llWater(mypos) + llVecMag(llGetScale())) {
//llSay(0, "collide() called due to air/water breach.");
            collide(<mypos.x, mypos.y, mypos.z - 0.3>);
        }
    }

    // Stay near spawn location
    if (llVecDist(mypos, spawn_location) > max_distance) {
        // Compensate for being near sim border
        if (spawn_location.x - mypos.x > 100) {
            mypos.x += 255;
        }
//llSay(0, "collide() called due to too much distance from my spawn point. mypos=" + (string)mypos + ", spawn_location = " + (string)spawn_location);
        collide(mypos - llVecNorm(spawn_location - mypos));
    }

    // Stay above ground level
    if (mypos.z <= llGround(ZERO_VECTOR)) {
        collide(mypos - llGroundNormal(ZERO_VECTOR));
    }
}

default {
    state_entry() {
        llResetTime();
        llSay(0, "Fishy spawned.");

        // School
        if (school) {
            llListen(school_comm_channel, "", NULL_KEY, "");
        }

        // Sandbox
        llSetStatus(STATUS_SANDBOX, sandbox);
        llSetStatus(STATUS_BLOCK_GRAB, FALSE);
        spawn_location = llGetPos();

        // Initialize physics behavior
        mass = llGetMass();
        llSetBuoyancy(1.0);
        llSetStatus(STATUS_PHYSICS, TRUE);
        llVolumeDetect(TRUE);

        // Initialize sensor
        llSensorRepeat(llGetObjectName(), NULL_KEY, ACTIVE|SCRIPTED, sensor_distance, PI, timer_length);

    }

    collision_start(integer total_number) {
//llSay(0, "collide() called due to physical object collision.");
        collide(llDetectedPos(0));
    }

    no_sensor() {
        sensor_any();
    }

    sensor(integer total_number) {
        sensor_any();

        // Populate neighbors with the positions of the two nearest neighbors.
        vector mypos = llGetPos();
        list neighbors = [];
        integer i;

        for (i = 0; i < total_number; i++) {
            vector current_pos = llDetectedPos(i);

            if (llGetListLength(neighbors) < 2) {
                // Add to list
                neighbors = llListInsertList(neighbors, [current_pos], llGetListLength(neighbors));
            } else {
                // Check to see if the current vector is closer than the list
                // vector which is furthest away.
                if (llVecDist(mypos, llList2Vector(neighbors, 0)) > llVecDist(mypos, llList2Vector(neighbors, 1))) {
                    // check against first list item
                    if (llVecDist(mypos, llList2Vector(neighbors, 0)) > llVecDist(mypos, current_pos)) {
                        llListInsertList(neighbors, [current_pos], 0);
                    }
                } else {
                    // check against second list item
                    if (llVecDist(mypos, llList2Vector(neighbors, 1)) > llVecDist(mypos, current_pos)) {
                        llListInsertList(neighbors, [current_pos], 1);
                    }
                }
            }
        }

        // Process movement

        // Apply force
        if (llGetListLength(neighbors) == 2) {
            vector neighbor1 = llList2Vector(neighbors, 0);
            vector neighbor2 = llList2Vector(neighbors, 1);
            vector target = neighbor2 + ((neighbor1 - neighbor2) * 0.5);
            vector impulse = <0,0,0>;
            if (school) {
                impulse = llVecNorm(target + school_modifier - mypos);
            } else {
                impulse = llVecNorm(target - mypos);
            }
//llSay(0, "setforce " + (string)(impulse * force_modifier * mass));
            llSetForce(impulse * force_modifier * mass, FALSE);
        }

        // Update rotation
        do_rotation(llGetPos(), llGetVel());
    }

    listen(integer channel, string name, key id, string message) {
        list myList = llCSV2List(llGetSubString(message, 1, llStringLength(message) - 2));
        if (llGetListLength(myList) == 3) {
            school_modifier = <llList2Float(myList, 0), llList2Float(myList, 1), llList2Float(myList, 2)>;
            school_timer = llGetTime();
        }
    }

    on_rez(integer start_param) {
        llResetScript();
//        spawn_location = llGetPos();
//        llResetTime();
    }
}


LibraryTeleporter

// Teleporter Script  v 3.0 by Asira Sakai (with help from LSLWiki)
// requires a notecard in the object's inventory with target vectors and destination descriptions
// in alternating lines (or just a single line with a target vector)
// Will teleport any agent who "sits" on the object containing the script within the simulator
// Touching the object will select the next destination if there are more than one

string gName;                       // name of a notecard in the object's inventory
integer gLine = 0;                  // current line number
key gQueryID;                       // id used to identify dataserver queries
vector homeVector;               // starting vector
vector targetVector;              // vector to teleport to
vector color = <0,0,1>;          // set float text color to blue
integer listIndex;              // current index on the list
list destinations;              // the list of all destinations, strided <vector> , string
string staticText = "Teleport Cube v 3.0"; // optional custom text
string destinationText;            // the text describing the destination

warpPos( vector destpos ) // copied straight from LSLWiki
{   //R&D by Keknehv Psaltery, 05/25/2006
    //with a little pokeing by Strife, and a bit more
    //some more munging by Talarus Luan
    //Final cleanup by Keknehv Psaltery
    // Compute the number of jumps necessary
    integer jumps = (integer)(llVecDist(destpos, llGetPos()) / 10.0) + 1;
    // Try and avoid stack/heap collisions
    if (jumps > 100 )
        jumps = 100;    //  1km should be plenty
    list rules = [ PRIM_POSITION, destpos ];  //The start for the rules list
    integer count = 1;
    while ( ( count = count << 1 ) < jumps)
        rules = (rules=[]) + rules + rules;   //should tighten memory use.
    llSetPrimitiveParams( rules + llList2List( rules, (count - jumps) << 1, count) );
}  // end of warpPos

init()
    {
        destinations = [];          // clear out destinations
        llSetText(staticText, color, 1.0);  // set optional initial floating text
        llSetSitText("Teleport");           // change "sit" option to "Teleport"
        llSitTarget(<0.0, 0.0, 0.1>, ZERO_ROTATION); // needed for llAvatarOnSitTarget to work
        gName = llGetInventoryName(INVENTORY_NOTECARD, 0); // select the first notecard in the object's inventory
        if (gName != "")  // if a notecard is found
            gQueryID = llGetNotecardLine(gName, gLine);    // request first notecard line
        else
            llSay(0,"No location set, insert notecard with target vector in <x,y,z> format");
        if(llAvatarOnSitTarget() != NULL_KEY)   // if someone is sitting
                llUnSit(llAvatarOnSitTarget()); // unsit him
    }

default
{
    state_entry()
    {
        init();
    }

    touch_start(integer total_number)
    {
        if (gName != ""){
            if (gLine <= 2)  // if there is only one destination available
                llSay(0, "Right click and select Teleport to activate");
            else {
                llSay(0, "Selecting next destination, right click and select Teleport to activate");
                // get next entries from the list
                if (listIndex >= gLine)
                    listIndex = 0;
                targetVector = (vector)llList2String(destinations, listIndex);
                destinationText = llList2String(destinations, listIndex + 1);
                llSetText(destinationText, color, 1.0);
                listIndex += 2;
            }
        }
        else
            llResetScript();
    }

    dataserver(key query_id, string data) {
        if (query_id == gQueryID) {
            if (data != EOF) {          // if not at the end of the notecard
                destinations = (destinations=[]) + destinations + data; // add the line data to the list
                gLine++;                // increase line count
                gQueryID = llGetNotecardLine(gName, gLine);    // request next line
            }
            else {  // display the loaded list to the owner
                llOwnerSay("Loaded Locations:");
                listIndex = 0;
                while (listIndex < gLine) {
                    llOwnerSay(llList2String(destinations, listIndex));
                    listIndex++;
                }
                // set up first destination
                targetVector = (vector)llList2String(destinations, 0);
                destinationText = llList2String(destinations, 1);
                if (destinationText != "")
                    llSetText(destinationText, color, 1.0);
                listIndex = 2;
            }
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK){  // if a link change occurs (sit or unsit)
            if(llAvatarOnSitTarget() != NULL_KEY){  // and there is someone sitting now
                if (gName != ""){  // if there is a valid notecard loaded
                    homeVector = llGetPos();  // record current position for return
                    warpPos(targetVector);  // teleport to selected coordinates
                    llUnSit(llAvatarOnSitTarget()); // unsit him
                    warpPos(homeVector);  // teleport back to old position
                }
                else
                    llResetScript();
            }
        }
    }
}


LibraryTextureSwitcher

I wrote this code for a client who needed a last minute gift. She asked i post it so here it is :-)
By setting a value to time you can make it shift images every time period.

if you want the image to change by touch remove the comments from the touch_start event. "//"

//From the script library
//Writen by Strife Onizuka
//http://secondlife.com/badgeo/wakka.php?wakka=LibraryTextureSwitcher

float time = 30; //give me a value if you want time based shifting otherwise set to zero

integer total;
integer counter;

next()
{
    string name = llGetInventoryName(INVENTORY_TEXTURE,counter);
    if(name == "")
    {
        total = llGetInventoryNumber(INVENTORY_TEXTURE);
        counter = 0;
        if(total < 1)
            return;
        else
            name = llGetInventoryName(INVENTORY_TEXTURE,counter);
    }
    else if(counter + 1 >= total)
        total = llGetInventoryNumber(INVENTORY_TEXTURE);
    llSetTexture(name ,ALL_SIDES);
    if(total)
        counter = (counter + 1) % total;
}

default
{
    state_entry()
    {
        total = llGetInventoryNumber(INVENTORY_TEXTURE);
        next();
        llSetTimerEvent(time);
    }
//    touch_start(integer a)
//    {
//        llSetTimerEvent(0);
//        next();
//        llSetTimerEvent(time);
//    }
    timer()
    {
        next();
    }
}
ScriptLibrary


LibraryThermometer

Thermometer

This script will calculate temperature at the objects vector.

float base;
float pascal;
float sealevel = 101.32500;
float temperatureF;
vector sun;
vector pos;
default
{
    touch_start(integer total_number)
    {
        sun = llGetSunDirection();
        pos = llGetPos();
        base = llLog10(5- ((pos.z - llWater(ZERO_VECTOR))/15500));
        pascal = (sealevel + base);
        temperatureF = ((((pascal * (2 * llPow(10,22)))/ (1.8311*llPow(10,20))/ 8.314472)/19.85553747) + (sun.z * 10));
        llSay(0,"Current Temperature is "+ (string)temperatureF +" Degrees Fahrenheit");
        llSleep(1);
        llSay(0,"Current Temperature is "+ (string)((temperatureF + 459.67) * 5/9) +" Degrees Kelvin");
        llSleep(1);
        llSay(0,"Current Temperature is "+ (string)((temperatureF - 32) * 5/9) +" Degrees Celsius");
    }
}


LibraryTimeElapsed

This function can report the amount of real time passed between subsequent calls
with millisecond precision. If it breaks, you can keep both pieces. -ChromalBrodsky

// string elapsed(string sTime)
// ----------------------------
// Chromal Brodsky - 2005-01-05
//
// elapsed() uses the highest precision clock available in LSL, llGetTimestamp(),
// to implement a timer with greater than one-second precision.
//
// Typical use:
//
//      string sStart = elapsed("");
//      some_function();
//      string sElapsed = elapsed(sStart);
//
// At the end of the above example, sElapsed is in the format of hh:mm:ss.ssssss,
// e.g.: "00:01:10.605000"
//
// On Lusk running at 1000 FPS on sim367.agni.lindenlab.com,
// I sampled the following times for { sTime = elapsed(""); llWhisper(0, elapsed(sTime) );}
//
//      00:00:00.010000     00:00:00.003000     00:00:00.003000     00:00:00.003998
//      00:00:00.002998     00:00:00.003002     00:00:00.012000     00:00:00.004001
//      00:00:00.004000     00:00:00.005001     00:00:00.003000     00:00:00.004999
// YMMV

string elapsed(string sTime)
{
    if (sTime == "" )
    {
        sTime = llGetTimestamp();
    }
    else
    {
        string sH;
        string sM;
        string sS;

        string sTimeEnd = llGetTimestamp();

        integer iHour = (integer)llGetSubString( sTimeEnd, 11, 12 );
        integer iMin =  (integer)llGetSubString( sTimeEnd, 14, 15 );
        float fSec =      (float)llGetSubString( sTimeEnd, 17, 22 );

        integer iHour_start =   (integer)llGetSubString( sTime, 11, 12 );
        integer iMin_start =    (integer)llGetSubString( sTime, 14, 15 );
        float fSec_start =        (float)llGetSubString( sTime, 17, 22 );

        if (iHour < iHour_start)
        {
            iHour += 24;
        }
        if (iMin < iMin_start)
        {
            iMin += 60;
        }
        if (fSec < fSec_start)
        {
            fSec += 60.0;
        }

        iHour = iHour - iHour_start;
        iMin = iMin - iMin_start;
        fSec = fSec - fSec_start;

        if (iHour < 10)
        {
            sH = "0";
        }
        if (iMin < 10)
        {
            sM = "0";
        }
        if (fSec < 10.0)
        {
            sS = "0";
        }

        sTime = sM + (string)iHour + ":" + sM + (string)iMin + ":" + sS + (string)fSec;
    }

    return sTime;
}

Based off of elapsed and this script. It calculates the difference between two timestamps.
// string timestamp_difference(string sTimeStart, string sTimeEnd)
//
// At the end of the above example, timestamp_difference is in the format of ddddd:hh:mm:ss.ssssss,
// e.g.: "00000:00:01:10.605000"

string timestamp_difference(string sTimeStart, string sTimeEnd)
{
    list daysPerMonth = [ 0, -1,  30, 58, 89, 119, 150, 180,  211, 242, 272, 303, 333 ];
    if (sTimeStart == "" )
        return sTimeEnd;
    if (sTimeEnd == "")
        sTimeEnd = llGetTimestamp();

    integer year    = (integer) llGetSubString(sTimeEnd,  0,  3);
    integer month   = (integer) llGetSubString(sTimeEnd,  5,  6);
    integer iDay    = (year * 365) + ((year - 2878169) / 4)
                        + llList2Integer( daysPerMonth, month )
                        + (integer) llGetSubString(sTimeEnd,  8,  9)
                        + (month>2) * !(year % 4);
    integer iHour = (integer)llGetSubString( sTimeEnd, 11, 12 );
    integer iMin =  (integer)llGetSubString( sTimeEnd, 14, 15 );
    float fSec =      (float)llGetSubString( sTimeEnd, 17, 22 );

    year    = (integer) llGetSubString(sTimeStart,  0,  3);
    month   = (integer) llGetSubString(sTimeStart,  5,  6);
    integer iDay_start  = (year * 365) + ((year - 2878169) / 4)
                            + llList2Integer( daysPerMonth, month )
                            + (integer) llGetSubString(sTimeStart,  8,  9)
                            + (month>2) * !(year % 4);
    integer iHour_start =   (integer)llGetSubString( sTimeStart, 11, 12 );
    integer iMin_start  =   (integer)llGetSubString( sTimeStart, 14, 15 );
    float fSec_start    =   (float)llGetSubString( sTimeStart, 17, 22 );
    while (fSec < fSec_start)
    {
        fSec += 60.0;
        --iMin;
    }
    while (iMin < iMin_start)
    {
        iMin += 60;
        --iHour;
    }
    while (iHour < iHour_start)
    {
        iHour += 24;
        --iDay;
    }
    iHour = iHour - iHour_start;
    iMin = iMin - iMin_start;
    fSec = fSec - fSec_start;
    string sH = ":";
    string sM = sH;
    string sS = sH;
    string sD;
    if (iHour < 10)
        sH = ":0";
    if (iMin < 10)
        sM = ":0";
    if (fSec < 10.0)
        sS = ":0";
    sD=(string)iHour;
    return llGetSubString("00000",llStringLength(sD),5) + sD + sH + (string)iHour + sM + (string)iMin + sS + (string)fSec;
}

ScriptLibrary


LibraryTimelessLinkedDoor

Timeless Linked Door

Conversational name: linked door.
Discussion thread in forums: http://forums.secondlife.com/showthread.php?t=102699

The Timeless Linked Door is a full-featured door script developed for ease of use by builders and users alike.

Simplest usage for builders:
1. Copy and paste this script into the door prim and link it to the other prims (of a house for example).
2. When the door is in the closed position say '/door closed' to record it.
3. When the door is in the open position say '/door opened' to record it.
The door is now ready for use.

More advanced features can be explored by reading the usage instructions at the top of the script.

The Timeless Linked Door script also has an API for advanced scripted to communicate with it.

I DO NOT support this script so please don't ask me about it.

This script is free to use, but the intellectual property is still mine. You may not sell Timeless Linked Door script unless it is part of your pre-fabricated product (eg. house with a door). If you are making a raging profit, please show your financial appreciation by donating to TimelessPrototype in SL.

Here is the script to place in the door prim:
//------------------------------------------------------
// Timeless Linked Door Script by Timeless Prototype
//------------------------------------------------------
// The latest version of this script can always be found
// in the Library section of the wiki:
// http://www.secondlife.com/badgeo/
// This script is free to use, but whereever it is used
// the SCRIPT's permissions MUST be set to:
// [x] Next owner can modify
// [x] Next owner can copy
// [x] Next owner can transfer
// [x] Allow anyone to copy
// [x] Share with group

//------------------------------------------------------
// USAGE INSTRUCTIONS FOR EVERYDAY USE:
//------------------------------------------------------
// Say the following commands on channel 0:
// 'unlock'     - Unlocks all doors in range.
// 'lock'       - Locks all doors in range and allows
//                only the permitted users to open it.
// To open the door, either Touch it, Walk into it or
// say 'open' or say 'close'.

//------------------------------------------------------
// USAGE INSTRUCTIONS FOR BUILDERS:
//------------------------------------------------------
// 1. Copy and paste this script into the door prim and
//    change the settings (see further down).
// 2. The door prim must be linked to at least one other
//    prim (could be linked to the house for example).
// 3. The door prim MUST NOT be the root prim.
// 4. Use Edit Linked Parts to move, rotate and size the
//    door prim for the closed state.
// 5. When ready, stand close to the door and say
//    '/door closed' (this records the closed door
//    position, rotation and size to the object's
//    name and description).
// 6. Use the Edit Linked parts to move, rotate and size
//    the door prim for the opened state.
// 7. When ready, stand close to the door and say
//    '/door opened' (this records the opened door
//    position, rotation and size).
// 8. Once recorded it will not accept these commands
//    again. If you do need to redo the settings then
//    delete the Name and Description of the door prim
//    (these are where the position information is
//    stored), and then follow the steps above again.
//    Note: deleting the object name won't save, so set
//    the object name to 'Object' to reset the object
//    name.

//------------------------------------------------------
// Change these settings to suit your needs.
//------------------------------------------------------
// To mute any/all of the sounds set the sound string(s)
// to "" (empty string).
// To get the UUID of a sound, right click on the sound
// in your inventory and choose "Copy Asset UUID", then
// paste the UUID in here.
string      doorOpenSound       = "cb340647-9680-dd5e-49c0-86edfa01b3ac";
string      doorCloseSound      = "e7ff1054-003d-d134-66be-207573f2b535";
string      confirmedSound      = "69743cb2-e509-ed4d-4e52-e697dc13d7ac";
string      accessDeniedSound   = "58da0f9f-42e5-8a8f-ee51-4fac6c247c98";
string      doorBellSound       = "ee871042-e272-d8ec-3d40-0b0cb3371346"; // Setting to empty stops door announcements too.
float       autoCloseTime       = 120.0; // 0 seconds to disable auto close.
integer     allowGroupToo       = TRUE; // Set to FALSE to disallow same group access to door.
list        allowedAgentUUIDs   = ["8efecbac-35de-4f40-89c1-2c772b83cafa"]; // Comma-separated, quoted list of avatar UUIDs who are allowed access to this door.
integer     listenChannel       = 0;

//------------------------------------------------------
// Leave the rest of the settings alone, these are
// handled by the script itself.
//------------------------------------------------------
integer     isLocked              = FALSE; // Only when the door is locked do the permissions apply.
integer     isOpen              = TRUE;
vector      openPos             = ZERO_VECTOR;
rotation    openRot             = ZERO_ROTATION;
vector      openScale           = ZERO_VECTOR;
vector      closedPos           = ZERO_VECTOR;
rotation    closedRot           = ZERO_ROTATION;
vector      closedScale         = ZERO_VECTOR;
key         openerKey           = NULL_KEY;
key         closerKey           = NULL_KEY;
integer     isSetup             = FALSE;
integer     listenHandle        = 0;
string      avatarName          = "";

mySayName(integer channel, string objectName, string message)
{
    string name = llGetObjectName();
    llSetObjectName(objectName);
    llSay(0, "/me " + message);
    llSetObjectName(name);
}

mySay(integer channel, string message)
{
    string name = llGetObjectName();
    llSetObjectName("Door");
    llSay(0, message);
    llSetObjectName(name);
}

myOwnerSay(string message)
{
    string name = llGetObjectName();
    llSetObjectName("Door");
    llOwnerSay(message);
    llSetObjectName(name);
}

mySoundConfirmed()
{
    if (confirmedSound != "")
    {
        llTriggerSound(confirmedSound, 1.0);
    }
}

mySoundAccessDenied()
{
    if (accessDeniedSound != "")
    {
        llTriggerSound(accessDeniedSound, 1.0);
    }
}

myGetDoorParams()
{
    isSetup = FALSE;
    if (llSubStringIndex(llGetObjectDesc(), "door;") == 0 && llSubStringIndex(llGetObjectName(), "door;") == 0)
    {
        list nameWords = llParseString2List(llGetObjectName(), [";"], []);
        list descWords = llParseString2List(llGetObjectDesc(), [";"], []);
        if (llGetListLength(nameWords) != 4 || llGetListLength(descWords) != 4)
        {
            myOwnerSay("The door prim's name and/or description has invalid syntax and/or number of parameters. Delete the door prim's name and description and setup the door prim again.");
        }
        else
        {
            openPos = (vector)llList2String(nameWords, 1);
            openRot = (rotation)llList2String(nameWords, 2);
            openScale = (vector)llList2String(nameWords, 3);
            closedPos = (vector)llList2String(descWords, 1);
            closedRot = (rotation)llList2String(descWords, 2);
            closedScale = (vector)llList2String(descWords, 3);
            isSetup = TRUE;
        }
    }
}

mySetDoorParams(vector openPos, rotation openRot, vector openScale, vector closedPos, rotation closedRot, vector closedScale)
{
    llSetObjectName("door;" +
        (string)openPos + ";" +
        (string)openRot + ";" +
        (string)openScale);
    llSetObjectDesc("door;" +
        (string)closedPos + ";" +
        (string)closedRot + ";" +
        (string)closedScale);
    isSetup = TRUE;
}

integer myPermissionCheck(key id)
{
    integer hasPermission = FALSE;
    if (isLocked == FALSE)
    {
        hasPermission = TRUE;
    }
    else if (llGetOwnerKey(id) == llGetOwner())
    {
        hasPermission = TRUE;
    }
    else if (allowGroupToo == TRUE && llSameGroup(id))
    {
        hasPermission = TRUE;
    }
    else if (llListFindList(allowedAgentUUIDs, [(string)id]) != -1)
    {
        hasPermission = TRUE;
    }
    return hasPermission;
}

myOpenDoor()
{
    isOpen = FALSE;
    myToggleDoor();
}

myCloseDoor()
{
    isOpen = TRUE;
    myToggleDoor();
}

myToggleDoor()
{
    if (isSetup == FALSE)
    {
        myOwnerSay("The door prim has not been configured yet. Please read the usage instructions in the door script.");
    }
    else if (llGetLinkNumber() == 0 || llGetLinkNumber() == 1)
    {
        myOwnerSay("The door prim must be linked to at least one other prim and the door prim must not be the root prim");
    }
    else
    {
        isOpen = !isOpen;
        if (isOpen)
        {
            if (doorBellSound != "")
            {
                llTriggerSound(doorBellSound, 1.0);
                if (avatarName != "")
                {
                    mySayName(0, avatarName, "is at the door.");
                    avatarName = "";
                }
            }
            if (doorOpenSound != "")
            {
                llTriggerSound(doorOpenSound, 1.0);
            }
            llSetPrimitiveParams([ PRIM_POSITION, openPos, PRIM_ROTATION, ZERO_ROTATION * openRot / llGetRootRotation(), PRIM_SIZE, openScale ]);
            // Door API.
            llMessageLinked(LINK_SET, 255, "cmd|door|opened", NULL_KEY);
        }
        else
        {
            if (doorCloseSound != "")
            {
                llTriggerSound(doorCloseSound, 1.0);
            }
            llSetPrimitiveParams([ PRIM_POSITION, closedPos, PRIM_ROTATION, ZERO_ROTATION * closedRot / llGetRootRotation(), PRIM_SIZE, closedScale ]);
            // Door API.
            llMessageLinked(LINK_SET, 255, "cmd|door|closed", NULL_KEY);
        }

        llSetTimerEvent(0.0);
        if (isOpen == TRUE && autoCloseTime != 0.0)
        {
            llSetTimerEvent(autoCloseTime);
        }
    }
}

default
{
    state_entry()
    {
        listenHandle = llListen(listenChannel, "", NULL_KEY, "");
        myGetDoorParams();
    }

    touch_start(integer total_number)
    {
        if (myPermissionCheck(llDetectedKey(0)) == TRUE)
        {
            avatarName = llDetectedName(0);
            myToggleDoor();
        }
        else
        {
            mySoundAccessDenied();
        }
    }

    timer()
    {
        myCloseDoor();
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        // Door API. The API is here in case you want to create PIN entry keypads or whatever.
        if (num == llGetLinkNumber())
        {
            if (str == "cmd|door|doOpen")
            {
                myOpenDoor();
            }
            else if (str == "cmd|door|doClose")
            {
                myCloseDoor();
            }
        }
        if (str == "cmd|door|discover")
        {
            llMessageLinked(LINK_SET, 255, "cmd|door|discovered|" + (string)llGetKey(), id);
        }
    }

    listen(integer channel, string name, key id, string message)
    {
        // Performance note: it's quicker to compare the strings than to compare permissions each time anyone says anything on this channel.
        if (message == "open")
        {
            if (myPermissionCheck(id) == TRUE)
            {
                // Only open the door if the person is quite close to this door.
                openerKey = id;
                closerKey = NULL_KEY;
                avatarName = name;
                llSensor(name, id, AGENT, 5.0, TWO_PI);
            }
            else
            {
                mySoundAccessDenied();
            }
        }
        else if (message == "close")
        {
            if (myPermissionCheck(id) == TRUE)
            {
                openerKey = NULL_KEY;
                closerKey = id;
                avatarName = name;
                // Only close the door if the person is quite close to this door.
                llSensor(name, id, AGENT, 5.0, TWO_PI);
            }
            else
            {
                mySoundAccessDenied();
            }
        }
        else if (message == "lock")
        {
            if (myPermissionCheck(id) == TRUE)
            {
                isLocked = TRUE;
                mySoundConfirmed();
            }
            else
            {
                mySoundAccessDenied();
            }
        }
        else if (message == "unlock")
        {
            if (myPermissionCheck(id) == TRUE)
            {
                isLocked = FALSE;
                mySoundConfirmed();
            }
            else
            {
                mySoundAccessDenied();
            }
        }
        else if (message == "/door opened" && llSubStringIndex(llGetObjectName(), "door;") == -1)
        {
            if (llGetOwnerKey(id) == llGetOwner())
            {
                mySoundConfirmed();
                openPos = llGetLocalPos();
                openRot = llGetLocalRot();
                openScale = llGetScale();
                isOpen = TRUE;
                if (! (closedPos == ZERO_VECTOR && closedRot == ZERO_ROTATION && closedScale == ZERO_VECTOR))
                {
                    mySetDoorParams(openPos, openRot, openScale, closedPos, closedRot, closedScale);
                }
            }
            else
            {
                mySoundAccessDenied();
            }
        }
        else if (message == "/door closed" && llSubStringIndex(llGetObjectDesc(), "door;") == -1)
        {
            if (llGetOwnerKey(id) == llGetOwner())
            {
                mySoundConfirmed();
                closedPos = llGetLocalPos();
                closedRot = llGetLocalRot();
                closedScale = llGetScale();
                isOpen = FALSE;
                if (! (openPos == ZERO_VECTOR && openRot == ZERO_ROTATION && openScale == ZERO_VECTOR))
                {
                    mySetDoorParams(openPos, openRot, openScale, closedPos, closedRot, closedScale);
                }
            }
            else
            {
                mySoundAccessDenied();
            }
        }
    }

    sensor(integer num_detected)
    {
        if (openerKey != NULL_KEY)
        {
            integer i;
            for (i = 0; i < num_detected; i++)
            {
                if (llDetectedKey(i) == openerKey && myPermissionCheck(llDetectedKey(i)) == TRUE)
                {
                    myOpenDoor();
                }
            }
            openerKey = NULL_KEY;
        }
        else
        {
            integer i;
            for (i = 0; i < num_detected; i++)
            {
                if (llDetectedKey(i) == closerKey && myPermissionCheck(llDetectedKey(i)) == TRUE)
                {
                    myCloseDoor();
                }
            }
            closerKey = NULL_KEY;
        }
    }

//------------------------------------------------------
// Uncomment the following code if you particularly want
// collisions to affect the door state.
//------------------------------------------------------

//    collision_start(integer num_detected)
//    {
//        integer i;
//        for (i = 0; i < num_detected; i++)
//        {
//            if (myPermissionCheck(llDetectedKey(i)) == TRUE)
//            {
//                avatarName = llDetectedName(i);
//                myOpenDoor();
//            }
//            else if (llDetectedType(i) & AGENT)
//            {
//                mySoundAccessDenied();
//            }
//        }
//    }

} // End of default state and end of script.


LibraryTimerEnhancer

This script allows you to more precisely control the code that executes in a timer event. Since a script can only have one timer at a time, scripters must use complex global variables to have, for example, code that executes after 3 seconds pass, and code that executes after 1 second passes using the same timer event "clock". This is an attempt to standardize that process.

When a script calls setTimeout, a new timeout is created, unless the identifier is already in use. If the identifier is already in use, then the function removes the old timeout associated with that identifier, and creates a new one. If the time passed to setTimeout is 0, then setTimeout removes the timeout associated with the identifier.

After the specified time has passed, the timeout function is called with the timeout's identifier. After timeout is called with the identifier, it will no longer be called with that identifier unless the timeout is recreated using setTimeout.

The timeouts can only be as accurate as the TIMER_INTERVAL specifies, a timeout of 2.4 will only trigger at exactly 2.4 seconds after setTimeout is called, if and only if 2.4 is a multiple of the TIMER_INTERVAL. If not, the timeout will be triggered as soon as the amount of time between timer events exceeds 2.4. For example, if TIMER_INTERVAL is 1, and setTimeout is asked to trigger timeout after 2.4 seconds, then the timeout will be triggered after 3 seconds. If TIMER_INTERVAL is 0.5, then timeout will be triggered after 2.5 seconds. Note that a smaller TIMER_INTERVAL creates more calls to the timer() event, which may contribute to lag.

float TIMER_INTERVAL = 1;

list timeoutData;
float timePassed;
setTimeout(string identifyer, float time) {
    if(timeoutData == [])
        llSetTimerEvent(TIMER_INTERVAL); // This helps reduce lag, thanks BW :-)
    integer identifyerIndex = llListFindList(timeoutData, [identifyer]);
    if (identifyerIndex != -1)
        timeoutData = llDeleteSubList(timeoutData, identifyerIndex - 1, identifyerIndex);
    if (time != 0) {
        timeoutData += time + timePassed;
        timeoutData += identifyer;
    }
    timeoutData = llListSort(timeoutData, 2, TRUE);
}

timeout(string identifyer) {
    // Stuff to do for each timeout goes here.
}

timerTick() {
    // Code that executes every timer event,
    // which manages the timeouts.
    timePassed += TIMER_INTERVAL;
    integer i;
    integer numTimers = llGetListLength(timeoutData);
    for (i = 0; i < numTimers; i += 2) {
        float triggerTime = llList2Float(timeoutData, i);
        if (triggerTime <= timePassed) {
            // Timeout will be triggered, remove it from list.
            string identifyer = llList2String(timeoutData, i + 1);
            timeoutData = llDeleteSubList(timeoutData, i, i + 1);
            // Execute the timeout code.
            timeout(identifyer);
            if (timeoutData == [])
                llSetTimerEvent(0); // This helps reduce lag, thanks BW :-)
        } else {
            // Since the list is sorted, when we encounter a timeout
            // that shouldn't trigger yet, we can stop iterating over the list.
            return;
        }
    }
}

default {
    timer() {
        // This function allows timeouts to execute.
        timerTick();
    }
}

Example Usage:

float TIMER_INTERVAL = 1;

list timeoutData;
float timePassed;
setTimeout(string identifyer, float time) {
    if(timeoutData == [])
        llSetTimerEvent(TIMER_INTERVAL);
    integer identifyerIndex = llListFindList(timeoutData, [identifyer]);
    if (identifyerIndex != -1)
        timeoutData = llDeleteSubList(timeoutData, identifyerIndex - 1, identifyerIndex);
    if (time != 0) {
        timeoutData += time + timePassed;
        timeoutData += identifyer;
    }
    timeoutData = llListSort(timeoutData, 2, TRUE);
}

timeout(string identifyer) {
    if (identifyer == "touched") {
        llSay(0, "Three seconds have passed since someone last touched me!");
    } else if (identifyer == "30 second ping") {
        llSay(0, "ping");
        // Say ping again, after 30 seconds.
        setTimeout("30 second ping", 30);
    }
}

timerTick() {
    timePassed += TIMER_INTERVAL;
    integer i;
    integer numTimers = llGetListLength(timeoutData);
    for (i = 0; i < numTimers; i += 2) {
        float triggerTime = llList2Float(timeoutData, i);
        if (triggerTime <= timePassed) {
            string identifyer = llList2String(timeoutData, i + 1);
            timeoutData = llDeleteSubList(timeoutData, i, i + 1);
            timeout(identifyer);
            if (timeoutData == [])
                llSetTimerEvent(0);
        } else {
            return;
        }
    }
}

default {
    state_entry() {
        setTimeout("30 second ping", 30);
    }

    touch_start(integer touchers) {
        // The "touched" timeout will execute after 3 seconds.
        setTimeout("touched", 3.0);
    }

    timer() {
        timerTick();
    }
}

Creative Commons License
This work is licensed under a Creative Commons Public Domain License.


LibraryTimestampDifference

A quicky to find the amount of time in seconds between two timestamps. Makes use of a simplified Julian Date formula from Weisstein's World of Science (website listed in the code) that works for years between 1901 and 2099. I include an example function illustrating converting a timestamp to unixtime.

// Find the difference between two timestamps in seconds
// Works for years between 1901 and 2099
//
// Ben Fassbinder  Oct 17, 2006
//

integer timestamp_diff(string ts1, string ts2)
{
    list ts = llParseString2List( ts1, ["-","T",":","Z"] , [] );

    integer y = (integer)llList2String(ts, 0);
    integer l = (integer)llList2String(ts, 1);
    integer d = (integer)llList2String(ts, 2);
    integer h = (integer)llList2String(ts, 3);
    integer m = (integer)llList2String(ts, 4);
    integer s1 = llRound((float)llList2String(ts, 5));

    s1 += 60*m + 3600*h;

    integer jd1 = julian_date(y, l, d);

    ts = llParseString2List( ts2, ["-","T",":","Z"] , [] );

    y = (integer)llList2String(ts, 0);
    l = (integer)llList2String(ts, 1);
    d = (integer)llList2String(ts, 2);
    h = (integer)llList2String(ts, 3);
    m = (integer)llList2String(ts, 4);
    integer s2 = llRound((float)llList2String(ts, 5));

    s2 += 60*m + 3600*h;

    integer jd2 = julian_date(y, l, d);

    return 86400*(jd2 - jd1) + s2 - s1;
}

// Julian date minus a constant (1721013.5)
// since we are only doing differences, the constant is unnecessary.
// valid for dates between 1901 and 2099
//
// source:  http://scienceworld.wolfram.com/astronomy/JulianDate.html

integer julian_date(integer y, integer m, integer d)
{
    integer result = 367 * y - llFloor(7.0 *
                                        (y + llFloor( (m+9.0)/12.0 ) )/4.0
                                    );

    result += llFloor(275.0*m/9.0) + d;

    return result;
}

// timestamp to unix time.
// unix time = 0 corresponds to midnight GMT Jan 1, 1970

integer timestamp_to_unixtime(string ts)
{
    return timestamp_diff("1970-01-01T00:00:00Z", ts);
}

default
{
    touch_start(integer total_number)
    {
        integer ut = llGetUnixTime();
        integer ts = timestamp_to_unixtime(llGetTimestamp());

        llSay(0, (string)ts + " vs. " + (string)ut);
    }
}

Creative Commons License
This work is licensed under a Creative Commons Public Domain License.


LibraryTrueTele

Examples ScriptLibrary AnorcaCalamari

This is not cap sensitive. If you put in an invalid simname for tele the map will still open up itll just show NONE FOUND in the search results box.
If you set your home to the wrong place itll do that till you change your home.

Command | Use | Example
-sethomehere | sets your home sim and vector to this position. | -sethomehere
-sethome SIMNAME | sets your home sim to SIMNAME | -sethome pullman
-sethomevec VECTOR | sets the vector you appear at when you say home(default <128,128,100>) | -sethomevec <100,61,42>
-home | Opens up the map targeted on your home | -home
-tele SIMNAME | Opens the map targeted on simulator SIMNAME | -tele pullman

Created by AnorcaCalamari
//Created by Anorca Calamari
//Please do not sell this script except as part of a larger creation. You may give it away for free.
//You may modify this script.
string home="NO_HOME";
string homevec="<128,128,100>";
default
{
    state_entry()
    {
        llListen(0,"",llGetOwner(),"");
    }

    listen(integer channel,string name,key id,string message)
    {
        string command=llToLower(message);
        if(llSubStringIndex(command,"-tele ") == 0)
        {
            llMapDestination(llDeleteSubString(message,0,5),<128,128,60>,<128,128,160>);
        }
        if(command == "-home")
        {
            if(home == "NO_HOME")
            {
                llOwnerSay("You have not set a home yet.");
            }
            if(home != "NO_HOME")
            {
                llMapDestination(home,(vector)homevec,(vector)homevec);
            }
        }
        if(llSubStringIndex(command,"-sethome ") == 0)
        {
            home=llDeleteSubString(message,0,8);
            llOwnerSay("Home sim set to:" + home);
        }
        if(llSubStringIndex(command,"-sethomevec ") == 0)
        {
            homevec=llDeleteSubString(message,0,11);
            llOwnerSay("Home vector set to:" + homevec);
        }
        if(command == "-sethomehere")
        {
            home=llGetRegionName();
            homevec=(string)llGetPos();
            llOwnerSay("Home set to here");
        }
    }
}


LibraryVendor

This is a pretty straight forward vendor. It needs 5 scripts, the main vendor script, XyText for the display of the price, next and prev buttons and a display script to show an image. This vendor can sell all kinds of items including objects (vehicles, attachments), clothes, tattoos etc.

Setting up the vendor

Some guidelines for building the vendor:
1. You will need 1 prim for the display, 1 prim for XyText for the price, 1 prim for the next button and 1 prim for the prev button.
2. The design of the vendor is largly up to you with 1 recommendation: make the screenshot display prim very thin and the sides black or hidden by other prims.
3. Link all the prims together and put the appropriate scripts on the appropriate pieces. The XyText prim should be set up prior to linking.
3b I recomend having a seperate parent prim for the main script, but it can just as easily be put anywhere.
4. Add all the items you with to sell, along with all their screenshots/display images
5. Create a notecard named "VendorConfig" that follows the format of the example notecard below.

Example setup notecard
itemForSale,imageName,price
AmaShirt1,amaShirtAd1,100
AmaShirt2,amaShirtAd2,50
AmaShirt3,amaShirtAd3,200

Thanks ChandraPage. The script now accepts blank lines anywhere in the notecard without causing an error.

Main Vendor Script
// Ama Omega Vendor Script v0.3.1
// 06/2004

string name = "Vendor ";

// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;

integer display;

list prices;
list pics;
list items;
list sold;

key k;
list temp;
integer line;
integer l;
string s;

string moneyText(string amount)
{
    s = "$" + amount;
    while (llStringLength(s) < 6)
    {
        s = " " + s;
        if (llStringLength(s) < 6) s = s + " ";
    }
    return s;
}

incSold(integer i)
{
    sold = llListInsertList(llDeleteSubList(sold,i,i),[(integer)(llList2Integer(sold,i) + 1)],i);
}

default
{
    state_entry()
    {
        llMessageLinked(LINK_SET, DISPLAY_STRING, "EMPTY!", "");
        state setup;
    }
}

state setup
{
    state_entry()
    {
        llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
    }

    run_time_permissions(integer perms)
    {
        if (perms & PERMISSION_DEBIT)
        {
            state running;
        }
    }
}

state running
{
    state_entry()
    {
        llListen(0,"",llGetOwner(),"reset vendor");
        llListen(0,"",llGetOwner(),"vendor report");
    }

    on_rez(integer param)
    {
        llResetScript();
    }

    listen(integer chan, string name, key id, string mes)
    {
        if (mes == "reset vendor")
        {
            state readCard;
        }
        else if (mes == "vendor report")
        {
            integer i;
            for(i=0;i<llGetListLength(sold);i++)
            {
                if (llList2Integer(sold,i) != 0) llWhisper(0,"* Sold " + (string)llList2Integer(sold,i) + " " + llList2String(items,i));
            }
        }
    }

    link_message(integer link, integer num, string mes, key id)
    {
        if (llGetPermissions() & !PERMISSION_DEBIT) state broken;
        if (mes == "NEXT")
        {
            display += 1;
            if (display >= llGetListLength(items)) display = 0;
            llSetObjectName(name + llList2String(items,display));
            llMessageLinked(LINK_SET, DISPLAY_STRING, moneyText(llList2String(prices,display)), "");
            llMessageLinked(LINK_SET,1,"PIC",llGetInventoryKey((key)llList2String(pics,display)));
            llMessageLinked(LINK_SET,1,"LOADNEXT",llGetInventoryKey((key)llList2String(pics,display + 1)));
            llMessageLinked(LINK_SET,1,"LOADPREV",llGetInventoryKey((key)llList2String(pics,display - 1)));
        }
        else if (mes == "PREV")
        {
            display -= 1;
            if (display < 0) display = llGetListLength(items) - 1;
            llSetObjectName(name + llList2String(items,display));
            llMessageLinked(LINK_SET, DISPLAY_STRING, moneyText(llList2String(prices,display)), "");
            llMessageLinked(LINK_SET,1,"PIC",llGetInventoryKey((key)llList2String(pics,display)));
            llMessageLinked(LINK_SET,1,"LOADNEXT",llGetInventoryKey((key)llList2String(pics,display + 1)));
            llMessageLinked(LINK_SET,1,"LOADPREV",llGetInventoryKey((key)llList2String(pics,display - 1)));
        }
    }

    money(key id, integer amount)
    {
        if (amount == (integer)llList2String(prices,display))
        {
            incSold(display);
            llGiveInventory(id, llList2String(items,display));
            llWhisper(0,"Thank you for your purchase!");
        }
        else
        {
            llWhisper(0,"Sorry that is the wrong amount for this item.");
            llGiveMoney(id, amount);
        }
    }
}

state readCard
{
    state_entry()
    {
        line = 0;
        k = llGetNotecardLine("VendorConfig",line++);
        items = [];
        pics = [];
        prices = [];
    }

    dataserver(key q, string data)
    {
        if (q == k)
        {
            temp = llCSV2List(data);
            if (llParseString2List(data,[" "],[""]) == []);
            else if (data == EOF)
            {
                display = 0;
                llMessageLinked(LINK_SET, DISPLAY_STRING, moneyText(llList2String(prices,display)), "");
                llMessageLinked(LINK_SET,1,"PIC",llGetInventoryKey(llList2String(pics,0)));
                llWhisper(0," * Vendor setup complete : " + (string)llGetFreeMemory() + " bytes free.");
                state running;
            }
            else if (llGetListLength(temp) != 3)
            {
                llWhisper(0,"Error, improperly formatted line #" + (string)(line - 1));
                state running;
            }
            else
            {
                items += llList2String(temp,0);
                pics += llList2String(temp,1);
                prices += llList2String(temp,2);
                sold += [(integer)0];
                if ( llGetInventoryKey((key)llList2String(temp,0)) == NULL_KEY || llGetInventoryKey((key)llList2String(temp,1)) == NULL_KEY)
                {
                    llWhisper(0,"Error, missing inventory or picture from line : " + data);
                    state running;
                }
                k = llGetNotecardLine("VendorConfig",line++);
            }
        }
    }
}

state broken
{
    state_entry()
    {
        llWhisper(0,"This machine has lost permissions! Please check back later!");
        llListen(0,"",llGetOwner(),"fix " + llGetObjectName());
    }

    listen(integer chan, string name, key id, string mes)
    {
        state setup;
    }
}

Next Button
default
{
    touch_start(integer total_number)
    {
        llMessageLinked(LINK_SET,1,"NEXT",llDetectedKey(0));
    }
}

Prev Button
default
{
    touch_start(integer total_number)
    {
        llMessageLinked(LINK_SET,1,"PREV",llDetectedKey(0));
    }
}

Image Display
This one may need to be modified depending on the orientation of your display.
integer front = 0;  // Set this to the face of the prim you want the image on.
//Note: This script assumes that the sides of the display are either black or nonvisible.

default
{
    link_message(integer link, integer num, string mes, key id)
    {
        if (id != NULL_KEY)
        {
            if (mes == "PIC")
            {
                llSetTexture(id,front);
            }
            else if (mes == "LOADPREV")
            {
                // If you change front then this may need to be adjusted, or disabled.
                llSetTexture(id,front + 1);
            }
            else if (mes == "LOADNEXT")
            {
                // If you change front then this may need to be adjusted, or disabled.
                llSetTexture(id,front + 2);
            }
        }
    }
}

ScriptLibrary


LibraryVigenereCipher

A Vigenere cipher. It works by taking the key that you input, repeating it to the same length of the text, and then rotates the alphabet with which it encrypts. You can read more about it here.

Thanks, BlindWanderer for the tip about using strings as opposed to lists.
string _key = "goat";

string keyRepeat(string text, string ky){
    integer i;
    integer j;
    integer l = llStringLength(text);
    string out;
    for(i = 0; i <= l; ++i){
        out += llGetSubString(ky,j,j);
        if(j == llStringLength(ky)){
            j = 0;
        } else {
            ++j;
        }
    }
    return out;
}

string encrypt(string text, string ky){
    string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz. ,!?&";
    ky = keyRepeat(text,ky);
    list t = llParseString2List(text,[],[]);
    integer l = llGetListLength(t);
    integer i;
    string o;
    integer prev;
    for(i = 0; i < l; ++i){
        integer x;
        integer y = llStringLength(llList2String(t,i));
        string ky_temp = llGetSubString(ky,prev,llStringLength(llList2String(t,i)));
        prev = llStringLength(llList2String(t,i));
        for(x = 0; x < y; ++x){
            integer text_p = llSubStringIndex(alphabet,llGetSubString(llList2String(t,i),x,x));
            integer key_p = llSubStringIndex(alphabet,llGetSubString(ky_temp,x,x));

            integer pos = (text_p + key_p)%57;

            o += llGetSubString(alphabet,pos,pos);
        }
        o += " ";
    }
    return o;
}

string decrypt(string text, string ky){
    string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz. ,!?&";
    ky = keyRepeat(text,ky);
    list t = llParseString2List(text,[],[]);
    integer l = llGetListLength(t);
    integer i;
    string o;
    integer prev;
    for(i = 0; i < l; ++i){
        integer x;
        integer y = llStringLength(llList2String(t,i));
        string ky_temp = llGetSubString(ky,prev,llStringLength(llList2String(t,i)));
        prev = llStringLength(llList2String(t,i));
        for(x = 0; x < y; ++x){
            integer text_p = llSubStringIndex(alphabet,llGetSubString(llList2String(t,i),x,x));
            integer key_p = llSubStringIndex(alphabet,llGetSubString(ky_temp,x,x));

            integer pos = (text_p - key_p + 57)%57;

            o += llGetSubString(alphabet,pos,pos);
        }
        o += " ";
    }
    return o;
}

default{
    state_entry(){
        llListen(0,"",llGetOwner(),"");
        llSetObjectName("Cipher");
    }
    listen(integer c, string n, key i, string ms){
        list m = llParseString2List(ms,[":"],[]);
        if(llList2String(m,0) == "encrypt"){
            llSay(0,encrypt(llList2String(m,1),_key));
        } else if(llList2String(m,0) == "decrypt"){
            llSay(0,decrypt(llList2String(m,1),_key));
        } else if(llList2String(m,0) == "key"){
            _key = llList2String(m,1);
            llSay(0,"Key set to \""+_key+"\".");
        }
    }
}


LibraryWarpPos

The corresponding forum thread for this can be found here.

For an alternate method that does not use llSetPrimitiveParams, but parallel calls to llSetPos (link messages to multiple llSetPos scripts); if you can understand it, try LibrarySenWarp instead.

WarpPos: a method by which the 10m limit on non-physical movement, which limits speed to about 50m/s, may be avoided.

llSetPrimitiveParams, it would seem, executes each rule in the list as it comes across it. This allows you to set up a list in the form of [PRIM_POSITION,pos,PRIM_POSITION,pos, ... ,PRIM_POSITION,pos] which, when run by the server, basically causes many llSetPos calls in a very short amount of time. As far as I can tell, this is done in one frame on the server-- so there are no intermediate steps in the journey.

It's completely public domain. Look at it, modify it, sell it in an item, whatever. But I'll be annoyed if you scam noobs into buying it.

warpPos( vector d ) //R&D by Keknehv Psaltery, ~05/25/2006
{
    if ( d.z > 768 )      //Otherwise we'll get stuck hitting the ceiling
        d.z = 768;
    integer s = (integer)(llVecMag(d-llGetPos())/10)+1; //The number of jumps necessary
    if ( s > 100 )  //Try and avoid stack/heap collisions with far away destinations
        s = 100;    //  1km should be plenty
    integer e = (integer)( llLog( s ) / llLog( 2 ) );   //Solve '2^n=s'
    list rules = [ PRIM_POSITION, d ];  //The start for the rules list
    integer i;
    for ( i = 0 ; i < e ; ++i )     //Start expanding the list
        rules += rules;
    integer r = s - (integer)llPow( 2, e );
    if ( r > 0 )                    //Finish it up
        rules += llList2List( rules, 0, r * 2 + 1 );
    llSetPrimitiveParams( rules );
}

A few observations:
Sim crossings are perilous for AVs. Depending on connection speed and whether or not you are connected to the sim (can see it on the mini-map), it may screw up your client. However, it seems like objects, by themselves, can cross great distances. I managed to send an object 4 sims away diagonally. Further testing would help us to understand these things.

The script limits the maximum distance to 1km, in order to prevent stack/heap collisions. Still, this is 100 times what llSetPos was limited to-- and you can, of course, modify it to allow larger distances, but beware.

The average time this function takes to execute is .2 seconds, which is barely noticeable at all, and can easily be attributed to general lag. A simple optimization for an object with a known destination might be to calculate the list beforehand, and then call llSetPrimitiveParams with that list.

BlindWanderer (Strife Onizuka) and a few other people took my code and smashed the list expansion, making it much faster and less memory intensive. Here's the modified code:
warpPos( vector destpos)
{   //R&D by Keknehv Psaltery, 05/25/2006
    //with a little pokeing by Strife, and a bit more
    //some more munging by Talarus Luan
    //Final cleanup by Keknehv Psaltery
    // Compute the number of jumps necessary
    integer jumps = (integer)(llVecDist(destpos, llGetPos()) / 10.0) + 1;
    // Try and avoid stack/heap collisions
    if (jumps > 100 )
        jumps = 100;    //  1km should be plenty
    list rules = [ PRIM_POSITION, destpos ];  //The start for the rules list
    integer count = 1;
    while ( ( count = count << 1 ) < jumps)
        rules = (rules=[]) + rules + rules;   //should tighten memory use.
    llSetPrimitiveParams( rules + llList2List( rules, (count - jumps) << 1, count) );
    if ( llVecDist( llGetPos(), destpos ) > .001 ) //Failsafe
        while ( --jumps )
            llSetPos( destpos );
}

Well, that's all. Have fun!


LibraryWindTurbine

Wind Turbine Capacitor

This script will calculate the amount of watts of energy an object can store based on certain variables.

Variables:

Variable Type | Variable Type
Air Density | Air Pressure
Coefficient of Perfomance | Cloud Cover
Curent Position | Gas Constant
Gearbox/Bearings Efficiency | Generator Efficiency
Rotor Size | Rotor Swept Area
Sea Level | Sun Angle
Temperature | Wind Speed

Capacitor Script:

float Area;
float base;
float Capacitor;
float GasConstant;
float pascal;
float Power;
float sealevel = 101.32500;
float temperatureF;
float temperatureK;
float WindSpeed;
vector Scale;
vector sun;
vector pos;
default
{
    state_entry()
    {
        llSetTimerEvent(1);
    }
    timer()
    {
        //Get Sun Angle
        sun = llGetSunDirection();
        //Calculate Rotor Size
        Scale = llGetScale();
        //Get Current Position
        pos = llGetPos();
        //Air Pressure Non Relative To Sea Level
        base = llLog10(5- ((pos.z - llWater(ZERO_VECTOR))/15500));
        //Calculate Air Pressure In KiloPascal
        pascal = (sealevel + base);
        //Calculate Wind Speed
        WindSpeed = llVecMag(llWind(<0.0, 0.0, 0.0>));
        //Calculate Gas Constant
        GasConstant = ((200 * llCloud(<0.0, 0.0, 0.0>)) + 280);
        //Calculate Temperature In Degrees Fahrenheit
        temperatureF = ((((pascal * (2 * llPow(10,22)))/ (1.8311*llPow(10,20))/ 8.314472)/19.85553747) + (sun.z * 10));
        //Convert Termperature To Kelvin
        temperatureK = ((temperatureF + 459.67) * 5/9);
        //Calculate Rotor Swept Area
        Area = (2 * PI * (Scale.x * Scale.x)) + (2 * PI * Scale.x * Scale.y);
        //Calculate Power Output
        Power = 0.5 * (pascal / (GasConstant * temperatureK)) * Area * 0.35 * WindSpeed * 0.75 * .95;
        Capacitor += Power;
        llSetText((string)Capacitor + " watts stored",<1,1,1>,1);
    }
}

Formulas:

Wind Turbine Power:

    P = 0.5 x rho x A x Cp x V3 x Ng x Nb

where:
P = power in watts (746 watts = 1 hp) (1,000 watts = 1 kilowatt)
rho = air density (about 1.225 kg/m3 at sea level, less higher up)
A = rotor swept area, exposed to the wind (m2)
Cp = Coefficient of performance (.59 {Betz limit} is the maximum thoretically possible, .35 for a good design)
V = wind speed in meters/sec (20 mph = 9 m/s)
Ng = generator efficiency (50% for car alternator, 80% or possibly more for a permanent magnet generator or grid-connected induction generator)
Nb = gearbox/bearings efficiency (depends, could be as high as 95% if good)

D = P / (R * T)
    where:   D = density, kg/m3
                 P = pressure, Pascals ( multiply mb by 100 to get Pascals)
                 R = gas constant , J/(kg*degK) = 287.05 for dry air
                 T = temperature, degK = deg C + 273.15

Dry air = 280
Wet air = 480

Area = (2 * PI * x^2) + (2 * Pi * x * z)


LibraryXyText12

XyText v1.2 -- llSetText Alternative

... continued from XyText

XyText 5face Single Character Texture

Modified by KermittQuirk 2006/01/19/01 to add support for 5 face prim instead of 3

The best balance between number of textures and number of prims.

Prim setup: Create generic object, insert script to transform it.
////////////////////////////////////////////
// XyText Prim Setup
//
// Written by Gearsawe Stonecutter
////////////////////////////////////////////

string FACE_TEXTURE = "09b04244-9569-d21f-6de0-4bbcf5552222";
string  TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";

default
{
    state_entry()
    {
        llSetPrimitiveParams([

          PRIM_TYPE, PRIM_TYPE_PRISM, PRIM_HOLE_DEFAULT, <0.199, 0.8, 0.0>, 0.68,
            ZERO_VECTOR, <1.0, 1.0, 0.0>, ZERO_VECTOR,

          // display a default face texture
          PRIM_TEXTURE, 1, FACE_TEXTURE, <2.48, 1.0, 0.0>, <-0.740013, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 6, FACE_TEXTURE, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 4, FACE_TEXTURE, <-14.75, 1.0, 0.0>, <0.130009, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 7, FACE_TEXTURE, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 3, FACE_TEXTURE, <2.48, 1.0, 0.0>, <-0.255989, 0.0, 0.0>, 0.0,

          // show transparent textures for the other sides
          PRIM_TEXTURE, 0, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
          PRIM_TEXTURE, 2, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
          PRIM_TEXTURE, 5, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,

          PRIM_SIZE, <0.03, 2.89, 0.5>
        ]);

        // Remove ourselves from inventory.
        llRemoveInventory(llGetScriptName());
    }
}

Main script: drop in each display prim.
////////////////////////////////////////////
// XyText v1.2 Script (5 Face, Single Texture)
//
// Written by Xylor Baysklef
//
// Modified by Kermitt Quirk 19/01/2006
// To add support for 5 face prim instead of 3
//
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;
integer SET_FONT_TEXTURE    = 204005;
integer SET_THICKNESS       = 204006;
integer SET_COLOR           = 204007;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "12345";

// Face numbers.
integer FACE_1          = 3;
integer FACE_2          = 7;
integer FACE_3          = 4;
integer FACE_4          = 6;
integer FACE_5          = 1;

// Used to hide the text after a fade-out.
key     TRANSPARENT     = "701917a8-d614-471f-13dd-5f4644e36e3c";
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the key of the font we are displaying.
key     gFontTexture        = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel        = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition   = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0  means don't fade out.
float   gCellHoldDelay      = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
   gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
   // \" <-- Fixes LSL syntax highlighting bug.
   gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
   gCharIndex += "\n\n\n\n\n";
}

vector GetGridOffset(integer index) {
   // Calculate the offset needed to display this character.
   integer Row = index / 10;
   integer Col = index % 10;

   // Return the offset in the texture.
   return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {
   // Set the primitive textures directly.

   // <-0.256, 0, 0>
   // <0, 0, 0>
   // <0.130, 0, 0>
   // <0, 0, 0>
   // <-0.74, 0, 0>

   llSetPrimitiveParams( [
        PRIM_TEXTURE, FACE_1, (string)gFontTexture, <0.12, 0.1, 0>, grid_offset1 + <0.037, 0, 0>, 0.0,
        PRIM_TEXTURE, FACE_2, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
        PRIM_TEXTURE, FACE_3, (string)gFontTexture, <-0.74, 0.1, 0>, grid_offset3 - <0.244, 0, 0>, 0.0,
        PRIM_TEXTURE, FACE_4, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset4, 0.0,
        PRIM_TEXTURE, FACE_5, (string)gFontTexture, <0.12, 0.1, 0>, grid_offset5 - <0.037, 0, 0>, 0.0
        ]);
}

RenderString(string str) {
   // Get the grid positions for each pair of characters.
   vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
   vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
   vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
   vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
   vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
}

RenderWithEffects(string str) {
   // Get the grid positions for each pair of characters.
   vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
   vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
   vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
   vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
   vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );

   // First set the alpha to the lowest possible.
   llSetAlpha(0.05, ALL_SIDES);

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);          // Now turn up the alpha until it is at full strength.
    float Alpha;
    for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // See if we want to fade out as well.
   if (gCellHoldDelay < 0.0)
       // No, bail out. (Just keep showing the string at full strength).
       return;
          // Hold the text for a while.
   llSleep(gCellHoldDelay);
      // Now fade out.
   for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // Make the text transparent to fully hide it.
   llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
   // Look for escape sequences.
   list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
   integer ParsedLen = llGetListLength(Parsed);

   // Create a list of index values to work with.
   list Indices;
   // We start with room for 5 indices.
   integer IndicesLeft = 5;

   integer i;
   string Token;
   integer Clipped;
   integer LastWasEscapeSequence = FALSE;
   // Work from left to right.
   for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
       Token = llList2String(Parsed, i);

       // If this is an escape sequence, just set the flag and move on.
       if (Token == ESCAPE_SEQUENCE) {
           LastWasEscapeSequence = TRUE;
       }
       else { // Token != ESCAPE_SEQUENCE
           // Otherwise this is a normal token.  Check its length.
           Clipped = FALSE;
           integer TokenLength = llStringLength(Token);
           // Clip if necessary.
           if (TokenLength > IndicesLeft) {
               Token = llGetSubString(Token, 0, IndicesLeft - 1);
               TokenLength = llStringLength(Token);
               IndicesLeft = 0;
               Clipped = TRUE;
           }
           else
               IndicesLeft -= TokenLength;

           // Was the previous token an escape sequence?
           if (LastWasEscapeSequence) {
               // Yes, the first character is an escape character, the rest are normal.

               // This is the extended character.
               Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

               // These are the normal characters.
               integer j;
               for (j = 1; j < TokenLength; j++)
                   Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
           }
           else { // Normal string.
               // Just add the characters normally.
               integer j;
               for (j = 0; j < TokenLength; j++)
                   Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
           }

           // Unset this flag, since this was not an escape sequence.
           LastWasEscapeSequence = FALSE;
       }
   }

   // Use the indices to create grid positions.
   vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0));
   vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
   vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );
   vector GridOffset4 = GetGridOffset( llList2Integer(Indices, 3) );
   vector GridOffset5 = GetGridOffset( llList2Integer(Indices, 4) );

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
}

integer ConvertIndex(integer index) {
   // This converts from an ASCII based index to our indexing scheme.
   if (index >= 32) // ' ' or higher
       index -= 32;
   else { // index < 32
       // Quick bounds check.
       if (index > 15)
           index = 15;

       index += 94; // extended characters
   }

   return index;
}

default {
   state_entry() {
       // Initialize the character index.
       ResetCharIndex();
   }

   link_message(integer sender, integer channel, string data, key id) {
       if (channel == DISPLAY_STRING) {
           RenderString(data);
           return;
       }
       if (channel == DISPLAY_EXTENDED) {
           RenderExtended(data);
           return;
       }
       if (channel == gCellChannel) {
           // Extract the characters we are interested in, and use those to render.
           string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 4);
                      // See if we need to show special effects.
           if (gCellUseFading)
               RenderWithEffects( TextToRender );
           else // !gCellUseFading
               RenderString( TextToRender );
           return;
       }
       if (channel == REMAP_INDICES) {
           // Parse the message, splitting it up into index values.
           list Parsed = llCSV2List(data);
           integer i;
           // Go through the list and swap each pair of indices.
           for (i = 0; i < llGetListLength(Parsed); i += 2) {
               integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
               integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

               // Swap these index values.
               string Value1 = llGetSubString(gCharIndex, Index1, Index1);
               string Value2 = llGetSubString(gCharIndex, Index2, Index2);

               gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
               gCharIndex = llInsertString(gCharIndex, Index1, Value2);

               gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
               gCharIndex = llInsertString(gCharIndex, Index2, Value1);
           }
           return;
       }
       if (channel == RESET_INDICES) {
           // Restore the character index back to default settings.
           ResetCharIndex();
           return;
       }
       if (channel == SET_CELL_INFO) {
           // Change the channel we listen to for cell commands, the
           // starting character position to extract from, and
           // special effect attributes.
           list Parsed = llCSV2List(data);
           gCellChannel        = (integer) llList2String(Parsed, 0);
           gCellCharPosition   = (integer) llList2String(Parsed, 1);
           gCellUseFading      = (integer) llList2String(Parsed, 2);
           gCellHoldDelay      = (float)   llList2String(Parsed, 3);
           return;
       }
       if (channel == SET_FONT_TEXTURE) {
           // Use the new texture instead of the current one.
           gFontTexture = id;
           // Change the currently shown texture.
           llSetTexture(gFontTexture, FACE_1);
           llSetTexture(gFontTexture, FACE_2);
           llSetTexture(gFontTexture, FACE_3);
           llSetTexture(gFontTexture, FACE_4);
           llSetTexture(gFontTexture, FACE_5);
           return;
       }
       if (channel == SET_THICKNESS) {
           // Set our z scale to thickness, while staying fixed
           // in position relative the prim below us.
           vector Scale    = llGetScale();
           float Thickness = (float) data;
                      // Reposition only if this isn't the root prim.
           integer ThisLink = llGetLinkNumber();
           if (ThisLink != 0 || ThisLink != 1) {
               // This is not the root prim.
               vector Up = llRot2Up(llGetLocalRot());
               float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
               vector Pos = llGetLocalPos();
               llSetPos(Pos + DistanceToMove * Up);
           }
                      // Apply the new thickness.
           Scale.z = Thickness;
           llSetScale(Scale);
           return;
       }
       if (channel == SET_COLOR) {
           vector newColor = (vector)data;
           llSetColor(newColor, ALL_SIDES);
       }
   }
}

XyText 5face Double Character Texture

Modified by ThraxisEpsilon 2006-01-20 to add support for 10 characters per prim

Prim setup: Create generic object, insert script to transform it.
////////////////////////////////////////////
// XyText v1.0.3 Prim Setup Script (5 Face, Multi Texture)
//
// Modified by Thraxis Epsilon January 20, 2006
//
////////////////////////////////////////////

string FACE_TEXTURE = "09b04244-9569-d21f-6de0-4bbcf5552222";
string  TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";

default
{
    state_entry()
    {
        llSetPrimitiveParams([
          PRIM_TYPE, PRIM_TYPE_PRISM, PRIM_HOLE_SQUARE, <0.199, 0.8, 0.0>, 0.30,
            ZERO_VECTOR, <1.0, 1.0, 0.0>, ZERO_VECTOR,

          // display a default face texture
          PRIM_TEXTURE, 1, FACE_TEXTURE, <2.48, 1.0, 0.0>, <-0.740013, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 6, FACE_TEXTURE, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 4, FACE_TEXTURE, <-14.75, 1.0, 0.0>, <0.130009, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 7, FACE_TEXTURE, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0,
          PRIM_TEXTURE, 3, FACE_TEXTURE, <2.48, 1.0, 0.0>, <-0.255989, 0.0, 0.0>, 0.0,

          // show transparent textures for the other sides
          PRIM_TEXTURE, 0, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
          PRIM_TEXTURE, 2, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
          PRIM_TEXTURE, 5, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,

          PRIM_SIZE, <0.03, 2.89, 0.5>
        ]);

        // Remove ourselves from inventory.
        llRemoveInventory(llGetScriptName());
    }
}

Main script: drop in each display prim.
////////////////////////////////////////////
// XyText v1.0.3 Script (5 Face, Multi Texture)
//
// Written by Xylor Baysklef
//
// Modified by Thraxis Epsilon January 20, 2006
// Added Support for 5 Face Prim, based on modification
// of XyText v1.1.1 by Kermitt Quick for Single Texture.
//
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;
integer SET_THICKNESS       = 204006;
integer SET_COLOR           = 204007;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "123456789abcdef";

// Face numbers.
integer FACE_1          = 3;
integer FACE_2          = 7;
integer FACE_3          = 4;
integer FACE_4          = 6;
integer FACE_5          = 1;

// Used to hide the text after a fade-out.
key     TRANSPARENT     = "701917a8-d614-471f-13dd-5f4644e36e3c";
// This is a list of textures for all 2-character combinations.
list    CHARACTER_GRID  = [
        "00e9f9f7-0669-181c-c192-7f8e67678c8d",
        "347a5cb6-0031-7ec0-2fcf-f298eebf3c0e",
        "4e7e689e-37f1-9eca-8596-a958bbd23963",
        "19ea9c21-67ba-8f6f-99db-573b1b877eb1",
        "dde7b412-cda1-652f-6fc2-73f4641f96e1",
        "af6fa3bb-3a6c-9c4f-4bf5-d1c126c830da",
        "a201d3a2-364b-43b6-8686-5881c0f82a94",
        "b674dec8-fead-99e5-c28d-2db8e4c51540",
        "366e05f3-be6b-e5cf-c33b-731dff649caa",
        "75c4925c-0427-dc0c-c71c-e28674ff4d27",
        "dcbe166b-6a97-efb2-fc8e-e5bc6a8b1be6",
        "0dca2feb-fc66-a762-db85-89026a4ecd68",
        "a0fca76f-503a-946b-9336-0a918e886f7a",
        "67fb375d-89a1-5a4f-8c7a-0cd1c066ffc4",
        "300470b2-da34-5470-074c-1b8464ca050c",
        "d1f8e91c-ce2b-d85e-2120-930d3b630946",
        "2a190e44-7b29-dadb-0bff-c31adaf5a170",
        "75d55e71-f6f8-9835-e746-a45f189f30a1",
        "300fac33-2b30-3da3-26bc-e2d70428ec19",
        "0747c776-011a-53ce-13ee-8b5bb9e87c1e",
        "85a855c3-a94f-01ca-33e0-7dde92e727e2",
        "cbc1dab2-2d61-2986-1949-7a5235c954e1",
        "f7aef047-f266-9596-16df-641010edd8e1",
        "4c34ebf7-e5e1-2e1a-579f-e224d9d5e71b",
        "4a69e98c-26a5-ad05-e92e-b5b906ad9ef9",
        "462a9226-2a97-91ac-2d89-57ab33334b78",
        "20b24b3a-8c57-82ee-c6ed-555003f5dbcd",
        "9b481daa-9ea8-a9fa-1ee4-ab9a0d38e217",
        "c231dbdc-c842-15b0-7aa6-6da14745cfdc",
        "c97e3cbb-c9a3-45df-a0ae-955c1f4bf9cf",
        "f1e7d030-ff80-a242-cb69-f6951d4eae3b",
        "ed32d6c4-d733-c0f1-f242-6df1d222220d",
        "88f96a30-dccf-9b20-31ef-da0dfeb23c72",
        "252f2595-58b8-4bcc-6515-fa274d0cfb65",
        "f2838c4f-de80-cced-dff8-195dfdf36b2c",
        "cc2594fe-add2-a3df-cdb3-a61711badf53",
        "e0ce2972-da00-955c-129e-3289b3676776",
        "3e0d336d-321f-ddfa-5c1b-e26131766f6a",
        "d43b1dc4-6b51-76a7-8b90-38865b82bf06",
        "06d16cbb-1868-fd1d-5c93-eae42164a37d",
        "dd5d98cf-273e-3fd0-f030-48be58ee3a0b",
        "0e47c89e-de4a-6233-a2da-cb852aad1b00",
        "fb9c4a55-0e13-495b-25c4-f0b459dc06de",
        "e3ce8def-312c-735b-0e48-018b6799c883",
        "2f713216-4e71-d123-03ed-9c8554710c6b",
        "4a417d8a-1f4f-404b-9783-6672f8527911",
        "ca5e21ec-5b20-5909-4c31-3f90d7316b33",
        "06a4fcc3-e1c4-296d-8817-01f88fbd7367",
        "130ac084-6f3c-95de-b5b6-d25c80703474",
        "59d540a0-ae9d-3606-5ae0-4f2842b64cfa",
        "8612ae9a-f53c-5bf4-2899-8174d7abc4fd",
        "12467401-e979-2c49-34e0-6ac761542797",
        "d53c3eaa-0404-3860-0675-3e375596c3e3",
        "9f5b26bd-81d3-b25e-62fe-5b671d1e3e79",
        "f57f0b64-a050-d617-ee00-c8e9e3adc9cb",
        "beff166a-f5f3-f05e-e020-98f2b00e27ed",
        "02278a65-94ba-6d5e-0d2b-93f2e4f4bf70",
        "a707197d-449e-5b58-846c-0c850c61f9d6",
        "021d4b1a-9503-a44f-ee2b-976eb5d80e68",
        "0ae2ffae-7265-524d-cb76-c2b691992706",
        "f6e41cf2-1104-bd0b-0190-dffad1bac813",
        "2b4bb15e-956d-56ae-69f5-d26a20de0ce7",
        "f816da2c-51f1-612a-2029-a542db7db882",
        "345fea05-c7be-465c-409f-9dcb3bd2aa07",
        "b3017e02-c063-5185-acd5-1ef5f9d79b89",
        "4dcff365-1971-3c2b-d73c-77e1dc54242a"
          ];

///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel      = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0  means don't fade out.
float   gCellHoldDelay      = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    // \" <-- Fixes LSL syntax highlighting bug.
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
    gCharIndex += "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
}

vector GetGridPos(integer index1, integer index2) {
    // There are two ways to use the lookup table...
    integer Col;
    integer Row;
    if (index1 >= index2) {
        // In this case, the row is the index of the first character:
        Row = index1;
        // And the col is the index of the second character (x2)
        Col = index2 * 2;
    }
    else { // Index1 < Index2
        // In this case, the row is the index of the second character:
        Row = index2;
        // And the col is the index of the first character, x2, offset by 1.
        Col = index1 * 2 + 1;
    }
    return <Col, Row, 0>;
}

string GetGridTexture(vector grid_pos) {
    // Calculate the texture in the grid to use.
    integer GridCol = llRound(grid_pos.x) / 20;
    integer GridRow = llRound(grid_pos.y) / 10;

    // Lookup the texture.
    key Texture = llList2Key(CHARACTER_GRID, GridRow * (GridRow + 1) / 2 + GridCol);
    return Texture;
}

vector GetGridOffset(vector grid_pos) {
    // Zoom in on the texture showing our character pair.
    integer Col = llRound(grid_pos.x) % 20;
    integer Row = llRound(grid_pos.y) % 10;

    // Return the offset in the texture.
    return <-0.45 + 0.05 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5) {
   // Set the primitive textures directly.

   llSetPrimitiveParams( [
        PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
        PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
        PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
        PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
        PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0
        ]);
}

RenderString(string str) {
    // Get the grid positions for each pair of characters.
    vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridPos2 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
    vector GridPos3 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 5, 5)) );
    vector GridPos4 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 6, 6)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 7, 7)) );
    vector GridPos5 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 8, 8)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 9, 9)) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
}

RenderWithEffects(string str) {
    // Get the grid positions for each pair of characters.
    vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridPos2 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
    vector GridPos3 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 5, 5)) );
    vector GridPos4 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 6, 6)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 7, 7)) );
    vector GridPos5 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 8, 8)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 9, 9)) );

      // First set the alpha to the lowest possible.
   llSetAlpha(0.05, ALL_SIDES);

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);

    float Alpha;
    for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // See if we want to fade out as well.
   if (gCellHoldDelay < 0.0)
       // No, bail out. (Just keep showing the string at full strength).
       return;
          // Hold the text for a while.
   llSleep(gCellHoldDelay);
      // Now fade out.
   for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // Make the text transparent to fully hide it.
   llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
    // Look for escape sequences.
    list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
    integer ParsedLen = llGetListLength(Parsed);

    // Create a list of index values to work with.
    list Indices;
    // We start with room for 6 indices.
    integer IndicesLeft = 10;

    integer i;
    string Token;
    integer Clipped;
    integer LastWasEscapeSequence = FALSE;
    // Work from left to right.
    for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
        Token = llList2String(Parsed, i);

        // If this is an escape sequence, just set the flag and move on.
        if (Token == ESCAPE_SEQUENCE) {
            LastWasEscapeSequence = TRUE;
        }
        else { // Token != ESCAPE_SEQUENCE
            // Otherwise this is a normal token.  Check its length.
            Clipped = FALSE;
            integer TokenLength = llStringLength(Token);
            // Clip if necessary.
            if (TokenLength > IndicesLeft) {
                Token = llGetSubString(Token, 0, IndicesLeft - 1);
                TokenLength = llStringLength(Token);
                IndicesLeft = 0;
                Clipped = TRUE;
            }
            else
                IndicesLeft -= TokenLength;

            // Was the previous token an escape sequence?
            if (LastWasEscapeSequence) {
                // Yes, the first character is an escape character, the rest are normal.

                // This is the extended character.
                Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

                // These are the normal characters.
                integer j;
                for (j = 1; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }
            else { // Normal string.
                // Just add the characters normally.
                integer j;
                for (j = 0; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }

            // Unset this flag, since this was not an escape sequence.
            LastWasEscapeSequence = FALSE;
        }
    }

    // Use the indices to create grid positions.
    vector GridPos1 = GetGridPos( llList2Integer(Indices, 0), llList2Integer(Indices, 1) );
    vector GridPos2 = GetGridPos( llList2Integer(Indices, 2), llList2Integer(Indices, 3) );
    vector GridPos3 = GetGridPos( llList2Integer(Indices, 4), llList2Integer(Indices, 5) );
    vector GridPos4 = GetGridPos( llList2Integer(Indices, 6), llList2Integer(Indices, 7) );
    vector GridPos5 = GetGridPos( llList2Integer(Indices, 8), llList2Integer(Indices, 9) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
}

integer ConvertIndex(integer index) {
    // This converts from an ASCII based index to our indexing scheme.
    if (index >= 32) // ' ' or higher
        index -= 32;
    else { // index < 32
        // Quick bounds check.
        if (index > 15)
            index = 15;

        index += 94; // extended characters
    }

    return index;
}

default {
    state_entry() {
        // Initialize the character index.
        ResetCharIndex();

        //llSay(0, "Free Memory: " + (string) llGetFreeMemory());
    }

    link_message(integer sender, integer channel, string data, key id) {
        if (channel == DISPLAY_STRING) {
            RenderString(data);
            return;
        }
        if (channel == DISPLAY_EXTENDED) {
            RenderExtended(data);
            return;
        }
        if (channel == gCellChannel) {
            // Extract the characters we are interested in, and use those to render.
            string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 9);
            if (gCellUseFading)
               RenderWithEffects( TextToRender );
            else // !gCellUseFading
                RenderString( TextToRender );
            return;
        }
        if (channel == REMAP_INDICES) {
            // Parse the message, splitting it up into index values.
            list Parsed = llCSV2List(data);
            integer i;
            // Go through the list and swap each pair of indices.
            for (i = 0; i < llGetListLength(Parsed); i += 2) {
                integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

                // Swap these index values.
                string Value1 = llGetSubString(gCharIndex, Index1, Index1);
                string Value2 = llGetSubString(gCharIndex, Index2, Index2);

                gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
                gCharIndex = llInsertString(gCharIndex, Index1, Value2);

                gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
                gCharIndex = llInsertString(gCharIndex, Index2, Value1);
            }
            return;
        }
        if (channel == RESET_INDICES) {
            // Restore the character index back to default settings.
            ResetCharIndex();
            return;
        }
        if (channel == SET_CELL_INFO) {
            // Change the channel we listen to for cell commands, and the
            // starting character position to extract from.
            list Parsed = llCSV2List(data);
            gCellChannel        = (integer) llList2String(Parsed, 0);
            gCellCharPosition   = (integer) llList2String(Parsed, 1);
           gCellUseFading      = (integer) llList2String(Parsed, 2);
           gCellHoldDelay      = (float)   llList2String(Parsed, 3);
            return;
        }
        if (channel == SET_THICKNESS) {
           // Set our z scale to thickness, while staying fixed
           // in position relative the prim below us.
           vector Scale    = llGetScale();
           float Thickness = (float) data;
                      // Reposition only if this isn't the root prim.
           integer ThisLink = llGetLinkNumber();
           if (ThisLink != 0 || ThisLink != 1) {
               // This is not the root prim.
               vector Up = llRot2Up(llGetLocalRot());
               float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
               vector Pos = llGetLocalPos();
               llSetPos(Pos + DistanceToMove * Up);
           }
           // Apply the new thickness.
           Scale.z = Thickness;
           llSetScale(Scale);
           return;
        }
        if (channel == SET_COLOR) {
           vector newColor = (vector)data;
           llSetColor(newColor, ALL_SIDES);
        }
    }
}

ScriptLibrary


NewbieNag

// newbienag.lsl
// Give an inventory object only to newbies
// Written by Kex Godel on March 20, 2005
// Released under Creative Commons Attribution license
// http://creativecommons.org/licenses/by/2.0/
//
// This script gives an inventory item only if the person who collides with
// the object is under a certian age.
//
// Features:
// - Remembers who it already recently checked, so it does not bother looking
//   them up again
// - Relays to other nearby objects with the same name running the same script
//   to let them know it has already checked this person and served them.
// - Age checking code incorporates leap days until 2100
// - Timeout will resume work if dataserver doesn't respond

// maximum newbie age to recieve item
integer NEWBIE_DAYS = 14;

// how long to wait for the dataserver before giving up
integer DATASERVER_TIMEOUT = 30;

// length of FIFO list of people to remember you already gave to.
integer NAG_LIST_LENGTH = 25;

// channel to communicate to other same objects to let them know you already gave.
integer MULTI_CONTROL_CHANNEL = 861562;

key gDataserveKey = NULL_KEY;
integer gLastCollisionTime = 0;
string gDetectedName;
string gDetectedKey;

list recentlyNagged;

// Return number of days since 2000-01-01
integer datestamp(integer year, integer month, integer day){
    year = year - 2000;
    list monthdays = [0,31,28,31,30,31,30,31,31,30,31,30,31];
    integer i=1;
    integer sum = year * 365 + (year/4) + day;
    for(i=1;i<month;i++){
        if( (i != 2) || (year % 4 != 0)){
            sum+=llList2Integer(monthdays,i);
        }else {
            sum+=29;
        }
    }
    if(year % 4 == 0){
        return sum - 1;
    }
    return sum;
}

// Convert the SL date string type (yyyy-mm-dd) to days since 2000-01-01
integer date2datestamp(string date){
    integer year = (integer)date;
    integer month = (integer)llGetSubString(date,5,6);
    integer day = (integer)llGetSubString(date,8,9);
    return datestamp(year,month,day);
}

// Add someone to our short-term do-not-nag list
addRecentlyNagged(string name){
    if(!wasRecentlyNagged(name)){
        recentlyNagged+=name;
        if(llGetListLength(recentlyNagged) > NAG_LIST_LENGTH){
            recentlyNagged = llDeleteSubList(recentlyNagged,0,0);
        }
    }
}

// Check if someone is already on our do-not-nag list
integer wasRecentlyNagged(string name){
    if(llListFindList(recentlyNagged,[name]) > -1){
        return TRUE;
    }
    return FALSE;
}

// Get someone's age in days based on the current date (PST)
integer getAge(string timestr){
    integer todayDate = date2datestamp(llGetTimestamp());
    integer agentBirthDate = date2datestamp(timestr);

    return todayDate - agentBirthDate;
}

default{

    state_entry(){
        gDataserveKey = NULL_KEY;
        gLastCollisionTime = 0;
        llListen(MULTI_CONTROL_CHANNEL,llGetObjectName(),"","");
    }

    listen(integer ch, string name, key id, string msg){
        addRecentlyNagged(msg);
    }

    collision_start(integer cnt){
        integer i;
        for(i=0;i<cnt;i++){
            string name = llDetectedName(i);
            if(!wasRecentlyNagged(name)){
                // check if already making a request, or if the request has timed out
                if(gDataserveKey == NULL_KEY || (gLastCollisionTime + DATASERVER_TIMEOUT < llGetTime())){
                    gDetectedName = name;
                    gDetectedKey = llDetectedKey(0);

                    addRecentlyNagged(name);
                    llSay(MULTI_CONTROL_CHANNEL,gDetectedName); // pass the message on not to nag

                    gDataserveKey = llRequestAgentData(gDetectedKey,DATA_BORN);
                }
            }
        }
    }

    dataserver(key req, string data){
        if(req == gDataserveKey){
            if(getAge(data) <= NEWBIE_DAYS){
                llGiveInventory(gDetectedKey,"notecard");
            }else{
                // do nothing for oldbies
            }
            gDataserveKey = NULL_KEY; // clear so a new request can be made
        }
    }
}
I corrected a problem within the datestamp function. 2 occurances of variable month being used where i should have been used instead. - AakanaarLaSalle
ScriptLibrary


randomVector

Still work in progress.
It compiles, and the little bits are good.

//////
//    OK, so randBetween is from the llFrand page
//    we can use it to generate a random number from  minimum to maximum.
//    You will see how it works in the example below
////    and yes, I flip around with color and colour - in my comments its colour, in code, I concede to LL

float randBetween(float min, float max)
{
    return llFrand(max - min) + min;
}

//////
//  Colours are expressed in vectors.
//   So, below gives us a basic random colour generator

vector randColor()
{
    return <llFrand(1.0), llFrand(1.0), llFrand(1.0)>;    //sends back a message to whoever asks the random colour
}

//////
//  Taking it one step further
//   So, below gives us a basic random colour generator

vector randVector()
{                         //HERE we use the randBetween we have up on top to
                           //make a random number from 100 to 500 - on the Z axis. Fun for a trampoline :)
    return <0,0,randBetween(100, 500)>;
}

default
{
    touch_start(integer total_number)
    {
        vector tempRandomColor = randColor();  //this loads temp ariable with random colur
        llSay(0, (string)tempRandomColor );    //this says the vector, just to know its working

        llSetColor(tempRandomColor, ALL_SIDES); //set colour
                          ///here we push the person who touched cube,
        llPushObject( llDetectedKey(0), randVector(), ZERO_VECTOR, 0);
    }
}

back to functionLibrary


Word---Scramble

Word Scramble Script (NOT A GAME)
If you want i can come up with a random word function too.
//Put this in an object and say "scramble:WORD" and it will scramble WORD. Simple Eh?

//***********************************************
//Scripted By Second Proctor
//Do Not Remove this Tag
//***********************************************

string scramble(string start)
{
    integer slength=llStringLength(start);
    integer x;
    list scamb=llParseString2List(start,[],["a","e","i","o","u","y"]);
    list endl;
    for(x=0;x<slength;x++)
    {
        integer temp=(integer)llFrand(llGetListLength(scamb));
        endl+=(endl=[])+endl+[llList2String(scamb,temp)];
        scamb=llDeleteSubList(scamb,temp,temp);
    }
    if(llDumpList2String(endl,"") == start) return scramble(start);
    else return llDumpList2String(endl,"");

}
default
{
    state_entry()
    {
        llListen(0,"",llGetOwner(),"");
    }
    listen(integer c, string n, key i, string m)
    {
        if(llToLower(llGetSubString(m,0,8))=="scramble:")
        {
            llSay(0,llGetSubString(m,0,8)+" scrambled becomes "+scramble(llGetSubString(m,9,-1)));
        }
    }
}


XyText

XyText v1.0.2 -- llSetText Alternative

XyText is a really cool script / set of scripts written by XylorBaysklef that displays 6 characters on a single prim using textures with 2-character pairs on 3 faces.

These scripts were originally posted in the official Second Life forums here. Xylor kindly granted permission to repost them here.

Yah know what would be cool? A XyText version history. I want to know what changed between versions 1.0.2 and 0.9.7, just to see if I need to update my screens or not. -Chris, the Obsessive Compulsive Updater

i have an old version of 0.9.7. The major change was that a format change on the handling of SET_CELL_INFO. but i haven't looked at the old version carefully to look at what else had changed. BW

Added the 1.2 version of XYScripts off on a second page. Major improvements: The basic primitive is now a truncated hollowed prism which allows 5 characters per prim using only one texture, and 10 characters using the two-character texture set. Fade in/out, color settings and resizing have been added to the control linkmessages. I found this new version at Odysseus Fairymeadow's store. He has an information kiosk that hands out the latest XyText scripts on a notecard. SM

Quick Instructions

  1. Setup the prim by placing the Prim Setup script into it - it will modify the prim and then delete itself from it.
  2. Place the XyText v1.0.2 script into the prim.
  3. Create a new script and add the Message Map lines to your script.
  4. Send a link message to the prim with DISPLAY_STRING in the integer and the text in the string parameters, example:
    llMessageLinked(LINK_SET, DISPLAY_STRING, "Xylor", "");
  5. If you want more than 6 characters, read the detailed instructions below (SET_CELL_INFO).

General Info

This script displays 6 characters on a prim. It can show any typeable character in SL, plus 15 extended characters. Prims can be chained together, and act as 'cells', allowing much larger display systems (as in the freely available XyText Notecard Reader).

The display works by displaying characters in pairs on 3 viewable texture faces. Use the following script to 'set up' your prim to be the correct shape:

////////////////////////////////////////////
// XyText Prim Setup
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// Transparent texture key.
string  TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";

default {
    state_entry() {
        // Set up the prim to be the correct shape.
        vector Scale = llGetScale();
        llSetPrimitiveParams([
            // Set the top size so this shows 3 faces at once.
            PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <0.333333, 1, 0>, ZERO_VECTOR,

            // Display the string "XyText" for now.
            PRIM_TEXTURE, 4, "0e47c89e-de4a-6233-a2da-cb852aad1b00",
                    <0.1, 0.1, 0>,  <0.200000, -0.450000, 0.000000>, PI_BY_TWO,

            PRIM_TEXTURE, 0, "20b24b3a-8c57-82ee-c6ed-555003f5dbcd",
                    <0.1, 0.1, 0>, <-0.200000, -0.450000, 0.000000>, 0.0,

            PRIM_TEXTURE, 2, "2f713216-4e71-d123-03ed-9c8554710c6b",
                    <0.1, 0.1, 0>, <-0.050000, -0.350000, 0.000000>, -PI_BY_TWO,

            // Show transparent textures for the other sides.
            PRIM_TEXTURE, 1, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
            PRIM_TEXTURE, 3, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
            PRIM_TEXTURE, 5, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,

            // Set the correct aspect ratio.
            PRIM_SIZE, <Scale.x, Scale.x / 3, 0.01>
            ]);

        // Remove ourselves from inventory.
        llRemoveInventory(llGetScriptName());
    }
}

Also, here is a chart of all displayable characters in the system. Drag this script onto a prim:

////////////////////////////////////////////
// XyText Chart Creator
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// Texture keys.
string     CHART = "f2959ca0-6d05-e1df-7ced-aadc9e5e4022";
string     LOGO  = "f74e6424-229f-3223-9077-fccf1a0c3087";
string     BLANK = "5748decc-f629-461c-9a36-a35a221fe21f";

default {
    state_entry() {
        // Set up the prim to be the correct shape, and textures.
        llSetPrimitiveParams([
                PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
                PRIM_SIZE, <5.0, 0.01, 2.5>,
                PRIM_COLOR,   0, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_COLOR,   1, <1.00000, 1.00000, 1.00000>, 1.0,
                PRIM_COLOR,   2, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_COLOR,   3, <1.00000, 1.00000, 1.00000>, 1.0,
                PRIM_COLOR,   4, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_COLOR,   5, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_TEXTURE, 0, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 1, CHART, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 2, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 3, LOGO,  <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 4, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 5, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0
            ]);

        llSetObjectName("XyText Chart");

        // Remove ourselves from inventory.
        llRemoveInventory(llGetScriptName());
    }
}

Here is the main script, which goes on each prim you wish to use as a display.

////////////////////////////////////////////
// XyText v1.0.2 Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "123456789abcdef";

// Face numbers.
integer LEFT_FACE       = 4;
integer MIDDLE_FACE     = 0;
integer RIGHT_FACE      = 2;

// This is a list of textures for all 2-character combinations.
list    CHARACTER_GRID  = [
        "00e9f9f7-0669-181c-c192-7f8e67678c8d",
        "347a5cb6-0031-7ec0-2fcf-f298eebf3c0e",
        "4e7e689e-37f1-9eca-8596-a958bbd23963",
        "19ea9c21-67ba-8f6f-99db-573b1b877eb1",
        "dde7b412-cda1-652f-6fc2-73f4641f96e1",
        "af6fa3bb-3a6c-9c4f-4bf5-d1c126c830da",
        "a201d3a2-364b-43b6-8686-5881c0f82a94",
        "b674dec8-fead-99e5-c28d-2db8e4c51540",
        "366e05f3-be6b-e5cf-c33b-731dff649caa",
        "75c4925c-0427-dc0c-c71c-e28674ff4d27",
        "dcbe166b-6a97-efb2-fc8e-e5bc6a8b1be6",
        "0dca2feb-fc66-a762-db85-89026a4ecd68",
        "a0fca76f-503a-946b-9336-0a918e886f7a",
        "67fb375d-89a1-5a4f-8c7a-0cd1c066ffc4",
        "300470b2-da34-5470-074c-1b8464ca050c",
        "d1f8e91c-ce2b-d85e-2120-930d3b630946",
        "2a190e44-7b29-dadb-0bff-c31adaf5a170",
        "75d55e71-f6f8-9835-e746-a45f189f30a1",
        "300fac33-2b30-3da3-26bc-e2d70428ec19",
        "0747c776-011a-53ce-13ee-8b5bb9e87c1e",
        "85a855c3-a94f-01ca-33e0-7dde92e727e2",
        "cbc1dab2-2d61-2986-1949-7a5235c954e1",
        "f7aef047-f266-9596-16df-641010edd8e1",
        "4c34ebf7-e5e1-2e1a-579f-e224d9d5e71b",
        "4a69e98c-26a5-ad05-e92e-b5b906ad9ef9",
        "462a9226-2a97-91ac-2d89-57ab33334b78",
        "20b24b3a-8c57-82ee-c6ed-555003f5dbcd",
        "9b481daa-9ea8-a9fa-1ee4-ab9a0d38e217",
        "c231dbdc-c842-15b0-7aa6-6da14745cfdc",
        "c97e3cbb-c9a3-45df-a0ae-955c1f4bf9cf",
        "f1e7d030-ff80-a242-cb69-f6951d4eae3b",
        "ed32d6c4-d733-c0f1-f242-6df1d222220d",
        "88f96a30-dccf-9b20-31ef-da0dfeb23c72",
        "252f2595-58b8-4bcc-6515-fa274d0cfb65",
        "f2838c4f-de80-cced-dff8-195dfdf36b2c",
        "cc2594fe-add2-a3df-cdb3-a61711badf53",
        "e0ce2972-da00-955c-129e-3289b3676776",
        "3e0d336d-321f-ddfa-5c1b-e26131766f6a",
        "d43b1dc4-6b51-76a7-8b90-38865b82bf06",
        "06d16cbb-1868-fd1d-5c93-eae42164a37d",
        "dd5d98cf-273e-3fd0-f030-48be58ee3a0b",
        "0e47c89e-de4a-6233-a2da-cb852aad1b00",
        "fb9c4a55-0e13-495b-25c4-f0b459dc06de",
        "e3ce8def-312c-735b-0e48-018b6799c883",
        "2f713216-4e71-d123-03ed-9c8554710c6b",
        "4a417d8a-1f4f-404b-9783-6672f8527911",
        "ca5e21ec-5b20-5909-4c31-3f90d7316b33",
        "06a4fcc3-e1c4-296d-8817-01f88fbd7367",
        "130ac084-6f3c-95de-b5b6-d25c80703474",
        "59d540a0-ae9d-3606-5ae0-4f2842b64cfa",
        "8612ae9a-f53c-5bf4-2899-8174d7abc4fd",
        "12467401-e979-2c49-34e0-6ac761542797",
        "d53c3eaa-0404-3860-0675-3e375596c3e3",
        "9f5b26bd-81d3-b25e-62fe-5b671d1e3e79",
        "f57f0b64-a050-d617-ee00-c8e9e3adc9cb",
        "beff166a-f5f3-f05e-e020-98f2b00e27ed",
        "02278a65-94ba-6d5e-0d2b-93f2e4f4bf70",
        "a707197d-449e-5b58-846c-0c850c61f9d6",
        "021d4b1a-9503-a44f-ee2b-976eb5d80e68",
        "0ae2ffae-7265-524d-cb76-c2b691992706",
        "f6e41cf2-1104-bd0b-0190-dffad1bac813",
        "2b4bb15e-956d-56ae-69f5-d26a20de0ce7",
        "f816da2c-51f1-612a-2029-a542db7db882",
        "345fea05-c7be-465c-409f-9dcb3bd2aa07",
        "b3017e02-c063-5185-acd5-1ef5f9d79b89",
        "4dcff365-1971-3c2b-d73c-77e1dc54242a"
          ];

///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel      = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition = 0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    // \" <-- Fixes LSL syntax highlighting bug.
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
    gCharIndex += "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
}

vector GetGridPos(integer index1, integer index2) {
    // There are two ways to use the lookup table...
    integer Col;
    integer Row;
    if (index1 >= index2) {
        // In this case, the row is the index of the first character:
        Row = index1;
        // And the col is the index of the second character (x2)
        Col = index2 * 2;
    }
    else { // Index1 < Index2
        // In this case, the row is the index of the second character:
        Row = index2;
        // And the col is the index of the first character, x2, offset by 1.
        Col = index1 * 2 + 1;
    }
    return < Col, Row, 0>;
}

string GetGridTexture(vector grid_pos) {
    // Calculate the texture in the grid to use.
    integer GridCol = llRound(grid_pos.x) / 20;
    integer GridRow = llRound(grid_pos.y) / 10;

    // Lookup the texture.
    key Texture = llList2Key(CHARACTER_GRID, GridRow * (GridRow + 1) / 2 + GridCol);
    return Texture;
}

vector GetGridOffset(vector grid_pos) {
    // Zoom in on the texture showing our character pair.
    integer Col = llRound(grid_pos.x) % 20;
    integer Row = llRound(grid_pos.y) % 10;

    // Return the offset in the texture.
    return <-0.45 + 0.05 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3) {
    // Set the primitive textures directly.
    llSetPrimitiveParams( [
        PRIM_TEXTURE, LEFT_FACE,   GetGridTexture(grid_pos1), <0.1, 0.1, 0>, GetGridOffset(grid_pos1), PI_BY_TWO,
        PRIM_TEXTURE, MIDDLE_FACE, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
        PRIM_TEXTURE, RIGHT_FACE,  GetGridTexture(grid_pos3), <0.1, 0.1, 0>, GetGridOffset(grid_pos3), -PI_BY_TWO
        ]);
}

RenderString(string str) {
    // Get the grid positions for each pair of characters.
    vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridPos2 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
    vector GridPos3 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 5, 5)) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3);
}

RenderExtended(string str) {
    // Look for escape sequences.
    list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
    integer ParsedLen = llGetListLength(Parsed);

    // Create a list of index values to work with.
    list Indices;
    // We start with room for 6 indices.
    integer IndicesLeft = 6;

    integer i;
    string Token;
    integer Clipped;
    integer LastWasEscapeSequence = FALSE;
    // Work from left to right.
    for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
        Token = llList2String(Parsed, i);

        // If this is an escape sequence, just set the flag and move on.
        if (Token == ESCAPE_SEQUENCE) {
            LastWasEscapeSequence = TRUE;
        }
        else { // Token != ESCAPE_SEQUENCE
            // Otherwise this is a normal token.  Check its length.
            Clipped = FALSE;
            integer TokenLength = llStringLength(Token);
            // Clip if necessary.
            if (TokenLength > IndicesLeft) {
                Token = llGetSubString(Token, 0, IndicesLeft - 1);
                TokenLength = llStringLength(Token);
                IndicesLeft = 0;
                Clipped = TRUE;
            }
            else
                IndicesLeft -= TokenLength;

            // Was the previous token an escape sequence?
            if (LastWasEscapeSequence) {
                // Yes, the first character is an escape character, the rest are normal.

                // This is the extended character.
                Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

                // These are the normal characters.
                integer j;
                for (j = 1; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }
            else { // Normal string.
                // Just add the characters normally.
                integer j;
                for (j = 0; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }

            // Unset this flag, since this was not an escape sequence.
            LastWasEscapeSequence = FALSE;
        }
    }

    // Use the indices to create grid positions.
    vector GridPos1 = GetGridPos( llList2Integer(Indices, 0), llList2Integer(Indices, 1) );
    vector GridPos2 = GetGridPos( llList2Integer(Indices, 2), llList2Integer(Indices, 3) );
    vector GridPos3 = GetGridPos( llList2Integer(Indices, 4), llList2Integer(Indices, 5) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3);
}

integer ConvertIndex(integer index) {
    // This converts from an ASCII based index to our indexing scheme.
    if (index >= 32) // ' ' or higher
        index -= 32;
    else { // index < 32
        // Quick bounds check.
        if (index > 15)
            index = 15;

        index += 94; // extended characters
    }

    return index;
}

default {
    state_entry() {
        // Initialize the character index.
        ResetCharIndex();

        //llSay(0, "Free Memory: " + (string) llGetFreeMemory());
    }

    link_message(integer sender, integer channel, string data, key id) {
        if (channel == DISPLAY_STRING) {
            RenderString(data);
            return;
        }
        if (channel == DISPLAY_EXTENDED) {
            RenderExtended(data);
            return;
        }
        if (channel == gCellChannel) {
            // Extract the characters we are interested in, and use those to render.
            RenderString( llGetSubString(data, gCellCharPosition, gCellCharPosition + 5) );
            return;
        }
        if (channel == REMAP_INDICES) {
            // Parse the message, splitting it up into index values.
            list Parsed = llCSV2List(data);
            integer i;
            // Go through the list and swap each pair of indices.
            for (i = 0; i < llGetListLength(Parsed); i += 2) {
                integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

                // Swap these index values.
                string Value1 = llGetSubString(gCharIndex, Index1, Index1);
                string Value2 = llGetSubString(gCharIndex, Index2, Index2);

                gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
                gCharIndex = llInsertString(gCharIndex, Index1, Value2);

                gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
                gCharIndex = llInsertString(gCharIndex, Index2, Value1);
            }
            return;
        }
        if (channel == RESET_INDICES) {
            // Restore the character index back to default settings.
            ResetCharIndex();
            return;
        }
        if (channel == SET_CELL_INFO) {
            // Change the channel we listen to for cell commands, and the
            // starting character position to extract from.
            list Parsed = llCSV2List(data);
            gCellChannel        = (integer) llList2String(Parsed, 0);;
            gCellCharPosition   = (integer) llList2String(Parsed, 1);
            return;
        }
    }
}

Basic Usage

The XyText script listens for link messages on various channels (Note, these are *link* messages, not listen events and the channels have nothing to do with chat). Here is the message map, which you can cut and paste into scripts which use the system:

// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;

If you plan to use XyText in a simple situation of say, 1 prim displaying 6 characters, then using DISPLAY_STRING would be the easiest way to accomplish your task. Here is an example use:

llMessageLinked(LINK_SET, DISPLAY_STRING, "Hello", "");

Here is the obligatory "Hello World!" script =P

////////////////////////////////////////////
// Hello World! Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
///////////// END CONSTANTS ////////////////

default {
    state_entry() {
        llMessageLinked(LINK_SET, DISPLAY_STRING, "Hello ", "");
        llSleep(10.0);
        llMessageLinked(LINK_SET, DISPLAY_STRING, "World!", "");
    }
}

Advanced Usage

Here is a breakdown of what each channel in the message map does:

DISPLAY_STRING

This channel takes a string to render as its parameter. Only the first 6 characters of this string are rendered.

llMessageLinked(LINK_SET, DISPLAY_STRING, "Xylor", "");

This would render the string "Xylor" on all XyText prims linked to this script.

Example:

integer i;
// Loop through and display the link number on the first 10 links of this object.
for (i = 1; i <= 10; i++)
llMessageLinked(i, DISPLAY_STRING, (string) i, "");

This would display the link number of each prim with the XyText script attached to it (up to link 10; change as needed). This is useful for a quick check of how the object was linked.

Note the changing link numbers in the llMessageLinked calls, instead of LINK_SET. Using DISPLAY_STRING and/or DISPLAY_EXTENDED messages with different link numbers is one way to 'chain' together different XyText displays, however there is a much more efficient way to do this, which will be discussed later (See: SET_CELL_INFO).

DISPLAY_EXTENDED

This channel also takes a string to render as its parameter. However, this string is parsed to search for extended character sequences. Each character sequence starts with a "\e", and is followed by a one character extended code.

Extended character codes are shown in the top row of the XyText Chart (see XyText Chart Creator script, above). Extended codes are shown in yellow, below the index value.

Here is a list of the current extended character codes and the characters they display, if you would rather not create a chart:

code | character
1 | pi
2 | theta
3 | x (times symbol)
4 | o (degrees)
5 | square root symbol
6 | ^2 (squared)
7 | ^3 (cubed)
8 | 1/4
9 | 1/2
a | 3/4
b | section symbol
c | copyright symbol
d | trademark symbol
e | bullet
f | ellipses (...)

Example:

llMessageLinked(llGetLinkNumber(), DISPLAY_EXTENDED, "A\\e6\\e3B\\e7", "");

Note the use of double backslashes. This would render "A^2xB^3" (A squared times B cubed, using appropriate symbols) on the prim this script is attached to.

REMAP_INDICES

This allows you to remap the index values of any character. Using this feature, you could swap a commonly used extended character with a normal character, letting you use the faster DISPLAY_STRING channel instead of DISPLAY_EXTENDED, and avoid the use of escape sequences entirely. This is also the only way to display extended characters using the cell channel set up by SET_CELL_INFO, which will be discussed soon. This channel takes a CSV string (comma separated values), and swaps indices in pairs.

In order to use this, you will need to know the indexing scheme used by XyText. The index values are initially in ASCII order for non-extended characters. Extended characters start at index 1. Please consult the XyText Chart (see the XyText Chart Creator script, above) where index values for each character are shown in white. Here is an outline of the index map for reference:

index | character | comment
0 | reserved |
1 | pi | extended values start here
2 | theta |
.
.
.
14 | bullet |
15 | ellipses |
16 | reserved |
17 | reserved |
.
.
.
30 | reserved |
31 | reserved |
32 | SPACE | normal ASCII values start here
33 | ! |
34 | " |
35 | # |
.
.
.
125 | } |
126 | ~ |

RESET_INDICES

This reverts all character indices back to default settings (those listed in the above table).

Example:

llMessageLinked(LINK_SET, REMAP_INDICES, "14,126", "");
llMessageLinked(LINK_SET, DISPLAY_STRING, "A~B~C", "");

The first line would swap the tilde character (~) with the Bullet character. The second line would render "A(bullet)B(bullet)C", since the tilde character now represents a Bullet.

Example:

llMessageLinked(LINK_SET, RESET_INDICES, "", "");
llMessageLinked(LINK_SET, REMAP_INDICES, "32,15,6,94", "");

The first line resets all index values back to 'default' settings. The second line swaps the SPACE character with the Ellipse character, and the 'squared' character with the ^ character, both in one call. Using two calls like this at script initialization is a good idea, since you don't know if the XyText script has indices set to a non-default setting from a previous run.

SET_CELL_INFO

This allows XyText scripts to act as 'cells' in a larger display. This message lets you set the channel that a particular XyText cell listens to for render messages, as well as which position in the message to start rendering on. This channel takes a CSV string (comma separated values), the first value being the channel to listen on, the second being the position in the string to start rendering on.

This feature is of high importance in creating large displays; be sure you understand its use.

Example:

// Channel to set the line with
integer SET_LINE_CHANNEL = 100100;

...

integer ThisLink = llGetLinkNumber();
// Set up each prim to listen on the same channel, but to render only a part of the string.
llMessageLinked(ThisLink , SET_CELL_INFO, llList2CSV([SET_LINE_CHANNEL, 0]), "");
llMessageLinked(ThisLink + 1, SET_CELL_INFO, llList2CSV([SET_LINE_CHANNEL, 6]), "");
llMessageLinked(ThisLink + 2, SET_CELL_INFO, llList2CSV([SET_LINE_CHANNEL, 12]), "");

...

llMessageLinked(LINK_SET, SET_LINE_CHANNEL, "Long line of text.", "");

This would set up 3 prims with the XyText script attached to act as a unit. Each 'cell' in this unit would listen to the same channel (SET_LINE_CHANNEL in this example). However they would render different portions of the string sent. The last line utilizes these cells, and sends a string to the channel set up earlier. In this case, the string would be split up into "Long l", "ine of" and " text.". Note that the link numbers of each XyText cell is assumed in this case, and the order in which the prims of the object were linked would matter. There are various ways to obtain link numbers for this purpose, but that is left for the user to provide.

SET_FONT_TEXTURE

Single character "XyText" version 1.1 or later only.

This changes the texture that the single character "XyText" uses for the font. There is no similar call for the double character version. In order to work, a font must be layed out appropriately for "XyText" to use. For more information on setting up a font, see LibraryXyTextFonts.

Font Information

The font used in the current system is ProFontWindows, which is the font included in SL for the script editing window. Any font used by the system in future versions would also be fixed-width.

Adding another font would take a couple hours (and cost at least $660 to upload, assuming no more extended characters enter the system) but if enough people want more fonts (and provide me with upload costs ^_~) I might be convinced to work on them. I have my eye on Courier, and Lucida Console, actually...

There seems to be enough memory left in the XyText script for one more row of extended characters (this works out to 10 more characters, indices 16-25). If anyone has suggestions as to which extended characters they would like added, please let me know.

I hope some of you find this system useful. If there is any information on its use that you could not figure out from the above info, please let me know so I can add it here. Thanks =)

Xylor

XyText Single Texture

This is a version which displays only one character per face, which means 3 per prim. The advantage of this is that it's using only one texture instead of 66 and thus doesn't require a huge amount of textures to be streamed to the client before all characters are readable.

////////////////////////////////////////////
// XyText v1.1 Script (Single Texture)
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;
integer SET_FONT_TEXTURE    = 204005;
integer SET_THICKNESS       = 204006;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "12345";

// Face numbers.
integer LEFT_FACE       = 4;
integer MIDDLE_FACE     = 0;
integer RIGHT_FACE      = 2;

// Used to hide the text after a fade-out.
key     TRANSPARENT     = "701917a8-d614-471f-13dd-5f4644e36e3c";
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the key of the font we are displaying.
key     gFontTexture        = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel        = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition   = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0  means don't fade out.
float   gCellHoldDelay      = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    // \" <-- Fixes LSL syntax highlighting bug.
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
    gCharIndex += "\n\n\n\n\n";
}

vector GetGridOffset(integer index) {
    // Calculate the offset needed to display this character.
    integer Row = index / 10;
    integer Col = index % 10;

    // Return the offset in the texture.
    return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3) {
    // Set the primitive textures directly.
    llSetPrimitiveParams( [
        PRIM_TEXTURE, LEFT_FACE,   (string) gFontTexture, <0.05, 0.1, 0>, grid_offset1, PI_BY_TWO,
        PRIM_TEXTURE, MIDDLE_FACE, (string) gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
        PRIM_TEXTURE, RIGHT_FACE,  (string) gFontTexture, <0.05, 0.1, 0>, grid_offset3, -PI_BY_TWO
        ]);
}

RenderString(string str) {
    // Get the grid positions for each pair of characters.
    vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
    vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

RenderWithEffects(string str) {
    // Get the grid positions for each pair of characters.
    vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
    vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

    // First set the alpha to the lowest possible.
    llSetAlpha(0.05, ALL_SIDES);

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridOffset1, GridOffset2, GridOffset3);

     // Now turn up the alpha until it is at full strength.
     float Alpha;
     for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
        llSetAlpha(Alpha, ALL_SIDES);

    // See if we want to fade out as well.
    if (gCellHoldDelay < 0.0)
        // No, bail out. (Just keep showing the string at full strength).
        return;

    // Hold the text for a while.
    llSleep(gCellHoldDelay);

    // Now fade out.
    for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
        llSetAlpha(Alpha, ALL_SIDES);

    // Make the text transparent to fully hide it.
    llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
    // Look for escape sequences.
    list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
    integer ParsedLen = llGetListLength(Parsed);

    // Create a list of index values to work with.
    list Indices;
    // We start with room for 3 indices.
    integer IndicesLeft = 3;

    integer i;
    string Token;
    integer Clipped;
    integer LastWasEscapeSequence = FALSE;
    // Work from left to right.
    for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
        Token = llList2String(Parsed, i);

        // If this is an escape sequence, just set the flag and move on.
        if (Token == ESCAPE_SEQUENCE) {
            LastWasEscapeSequence = TRUE;
        }
        else { // Token != ESCAPE_SEQUENCE
            // Otherwise this is a normal token.  Check its length.
            Clipped = FALSE;
            integer TokenLength = llStringLength(Token);
            // Clip if necessary.
            if (TokenLength > IndicesLeft) {
                Token = llGetSubString(Token, 0, IndicesLeft - 1);
                TokenLength = llStringLength(Token);
                IndicesLeft = 0;
                Clipped = TRUE;
            }
            else
                IndicesLeft -= TokenLength;

            // Was the previous token an escape sequence?
            if (LastWasEscapeSequence) {
                // Yes, the first character is an escape character, the rest are normal.

                // This is the extended character.
                Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

                // These are the normal characters.
                integer j;
                for (j = 1; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }
            else { // Normal string.
                // Just add the characters normally.
                integer j;
                for (j = 0; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }

            // Unset this flag, since this was not an escape sequence.
            LastWasEscapeSequence = FALSE;
        }
    }

    // Use the indices to create grid positions.
    vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0));
    vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
    vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

integer ConvertIndex(integer index) {
    // This converts from an ASCII based index to our indexing scheme.
    if (index >= 32) // ' ' or higher
        index -= 32;
    else { // index < 32
        // Quick bounds check.
        if (index > 15)
            index = 15;

        index += 94; // extended characters
    }

    return index;
}

default {
    state_entry() {
        // Initialize the character index.
        ResetCharIndex();
    }

    link_message(integer sender, integer channel, string data, key id) {
        if (channel == DISPLAY_STRING) {
            RenderString(data);
            return;
        }
        if (channel == DISPLAY_EXTENDED) {
            RenderExtended(data);
            return;
        }
        if (channel == gCellChannel) {
            // Extract the characters we are interested in, and use those to render.
            string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 2);

            // See if we need to show special effects.
            if (gCellUseFading)
                RenderWithEffects( TextToRender );
            else // !gCellUseFading
                RenderString( TextToRender );
            return;
        }
        if (channel == REMAP_INDICES) {
            // Parse the message, splitting it up into index values.
            list Parsed = llCSV2List(data);
            integer i;
            // Go through the list and swap each pair of indices.
            for (i = 0; i < llGetListLength(Parsed); i += 2) {
                integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

                // Swap these index values.
                string Value1 = llGetSubString(gCharIndex, Index1, Index1);
                string Value2 = llGetSubString(gCharIndex, Index2, Index2);

                gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
                gCharIndex = llInsertString(gCharIndex, Index1, Value2);

                gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
                gCharIndex = llInsertString(gCharIndex, Index2, Value1);
            }
            return;
        }
        if (channel == RESET_INDICES) {
            // Restore the character index back to default settings.
            ResetCharIndex();
            return;
        }
        if (channel == SET_CELL_INFO) {
            // Change the channel we listen to for cell commands, the
            // starting character position to extract from, and
            // special effect attributes.
            list Parsed = llCSV2List(data);
            gCellChannel        = (integer) llList2String(Parsed, 0);
            gCellCharPosition   = (integer) llList2String(Parsed, 1);
            gCellUseFading      = (integer) llList2String(Parsed, 2);
            gCellHoldDelay      = (float)   llList2String(Parsed, 3);
            return;
        }
        if (channel == SET_FONT_TEXTURE) {
            // Use the new texture instead of the current one.
            gFontTexture = id;
            // Change the currently shown texture.
            llSetTexture(gFontTexture, LEFT_FACE);
            llSetTexture(gFontTexture, MIDDLE_FACE);
            llSetTexture(gFontTexture, RIGHT_FACE);
            return;
        }
        if (channel == SET_THICKNESS) {
            // Set our z scale to thickness, while staying fixed
            // in position relative the prim below us.
            vector Scale    = llGetScale();
            float Thickness = (float) data;

            // Reposition only if this isn't the root prim.
            integer ThisLink = llGetLinkNumber();
            if (ThisLink != 0 || ThisLink != 1) {
                // This is not the root prim.
                vector Up = llRot2Up(llGetLocalRot());
                float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
                vector Pos = llGetLocalPos();
                llSetPos(Pos + DistanceToMove * Up);
            }

            // Apply the new thickness.
            Scale.z = Thickness;
            llSetScale(Scale);
            return;
        }
    }
}

XyText Single Texture

( Newer Version to above )

////////////////////////////////////////////
// XyText v1.1.1 Script (Single Texture)
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;
integer SET_FONT_TEXTURE    = 204005;
integer SET_THICKNESS       = 204006;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "12345";

// Face numbers.
integer LEFT_FACE       = 4;
integer MIDDLE_FACE     = 0;
integer RIGHT_FACE      = 2;

// Used to hide the text after a fade-out.
key     TRANSPARENT     = "701917a8-d614-471f-13dd-5f4644e36e3c";
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the key of the font we are displaying.
key     gFontTexture        = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel        = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition   = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0  means don't fade out.
float   gCellHoldDelay      = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
   gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
   // \" <-- Fixes LSL syntax highlighting bug.
   gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
   gCharIndex += "\n\n\n\n\n";
}

vector GetGridOffset(integer index) {
   // Calculate the offset needed to display this character.
   integer Row = index / 10;
   integer Col = index % 10;

   // Return the offset in the texture.
   return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3) {
   // Set the primitive textures directly.
   llSetPrimitiveParams( [
       PRIM_TEXTURE, LEFT_FACE,   (string) gFontTexture, <0.05, 0.1, 0>, grid_offset1, PI_BY_TWO,
       PRIM_TEXTURE, MIDDLE_FACE, (string) gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
       PRIM_TEXTURE, RIGHT_FACE,  (string) gFontTexture, <0.05, 0.1, 0>, grid_offset3, -PI_BY_TWO
       ]);
}

RenderString(string str) {
   // Get the grid positions for each pair of characters.
   vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
   vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
   vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

RenderWithEffects(string str) {
   // Get the grid positions for each pair of characters.
   vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
   vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
   vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

   // First set the alpha to the lowest possible.
   llSetAlpha(0.05, ALL_SIDES);

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3);          // Now turn up the alpha until it is at full strength.
    float Alpha;
    for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // See if we want to fade out as well.
   if (gCellHoldDelay < 0.0)
       // No, bail out. (Just keep showing the string at full strength).
       return;
          // Hold the text for a while.
   llSleep(gCellHoldDelay);
      // Now fade out.
   for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // Make the text transparent to fully hide it.
   llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
   // Look for escape sequences.
   list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
   integer ParsedLen = llGetListLength(Parsed);

   // Create a list of index values to work with.
   list Indices;
   // We start with room for 3 indices.
   integer IndicesLeft = 3;

   integer i;
   string Token;
   integer Clipped;
   integer LastWasEscapeSequence = FALSE;
   // Work from left to right.
   for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
       Token = llList2String(Parsed, i);

       // If this is an escape sequence, just set the flag and move on.
       if (Token == ESCAPE_SEQUENCE) {
           LastWasEscapeSequence = TRUE;
       }
       else { // Token != ESCAPE_SEQUENCE
           // Otherwise this is a normal token.  Check its length.
           Clipped = FALSE;
           integer TokenLength = llStringLength(Token);
           // Clip if necessary.
           if (TokenLength > IndicesLeft) {
               Token = llGetSubString(Token, 0, IndicesLeft - 1);
               TokenLength = llStringLength(Token);
               IndicesLeft = 0;
               Clipped = TRUE;
           }
           else
               IndicesLeft -= TokenLength;

           // Was the previous token an escape sequence?
           if (LastWasEscapeSequence) {
               // Yes, the first character is an escape character, the rest are normal.

               // This is the extended character.
               Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

               // These are the normal characters.
               integer j;
               for (j = 1; j < TokenLength; j++)
                   Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
           }
           else { // Normal string.
               // Just add the characters normally.
               integer j;
               for (j = 0; j < TokenLength; j++)
                   Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
           }

           // Unset this flag, since this was not an escape sequence.
           LastWasEscapeSequence = FALSE;
       }
   }

   // Use the indices to create grid positions.
   vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0));
   vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
   vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

integer ConvertIndex(integer index) {
   // This converts from an ASCII based index to our indexing scheme.
   if (index >= 32) // ' ' or higher
       index -= 32;
   else { // index < 32
       // Quick bounds check.
       if (index > 15)
           index = 15;

       index += 94; // extended characters
   }

   return index;
}

default {
   state_entry() {
       // Initialize the character index.
       ResetCharIndex();
   }

   link_message(integer sender, integer channel, string data, key id) {
       if (channel == DISPLAY_STRING) {
           RenderString(data);
           return;
       }
       if (channel == DISPLAY_EXTENDED) {
           RenderExtended(data);
           return;
       }
       if (channel == gCellChannel) {
           // Extract the characters we are interested in, and use those to render.
           string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 2);
                      // See if we need to show special effects.
           if (gCellUseFading)
               RenderWithEffects( TextToRender );
           else // !gCellUseFading
               RenderString( TextToRender );
           return;
       }
       if (channel == REMAP_INDICES) {
           // Parse the message, splitting it up into index values.
           list Parsed = llCSV2List(data);
           integer i;
           // Go through the list and swap each pair of indices.
           for (i = 0; i < llGetListLength(Parsed); i += 2) {
               integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
               integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

               // Swap these index values.
               string Value1 = llGetSubString(gCharIndex, Index1, Index1);
               string Value2 = llGetSubString(gCharIndex, Index2, Index2);

               gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
               gCharIndex = llInsertString(gCharIndex, Index1, Value2);

               gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
               gCharIndex = llInsertString(gCharIndex, Index2, Value1);
           }
           return;
       }
       if (channel == RESET_INDICES) {
           // Restore the character index back to default settings.
           ResetCharIndex();
           return;
       }
       if (channel == SET_CELL_INFO) {
           // Change the channel we listen to for cell commands, the
           // starting character position to extract from, and
           // special effect attributes.
           list Parsed = llCSV2List(data);
           gCellChannel        = (integer) llList2String(Parsed, 0);
           gCellCharPosition   = (integer) llList2String(Parsed, 1);
           gCellUseFading      = (integer) llList2String(Parsed, 2);
           gCellHoldDelay      = (float)   llList2String(Parsed, 3);
           return;
       }
       if (channel == SET_FONT_TEXTURE) {
           // Use the new texture instead of the current one.
           gFontTexture = id;
           // Change the currently shown texture.
           llSetTexture(gFontTexture, LEFT_FACE);
           llSetTexture(gFontTexture, MIDDLE_FACE);
           llSetTexture(gFontTexture, RIGHT_FACE);
           return;
       }
       if (channel == SET_THICKNESS) {
           // Set our z scale to thickness, while staying fixed
           // in position relative the prim below us.
           vector Scale    = llGetScale();
           float Thickness = (float) data;
                      // Reposition only if this isn't the root prim.
           integer ThisLink = llGetLinkNumber();
           if (ThisLink != 0 || ThisLink != 1) {
               // This is not the root prim.
               vector Up = llRot2Up(llGetLocalRot());
               float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
               vector Pos = llGetLocalPos();
               llSetPos(Pos + DistanceToMove * Up);
           }
                      // Apply the new thickness.
           Scale.z = Thickness;
           llSetScale(Scale);
           return;
       }
   }
}

Continued on XyText1.2

Library


Examples

These are examples of LSL scripts you may find useful. For more, try the ScriptLibrary page or the Script Library forum on the Second Life website.

You may use these scripts freely as they stand, or as a base to build your own. Taking them and selling them would be considered impolite, however. If you want to use them in a "commercial" script, talk to the original author first.

Posting Guidelines:
Please preface new example script names with "Example", as in "ExampleAnimationScript". Also, put a return link to "Examples" at the end of the page. This makes it so if you click on this page's title, all the examples will come up. Remember to enclose your code using the "code" tags! (%% code goes here. %%)

Script Name: | Purpose: | Author:
Animation Basic | quickly demonstrates the basics of animating an avatar | CatherineOmega
Animation Script | animates an avatar | XylorBaysklef
Attachment Type Checker | Checks if an object is attached, and where it's attached. | CatherineOmega
Controls | demonstrates the use of llTakeControls | ReadyJack
Event Order | says each event as it happens | AngelaSalome
Get Root Position | get link parent position from within child | XylorBaysklef
Get Root Rotation | get link parent rotation from within child | XylorBaysklef
Gun | a gun script that fires physical bullets | ReadyJack
Link Message | communicate between prims | CatherineOmega
List Conversion | convert lists to strings and back | ReadyJack
Safe List Conversion | converts lists to strings and back, with no possible errors | KeknehvPsaltery
Look At | looks at an agent | JimBunderfeld
Money | waits for payment and refunds invalid amounts | CatherineOmega
Number Conversion | charset/number system conversion | EzharFairlight
ParticleScript | creates particle effects | AmaOmega
ParticleScript1 | modified version | EepQuirk
Point Child At Target | point a selected axis of a child prim at a region-coordinate target | CrossLament
Proximity Door | controls access to an area | JordanCotton
Self Replication | demonstrates self replication | StefanNilsson
States | multiple states | JimBunderfeld
States Concise | 2-state script | ChromalBrodsky
Argument Multiplexing | argument multiplexing, eg for linkmessages | HughPerkins
Left Or Right | A function that returns whether one vector is to the left or right of another | TiPBaKeR
RCP to PHP | communicating with a PHP backend system | MarcEisenberg
Browser/Object RPC | Browser sends RPC request to SecondLife object and gets and shows response | SomeScripter
ExampleHomeAlarmSystem | How to write a home alarm system: monitor your SecondLife home from your browser in your FirstLife home, or on the go. | SomeScripter
HUD Communication | Allows a object only listen to your huds or you | YiffyYaffle
Fixed Precision | Truncate Floats to fixed precision | GigsTaggart
32bit Hash | Hash keys or strings into a 32 bit integer | GigsTaggart
Authenticated Chat | Autheticated Chat. | GigsTaggart
Text to Leet | Converts regular text into leet speak. | Jadz0rConover
Text to Cap Alt | Converts regular text into the capital alternation format. | Jadz0rConover
ExamplellPushObject | Throws an avatar in a specified direction. | JokerOpus
Position on a Sphere | Calculates the position on a sphere. | AsiraSakai
Rotation to Vector/Angle | Converts a rotation to a vector and the angle the vector is rotated around. | AsiraSakai
Vector Gradient | Enables one to transition smoothly from one vector to another, specially good for colors | TigroSpottystripesKatsu
ExampleSides | Numbers the sides of an object | AnorcaCalamari
GetNewInventoryName | Function that returns a list of names of items added to an object's inventory | ZenMondo
llVector2String | Function that returns a string form of a vector. | VinceBosen

Q: What's the difference between the Script Library and the Examples page?
A: The Script Library is for full scripts. The Examples page is best suited for explaining specific functions, or parts of scripts.

HomePage | ScriptLibrary


Example32BitHash

The need often arises that you need to reduce an agent key or other string into a 32 bit hash. This happens a lot because on_rez can only pass the new object an integer as a parameter, and you might need to pass it information on which agent to act on, or respond to.

This function will hash data into an integer so that you can pass it to an object via on_rez. Of course you can't jam 128 bits or more of data into 32 without collisions, but the chances of a collision are usually slim, depending on the application. Beware possible birthday problem implications if you plan to use this to construct a list.

It's actually probably not necessary to MD5 an agent's key before extracting the first 4 bytes, because UUIDs are pretty evenly distributed, but this way is easier to convert to handling arbitrary string data. You will definitely want to MD5 any strings other than UUIDs.

integer keytoint(string inkey)
{
    return (integer)("0x" + llGetSubString(llMD5String(inkey,0), 0, 7));
}

Examples


ExampleAnimationBasic

// Basic Animation / Attachment 1.0
// Catherine Omega Heavy Industries

string gAnimName = "sit"; // what animation to play?

default
{
    state_entry()
    {
        llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION); // ask the owner for permission to trigger animations
        llStartAnimation(gAnimName); // automatically trigger animation.
    }

    on_rez(integer param)
    {
        llResetScript(); // reset the script as soon as it starts.
    }

    attach(key id)
    {
        integer perm = llGetPermissions();

        if (id != NULL_KEY) // make sure we're actually attached.
        {
            if (! (perm & PERMISSION_TRIGGER_ANIMATION)) // remember to use bitwise operators!
            {
                llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION); // request permissions from the owner.
            }
        }
        else
        {
            if (perm & PERMISSION_TRIGGER_ANIMATION)
            {
                llStopAnimation(gAnimName); // stop the animation
            }
        }
    }
}

For a more in-depth animation script, see Xylor's ExampleAnimationScript.

Examples / Permissions


ExampleAnimationScript

Here is an example of how to animate an avatar. I wrote this soon after starting
SL to test out the animation system, and it is now free for anyone's use.

-- Xylor Baysklef

////////////////////////////////////////////
// Animation Script v1.2.1
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
list ANIMATIONS  = [ "aim_L_bow", "aim_R_bazooka", "aim_R_handgun", "aim_R_rifle", "angry_fingerwag",
"angry_tantrum", "away", "backflip", "blowkiss", "bow", "brush", "clap",
"courtbow", "cross_arms", "crouch", "crouchwalk", "curtsy",
"dance1", "dance2", "dance3", "dance4", "dance5", "dance6", "dance7", "dance8",
"dead", "drink", "express_afraid", "express_anger", "express_bored",
"express_cry", "express_embarrased", "express_laugh", "express_repulsed",
"express_sad", "express_shrug", "express_surprise", "express_wink",
"express_worry", "falldown", "female_walk", "fist_pump", "fly", "flyslow",
"hello", "hold_R_bow", "hold_R_bazooka", "hold_R_handgun", "hold_R_rifle",
"hold_throw_R", "hover", "hover_down", "hover_up", "impatient",
"jump", "jumpforjoy", "kick_roundhouse_R", "kissmybutt", "kneel_left",
"kneel_right", "land", "laugh_short", "motorcycle_sit", "musclebeach", "no_head", "no_unhappy",
"nyanya", "peace", "point_me", "point_you" ];

list ANIMATIONS2 = [ "prejump", "punch_L", "punch_onetwo", "punch_R",
"RPS_countdown", "RPS_paper", "RPS_rock",
"RPS_scissors", "run", "salute", "shoot_L_bow", "shout", "sit", "sit_female", "sit_ground",
"sit_to_stand", "sleep", "slowwalk", "smoke_idle", "smoke_inhale", "smoke_throw_down",
"snapshot", "soft_land", "stand", "standup", "stand_1", "stand_2",
"stand_3", "stand_4", "stretch", "stride", "surf", "sword_strike_R",
"talk", "throw_R", "tryon_shirt", "turnback_180", "turnleft", "turnright",
"turn_180", "type", "uphillwalk", "walk", "whisper", "whistle", "wink_hollywood", "yell",
"yes_happy", "yes_head", "yoga_float" ];
///////////// END CONSTANTS /////////////////

///////////// GLOBAL VARIABLES ///////////////
//integer gToggle = 0;
integer gAnimNumber;
integer gTotalAnims;

string gAnimName = "type";
/////////// END GLOBAL VARIABLES /////////////

default {
    state_entry() {
        //llSay(0, "Init...");
        llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);

        ANIMATIONS += ANIMATIONS2;
        ANIMATIONS2 = [];

        gTotalAnims = llGetListLength(ANIMATIONS);
        gAnimNumber = -1;
        llListen(0, "", llGetOwner(), "");
    }

    on_rez(integer param) {
        //llGiveInventory(llGetOwner(), "Animation Names");
        llResetScript();
    }

    listen(integer channel, string name, key id, string mesg) {
        string preamble = llGetSubString(mesg, 0, 3);
        if (preamble != "anim" && preamble != "stop")
            return;

        integer perm = llGetPermissions();

        if ( !(perm & PERMISSION_TRIGGER_ANIMATION)) {
            llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
            return;
        }

        list parsed = llParseString2List(mesg, [ " " ], []);
        //llSay(0, (string)parsed);

        string anim = llList2String(parsed, 1);

        if (preamble == "stop") {
            //llSay(0, "Stopping: " + llGetAnimation(llGetOwner()));
            //llStopAnimation(llGetAnimation(llGetOwner()));
            if (anim == "")
                anim = gAnimName;

            if (anim == "all") {
                integer i;
                llSay(0, "Stopping all animations... please wait.");
                for (i=0; i<gTotalAnims; i++)
                    llStopAnimation(llList2String(ANIMATIONS, i));

                llSay(0, "Done.");

                return;
            }

            //llSay(0, "Stopping: " + anim);
            llStopAnimation(anim);
            return;
        }

        gAnimName = anim;
        //llSay(0, "Animation: " + gAnimName);
        llStartAnimation(gAnimName);
    }

    run_time_permissions(integer perm) {
        //llStopAnimation(gAnimName);
        //gToggle = 0;
    }

    attach(key id) {
        integer perm = llGetPermissions();

        if (id != NULL_KEY) {

            if (! (perm & PERMISSION_TRIGGER_ANIMATION)) {
                llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
            }
        }
        else {

            if (perm & PERMISSION_TRIGGER_ANIMATION) {
                llStopAnimation(gAnimName);
            }
        }
    }

    touch_start(integer total_number) {
        if (llDetectedKey(0) != llGetOwner())
            return;

        integer perm = llGetPermissions();

        if (perm & PERMISSION_TRIGGER_ANIMATION) {
            if (gAnimNumber != -1) {
                llStopAnimation( llList2String(ANIMATIONS, gAnimNumber) );
            }

            gAnimNumber++;
            if (gAnimNumber == gTotalAnims)
                gAnimNumber = 0;

            gAnimName = llList2String(ANIMATIONS, gAnimNumber);

            llStartAnimation( gAnimName );
            llSay(0, "Animation: " + gAnimName);
        }
        else {
            llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
        }
    }
}

For a simpler explanation of animation permissions, see ExampleAnimationBasic.

Examples


ExampleArgumentMultiplexing

Eg, sending a linked message:

list command=["DOSOMETHING",llGetKey(),llGetPos(),TRUE];
llMessageLinked( LINK_ROOT, 0, llDumpList2String(command, "-=-"), "" );

This sends command "DOSOMETHING" followed by a key, a vector and an integer.

When you're receiving this, you can do this:

link_message(integer sender_num, integer num, string str, key id)
{
     list Arguments = llParseStringKeepNulls( str, ["-=-"], [] );
     string Command = llList2String( Arguments, 0 );

     if( Command == "DOSOMETHING" )
     {
          key kSenderKey = (key)llList2String( Arguments, 1 );
          vector vSenderPos = (vector)llList2String( Arguments, 2 );
          integer bSomeInteger = (integer)llList2String( Arguments, 3 );
          ...
     }
     else ...
}

The great thing about this is that you can send any data type, including strings with spaces.


ExampleAttachmentTypeChecker

// Attachment Type Checker 1.0
// Is this object a HUD attachment, a regular attachment, or is it unattached?

checkAttachmentSlot()
{
    integer slot = llGetAttached();
    if (slot == 0)
    {
        llOwnerSay("Not attached.");
    }

    else if (slot > 0 && slot <= 30)
    {
        llOwnerSay("Attached to the avatar.");
    }

    else if (slot > 30 && slot <= 38)
    {
        llOwnerSay("Attached to the HUD.");
    }
}

default
{
    state_entry()
    {
        checkAttachmentSlot();
    }

    on_rez(integer start_param)
    {
        llResetScript();
    }

    attach(key id)
    {
        llResetScript();
    }
}

Examples | Attachments | llGetAttached


ExampleAuthenticatedChat

This is a very secure authenticated message system for chat. It could use some tweaking, as if the message contains commas it will likely break.

//By: Gigs Taggart
//Released under BSD license

key gSetupQueryId;

integer gSetupNotecardLine;

string  gSetupNotecardName = "setup";

string gPassword;

//to sign a message we hash message+gSecret+llGetKey()
string sign_message(string message)
{
        string hash=llMD5String(message+gPassword+(string)llGetKey(), 0);
        return llList2CSV([message,hash]);
}

//to verify a message we do all the same stuff except we get the sender's key
integer verify_message(string message, key sender)
{
    list incoming=llCSV2List(message);
    string hash=llMD5String(llList2String(incoming, 0) +gPassword+(string)sender, 0);
    if (llList2String(incoming,1)!=hash)
        return FALSE;
    else
        return TRUE;
}

readSettingsNotecard()
{
   gSetupNotecardLine = 0;
   gSetupQueryId = llGetNotecardLine(gSetupNotecardName,gSetupNotecardLine);
}

default
{
    state_entry()
    {
         readSettingsNotecard();
    }
    dataserver(key queryId, string data)
    {
        if(queryId == gSetupQueryId)
        {
            if(data != EOF)
            {
                list tmp = llParseString2List(data, ["="], []);
                string setting = llList2String(tmp,0);
                if (setting == "password")
                {
                    gPassword=llList2String(tmp,1);
                }

                gSetupQueryId = llGetNotecardLine(gSetupNotecardName,++gSetupNotecardLine);
            }
            else
            {
                state running;
            }
        }
    }
    changed(integer change)
    {
        llResetScript();
    }
}

state running
{

    changed(integer change)
    {
        llResetScript();
    }
}

Examples


ExampleBrowserXMLRPC

Getting Started With XML-RPC In Second Life (version 1)

Ruben Kleiman (SomeScripter) 11/14/06

Comments: ruben@rubenkleiman.com

This "Getting Started" will allow you to talk XML-RPC to Second Life just by using your web browser. A server is not necessary.

In a single HTML page, I provide you with (read Instructions below for accessing it):

  • A ready-to-run, XML-RPC capable SL script that you can attach to any SL object.
  • An HTML page that sends RPC requests to and receives responses from the SL script.
  • Important Tidbits section about XML-RPC in SL (limitations, etc.) to get you going.

I limited the functionality so that you can easily tweak the code until you're on your own two feet.

IMPORTANT: This works only in Internet Explorer right now. An ActiveX object is used to communicate via XML-RPC: you must unblock it if your IE browser blocks it (the default). I'll change it to work with other browsers when I have the time.

Instructions

Simply copy the contents of the html code below into a file in your computer, save it (with an .html extension, of course), and open it in Internet Explorer.

I will update the code and the Important Tidbits section as things change in the SL protocol/limitations.

<html>
<!-- Getting Started Second Life XML-RPC (version 1) -- Ruben Kleiman 11/14/06
     Sample Code
-->
<head>
<title>Getting Started Second Life XML-RPC (ver. 1) -- Ruben Kleiman 11/14/06</title>
<style>
.code {
    background: #EEEEEE;
    border: solid #888888 2px;
    font-family: "Courier New", monospace;
    color: black;
    font-size: 10pt;
    width: 96%;
    max-height: 200px;
    overflow: auto;
    padding: 3px;
    margin: 2px;
    margin-left: 1em;
}

.style1 {
    font-size: x-small;
    font-weight: bold;
}
</style>
<script>
// Request object
var request = null;

// callRPCScript -- make RPC to SL script
function callRPCScript() {
  try {

  var key = document.getElementById('key');
  var stringMessage = document.getElementById('stringMessage');
  var intMessage = document.getElementById('intMessage');

  request = getXMLHttpRequest();
  if (request == null) {
      alert("Sorry, you must use Internet Explorer.");
    return;
  }

  var xml = '<?xml version=\"1.0\"?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct><member><name>Channel</name><value><string>'+key.value+'</string></value></member><member><name>IntValue</name><value><int>'+intMessage.value+'</int></value></member><member><name>StringValue</name><value><string>'+stringMessage.value+'</string></value></member></struct></value></param></params></methodCall>';
  alert("Sending: "+xml);

  request.open('POST', 'http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi', true);
  request.onreadystatechange = getHttpResponse;
  request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  request.setRequestHeader('Cache-Control', 'no-cache');
  request.send(xml);
  } catch (err) {
    alert("XML-RPC Error: "+err.message);
    return;
  }
}

// getHttpResponse -- get RPC response from SL script
function getHttpResponse() {
  if (request.readyState == 4) {
      alert("Response Status: "+request.status+" "+request.statusText);
      alert("Response Text:"+request.responseText);
  }
}

// getXMLHttpRequest -- create a standard Http connection object
function getXMLHttpRequest() {
    if (request != null) return request; // re-use
    if (typeof XMLHttpRequest != 'undefined')
        { return new XMLHttpRequest(); }
    try { return new ActiveXObject("Msxml2.XMLHTTP"); }
    catch (err1) {
        try { return new ActiveXObject("Microsoft.XMLHTTP");
        } catch (err2) {
            // ignore
        }
    }
    return false;
}
</script>
</head>
<body>
<p><b>Getting Started With XML-RPC In Second Life</b></p>
<p class="style1">Ruben Kleiman (SomeScripter) 11/14/06</p>
<p>This page will help you to quickly get started sending and receiving XML requests in Second Life.</p>
<p>Copy the HTML contents of this page into a file in your own computer and run it from there.</p>
<p><em><strong>Important</strong>: </em>This works only in Internet Explorer. Why? For all other parental-guidance-required browsers, I would need to use digital signatures or gruesome hacks that defeat it--which, unfortunately, is a waste of time for me. </p>
<p>The way it works is as follows (detailed instructions <a href="#details">here</a>):</p>
<ul>
  <li>You create an object in Second Life with a script provided here.</li>
<li>This HTML page  sends a request to that object.</li>
<li>The Second Life object replies to the request.</li>
<li>This HTML page displays the data in the object's reply. </li>
</ul>
<p><b><a name="details"></a>Detailed Instructions</b></p>
<p>First, in Second Life, do the following:</p>
<ol>
<li>Create some object (any type or size will do).</li>
<li>For the sake of reference in what follows, name the object MyRCPObject. The name doesn't really matter.</li>
<li>Create a script for the object.</li>
<li>Replace the default script in MyRCPObject with  this script:</li>
<div class="code"><pre><span style="color: #FF7700;">
string EXPECTED_REQUEST_STRING_MESSAGE = "hello there";
string BAD_RESPONSE_STRING_MESSAGE = "I do not understand what you said";
string GOOD_RESPONSE_STRING_MESSAGE = "Hi.";
key myChannel;

default {
  state_entry() {
    llOwnerSay("Touch me to start listening for XML-RPC requests.");
  }

  touch_start(integer n) {
    llOpenRemoteDataChannel();
    llOwnerSay("Waiting for channel to open");
  }

  remote_data(integer type, key channel, key uid, string from, integer integerValue, string stringValue) {
    if (type == REMOTE_DATA_CHANNEL) {
      myChannel = channel;
      llOwnerSay("Channel Open. Channel Key="+(string)channel);
      state waiting;
    }
  }
}

state waiting {

  state_entry() {
    llOwnerSay("Waiting for XML-RPC Requests...");
  }

  remote_data(integer type, key channel, key uid, string from, integer integerValue, string stringValue) {
    if (type == REMOTE_DATA_REQUEST) {
      if (stringValue == EXPECTED_REQUEST_STRING_MESSAGE) {
    // uid is the identifier for the request message.
    llRemoteDataReply(channel, uid, GOOD_RESPONSE_STRING_MESSAGE, 1);
      } else {
    llRemoteDataReply(channel, uid, BAD_RESPONSE_STRING_MESSAGE, 0);
      }
    } else {
      // Code would go here to respond to a new open channel, etc...
      // Check the options for 'type' in the LSL Wiki Mirror 7-7-7.
    }
  }

  touch_start(integer n) {
    state default;
  }

  state_exit() {
    llCloseRemoteDataChannel(myChannel);
    llOwnerSay("Channel closed. Touch again to start listening on channel.");
  }
}
</span></pre></div>
<li>What does this script do? It listens for an XML-RPC request and checks whether its string message is 'hello there'. If it is, it sends a response to the browser with a string message 'Hi.' Otherwise, it sends a response with a string message 'I do not understand what you said'.</li>
<li>Save the script and follow its instructions.</li>
</ol>
<p>Finally, fill-in the <a href="#form">form below</a>:</p>
<ol>
<li>Enter the key of MyRCPObject's channel. This key would have been printed by MyRCPObject on the chat line after you touch that object. You can use copy/paste from the chat history. You can click on the 'Talk With...' button now and see what happens; else, read the following steps. </li>
<li>Leave the String Message and Integer Message as they are.</li>
<li>Click on the 'Talk With XML-RPC Script' button.</li>
<li>After you click on the button, an XML-RPC request will be sent to MyRCPObject, but, before sending it, an alert box will show you the contents of the XML request. Click OK on the alert box to allow the request to be sent.</li>
<li>The browser will wait for a response from MyRCPObject. When it arrives, an alert box will show a status code: if is equal to 200, then the request has succeeded (it has found the server and the response has been answered by MyRCPObject. Click OK on the alert box.</li>
<li>Finally, another alert box will show you the XML document returned in the response by MyRCPObject. If you look hard at the XML document, you'll notice that the string "Hi." has been returned in the response.</li>
</ol>
<p>For more details, read this page's HTML code; in particular, the callRPCScript() script, which sends the message and receives the response. </p>
<p><strong>Important Tidbits (valid 11/14/06):</strong></p>
<ul>
  <li>You can't initiate an XML-RPC request to a server from Second Life. You can only respond to requests.</li>
<li>You can communicate with a Second Life object when the owner is logged off.</li>
<li>Once you create an object, the channel key string appears to persist for the object between channel deactivation and activation.</li>
<li>The string value in an RPC response is limited to about 255 characters.</li>
<li>There's a minimum response latency from SL servers for RPC calls of about 3 seconds.</li>
</ul>
<a name="form"></a>
<hr/>
<table style='background-color: #00CCFF;' width='400'>
<tr><td>Channel's Key: </td><td><input type='text' id='key' size='40' maxlength='50' value='b46c0c9b-9824-a1bb-bc2a-2853b49648f2' /></td></tr>
<tr><td>String Message: </td><td><input type='text' name='stringMessage' id='stringMessage' size='40' maxlength='255' value='hello there' /></td></tr>
<tr><td>Integer Message: </td><td><input type='text' name='intMessage' id='intMessage' size='40' maxlength='10' value='32' /></td></tr>
<tr><td colspan='2' align='center'><input onclick='callRPCScript()' type='button' value="Talk With XML-RPC Script" /></td></tr>
</table>
</body>
</html>

Examples


Example Controls

// example_controls.lsl // ready jack // 21 jan 2006 // v1.0

// Put this in an attachment. When you put the script in or make
// a change, you have to re-attach the object to activate the controls.

default
{
    attach(key who)
    {
        if (who == NULL_KEY) {
            // detached
            if (llGetPermissions() & PERMISSION_TAKE_CONTROLS) {
                llReleaseControls();
            }
        } else {
            llRequestPermissions(who, PERMISSION_TAKE_CONTROLS);
        }
    }

    run_time_permissions(integer perms)
    {
        integer desired_controls =
                CONTROL_FWD |
                CONTROL_BACK |
                CONTROL_LEFT |
                CONTROL_RIGHT |
                CONTROL_ROT_LEFT |
                CONTROL_ROT_RIGHT |
                CONTROL_UP |
                CONTROL_DOWN |
                CONTROL_LBUTTON |
                CONTROL_ML_LBUTTON
        ;

        if (perms & PERMISSION_TAKE_CONTROLS) {
            llTakeControls(desired_controls, TRUE, TRUE);
        }
    }

    control(key id, integer down, integer new)
    {
        integer pressed = down & new;
        integer held = down & ~new;
        integer released = ~down & new;

        if (pressed & CONTROL_LBUTTON) llOwnerSay("click");
        if (held & CONTROL_FWD) llOwnerSay("forward held");
        // etc.
    }
}

Examples


ExampleEventOrder

// Simple script to say the events as they occur.

default
{
    state_entry ()
    {
        llSay (0, "state_entry");
    }
    on_rez (integer start_parameter)
    {
        llSay (0, "on_rez");
    }
    touch_start (integer total_number)
    {
        llSay (0, "touch_start");
    }
    attach (key Avatar)
    {
        if (NULL_KEY == Avatar)
        {
            llSay (0, "attach - detached");
        }
        else
        {
            llSay (0, "attach - attached");
        }
    }
    changed (integer Change)
    {
        if (Change & CHANGED_LINK)
        {
            llSay (0, "changed - link");
        }
        if (Change & CHANGED_OWNER)
        {
            llSay (0, "changed - owner");
        }
    }
}

Examples | Events


ExampleFixedPrecision

If you need to cast a float to a string, you'll get lots of extra zeros you probably don't need. This function will let you specify a fixed precision for the resulting float->string conversion. Both functions are based on the assumption that LSL will use the default precision for the float->string conversion, which is to say 6 decimal places.

Fast and accurate
string fixedPrecision(float input, integer precision)
{
    precision = precision - 7 - (precision < 1);
    if(precision < 0)
        return llGetSubString((string)input, 0, precision);
    return (string)input;
}

Faster, and uses 7 fewer bytes then the above (and just as accurate).
string fixedPrecision(float input, integer precision)
{
    if((precision = (precision - 7 - (precision < 1))) & 0x80000000)
        return llGetSubString((string)input, 0, precision);
    return (string)input;
}

The second version of the funcion exploits some quirks in LSL; with the result of faster tighter code. LSL has always displayed 6 decimal places and I have never seen it do otherwise, even when the number is at the extreams of the range.

Examples


ExampleGetNewInventoryName

////////////////////////
// Function GetNewInventoryName by ZenMondo Wormser
//
// This function returns a list of the names of
// inventory items added to an object's inventory
// since the last time the function was called.
//
// This function works by keeping a list of inventory items
// and comparing against that list as inventory changes.
//
// NOTE: If items are deleted between calls and nothing is added
// this function will return an empty list.
////////////////////////

list gOld_Inventory;    //This stores the Inventory since the last function call.
                        //It is global so it persists between function calls.
                        //It may also be handy to have a list of your object's inventory.

list GetNewInventoryName()
{
    list found_new = [];  //This list will contain the names of New Inventory items.
    integer inventory_type = INVENTORY_ALL; //Change to look at inventory type you want.

    integer inventory_num = llGetInventoryNumber(inventory_type);

    list new_inventory = []; //This list will contain the current inventory.
    integer counter = 0;

    while(counter < inventory_num)
    {
        new_inventory = (new_inventory=[]) + new_inventory + llGetInventoryName(inventory_type, counter);
            counter ++;
    }

    integer list_length = llGetListLength(new_inventory);

    counter = 0;

    list scratch;

    while(counter < list_length)
    {
        scratch = llList2List(new_inventory, counter, counter);

        if(llListFindList(gOld_Inventory, scratch) == -1) //New Inventory Object
        {

               found_new += llList2String(scratch,0); //Add the name of the new inventory item to the found_new list
        }

        counter ++;

    }

    gOld_Inventory = new_inventory; //Store the Inventory List to be compared the next time the function is called.
    return found_new;   //Return a list of the new inventory items.
}

default
{
    state_entry()
    {
        GetNewInventoryName(); //Populate the list on reset.
    }

    touch_start(integer num_detected)
    {
        ///////////
        // Example Use in touch_start()
        // Because Multiple Items could have been
        // added between calls we work through
        // the list that is retunred.
        ///////////

        list new_inv = GetNewInventoryName();

        llSay(0, "New Inventory Items:");

        integer list_len = llGetListLength(new_inv);

        integer counter;

        for(counter = 0; counter < list_len; counter++)
        {
           llSay(0, llList2String(new_inv, counter));
        }
    }

    changed(integer mask)
    {
        //////////////////
        // Example Used in changed()
        // This is much simpler, as changed()
        // will be triggered each time something
        // is added to the object's inventory
        // resulting in only one result in the
        // returned list from GetNewInventoryName()
        ////////////////////

        if(mask & CHANGED_INVENTORY)
        {
            list new_inv = GetNewInventoryName();

            llSay(0, "New Inventory Item:" + llList2String(new_inv, 0));
        }
    }

}

Examples


ExampleGetRootPos

This function returns the position of the root object, even if called
from a child object.

-- Xylor

Also see llGetRootPosition.

vector GetRootPos() {
    // If this object is not linked, or if it is the
    // root object, just return llGetPos()
    integer LinkNum = llGetLinkNumber();

    if (LinkNum <= 1)
        return llGetPos();

    // Otherwise take local position into account.
    return llGetPos() - llGetLocalPos();
}
Examples


ExampleGetRootRot

This function can be used to get the rotation of the root object, even if
called from a child object.

-- Xylor Baysklef

rotation GetRootRot() {
    // If this object is not linked, or if it is the
    // root object, just return llGetRot
    integer LinkNum = llGetLinkNumber();

    if (LinkNum == 0 || LinkNum == 1)
        return llGetRot();

    // Otherwise take local rotation into account.

    // This is the rotation of this object with the
    // root object's rotation as the reference frame.
    rotation LocalRot = llGetLocalRot();
    // This uses the global coord system as the
    // reference frame.
    rotation GlobalRot = llGetRot();

    // Reverse the local rotation, so we can undo it.
    LocalRot.s = -LocalRot.s;

    // Convert from local rotation to just root rotation.
    rotation RootRot = LocalRot * GlobalRot;

    // Make the sign match (mathematically, this isn't necessary,
    // but it makes the rotations look the same when printed out).
    RootRot = -RootRot;

    return RootRot;
}

Examples


ExampleGun

By ReadyJack: (moved)

This updated script fixes a serious bug when rezzing the bullet, cleans up some useless code and allows the user to either relax or ready the weapon by touching it.

Futzed with by StrifeOnizuka to fix a bug in the rez offset and a possible permission hole. this bug was inherited from the revolver script. On each attach it would corrupt the rez offset, eventualy going to zero or infinity, depending on if your avatar was taller then 2 meters. My guess is the original revolver script did a llResetScript on_rez then later that was removed. -BW

Pistol:
// Pistol // Ready Jack // 5.22.04 // 1.4

// Make sure these are right
string gBulletName = "Bullet";
string gShotSound = "Shot";
float gBulletSpeed = 75.0;
integer gBulletDamage = 50; // Passed to the bullet script via the rez param

// use a hold anim, you'll go
// into the aim anim automatically when entering mouselook
string gAnim = "hold_R_handgun";
integer gGrip = ATTACH_RHAND;
string gGripMessage = "this weapon only fits in your right hand";
vector gAimOffsetConstant = <0.0, 0.0, 0.84>; // borrowed from the Revolver script
integer gEnableBullet;
integer gEnableSound;
integer gArmed;
vector gAimOffset;

say(string message)
{
    llOwnerSay(message);
}

getPerms()
{
    integer perms = llGetPermissions()
            | PERMISSION_TAKE_CONTROLS
            | PERMISSION_TRIGGER_ANIMATION
            | PERMISSION_ATTACH;
    llRequestPermissions(llGetOwner(), perms);
}

verifyInventory()
{
    if (llGetInventoryKey(gBulletName) != NULL_KEY) {
            gEnableBullet = TRUE;
    } else {
        gEnableBullet = FALSE;
        say("bullet not found: " + gBulletName);
    }
    if (llGetInventoryKey(gShotSound) != NULL_KEY) {
        gEnableSound = TRUE;
    } else {
        gEnableSound = FALSE;
        say("sound not found: " + gShotSound);
    }
}

arm() {
    integer perm = PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION;
    if ((llGetPermissions() & perm) != perm ) {
        getPerms();
    } else {
        llTakeControls(CONTROL_ML_LBUTTON, TRUE, FALSE);
        llStartAnimation(gAnim);
        gArmed = TRUE;
    }
}

disarm() {
    if(llGetAgentSize(llGetPermissionsKey()) != ZERO_VECTOR)
        llStopAnimation(gAnim);
    llReleaseControls();
    gArmed = FALSE;
}

default
{
    state_entry()
    {
        verifyInventory();
    }

    touch_start(integer count)
    {
        if (llDetectedKey(0) != llGetOwner()) {
            return;
        } else if (!llGetAttached()) {
            getPerms();
        } else if (gArmed) {
            disarm();
        } else {
            arm();
        }
    }

    run_time_permissions(integer perms)
    {
        if (perms & (PERMISSION_TAKE_CONTROLS
                | PERMISSION_TRIGGER_ANIMATION
                | PERMISSION_ATTACH)) {
            if (!llGetAttached()) {
                llAttachToAvatar(gGrip);
            } else if (llGetAttached() != gGrip) {
                say(gGripMessage);
                llDetachFromAvatar();
            } else {
                verifyInventory();
                arm();
            }
        } else {
            say("insufficient permissions");
            if (llGetAttached()) llDetachFromAvatar();
        }
    }

    attach(key avatar)
    {
        if (avatar != NULL_KEY) {
            // attaching
            vector size = llGetAgentSize(avatar);
            gAimOffset = gAimOffsetConstant;
            gAimOffset.z *= size.z / 2.0;
            getPerms();
        } else {
            // detaching
            if (gArmed) disarm();
        }
    }

    control(key avatar, integer levels, integer edges)
    {
        // mouse press
        if ((levels & CONTROL_ML_LBUTTON) && (edges & CONTROL_ML_LBUTTON)) {
        }

        // mouse release
        if (!(levels & CONTROL_ML_LBUTTON) && (edges & CONTROL_ML_LBUTTON)) {
            if (gEnableSound) llTriggerSound(gShotSound, 1.0);
            rotation rot = llGetRot();
            vector aim = llRot2Fwd(rot);
            vector pos = llGetPos() + gAimOffset + (aim * 1.0);
            llRezObject(gBulletName, pos, aim * gBulletSpeed, rot, gBulletDamage);
        }

        // mouse down
        if ((levels & CONTROL_ML_LBUTTON) && !(edges & CONTROL_ML_LBUTTON)) {
        }
    }
}

Bullet:
// Bullet // Ready Jack // 6.23.04 // 1.3

// The other safety precautions seem unreliable if the bullet crosses
// into another sim, but this die timer seems to work pretty well.
float gTimeToDie = 20.0;

default
{
    state_entry()
    {
        llSetPrimitiveParams([PRIM_TEMP_ON_REZ, TRUE]); // unreliable
        llSetStatus(STATUS_PHYSICS | STATUS_DIE_AT_EDGE, TRUE);
        //llSetBuoyancy(1.0); // uncomment for slow bullets
    }

    on_rez(integer start_param)
    {
        if (!start_param) return;

        llCollisionFilter("", llGetOwner(), FALSE);
        llSetDamage((float)start_param);
        llCollisionSound("", 1.0); // cancel default sound
        llSetTimerEvent(gTimeToDie);
    }

    collision_start(integer count)
    {
        integer type = llDetectedType(0);
        if (type & AGENT) {
            // tricky stuff like proprietary damage goes here
            llDie();
        } else if (llGetStartParameter()) {
            //llDie(); // uncomment to disallow ricochet, unreliable
        }
    }

    land_collision_start(vector pos) {
        //if (llGetStartParameter()) llDie(); // uncomment for no-bounce, unreliable
    }

    timer()
    {
        llDie();
    }
}

Props to Bel Muse, Water Rogers, Ezhar Fairlight and the author of the Revolver Script.

Examples


ExampleHomeAlarmSystem

Example Home Alarm System (version 0.4)

Ruben Kleiman (SomeScripter)

The code is licensed under LGPL: that is, you can use it for any purpose, but
you should acknowledge the author in the sources.

Comments: ruben@rubenkleiman.com

This code teaches you how to monitor your Second Life home using a web browser. A server is not necessary.
In addition, as people ask me questions, I will add information on useful variations on this code
in the Variations On The Theme section at the end of this page.

Pre-requisites: Basic familiarity with LSL language and with DHTML.

The code for this tutorial consists of two files:

I provide you with (read Instructions below to access them):

  • A ready-to-run, SL script that you can attach to any SL object in your Second Life property.
  • An HTML page that communicates with the SL script.

Please carefully read the instructions in this page before asking me any questions.
This is not a trivial application. Thanks!

IMPORTANT 1: Until version 1.0 is released, some of the
functionality will be turned on or off, but the main system will function. Documentation will be
available in version 1.0. Until then, the code documentation are
meaningful variable and function names and some comments.
Please do not contact me unless you have carefully read
the instructions in this page (thanks!).

IMPORTANT 2: This works only in Internet Explorer right now. An ActiveX object is used
to communicate via XML-RPC: you must unblock it if your IE browser blocks it (the default).
I'll change it to work with other browsers when I have the time; the only issue is
the different way Mozilla-based browsers handle certification.

Instructions

Follow these steps in order:

1. Copy the contents of the following LSL script to an object located in your property:
http://home.earthlink.net/~kleiman/examples/home_alarm04_script.txt
2. Enable the script by touching the object (follow object's instructions); disable it by touching it again.
The alarm object in SL should acknowledge that it started (chat message).
3. When the alarm starts, you will get a chat message with the alarm's monitoring channel key. Copy and
paste it so that you can use it in step 6, below.
4. Copy the contents of the html code in http://home.earthlink.net/~kleiman/examples/home_alarm04_html.txt
into a text file in your computer, save it with an .html extension,
and open it in Internet Explorer. Save it in Notepad (Windows),
but not in a word processing program like Word.
5. Open the HTML page.
5.a If the Internet Explorer banner at the top of the page asks you whether
to allow an ActiveX control to run, click on the banner message and select
"Allow Blocked Content..." I use a standard ActiveX component that lets your
browser communicate with the Second Life server (and thus to your script).
5.b Ensure that cookies are enabled in your browser or else accept cookies from the page.
I use a cookie to save your configuration information.
6. Enter the alarm system key (see step 3, above) into the "Channel Key"
field in the "Alarm Configuration Settings" sections of the page.
The setting will take effect once you out of the field.
(Right now, the only way to go back to the default settings is
to remove the alarm system's cookie.)
7. On the web browser, click on the "Start" button to start monitoring from your browser location.
Your should immediately see status information and you will get results in a few
seconds. Wait 20 seconds. In version 0.4, the browser will poll the home alarm system every 20 seconds
This is a configurable setting.
(being nice to the poor little overworked SL servers out there).
Some debugging information from your SL alarm object may from time to time show up as chat.
8. Until the 1.0 release, I urge you not to change the configuration settings
other than the alarm's monitoring channel key, as necessary.
If you change any settings, some may not work as expected or your alarm
system might crash due to problem described in Known Problems, below.

Have fun!
- Ruben

Troubleshooting

1. Have you followed the steps in the instructions in the right order?
2. Are you sure that you started the alarm? The alarm will give
you a message when it starts. Touch it again if you are not sure.
3. Are you sure that the alarm key that you entered in your
browser is the same as the home alarm's key?
4. Have you limited your monitoring to one browser at a time?
5. Are you not sure that your Internet Explorer page is
properly initialized? Start Internet Explorer and
delete all cookies (Tools -> Internet Options); then open
the alarm system page again and enter the monitoring channel key.
(If you want to manually delete just the alarm system cookie, its
name starts with _rk_.)
6. Nothing happens? Make sure that you have started the alarm system object in SL
and that you have clicked the "Start Monitoring" button in the web browser page.
Look at your chat messages in SL to make sure that your alarm object is healthy
(it will tell you if/when it crashes).

Known Problems

1. If the number of sensed objects is large, you will get a stack heap crash.
The default configuration settings will minimize but
not necessarily eliminate this possibility.
2. Changing configuration settings may precipitate problem #1.

Variations On The Theme

Is it possible to send binary data to the SL server?

In theory, yes. In practice, you need to be aware that:

(1) Data is marshalled in XML, which is based
on a character set rather than on a binary format.

(2) The file size of an XML file accepted by the SL server
is arbitrary; we have no control over it.

Solution:

(1) Binary format: Pack and unpack your binary file data into a format that will
survive the network and transport layers. One such format is Base64.
On the HTML page, use Javascript to marshall the binary data
into a Base64 string. Similarly, on the SL script side, change it
to convert the received (Base64) string into binary
using llBase64ToInteger. What you do with the binary data
is up to you from here on.

(2) File size: experiment with increasingly large file sizes until you get an error.
That will tell you the actual maximum file size.

Examples


ExampleHUDCommunication

Lets say your building a item that is to listen to a remote object (HUD, or Relay) as well as yourself, you might find this handy. This will prevent your attachments from listening to other peoples HUDs, also allowing you to give it commands by chat.

default
{
    state_entry()
    {
        llListen(1,"","","");
// Notice "llListen(1,"","","");" does not list a owner! Leave this alone.
// There is no need for a owner. You may however still use another channel.
    }
    listen(integer channel, string name, key id, string message)
    {
        if(llGetOwnerKey(id) == llGetOwner())
// Notice "if(llGetOwnerKey(id) == llGetOwner())" was added?
// This will check if the object or person talking to it is owned
// by this scripts owner. All Agents own themselves! This is to
// filter out the listener. Everthing else is ignored!
        {
            if (message == "start")
            {
                llOwnerSay("Starting...");
            }
        }
    }
}


Example: Left or Right

This function returns a value depending on whether one vector is to the left or right of another. This can be used to tell where an object is in relation to another.
integer left_or_right(vector baseline, vector target)
{
    //Returns whether the target vector is to the left or right of the baseline vector
    //returns 0
    //straight ahead = 0 (baseline == target)
    //left  = 1
    //right = -1
    //directly behind = 2
    if (llVecNorm(baseline) == llVecNorm(target))
    {
        return 0;
    }
    if (llVecNorm(baseline) == llVecNorm(target) * -1)
    {
        return 2;
    }
    rotation rot_between = llRotBetween(baseline,target);
    vector euler_rot = llRot2Euler(rot_between);
    float z_rot = euler_rot.z;
    if (z_rot > 0)
    {
        return 1;
    }
    else
    {
        return -1;
    }
}

//Example uses
default
{
    state_entry()
    {
        llOwnerSay("Left or Right example");
    }

    touch_start(integer total_number)
    {
        llOwnerSay("Detecting Avatar");
        llSensor("","",AGENT,60,PI);
    }
    sensor(integer num)
    {

        //Is the detected avatar to the left or the right of this object's current heading
        integer target_L_or_R = left_or_right(llRot2Fwd(llGetRot()),llDetectedPos(0) - llGetPos());
        if (target_L_or_R == 1)
        {
            llOwnerSay("Avatar is to the left of me");
        }
        else
        {
            llOwnerSay("Avatar is to my right");
        }

        //In relation to the object's heading, is the detected avatar facing to the left or right?
        integer moving_L_or_R = left_or_right(llRot2Fwd(llGetRot()),llRot2Fwd(llDetectedRot(0)));
        if (moving_L_or_R == 1)
        {
            llOwnerSay("Avatar is facing to my left");
        }
        else
        {
            llOwnerSay("Avatar is facing to my right");
        }
    }
}

Examples | Sensor


ExampleLinkMessage

For more on LinkMessages, see llMessageLinked and link_message.

Sender script:
default
{
    touch_start(integer total_number)
    {
        // Sends the message 0, "Touched.", NULL_KEY to all scripts in this prim,
        // that contain a link_message() event handler. That would _this_ script
        // if it has one.
        llMessageLinked(LINK_THIS, 0, "Touched.", NULL_KEY);
        // The 'LINK_THIS' constant makes llMessageLinked ONLY send the message
        // to the current prim.
        // It's the same as using 'llGetLinkNumber()' to return the current link number.
    }
}

Receiver script:
default
{
    // Waits for another script to send a link message.
    link_message(integer sender_num, integer num, string str, key id)
    {
        llSay(0,str);
    }
}

Examples | LinkMessages | llMessageLinked | link_message


Example List Conversion

Here's a snippet that converts lists to strings and back while preserving the actual element type. This is useful for passing lists via chat or link messages, etc. Drop it into your code and away you go.

This version uses the newer dump/parse functions. Props to the author of the original which can be found here.

Feel free to use and modify this in your own code for any purpose.

WARNING: Be careful passing floats by themselves or as part of a vector. The way LSL does string conversion on floats can result in inaccuracies at the 6th significant digit. For more info see float.

////////////////////////////////////////////////////////
// snip list convert // ready jack // 11 Nov 2005 // 1.0
string slc_sep = "#!$"; // funky name for clean namespaces

string list_2_string(list l) {
    list result = [];
    integer len = llGetListLength(l); // optimization
    integer i;
    for (i = 0; i < len; ++i) {
        result += [llGetListEntryType(l, i)] + llList2List(l, i, i);
    }
    return llDumpList2String(result, slc_sep);
}

list string_2_list(string s) {
    list l = [];
    list result = [];
    l = llParseStringKeepNulls(s, [slc_sep], []);
    integer len = llGetListLength(l);
    integer i;
    for (i = 0; i < len; ++i) {
        integer type = (integer) llList2String(l, i);
        ++i; // CLEVERNESS WARNING...BE VERY AFRAID
        if (type == TYPE_INTEGER) result += (integer)llList2String(l, i);
        else if (type == TYPE_FLOAT) result += (float)llList2String(l, i);
        else if (type == TYPE_KEY) result += (key)llList2String(l, i);
        else if (type == TYPE_VECTOR) result += (vector)llList2String(l, i);
        else if (type == TYPE_ROTATION) result += (rotation)llList2String(l, i);
        else result += llList2String(l, i);
    }
    return result;
}
//////////////////// snip list convert /////////////////

Examples


ExamplellPushObject

llPushObject

* llPushObject is a function used to shove an avatar a fixed distance or hight, however in a recent update parcels were given an option to restrict push.

When coming up with the velocity to push, the max will be 2147483647. Anything over that will act as 2147483647. In addition, the push will only be as strong as the objects energy will allow, so larger prims will have more energy to push with.
Here is an example for llPushObject on the y axis -
/////A simple script piece that throws an avatar a few hundred meters on the y axis/////
//The 9999999 does not represent meters, its an amount of power

default
{
    collision_start(integer total_number)
    {
        llPushObject(llDetectedKey(0), <0,9999999,0>, <0,0,0>, FALSE);
    }
}

And on the x axis-
default
{
    collision_start(integer total_number)
    {
        llPushObject(llDetectedKey(0), <9999999,0,0>, <0,0,0>, FALSE);
    }
}
And finaly a push upwards-
default
{
    collision_start(integer total_number)
    {
        llPushObject(llDetectedKey(0), <0,0,9999999>, <0,0,0>, FALSE);
    }
}
Note that using llPushObject to grief in inappropriate areas, other users will result in an abuse report being filed. If you want to remove someone from your land, use llEjectFromLand. llPushObject is not to be used for that. Using it for something like a system of tubes or a method of transport would probably be better.

Q: Is there a way to make llPushObject work in areas that restrict push?

A: It is impossible to implement push on a push-restricted parcel. However, you can for artificial pushes by shooting big prims, or spinning prims.
A2: An attachment will still be able to perform llPushObject on it's owner, even in an area that disallows scripts, though in most cases llMoveToTarget would work better. -Oh, good point -Joker~


ExampleLookAt

////////////////////////////////////////////////////////////////
// Look At
// by Jim Bunderfeld
// This script is open source. Please make changes as necessary.
// For best results, put this script in a cone.
////////////////////////////////////////////////////////////////

default
{
    state_entry()
    {
        llSetStatus(STATUS_PHYSICS,TRUE); // make the object physical.
        llSetHoverHeight(1.0,FALSE,1); // Object hovers at 1 meter above ground.
        llSensorRepeat("","",AGENT,10,PI,11); // Runs a sensor for an agent within 10 meters. It repeats every 11 seconds.
    }

    sensor(integer det)
    {
        vector pos = llDetectedPos(0); // Gets the pos of the nearest avatar.
        llLookAt(pos,1,1); // Point the object to pos.
    }
}
Examples


// ExampleMoney
// Pay the object money, this script refunds incorrect payments
// This code is public domain.

integer gCorrectAmount = 10; // this defines the correct price -- we only accept L$10

default
{
    state_entry()
    {
        // get permission from the owner to pay out money using the llGiveMoney function.
        llRequestPermissions(llGetOwner(),PERMISSION_DEBIT);
    }

    money(key id, integer amount)
    {
        // has the user paid the correct amount?
        if (amount == gCorrectAmount)
        {
            // if so, thank the payer by name.
            llSay(0,"Thank you, " + llKey2Name(id) + ".");
        }

        // is the amount paid less than it needs to be?
        else if (amount < gCorrectAmount)
        {
            // if so, tell them they're getting a refund, then refund their money.
            llSay(0,"You didn't pay enough, " + llKey2Name(id) + ". Refunding your payment of L$" + (string)amount + ".");
            llGiveMoney(id, amount); // refund amount paid.
        }

        // if it's not exactly the amount required, and it's not less than the amount required,
        // the payer has paid too much.
        else
        {
            // tell them they've overpaid.
            integer refund = amount - gCorrectAmount; // determine how much extra they've paid.
            llSay(0,"You paid too much, " + llKey2Name(id) + ". Your change is L$" + (string)refund + ".");
            llGiveMoney(id, refund); // refund their change.
        }
    }
}

Examples | money() | llGiveMoney


ExampleNumberConversion

These two functions allow conversion between a string of any charset (like hexadecimal, octal, etc.) and integer. Simply adjust the charset variables to change your charset. You could even do something like an octal system that uses the letters A-H instead of 0-7.

string  charset = "0123456789abcdef";

integer chr2int(string chr) { // convert unsigned charset to integer
    integer base = llStringLength(charset);
    integer i = -llStringLength(chr);
    integer j = 0;
    while(i)
        j = (j * base) + llSubStringIndex(charset, llGetSubString(chr, i, i++));
    return j;
}

string int2chr(integer int) { // convert integer to unsigned charset
    integer base = llStringLength(charset);
    string  out;
    integer j;
    if(int < 0) {
        j = ((0x7FFFFFFF & int) % base) - (0x80000000 % base);
        integer k = j % base;
        int = (j / base) + ((0x7FFFFFFF & int) / base) - (0x80000000 / base);
        out = llGetSubString(charset, k, k);
    }
    do
        out = llGetSubString(charset, j = int % base, j) + out;
    while(int /= base);
    return out;
}

default {
    state_entry() {
        integer a = 0x80000000;
        string hex = int2chr(a);
        llOwnerSay( llList2CSV([a, hex, chr2int(hex)]));
    }
}

Examples


ExamplePointChildAtTarget

This function, when called in a child prim of an object, will point the chosen axis of the child prim towards a target location specified in region-coordinates; a demonstration of how, if you wish to relate a child prim's rotation to the global axes, you need to unrotate your result by the root prim's rotation.

In this example, it is using a repeating sensor to point the child prim's x-axis at the nearest avatar.

-- Cross Lament

// PointChildAtTarget - points child's 'axis' towards region-coordinate 'pos'.
PointChildAtTarget( vector pos, vector axis )
{
    // The global position of the child prim.
    vector mypos = llGetPos() ;

    // The unit vector towards the target.
    vector targetvector = llVecNorm( pos - mypos ) ;

    // Find the rotation required to point the unrotated (ie. world) axis
    // towards the target, then unrotate this by the root prim's rotation.
    rotation targetrot = llRotBetween( axis, targetvector ) / llGetRootRotation() ;

    llSetLocalRot( targetrot ) ;
}

// The axis of the child prim you wish to point at the target object.
vector desiredaxis = < 1, 0, 0 > ;

default
{
    state_entry()
    {
        llSensorRepeat( "", NULL_KEY, AGENT, 10, PI, 1 ) ;
    }

    sensor( integer num )
    {
        PointChildAtTarget( llDetectedPos( 0 ), desiredaxis ) ;
    }
}

Examples


ExamplePositionOnASphere

How to calculate the position on a sphere. Usefull to get something to rotate around a point other than it's center, among other things.

vector spherePos(vector offset, float radius, float zenith, float azimuth)
{
    // zenith is the angle between the positive z-axis and the vector.  Ranges from 0 to PI.  0 will put the object at the top of the sphere, PI will put it at the bottom.  This represents a horizontal slice of the sphere, the height the object will be placed at.
    // azimuth is the angle between the positive x-axis and the vector projected onto the xy-plane.  Ranges from 0 to TWO_PI.  0  or TWO_PI will put the object at the positive radius for x and 0 for y.  This is how far around the horiztonal sphere-slice determined from the zenith the object will be placed at.
    // see http://en.wikipedia.org/wiki/Spherical_coordinates

    float sinzenith = llFabs(llSin(zenith));
    float x = sinzenith * llCos(azimuth);
    float y = sinzenith * llSin(azimuth);
    float z = llCos(zenith);
    return offset + (radius * <x, y, z>);
}


ExampleProximityDoor

This is a script I use to control access to an area.
I'm sure there are a hundred better scripts out there like this one.
Feel free to use it, and please let me know how I can make it better...

- Jordan Cotton

// This script was authored by (in game) Jordan Cotton.
// and has been placed in the public domain.
// Feel free to use and abuse it as you feel fit.
// When not in game, the author can be contacted through the private message
// feature of the on the secondlife forums:
//               http://forums.secondlife.com/private.php?s=&action=newmessage
//
// Enjoy!

// -----------------------------------
// CONSTANT DEFINITIONS
// -----------------------------------

list     DEFAULT_USERS = [ "Jordan Cotton"];  // Put you name here so you don't get locked out...

// -----------------------------------
// GLOBAL VARIABLES
// -----------------------------------

integer  g_listen_control;
integer  g_isScanning     = FALSE;
integer  g_isOpen         = FALSE;
integer  g_debug          = FALSE;
list     g_protectionList = DEFAULT_USERS;
integer  g_OLD_STATE;

dumpList( )
{
    integer i;
    string entry;

    for( i = 0; i< llGetListLength( g_protectionList ); i++ )
    {
        entry = llList2String( g_protectionList, i);
        llSay( 0, (string) i + ") " + entry);
    }
}

integer isInList( string who )
{
    integer i;
    string entry;

    for( i = 0; i< llGetListLength( g_protectionList ); i++ )
    {
        entry = llList2String( g_protectionList, i);
        if ( llSubStringIndex(entry, who ) != -1) return TRUE;
    }

    return FALSE;
}

addToList( string who )
{
    if ( ! isInList( who ) )
    {
        g_protectionList = who + g_protectionList;
    }
}

removeFromList( string who)
{
    integer i;
    string entry;

    for( i = 0; i< llGetListLength( g_protectionList ); i++ )
    {
        entry = llList2String( g_protectionList, i);
        //llSay( 0, (string) i + ") " + entry);
        //llSay( 0, "Does [" + entry + "] = [" + who + "]" );
        if ( llSubStringIndex(entry, who ) != -1){
            llSay( 0, "deleting ->" +  entry);
             g_protectionList = llDeleteSubList( g_protectionList, i, i);
        }
    }
}

default
{
    on_rez(integer param)
    {
        llResetScript();
    }

    state_entry()
    {
        if( g_debug) llSay(0, "Scanner Online.");

        g_listen_control = llListen(0, "", NULL_KEY, "");
    }

    state_exit()
    {
        llListenRemove(g_listen_control);
        llSetTimerEvent(0.0);
    }

    listen(integer number, string name, key id, string message)
    {
        string who;

        if ( isInList( name ) ||
             ( llSubStringIndex( llKey2Name( llGetOwner()) , name ) != -1 ) )
        {
            if( message=="door:help" ) {
                llWhisper( 0, "Door Commands ");
                llWhisper( 0, "door:open Toggles the door's open status.");
                llWhisper( 0, "door:scan start/stop looking for people who I will open for.");
                llWhisper( 0, "door:whoisonlist Dislay who is on the access list.");
                llWhisper( 0, "door:reset reset the access control list");
                llWhisper( 0, "door:remove remove someone from the list.");
                llWhisper( 0, "door:add add someone to the list.");

            } else if( message=="door:open" ) {
                g_isOpen = !g_isOpen;
                llSetStatus( STATUS_PHANTOM, g_isOpen );
                if ( g_isOpen ) {
                    llSetAlpha( 0.3, ALL_SIDES );
                } else {
                    llSetAlpha(1.0, ALL_SIDES );
                }
            } else if( message=="door:scan" ) {
                g_isScanning = !g_isScanning;
                if ( g_isScanning ){
                    llWhisper( 0, "door is scanning.");
                    llSensorRepeat("", "", AGENT, 5, 2*PI, .1);
                } else {
                    llWhisper( 0, "door stopped scanning.");
                    llSensorRemove();
                }
            }else if( llSubStringIndex(message, "door:whoisonlist") != -1 ) {
                dumpList();
            }else if( llSubStringIndex(message, "door:reset") != -1 ) {
                g_protectionList = DEFAULT_USERS;
            }else if( llSubStringIndex(message, "door:remove") != -1 ) {
                who = llGetSubString( message, 12, llStringLength(message));
                llSay( 0, "removing: " + who );
                removeFromList( who );
            }else if( llSubStringIndex(message, "door:add") != -1 ) {
                who = llGetSubString( message, 9, llStringLength(message));
                llSay( 0, "adding: " + who );
                addToList( who );
            }
        }
    }

    sensor(integer detected)
    {
        integer i;
        for(i=0; i<detected; i++)
        {
            if  (  isInList( llDetectedName(i) ) )
            {
                if (g_debug) llWhisper( 0 , "opening");
                llSetStatus( STATUS_PHANTOM, TRUE );
                llSetAlpha( 0.3, ALL_SIDES );
                llSetTimerEvent(2.0);
            }
        }
    }

    timer()
    {
        if (g_debug) llWhisper( 0 , "closing");
        llSetStatus( STATUS_PHANTOM, FALSE );
        llSetAlpha(1.0, ALL_SIDES );
    }
}

Examples


ExampleRotToVecAngle

These functions will let you convert rotations into a vector (axis) and the angle the vector is rotated by (the rotation is perpendicular to the vector). The final function lets you convert back.

vector getVecFromRot(rotation r)
{//llRot2Axis
    vector v = <r.x, r.y, r.z>;
    if(v)//Is the vector a zero vector?
        return llVecNorm(v);//not a zero vector, so normalize it
    return v;//vector was zero.
}

float getAngleFromRot(rotation r)
{//llRot2Angle
    return 2 * llAcos(r.s / llSqrt(r.x * r.x + r.y * r.y + r.z * r.z + r.s * r.s));
}

rotation getRotFromVecAngle(vector v, float a)
{//llAxisAngle2Rot, not normalized
    return <v.x, v.y, v.z, 1 / llTan(a / 2)>;
}

I optimized the functions a bit. These are examples how the Axis-Angle functions work. --BW


ExampleRPC2PHP

I will explain the code later. Here's the code so you can start playing with it. I'm at work right now, so I can't check the code, but I will mess with it later tonight and finish this page up. This is basically LSL and PHP code that can be used to communicate between an object in SecondLife and your web server.

LSL code for your in-game object.
// This is the email address that we send our initial notification request to. Your PHP script will read and parse the email at
// this address later through POP3, so you should probably use one that is not used by anyone or anything else.
string NOTIFY_EMAIL = 'whatever@yourdomain.com';

// I always set constants for different idata commands sent through RPC. Then I just make sure they match the constants
// that I set in my PHP file, and everything works great.
integer RPC_ERROR = 0;
integer RPC_COMMAND1 = 1;
integer RPC_COMMAND2 = 2;

// This will be used as a reference later in case we need to know it.
key rpc_chan;

default {
    state_entry()
    {
        // First, open an RPC channel so the website can contact us.
        llOpenRemoteDataChannel();
    }

    remote_data(integer event_type, key channel, key message_id, string sender, integer idata, string sdata)
    {
        // If the RPC channel has just been opened, then we need to send off a notice to the webserver.
        if (event_type == REMOTE_DATA_CHANNEL)
        {
            // We should keep a reference to the RPC channel that has been opened.
            rpc_chan = channel;

            // Here, we can specify extra parameters in the email that we need the webserver to know, such as the RPC channel.
            string msg;
            msg = "Command: OpenRPCChannel\n";
            msg += "RPC-Chan: "+(string)rpc_chan+"\n";
            llEmail(NOTIFY_EMAIL, "RPC Chan Opened", msg);

            llSay(0, "Channel has been opened, and email has been sent. Waiting for ET to phone home.");
        }

        // If an RPC request has been sent from the webserver.
        else if (event_type == REMOTE_DATA_REQUEST)
        {
            // We can check what command is coming through here, and handle them as needed.
            if (idata == RPC_COMMAND1)
            {
                llSay(0, "We have received a request for RPC_COMMAND1. Sending acknowledment.");
                llRemoteDataReply(channel, message_id, "success", RPC_COMMAND1);

            // Just checking for another command.
            }
            else if (idata == RPC_COMMAND2)
            {
                llSay(0, "We have received a request for RPC_COMMAND2. Sending acknowledment.");
                llRemoteDataReply(channel, message_id, "success", RPC_COMMAND2);
             // If it was an unrecognized command, we still have to send some sort of reply back.
            }
            else
            {
                llSay(0, "An unrecognized RPC command was sent. Sending back error reply.");
                llRemoteDataReply(channel, message_id, "", RPC_ERROR);
            }
        }
    }
}

PHP code for your web server. Run this from the command prompt, not from the browser!
<?php
    // These are the same as the RPC commands we defined in the LSL script.
    define('RPC_ERROR', 0);
    define('RPC_COMMAND1', 1);
    define('RPC_COMMAND2', 2);

    // This is the hostname, username and password to login to your POP3 email account.
    define('POP3_HOST', 'localhost');
    define('POP3_PORT', 110);
    define('POP3_USER', 'whatever@yourdomain.com');
    define('POP3_PASS', 'password');

    // This is the SecondLife RPC url.
    define('SL_URL', 'http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi');

    // Minimum amount of time for each loop through the 'while' below.
    define('MIN_LOOP_TIME', 10);

    // Just a generic command to send an RPC request to SL and retrieve a reply response.
    // Requires CURL.
    function sendrpc($chan, $intval, $strval) {
        $req = xmlrpc_encode_request("llRemoteData", array("Channel"=>(string)$chan, "IntValue"=>(int)$intval, "StringValue"=>(string)$strval));
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, SL_URL);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $reply = curl_exec($ch);
        curl_close($ch);

        $replyVars = xmlrpc_decode($reply);
        return $replyVars;
    }

    // We want to continuously loop, checking for emails.
    while (true) {
        echo "Checking mail...\n";

        // Get the starting time.
        $starttime = time();

        // Open your POP3 email box.
        $mbox = imap_open("{".POP3_HOST."/pop3:".POP3_PORT."/notls}INBOX", POP3_USER, POP3_PASS);
        $msg_headers = imap_headers($mbox);
        for ($i=1; $i <= count($msg_headers); $i++) {
            $temp = imap_fetchheader($mbox, $i);

            // Match email headers for emails coming from SL.
            if (preg_match("/From: .*? <(.*?)@lsl.secondlife.com>/i", $temp, $regs) > 0) {
                // Extract information that can be used later, like for storing in a DB.
                $info = array();
                $info['object_key'] = $regs[1];

                $temp = imap_body($mbox, 1);
                if (preg_match("/Object-Name: (.*?)\r/i", $temp, $regs) > 0) {
                    $info['object_name'] = $regs[1];
                }
                if (preg_match("/Region: (.*?) \((.*?), (.*?)\)\r/i", $temp, $regs) > 0) {
                    $info['region_name'] = $regs[1];
                    $info['region_x'] = $regs[2];
                    $info['region_y'] = $regs[3];
                }
                if (preg_match("/Local-Position: \((.*?), (.*?), (.*?)\)\r/i", $temp, $regs) > 0) {
                    $info['local_x'] = $regs[1];
                    $info['local_y'] = $regs[2];
                    $info['local_z'] = $regs[3];
                }

                // Check for the command parameter we send in the email.
                if (preg_match("/Command: (.*?)\r/i", $temp, $regs) > 0) {
                    $info['command'] = $regs[1];
                }

                // Check what command we were sent. In this example, we only have one possibility.
                switch ($info['command']) {
                    case 'OpenRPCChannel':
                        // Additional information is sent with this command as well, so we need to get that.
                        if (preg_match("/RPC-Chan: (.*?)\r/i", $temp, $regs) > 0) {
                            $info['rpc_key'] = $regs[1];
                        }

                        // Let's go ahead and send off a request for RPC_COMMAND1 and see if we get anything back.
                        $reply = sendrpc($info['rpc_key'], RPC_COMMAND1, "This is a test message.");

                        // Now we can check the response.
                        if ($reply['IntValue'] == RPC_COMMAND1) {
                            if ($reply['StringValue'] == 'success') {
                                echo "Successfully sent and received RPC_COMMAND1.\n";
                            }
                        } else {
                            echo "There was an error in reading the response for RPC_COMMAND1.\n";
                        }
                        break;

                    // You can add more command types here.
                }

                // Since the message was for this script and it was parsed, mark it for deletion.
                imap_delete($mbox, $i);
            }
        }
        // Close the mailbox, and delete marked emails.
        imap_close($mbox, CL_EXPUNGE);
        echo "Done checking mail...\n";

        // Find out how long it took to run this loop.
        $endtime = time();
        $totaltime = $endtime - $starttime;

        // Delay it a little bit if it took less than a certain amount of time to run.
        if ($totaltime < MIN_LOOP_TIME) {
            echo "Waiting " . (MIN_LOOP_TIME-$totaltime) . " seconds...\n";
            sleep(MIN_LOOP_TIME-$totaltime);
        }

        echo "\n";
    }
?>

Examples


ExampleSafeListConversion

These are (yet more) functions to convert lists to strings and back. However, these functions have two main advantages.
1) It retains the types of the list data
2) It can't be fooled -- it doesn't rely on obscure character strings

// list2string and string2list functions...
//         For the safest transfers of lists with strings

// By Keknehv Psaltery, 1/12/06 -- Public domain, do whatever you wish (Although I'd prefer if I still get credit)

//Known limitations -- It doesn't preserve a small amount of the floating point data.
//Of course, there is the memory limitation. (I could do some float trimming, but that would make it much slower)

//How it works:
//  Instead of using something like llParseString2List with an uncommon separator, these list-strings
//        actually have an index of where the strings are, and, as a bonus, it also preserves data types
//    The data structure of the string looks like this:
//    [index1][type1] [index2][type2] [index3][type3]...[indexN][typeN] [data1][data2][data3]

// So, if you had a list like this: [The, 1, <1, 5.3, 6>, dog], it would look like
//        this (without quotation marks) when it passes through the list2string function:
// "00163001910020500503The1<1.000000, 5.300000, 6.000000>dog"
//  ====^====^====^====^---_------------------------------___
//  ind1|ind2|ind3|ind4|data1    data3                    data4
//     typ1 typ2 typ3 typ4 data2
// This formatting allows for **any** conceivable data to be transferred.

string padToFour( string inString )      //Outputs the string padded to four characters with spaces on the left
{                                                //( or cut to four characters -- pad("01234")="1234")
    //Note: this function uses a clever trick for efficiency
    integer inStringLength = llStringLength( inString );        //The length of the input string
    if ( inStringLength < 4 )
        return ( llGetSubString( "0000", 0, 3 - inStringLength ) + inString );
    else if ( inStringLength == 4 )
        return inString;
    else
        return llGetSubString( inString, inStringLength - 4, inStringLength );
}

string list2String( list inList )        //Converts a list to a string -- with no possible chance of error
{
    string outputString = llDumpList2String( inList, "" );
    integer listLength = llGetListLength( inList );
    integer baseOffset = listLength * 4;
    integer lastOffset;
    string prefixString;

    integer listElementNum;
    for ( listElementNum = 0 ; listElementNum < listLength ; ++listElementNum )
    {
        prefixString    += padToFour( (string)( baseOffset + lastOffset ) )
                        + (string)llGetListEntryType( inList, listElementNum );
        lastOffset += llStringLength( llList2String( inList, listElementNum ) );
    }

    outputString = prefixString + outputString;
    return outputString;
}

list string2List( string inString )
{
    list outputList;
    integer baseOffset = (integer)llGetSubString( inString, 0, 3 );
    integer listLength = baseOffset / 5;

    integer elementLength;
    integer elementType;

    integer listElementNum;
    for ( listElementNum = 0 ; listElementNum < listLength ; ++listElementNum )
    {
        elementType = (integer)llGetSubString( inString, listElementNum * 5 + 4, listElementNum * 5 + 4 );
        elementLength = (integer)llGetSubString( inString, listElementNum * 5, listElementNum * 5 + 3 )
                - baseOffset;

        if         ( elementType == 1 )
            outputList += [ (integer)llGetSubString( inString, baseOffset, baseOffset + elementLength ) ];
        else if ( elementType == 2 )
            outputList += [ (float)llGetSubString( inString, baseOffset, baseOffset + elementLength ) ];
        else if ( elementType == 3 )
            outputList += [ llGetSubString( inString, baseOffset, baseOffset + elementLength ) ];
        else if ( elementType == 4 )
            outputList += [ (key)llGetSubString( inString, baseOffset, baseOffset + elementLength ) ];
        else if ( elementType == 5 )
            outputList += [ (vector)llGetSubString( inString, baseOffset, baseOffset + elementLength ) ];
        else if ( elementType == 6 )
            outputList += [ (rotation)llGetSubString( inString, baseOffset, baseOffset + elementLength ) ];
    }

    return outputList;
}

default
{
    touch_start( integer num )
    {
        list testList = [ "The", "red", "dog" ];
        llOwnerSay( "testList = [" + llDumpList2String( testList, ", " ) + "]" );
        string testList2String = list2String( testList );
        llOwnerSay( "testList2String = \"" + testList2String + "\"" );
        list reversedL2S = string2List( testList2String );
        llOwnerSay( "Reversed list = [" + llDumpList2String( testList, ", " ) + "]" );
    }
}


ExampleSelfReplication

This is an very basic self-replicating script. It has a 'Time To Live' (ttl) counter, which ensures that the object won't keep replicating.

This example shows uses of the llGiveInventory and llRezObject functions as well as the object_rez, on_rez and changed events.

Please note that a ttl mechanism is nowhere near enough for a 'real' script!

See the page on self replication for more info about fail-safing and an assortment of amusing moral stories.

Basic steps

- Create a prim, call it 'rezTest'
- Add this script to it
- Take a copy and put in into the inventory of the first object
- Touch the first object

// The ttl variable is a counter, decreased for each subsequent replication,
// the child that get rezzed with a ttl value of zero will not procreate.

integer ttl = 3;

rez_child()
{
    // Check if we're allowed to create any more offspring
    if(ttl > 0)
    {
        // Offspring ttl should be one closer to zero
        integer ttl_child = ttl - 1;

        // Rez child one meter above me
        llRezObject("rezTest", llGetPos()+<0,0,1>, <0,0,0>, <0,0,0,0>, ttl_child);
    }
}

default
{
    // If you touch me, we'll have babies!
    touch_start(integer total_number)
    {
        rez_child();
    }

    // llRezObject throws event object_rez on the parent with the offspring as parameter
    object_rez(key child)
    {
        // Give copy of child to child
        llGiveInventory(child, llKey2Name(child));
    }

    // When rezzed, the event on_rez is called on me as the child
    on_rez(integer ttl_in)
    {
        // Save child (this one) ttl;
        ttl = ttl_in;
    }

    // Because of the script-delay we have to connect the grand-child rezzing
    // to the 'changed' event, which happens when someone (the parent) has
    // given me (as the child) something (a copy of myself)
    changed(integer change)
    {
        if(change == CHANGED_INVENTORY)
        {
            rez_child();
        }
    }
}

Examples


ExampleSides

ScriptLibrary Examples AnorcaCalamari
This script numbers the sides of an object.

Side Number | Side
0 | Top
1 | Lesser Y
2 | Greater X
3 | Greater Y
4 | Lesser X
5 | Bottom

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        //on an axis aligned box
        llSetTexture("1494e996-a91b-f770-bd10-ad788642d859",0);//TOP
        llSleep(1);
        llSetTexture("16a84092-421f-3225-bb36-4071b55fab2d",2);//GREATER X
        llSleep(1);
        llSetTexture("6f579c89-bf1f-71d7-854d-b08341edf51e",5);//BOTTOM
        llSleep(1);
        llSetTexture("e0e7eacd-e956-14e6-df65-3bc3b7d4e679",4);//LESSER X
        llSleep(1);
        llSetTexture("f545e486-2a2e-730d-845a-cbe9d4bcb9fd",1);//LESSER Y
        llSleep(1);
        llSetTexture("4b0a62c4-65f4-932e-d440-7fe3cf5a1540",3); //GREATER Y
//0 - top
//1 - lesser y
//2 - greater x
//3 - greater y
//4 - lesser x
//5 - bottom

    }
}


ExampleStates

This should help you understand how multiple states work.

// States in LSL example by Jim Bunderfeld

integer x = 0;

default
{
    touch_start(integer total_number)
    {
        if (x == 0)
        {
            llSay(0,"Chakra System Engaged");
            x = 1;
            llSleep(0.5);
            state k; // change to state k.
        }
    }
}

state k
{
    touch_start(integer total_number)
    {
        if (x == 1)
        {
            llSay(0,"Chakra System Disengaged");
            x = 0;
            state default; // change to default.
        }
    }
}

Unless you have some other use for variable x in this example, it is redundant and potentially confusing. You're using x to keep track of the "current system state", yet that is the purpose (and actual use here) of the state machine. You could easily get rid of all references to x in this example, making the example much simpler, clearer and a proper use of the state machine.
See ExampleStatesConcise for a clearer example.

Examples / States


ExampleStatesConcise

This is a basic demonstration of how, in LSL, a script may change its state. I've purposefully omitted the use of GlobalVariables because, and this is one of the really nice things about the whole state concept, by structuring your code appropriately, you may infer things about the operating conditions of the task by what state it is in. --ChromalBrodsky

default
{
    state_entry()
    {
        llWhisper(0, "Now in default state. *yawn*");
    }

    touch_start( integer iTouched )
    {
        state funky;
    }

    state_exit()
    {
        llWhisper(0, "Hey... I feel a little funny... Strange...");
    }
}

state funky
{
    state_entry()
    {
        llSay(0, "I'm in a FuNkY state!");
    }

    touch_start( integer iTouched )
    {
        state default;
    }

    state_exit()
    {
        llSay(0, "Aw, I'm starting to mellow, man...");
    }
}

Examples


ExampleStringVector

Small script I use to get a vector with floats into a vector string.
IE: <1.123, 1.345, 555.123> returns <1, 1, 555> as a string.
Dunno, pretty useless.
string llVector2String(vector w)
{
    integer x = (integer)w.x;
    integer y = (integer)w.y;
    integer z = (integer)w.z;
    string xx = (string)x;
    string yy = (string)y;
    string zz = (string)z;
    return "<" + xx + "," + yy + "," + zz + ">";
}


ExampleText2CapAlt

A quite simple function that converts a string into the commonly used (and abused) format that consists of alternating the capitalization of the letters in a sentence.

Example: "What's your Second Life name?" would translate to "WhAt's yOuR SeCoNd lIfE NaMe?"

// Text2CapAlt(string text)
// by jadz0r Conover
// Sept. 04, 2006

string Text2CapAlt(string text) {
    integer x;
    string CapAltString;
    for (x = 0; x < llStringLength(text); x++) {
        if ((x % 2) == 0) {
            CapAltString = CapAltString + llToUpper(llGetSubString(text, x, x));
        } else {
            CapAltString = CapAltString + llToLower(llGetSubString(text, x, x));
        }
    }

    return CapAltString;
}

Examples


ExampleText2Leet

A little function that converts regular text into leet (l33t, 1337, 31337) speak.

// Text2Leet(string text)
// by jadz0r Conover
// Sept. 04, 2006

// Some might argue that a single strided list could replace the following two to make this better, but I hate them.
list Text = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
list Leet = ["4", "|3", "(", "D", "3", "ph", "6", "|-|", "!", "J", "|<", "L", "|\\/|", "|\\|", "0", "P", "Q", "|2", "5", "7", "|_|", "\\/", "\\/\\/", "><", "'/", "Z"];

string Text2Leet(string text) {
    text = llToLower(text); // Since our "Text" list only contains lower case letters, we'll turn the 'text' parameter to the same capitalization.
    string LeetString; // This will be our end result and what this function will return.
    integer x;
    for (x = 0; x < llStringLength(text); x++) { // We run a loop as many times as many characters (including spaces) the 'text' parameter has.
        string CurChar = llGetSubString(text, x, x); // Define the current character we want to evaluate.
        if (llListFindList(Text, [CurChar]) != -1) { // If it's found in the "Text" list, do the following:
            LeetString = LeetString + llList2String(Leet, llListFindList(Text, [CurChar])); // Append the corresponding "Leet" character to our end result.
        } else { // If the current character we're evaluating is not in the "Text" list,
            LeetString = LeetString + CurChar; // simply append it to our end result.
        }
    }

    return LeetString; // Voila!
}

Examples


ExampleVectorGradient

vector gradient(list gradient, float rangestart,  float rangeend, float index)
{

//////////////////////////////////////////////////////
///originally coded by TigroSpottystripes Katsu////
//////////////////////////////////////////////////////
   // this function take as input a list containg vectors, a float especifying the min tange of the input and anothe for the end,
   //and finally a float indication which point of the gradiente ramp the result will be
    float transfindex = ((index - rangestart)/(rangeend - rangestart)) * (llGetListLength(gradient)- 1);//converts the index to the range of the vetors list

      integer pos = llFloor(transfindex); //get the position on the list
        float firstmulti =  (1-(transfindex-pos)); // the multiplier for the first vector
        float secondmulti = (transfindex-pos); //the multiplier for the second vector

        vector firstcolor = (llList2Vector(gradient, pos) * firstmulti); //set how muc h fo the first colorwill
        vector secondcolor = (llList2Vector(gradient, pos+1) * secondmulti); //smae thign but with the next color
         vector newcolor = firstcolor + secondcolor ; //mix the tqo

         return newcolor; //nad the cross-fade is done =^.^=
}
Examples


exchange

Protocol Exchange

I have added this page to allow scripters to communicate between their programs and 3rd parties, without having to open the scripts up to the public domain, also to be used for specifying standards for script communication. -AdamZaius

Listing New Entries

This page covers protocols defined by scripts that they wish others to be able to interact with. All entries in this index should be named in a common format: exchangeScriptName, such as exchangeGigasVendor, this is to prevent cluttering legitimate pages (like the library).

Why I should post my protocol?

You don't have to. However, if you plan on having other scripters improve on your scripts, without having to see the scripts themselves, this can be a great way of doing it. It also improves compatibility between products (such as a FooBar that can connect to a BarFoo, and perform some interaction).

What information do I need to include?

Any information relevant to communicating, this should include chat channels for objects that use listens, or the specific format of link messages. Example scripts that can extend your script through these communication protocols are a definite bonus.

Script Name | Version | Author | Date Added | Last Update
Gigas Vendor | 1.1 | Adam Zaius, Nexus Nash | December 23, 2004 | December 23, 2004
Pet Protocol | 0.32 | bUTTONpUSHER Jones | December 23, 2004 | December 23, 2004
Lockmeister System | 1.2 | Kyrah Abattoir | July 7, 2005 | January 4, 2007
LSL.php | 0.1 | Cyberleo.net | December 25, 2004 | December 25, 2004
Mini Second Logo | 0.1 | Bob Bauhaus | March 17, 2005 | March 17, 2005
Touch-Link Transfer Protocol | 0.32a | JesradSeraph | October 12, 2005 | October 12, 2005
Tight List Markup Language | 0.33f | Strife Onizuka | October 12, 2005 | October 12, 2005
Chat Extension Protocol | 1.0 | Christopher Omega | April 1, 2006 | April 1, 2006
Script Module "Protocol" | 1.0 | Christopher Omega | July 2, 2006 | July 2, 2006
Simple Link Protocol | 1.0 | Dale Glass | May 2, 2006 | May 2, 2006
Simple HTTPREQ Cruncher | 0.9 | Max Case | July 8, 2006 | July 8, 2006
Second Simple Syndication (SSS Feeds) | 1.0 | Dedric Mauriac | July 16, 2006 | July 27, 2006
LockGuard Specification | 2.0 | Lillani Lowell | August 17, 2006 | September 5, 2006
TrustNet API | 1.0 | Dale Glass | October 22, 2006 | October 22, 2006
Tool Discovery | 1.0 | Dale Glass | December 27, 2006 | December 27, 2006
SecondNet Transmissions Protocol | 0.1 | CartmanNoel | July 2, 2007 | July 2, 2007
Homepage | Script Library


exchangeGigasVendor

Gigas Vendor (aka oVendor)

Note: This information is depreciated as of the 2.0 vendor model, it is only compatible with the 1.0 and 1.1 vendors, A new version of this information for the new model will be provided shortly.

Return to protocol exchange

Button Scripts

Link Messages

Std. Button Names
BUTTON_LEFT
BUTTON_RIGHT
BUTTON_DIALOG
BUTTON_NOTECARD
BUTTON_FIRST
BUTTON_LAST

Recieves | Integer | String | Key | Response
Set Texture Face | 700 | Name of Button | Texture UUID | None
Set Alpha Face | 701 | Name of Button | Typecast Float (0.0 to 1.0) | None
Ping | 702 | Name of Button | Typecast "PING" | "PONG"

Sends (Event) | Integer | String | Key
On Touch | 800 | Name of Button | Key of Button

Listens
None

Inventory Script

Link Messages

Recieves | Integer | String | Key | Notes
Retrieve Item Info | 1100 | CATALOGUE_NEXT | Unused | Triggers all sends for next item
Retrieve Item Info | 1100 | CATALOGUE_PREV | Unused | Triggers all sends for previous item
Retrieve Item Info | 1100 | CATALOGUE_FIRST | Unused | Triggers all sends for first item
Retrieve Item Info | 1100 | CATALOGUE_LAST | Unused | Triggers all sends for last item

Sends (Event) | Integer | String | Key
Inventory Request | 1001 | 'NAME' | Typecast of name
Inventory Request | 1002 | 'DESC' | Typecast of description
Inventory Request | 1003 | 'PREVIEW' | Typecast of Texture UUID
Inventory Request | 1004 | 'NOTECARD' | Typecast of Notecard
Inventory Request | 1005 | 'CATALOGUE' | Typecast of Catalogue Key
Inventory Request | 1006 | 'CATALOGUE_NAME' | Typecast of Catalogue Name
Inventory Request | 1007 | 'PRICE' | Typecast of Price
Inventory Request | 1008 | 'GROUP_PRICE' | Typecast of llSameGroup() Price

Listens
None


exchangeLockGuard

LockGuard V2 Specification

LockGuard V2 is a particle chain system designed for creating and linking particle chains from one object to another using the simplest possible method while providing the framework for the most elaborate linking schemes imaginable. Designed to be modular, expandable, and with versatility in mind, LockGuard can link particle chains from one attachment to another, from an attachment to a world object, from a world object to an attachment, or from one world object to another world object. LockGuard can even be told to link from one object to another from an entirely different object or call a whole set of LockGuard particle chains at once..... with very minimal scripting. In fact, in one single line of script you can command the LockGuard V2 Script to configure and link a whole set of particle chains at once, that's the power of LockGuard V2.

LockGuard V2 and the LockGuard V2 Script are free for personal and commercial use with no limitations and no restrictions. The LockGuard Logo is also free for you to use with your product should you choose to use LockGuard (the Logo is available in any LockGuard package in-world).

If you have any questions, comments, or suggestions for LockGuard then send me, Lillani Lowell, a message and I will try my best to assist you.

Return to Protocol Exchange

LockGuard V2 Links

Creating a LockGuard-Enabled Object

LockGuard V2 Script - Open Source

Calling LockGuard V2

So, enough with the babble, let's get right to the code examples:

default
{

   touch_start( integer num )
   {

      llWhisper( -9119, "lockguard " + (string)llDetectedKey(0) + " leftwrist link " + (string)llGetKey() );
      llWhisper( -9119, "lockguard " + (string)llDetectedKey(0) + " rightwrist link " + (string)llGetKey() );

   }

}

Alternatively.....

default
{

   touch_start( integer num )
   {

      llWhisper( -9119, "lockguard " + (string)llDetectedKey(0) + " wrists link " + (string)llGetKey() );

   }

}

The above are two LockGuard V2 compatible calling scripts which you can place into the prim which you want to attach chains to. Once you've done so, put on a pair of LockGuard cuffs, touch the prim, and the LockGuard System will link the respective particle chains from the left and right wrist to the prim. This is a bare bones script for calling a LockGuard particle chain from a LockGuard Item, and it is that easy.

Here are a few more examples which uses multiple commands to perform chain configuration and then link:

default
{

   touch_start( integer num )
   {

      llWhisper( -9119, "lockguard " + (string)llDetectedKey(0) + " wrists life 2 size 0.6 0.6 link " + (string)llGetKey() );

   }

}

default
{

   touch_start( integer num )
   {

      llWhisper( -9119, "lockguard " + (string)llDetectedKey(0) + " wrists gravity 4 life 1.5 color 1 0 0 link " + (string)llGetKey() );

   }

}

LockGuard Commandline

Let's disect this line.....

llWhisper( -9119, "lockguard " + (string)llDetectedKey(0) + " wrists link " + (string)llGetKey() );

.....into its basic components.

The above line, is in essence, a LockGuard commandline which tells the LockGuard V2 Script what to do. It can be broken down into the follow components.....

lockguard [avatarKey/ownerKey] [item] [command] [variable(s)]

Command | Action
lockguard | The very first thing the LockGuard Library looks for is this command, it does this to validate that the command on the given channel is indeed a LockGuard Command. If the message is not prefixed by lockguard then LockGuard will ignore the entire message.
[avatarKey/ownerKey] | The second thing the LockGuard Library will look for is ownership. LockGuard will want to know whose particle chains should be responding to the commandline. In the example above, the key of the avatar is gathered through llDetectedKey during a touch event.
[item] | The next thing the LockGuard Library requires is the name of the LockGuard Item from which you wish to call the particle chain from. In the example above, the leftwrist and rightwrist are called, because the example expects you to be wearing a pair of LockGuard cuffs which are defined as leftwrist and rightwrist. If you specify ALL for item, then all nearby LockGuard Items attributed to the avatarKey above will respond.
[command] [variable(s)] | The heart of the LockGuard Library is its command set, which allows you to link, unlink, configure, and control the particle chain. The commands are explained a little further below, along with their respective variables. Multiple commands and variables can be places one after the other. So, you can place texture, size, life, and link commands in one single chat block.

So, the best way to explain the line of script above is to compare it to saying, "Hey LockGuard! Yeah, the stuff belonging to Lillani Lowell! I want the wrists to link to me, and here's the key you should link to!"

LockGuard Commands and Variables

The only commands which are necessary for LockGuard to operate are the link and unlink commands which are used to create and break particle chains. All other commands are optional, and only need to be used when you have a use for them. LockGuard Items will automatically revert back to their default settings when unlinked or reattached.

Command | Variable | Explaination
link | key | This command will inform LockGuard that it should create a particle chain and link it to the key provided, the key can be any valid object or avatar key.
unlink | N/A | This command will inform LockGuard to unlink its particle chain. No variables are required.
gravity | #grav | This command will change the effect gravity has on your particle chains.
life | #sec | Sending this command will tell LockGuard how long each particle chain link should live in seconds, or in other words, how fast it should move from origin to destination. For instance, a value of 1 will create a faster, tigher particle chain while a value of 5 would create a loose, slower particle chain. Chain life can be modified "on the fly" as needed.
color | #r #g #b | Sending this command will change the color or tint of your particle chain. The values are red, green, blue, and each one is a value between 0 and 1, with 0 being dark, 0.5 being middle, and 1 being bright.
size | #x #y | Sending this command will tell LockGuard how large each particle chain link should be. The smaller the value, the smaller the particle links, and the less visible they are from a distance.
texture | UUID | If you want your furniture to change the default particle chain texture of the LockGuard Library then simply send this command and LockGuard will obediently swap out textures for you. There are two default textures included with LockGuard. One is a chain, the other is a rope. You can choose either of these by sending "rope" or "chain" as the UUID and LockGuard will automatically assign the default UUID for the one you choose.

Summary

If you have any questions, comments, or suggestions about LockGuard, or if you just need general scripting help getting started with LockGuard then feel free to contact me, Lillani Lowell. I'll be more than happy to assist you in any way I can.

Return to Protocol Exchange


exchangeLockMeisterSystem

The LockMeister system is a set of commands used for enhanced features in bondage toys. Its main function is to get the keys needed for particle chains, but as the protocol grows more and more commands are added. This protocol is maintained and designed by Kyrah Abattoir; IM her inworld if you have suggestions.

Return to the Protocol Exchange

Main features:
-simple to use
-flexible
-fully backward compatible
-sensorless key grabbing (possible on child prims too) for particle chains (several mooring points)
-boot/shoes animation overrider controlling (on/off)
-color propagation in a lockmeister item set

How does it work?

All lockmeister items listen on channel -8888. It's strongly recommended that you use only the llWhisper() function for sending the commands.

When someone sits on a lockmeister compliant item, it will sequentially send the ping messages to each of the mooring points it needs. For example, for a St. Andrew's Cross we will ping the wrist and ankle cuffs. Thus, we will have to send the messages for four attachment points: lcuff,rcuff,llcuff,rlcuff.
When a lockmeister attachment detect the message corresponding to itself on channel -8888, it will answer by appending " ok" to the original message.

Messages are in the following format:

Sender | Format | Example | Comments
LockMeister item | <avatar key><command> | 748bb591-0d9d-4907-8287-dc27b8267e24lcuff | Ping the left wrist cuff of the avatar with the given key
Attachment Response | <avatar key><command> ok | 748bb591-0d9d-4907-8287-dc27b8267e24lcuff ok | The attachment on the left wrist sends this response

In order to draw a chain of particles, we need the key of the attachment, not the key of the avatar wearing the attachment. This is easy, because the listen event that gets the answer message has the attachment key in the key_id event parameter.

List of the different mooring points

1: rcuff - right wrist cuff
2: rbiceps - right upperarm cuff
3: lbiceps - left upper arm cuff
4: lcuff - left wrist cuff
5: lblade - left shoulder blade
6: rblade - right shoulder blade
7: rnipple - right nipple point
8: lnipple - left nipple point
9: rfbelt - right front of the belt
10: lfbelt - left front of the belt
11: rtigh - right upper leg cuff
12: ltigh - left upper leg cuff
13: rlcuff - right ankle cuff
14: llcuff - left ankle cuff
15: lbbelt - left back of the belt
16: rbbelt - right back of the belt
17: pelvis - lower front of the pelvis
18: fbelt - front of the belt
19: bbelt - back of the belt
20: rcollar - right of the collar
21: lcollar - left of the collar
22: thead - top of the head
23: collar - front of the collar
24: lbit - left corner of mouth/cheek (for pony bits)
25: rbit - right corner of mouth/cheek (for pony bits)
26: nose - nose
27: bcollar - back of the collar
28: back - middle of the back

OTHERS:
handle - type "leash handle" , this is useful for leashing to a leash handle.
rigmiddle - type "rig", target for suspending to the middle point.
rigleft - type "rig", target for suspending to the left point.
rigright - type "rig", target for suspending to the right point.

special commands
<avatar key>col<vector> - order a color change (used in lockmeister collars to tint the cuffs like the collar) OPTIONAL
ex: "748bb591-0d9d-4907-8287-dc27b8267e24col<1.0,1.0,1.0>" will say to an item able to understand it (color yourself in white)

<avatar key>booton/bootoff = commands sent by collar objects, asking for the shoes animation overrider to be stopped
ex: "748bb591-0d9d-4907-8287-dc27b8267e24booton" activate the animation overrider
ex: "748bb591-0d9d-4907-8287-dc27b8267e24bootoff" deactivate the animation overrider

stuffs to be added
-freeform data channel
-all purpose locking system
-various probing commands

amethyst extension commands

The following commands are not part of the main lockmeister protocol, but additions Amethyst Rosencrans made in her sold and free cuff scripts. They are here to document the extensions so others can utilize them.

<avatar key>target|point1|point2 = Connect a chain from mooring point 1 to mooring point 2
ex: "748bb591-0d9d-4907-8287-dc27b8267e24target|lcuff|rcuff" will create a chain from the left cuff to the right cuff

<avatar key>target|point = Remove a chain from mooring point
ex: "748bb591-0d9d-4907-8287-dc27b8267e24target|lcuff" will remove a chain from the left cuff

<avatar key>texture|<texture key> = Change the texture of the chains used by the target command
ex: "748bb591-0d9d-4907-8287-dc27b8267e24texture|1ffb37fa-2fc1-dbec-d8ea-0607583a03c6" will change the chain texture

<avatar key>gravity|<downward acceleration float> = Change the rate at which the chain particles move down
ex: "748bb591-0d9d-4907-8287-dc27b8267e24gravity|0.5" will change the chain texture to move down half a meter a second

<avatar key>age|<age float> = Change how fast the chain particles move from point 1 to point 2
ex: "748bb591-0d9d-4907-8287-dc27b8267e24age|2.0" will change the chain texture to move the distance in 2 seconds

if you plan to integrate the lockmeister system to your products, contact KyrahAbattoir inworld if you want to get the approval logo (it's a plus not an obligation)
a basic lockmeister furniture need to receive the seal of approval:

-high quality building
-use a single listener for the lockmeister channel
-test the presence of the cuffs it need when the avatar sit on it
-if cuffs respond, draw the chains properly
-must provide a free cuff set or the required attachments it need to work properly (you can get mines)

Example scripts

Lockmeister basic opensource project - courtesy of Lynn Kukulcan
Lockmeister basic furniture script - courtesy of KDC

example attachment script:
//KDC sample lockmeister attachment version 1.0
//
//NOTE: this script must be in the root prim of the object for working properly and the root prim must ALSO be the prim you want the particles to go to, since this script use an attach event, if you want the root prim to be another prim you will need to write your own code
//...
//and in a general way this script is here as an explanation to the lockmeister system so ... WRITE YOUR OWN CODE!!
//
//KDC, kyrah design concept

string mooring_point = "lcuff";
//write here the mooring point you want to use
integer attach_control = TRUE;
//TRUE if you want to control the attach point (and detach if attached on the wrong part of the body, else, FALSE
integer attach_point = ATTACH_LLARM;
//write here the attachment point that need to be controlled
//example :
//left wrist/arm = ATTACH_LLARM
//right wrist/arm = ATTACH_RLARM
string attach_text = "this cuff must be attached to the left forearm";
//the text to display when the object detach itself
default
{
    attach(key id)
    {
        if(id != NULL_KEY)
        llResetScript();
    }
    state_entry()
    {
        if(llGetAttached() != attach_point && attach_control)
            llRequestPermissions(llGetOwner(),PERMISSION_ATTACH);
        else
            llListen(-8888,"","","");//open the lockmeister channel
    }
    listen(integer channel,string name,key id,string message)
    {
        if (message == (string)llGetOwner()+ mooring_point)//did we get a ping message for us?
            llWhisper(-8888,(string)llGetOwner()+ mooring_point+" ok");//answering it
    }
    run_time_permissions(integer perm)
    {
        llOwnerSay(attach_text);
        llDetachFromAvatar();
    }
}


ExchangePacketChat

Packet Chat Protocol

This protocol was developed so that chat messages can be encoded in such a way that a large (> 255 character) string can be sent through the chat system in 255-character substrings ("packets") and reconstructed at the receiving end.

Each packet is composed of four sections, three of which are of a constant length:
<message identifyer><packet index><packet count><packet data>

The <message identifyer> is a two-byte little-endian hexadecimal number. It must be four characters in length, or padded with zeroes. All of the packets that compose a particular message must have the same message identifyer. The message identifyer should carry with it no meaningful information about the message itself - it only serves to associate multiple packets with the same message. It should be randomly generated to avoid collisions.

The <packet index> is a one-byte little-endian hexadecimal number. It must be two-characters in length, or padded with zeroes. The first packet that a message is composed of must have packet index 0, which will appear as "00". The next packet composing the message has packet index 1 ("01") and so on.

The <packet count> is a one-byte little-endian hexadecimal number. It likewise must be two-characters in length, or padded with zeroes. This number must remain constant throughout the packet transfer, and represents the number of packets the message is composed of.

The <packet data> portion of the packet may be of variable length, with a maximum length of 247 characters (255 max chat characters - 8 metadata characters).

To encode a message, the original message string is divided into segments, 247 characters in length at maximum. Each of these is assigned a packet index corresponding to the location of the segment in the original string. (The first segment has index 0, the second index 1, etc). The packet metadata (message identifyer, packet index, and packet count, as described above) is then prepended to the segment, and the resulting <=255-character string sent to one of the chat functions. The packets must be sent in sequence.

Example:
Original message:
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi est mi, scelerisque id, laoreet nec, sollicitudin elementum, mi. Sed laoreet interdum magna. Aenean rhoncus imperdiet justo. Vestibulum dui dolor, condimentum sagittis, interdum eu, adipiscing ac, velit. Etiam faucibus, lectus in metus."

Segments (assuming maximum space efficiancy, 247 data chars/packet):
SEGMENT 1:
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi est mi, scelerisque id, laoreet nec, sollicitudin elementum, mi. Sed laoreet interdum magna. Aenean rhoncus imperdiet justo. Vestibulum dui dolor, condimentum sagittis, interdum eu, a"

SEGMENT 2:
"dipiscing ac, velit. Etiam faucibus, lectus in metus."

Packets:
PACKET 1:
"F92C0002Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi est mi, scelerisque id, laoreet nec, sollicitudin elementum, mi. Sed laoreet interdum magna. Aenean rhoncus imperdiet justo. Vestibulum dui dolor, condimentum sagittis, interdum eu, a"

PACKET 2:
"F92C0102dipiscing ac, velit. Etiam faucibus, lectus in metus."

Protocol Implementations:
LibraryChatCodec

Protocol Exchange


ExchangeScriptModule

Script Modules

This is not actually a protocol, but I think it fits more nicely in this category then in any other.

A module is a self-contained script that is designed to work with other modules within the same object. They are based upon Object-Oriented Programming's objects and classes. Implemented properly, they can be reused easily between different projects. Scripting this way allows the scripter to abstract away complexity inherant in large projects.

Each module exposes an 'interface' to other modules. This interface allows other modules to communicate with it.

Module Syntax

Each module always declares these global functions:

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

A module's "methods," the interface it exposes to the rest of the system, are declared within its link_message event handler.

// Turner Module
default {
    link_message(integer s, integer call, string params, key methodName) {
        if (methodName == "turn") {
            list paramList = stringToList(params);
            string direction = llList2String(paramList, 0);
            float speed = (float) llList2String(paramList, 1);
            // Code goes here...
        }
    }
}

The key parameter contains the method's name. Method names can only contain alphanumeric characters. Using underscores and dollar signs is not good practice (they may be used for different purposes later). The string parameter contains the method's parameters as list encoded as a string. The seperator between each element is the first three characters of the string. It is randomly generated so that it does not corrupt any of the list data. The integer parameter is optional, it is used to return values to callers of the method.

To call another module's methods, a "stub function" is used. These are usually prefixed with m_:
m_turn(string direction, float speed) {
    callMethod(0, "turn", [direction, speed]);
}

Return Values

Return values are simply method calls with a "_ret" name suffix. For example, if the turn method returned a value to its caller, and the caller wanted the return value, the caller would declare a turn_ret method:

integer m_turn(string direction, float speed) {
    integer retID = (integer)llFrand(188412);
    callMethod(retID, "turn", [direction, speed]);
    return retID;
}

integer turnCall;
default {
    state_entry() {
        turnCall = m_turn("left", 1.0);
    }
    link_message(integer s, integer call, string params, key methodName) {
        if (methodName == "turn_ret" && call == turnCall) {
            list paramList = stringToList(params);
            integer successful = (integer) llList2String(paramList, 0);
            if (successful) {
                llOwnerSay("Yay");
            }
        }
    }
}

Referring back to our Turner module example, it could be rewriten to return a value like so:
// Turner Module
default {
    link_message(integer s, integer call, string params, key methodName) {
        if (methodName == "turn") {
            list paramList = stringToList(params);
            string direction = llList2String(paramList, 0);
            float speed = (float) llList2String(paramList, 1);
            // Code goes here...
            returnValue(call, methodName, [TRUE]);
        }
    }
}
I created a way to return values synchronously, but havent used it to a great extent.

Standard Signaling

A well-formed module will always define a moduleReady(string module) method that returns a boolean indicating to the rest of the system that the module has finished "booting" - initializing after a reset. A module will always call moduleReset(string module) when and only when it has been reset. Here's a template:

// Assuming method invocation funcs have been declared above

m_moduleReset(string moduleName) {
    callMethod(0, "moduleReset", [moduleName]);
}

integer reset = TRUE;
firstRun() {
    if (!reset)
        return;
    this = llGetScriptName();
    m_moduleReset(this);
    reset = FALSE;
}

string this;
default {
    state_entry() {
        firstRun();
    }
    link_message(integer c, integer call, string params, key methodName) {
        if (methodName == "moduleReady") {
            list paramList = stringToList(params);
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                returnValue(call, methodName, [TRUE]);
            // Assuming that module has finished starting up at this point.
        }
    }
}

m_moduleReset allows other modules to release handles that the calling module will re-register. For example, the LibraryChatCodec will release listen handles held by the module that was reset. m_moduleReady is a legacy method that is used in startup dependancy resolution (so a module relying on another for initialization can wait until the other module is initialized)
Exchange


exchangeSecondSimpleSyndication

Due to memory limitations of SecondLife with its first implementations of requesting data over HTTP on the internet, the RSS format can become too large to parse any data that is needed. The Second Simple Syndication format was created on the protocol exchange as an attempt to work around this problem. A SSS feed is in XML format. Any valid RSS feed can be converted to SSS using XSL Transformations. The SSS feed is made public as an open format so that others may adapt it easily without the need to depend on only one service for future products. XML can be transformed and served from just about any web server out there.

This document explains the format of SSS feeds.

When designing SSS feeds, only the bare minimum information was identified. This is to keep the data transferred within the 2048 character limit imposed with the first implementations of the llHTTPRequest method. The following elements are found within an <sss> root element:

<feed>
This is the identification of the podcast, blog, or information source.

Attribute | Description
title | represents the name of the information source
link | represents the web page url to the homepage of the information source. (see llLoadURL)

<item>
This is the identification of an individual post, item, or entry within the information source.

Attribute | Description
title | The name of the item
link | The web page url to the item. (see llLoadURL)
enclosure | An associated file. Most likely an audio/video file/stream that can be assigned on a parcel. (see llSetParcelMusicURL / llParcelMediaCommandList)

Here is an example of an SSS feed:

<sss>
<feed title="My Podcast" link="http://www.mydomain.com/"/>
<item title="July 16, 2006" link="http://www.mydomain.com/20060716.html" enclosure="http://www.mydomain.com/20060716.mp3"/>
<item title="July 15, 2006" link="http://www.mydomain.com/20060715.html" enclosure="http://www.mydomain.com/20060715.mp3"/>
<item title="July 14, 2006" link="http://www.mydomain.com/20060714.html" enclosure="http://www.mydomain.com/20060714.mp3"/>
<item title="July 13, 2006" link="http://www.mydomain.com/20060713.html" enclosure="http://www.mydomain.com/20060713.mp3"/>
<item title="July 12, 2006" link="http://www.mydomain.com/20060712.html" enclosure="http://www.mydomain.com/20060712.mp3"/>
<item title="July 11, 2006" link="http://www.mydomain.com/20060711.html" enclosure="http://www.mydomain.com/20060711.mp3"/>
<item title="July 10, 2006" link="http://www.mydomain.com/20060710.html" enclosure="http://www.mydomain.com/20060710.mp3"/>
<item title="July 9, 2006" link="http://www.mydomain.com/20060709.html" enclosure="http://www.mydomain.com/20060709.mp3"/>
<item title="July 8, 2006" link="http://www.mydomain.com/20060708.html" enclosure="http://www.mydomain.com/20060708.mp3"/>
<item title="July 7, 2006" link="http://www.mydomain.com/20060707.html" enclosure="http://www.mydomain.com/20060707.mp3"/>
</sss>

Here is an exmple of an XSL file to transform existing RSS 2.0 feeds

<?xml version="1.0" ?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"
    xmlns:rss="http://purl.org/rss/2.0/"
    exclude-result-prefixes="rss"
    >
    <xsl:output indent="yes" method="xml" encoding="UTF-8" />
    <xsl:template match="channel">
        <sss>
            <xsl:element name="feed">
                <xsl:attribute name="title">
                    <xsl:value-of select="title" />
                </xsl:attribute>
                <xsl:attribute name="link">
                    <xsl:value-of select="link"/>
                </xsl:attribute>
            </xsl:element>
            <xsl:call-template name="items"/>
        </sss>
    </xsl:template>
    <xsl:template name="items">
        <xsl:for-each select="item">
            <xsl:element name="item">
                <xsl:attribute name="title">
                    <xsl:value-of select="title"/>
                </xsl:attribute>
                <xsl:attribute name="link">
                    <xsl:value-of select="link"/>
                </xsl:attribute>
                <xsl:attribute name="enclosure">
                    <xsl:value-of select="enclosure/@url"/>
                </xsl:attribute>
            </xsl:element>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Here is an example of how to transform an RSS feed into an SSS feed, using PHP with an XSL Transformation.

$xsl_file = 'rss2sss.xsl';
$xml_file = $_GET['feed'];

header('Content-Type: text/xml; charset=UTF-8');

$xml = domxml_open_file ($xml_file);
$xsl = domxml_xslt_stylesheet_file ($xsl_file);
$out = $xsl->process ($xml, array ());

echo $xsl->result_dump_mem ($out);

Here is an example of how to read an SSS feed using LSL

integer LOAD_FEED = 100001;

string feedTitle;
string feedLink;

list itemTitles;
list itemLinks;
list itemEnclosures;

parseElement(string line)
{
    /// sss element - new feed
    if(line == "sss")
    {
        feedTitle = "";
        feedLink = "";
        itemTitles = [];
        itemLinks = [];
        itemEnclosures = [];
        return;
    }

    // determine type of element
    string element = llGetSubString(line, 0, llSubStringIndex(line, " ") - 1);
    element = llToLower(element);

    // parse known elements
    if(element == "item")
        parseItemElement(llGetSubString(line, llStringLength(element) + 1, -1));
    else if(element == "feed")
        parseFeedElement(llGetSubString(line, llStringLength(element) + 1, -1));
}
parseFeedElement(string line)
{
    // parse attribute name/value pairs
    list parts = llParseString2List(line, ["=\"", "\" "], []);
    integer n = llGetListLength(parts);
    integer i;

    // loop through name/value pairs
    for(i = 0; i < n; i += 2)
    {
        // read name/value pair
        string name = llList2String(parts, i);
        name = llToLower(name);
        string value = llList2String(parts, i + 1);

        // assign known values
        if(name == "title") feedTitle = value;
        if(name == "link") feedLink = value;
    }
}
parseItemElement(string line)
{
    // parse attribute name/value pairs
    list parts = llParseString2List(line, ["=\"", "\" ", "\""], []);
    integer n = llGetListLength(parts);
    integer i;

    // set default values
    string title = "";
    string link = "";
    string enclosure = "";

    // loop through name/value pairs
    for(i = 0; i < n; i += 1)
    {
        // read name/value pair
        string name = llList2String(parts, i);
        name = llToLower(name);
        string value = llList2String(parts, i + 1);

        // assign known values
        if(name == "title") title = value;
        if(name == "link") link = value;
        if(name == "enclosure") enclosure = value;
    }

    // add new item
    itemTitles += [title];
    itemLinks += [link];
    itemEnclosures += [enclosure];
}

default
{
    http_response(key request_id, integer status, list metadata, string body)
    {
        llWhisper(0, "Parsing feed.");

        // parse out xml elements
        list lines = llParseString2List(body, ["/>","<", ">", "\n"], []);

        // loop through elements
        integer n = llGetListLength(lines);
        integer i;
        for(i = 0; i < n; i++)
            parseElement(llList2String(lines, i));

        // notify results
        llWhisper(0, "Done reading feed for " + feedTitle + " with " + (string)llGetListLength(itemTitles) + " items.");
    }
    link_message(integer sender_num, integer num, string str, key id)
    {
        if(num == LOAD_FEED)
        {
            llWhisper(0, "Requesting feed.");
            llHTTPRequest(str, [], "");
        }
    }
}


exchangeSimpleLink

Introduction

This protocol is currently used in my particle generator. It's also intended to be used in future works. Other people are welcome to use it, and to write scripts that interoperate with mine.

Return to protocol exchange

Format

The num argument of llMessageLinked is used to specify the destination script. The number encodes in hex the script's creator, script number, and script function. The other arguments are used to pass data for the script. The script number is decoded as follows: 0xCCCCSSFF, where CCCC = Creator, SS = Script, FF = Function.

For instance, number 0x44770100 decodes as: Creator 0x4477 (Dale Glass), Script 01 (Speaker), Function 00 (Display message).

List of Creators

ID | Name
0x4477 | Dale Glass

List of Scripts for Dale Glass

ID | Name | Description
0x00 | Reserved | Used in pattern script as default, script fails if used to force choosing a script number
0x01 | Speaker | Generates messages using specified method: say, whisper, etc
0x02 | Message Parser | Parses user commands, sends link messages to other scripts to perform the actions
0x03 | Particle Generator | Generates particle effects
0x04 | Demo Mode | Interfaces with Notecard Loader, loading cards at an interval
0x05 | Link Message Debugger | Helper script that dumps link messages
0x06 | Notecard Loader | Loads notecards, feeds the lines into the Message Parser
0x07 | Particle Randomizer | Randomly tweaks values in Particle Generator
0xFF | Global Functions | Reserved for debug functions handled by all the scripts

Speaker

This script produces messages for all the other ones. The other scripts never produce any output directly, sending it instead through the Speaker. This allows to easily control the output type for all the other scripts, or to silence it completely.

ID | Name | String | Key | Description
0x00 | Say | Message | Unused | Outputs message specified in striing using the current output method
0x01 | Type | Message type | Unused | Sets the type of output: whisper, say, shout, owner, instant, debug or none. The current default is "owner" (llOwnerSay)
0x02 | Channel | Channel | Unused | Sets the channel for the message types that use it

Message Parser

The message parser is the controlling script in my objects. It parses commands and sends link messages to other scripts to perform the work. The ID used for it will remain constant through all my future scripts, although the function performed will be different. This way, some things like for instance the Notecard Loader can be reused as-is in future works.

ID | Name | String | Key | Description
0x00 | Parse | Message | Unused | Parse the passed message as if it was typed

Particle Generator

ID | Name | String | Key | Description
0x00 | Enable | Unused | Unused | Enables the generator
0x01 | Disable | Unused | Unused | Disables the generator. The configuration is kept
0x02 | Target | Unused | Target | Makes the particles target the specified key
0x03 | Set variable | Data | Unused | Data is formatted as follows: var + "|" + arg1 + "|" + arg2 + "|" + arg3. The separators are required even if the argument is empty
0x04 | Set pattern | Pattern | Unused | Sets the particle pattern. The argument is the numeric value that would be passed to llParticleSystem
0x05 | Show status | Type | Format | Type is the dump type, "config" or "lsl". Format is the dump format for the "config" type: "condensed" or "long"
0x06 | Set texture | Texture | Unused | Argument is the texture's name or key
0x07 | Enable variable change messages | Unused | Unused | Enables notification when a variable changes
0x08 | Disable variable change messages | Unused | Unused | Disables notification when a variable changes
0x09 | Get variable | Callback | Variable name | Sends the variable's value with the "num" specified in the string
0x0A | Set rotation | Rotation | Unused | Calls llTargetOmega with the rotation in the string (values joined with "|")

Demo Mode

This script works together with the notecard loader. It scans the object's inventory for notecards starting with "config_" and asks the Notecard Loader to load them.

ID | Name | String | Key | Description
0x00 | Set message | Message | Unused | Sets the message to be displayed on top of the object when in demo mode
0x01 | Start | Interval | Unused | Starts the demo mode. The argument is the number of seconds to wait between each card
0x02 | Stop | Unused | Unused | Leaves the demo mode

Link Message Debugger

This is a debug helper script. It has no functions of its own, except the global ones (though it some day might get some). It listens for link messages and outputs them to the Speaker. Messages with the ID of the Speaker script, and string starting with "LINK:" are ignored, to avoid outputting its own output.

Notecard Loader

This script handles the loading of notecards. Its function is reading the cards, and feeding the output to the Message Parser. Each line is processed in this way:

First, it splits the line using the ":" separator, and throws out the first element in the list if there's more than one. This allows copy/pasting output from the message window into a notecard and have it load correctly without manual output. That is, this turns "Object: command" into "command".

Next, it splits the line using the "|" separator. Each element of the resulting list is processed as an individual line. This is done to work around the slowness of the dataserver. This is optional, and the loader will handle one command per line fine.

Finally, each line is fed to the Message Parser.

ID | Name | String | Key | Description
0x00 | Load card | Card name | Unused | Starts loading the specified notecard. The passed name will be appended to "config_" (that is, a request to load "test" will load "config_test"). If a card is already being loaded, then the command will be rejected, and an error message produced
0x01 | Set callback | Callback number | Unused | Sets the notecard load completion callback. Each call will overwrite the value set by the previous one. If called, the Notecard Loader will send a link message using the specified number to notify the interested party that the card finished loading.

Global Functions

All the scripts respond to them, at once. Some may be unrecognized by older scripts. Unrecognized commands will result in a whisper of "Unrecognized reserved link command".

ID | Name | String | Key | Description
0x00 | Version | Unused | Unused | Displays script name and version
0x01 | Memory | Unused | Unused | Shows the available amount of memory
0x02 | Reset | Unused | Unused | Resets the script
0x03 | Enable debug | Unused | Unused | Enables debug mode. Some scripts will produce extra messages
0x04 | Disable debug | Unused | Unused | Disables debug mode


exchangeTLML

Tight List Markup Language (TLML)

Version 0.6

TLML

This is the language in which the prim-pages sent over TLTP are coded. A prim-page is made of one or more prims, which have each their own TLML description. This description is called a TLML command, and is a single string composed of the following substrings:

Command Format:
Field: | Description:
separator | char, defined here then used to parse the rest of the command
target part number | integer cast as string, representation of the target's link number for differentiating prims of a page
separator | same as above
URL | optional URL the prim should point at, see format here
separator | same as above
prim attribute mask | hex-integer cast into a string, contains bitflags indicating how to read the rest of the command, see below
separator parameters | 1 paramater for each entry in the mask that requires a paramater, if an entry uses multiple parmamaters they are TightLists
separator | same as above
face attribute mask | hex-integer cast into a string, contains bitflags indicating how to read the rest of the command, see below
separator parameters | 1 paramater for each entry in the mask that requires a paramater, if an entry uses multiple parmamaters they are TightLists

TightList([prim_number, TLTP_command, prim_mask] + prim_mask_data + [face_mask_1] + face_mask_data_1 + ... [face_mask_n] + face_mask_data_n)

The server can also say a special code instead of a target part number:

Code | Meaning
-1 | Server unavailable
-2 | Server Error
-3 | Access Refused (if your server enforces access restriction on some pages)
-4 | Page Missing. Should provide the index of the default page. (404 here we are :))
-97 | Wait. Should provide a positive float value, the browser will wait this much before processing the next line
-98 | Comment. Everything after this code is skipped by the browser
-99 | Page Clear. This will clear the display of the browser.

Prim mask and parameters:
Bit | Hex | Name | Description
4 | 0x1 | BITPrimConfigMask | Mask, that defaults values or recycles them
5 | 0x2 | BITShowOnCommand | Show On Command, string
6 & 7 | 0xC | shape change | 0x0 = recycle, 0x40 = PRIM_TYPE TightList, 0x80 = SetPrimTypeChained TightList, 0xC0 = raw TightListType
8 | 0x10 | BITSize | prim size (vector)
9 | 0x20 | BITPos | prim position (vector)
10 | 0x40 | BITCourners | prim corners TightList(vector,vector)
11 | 0x80 | BITLocalRot | llSetLocalRot(euler-vector | rotation)
12 | 0x100 | BITSetTextText | llSetText(string text)
13 | 0x200 | BITSetTextAtrib | llSetText(<r,g,b> | (<r,g,b,a>))
14 | 0x400 | BITParticleSystem | llParticleSystem TightListType
15 | 0x800 | BITTargetOmega | llTargetOmega TightList(vector axis, float spinrate, float gain)
16 | 0x1000 | BITTextureAnim | llSetTextureAnim TightList(ta_mode, ta_face, ta_xf, ta_yf, ta_sf, ta_ef, ta_speed)
17 | 0x2000 | BITXyText | XyTextMod See Table TightList
18 | 0x4000 | BITSetURL | Not implemented, will eat 1 parmater

Prim Config Mask:
Bit | Hex | Name | Description
0 | 0x1 | BITPrimDefaultShape | default box-shape
1 | 0x2 | BITPrimDefaultLocalRot | set local rotation to <0,0,0,1>
2 | 0x4 | BITPrimDefaultSetText_text | keep llSetText text
3 | 0x8 | BITPrimDefaultSetText_atrib | keep llSetText color & alpha
4 | 0x10 | BITPrimKeepParticle | keep llParticleSystem
5 | 0x20 | BITPrimKeepTargetOmega | keep llTargetOmega axis, spinrate, gain
6 | 0x40 | BITPrimKeepTextureAnim | keep llSetTextureAnim ta_mode, ta_face, ta_xf, ta_yf, ta_sf, ta_ef, ta_speed
7 | 0x80 | BITPrimKeepXyText | keep XyTextMod, this doesn't do anything currently
8 | 0x100 | BITPrimKeepURL | keep llSetPrimURL, this doesn't do anything currently

Face mask and parameters:
Bit | Hex | Name | Description
0-3 | 0xF | face | face number, if ALL_SIDES face = 0xF
4 | 0x10 | BITFaceConfigMask | Mask, that defaults values or recycles them
5 | 0x20 | reserved | TightList or (To-Be-Determined), will eat 1 parmater
6 & 7 | 0xC0 | fullbright | 0x0 = recycle, 0x40 = No Full Bright, 0x80 = reserved, 0xC0 = Full Bright
8 | 0x100 | BITTexture | texture_key or TightList([notecard_texture_key, subgroup, notecard_line])
9 | 0x200 | BITColor | color <r,g,b>
10 | 0x400 | BITAlpha | alpha
11 | 0x800 | BITTexture_scale _scale | texture_scale
12 | 0x1000 | BITTexture_offset | texture_offset
13 | 0x2000 | BITTexture_rot | texture_rot
14 | 0x4000 | BITShiny | shiny
15 | 0x8000 | BITBump | bump

Face Config Mask:
Bit | Hex | Name | Description
0 | 0x1 | BITFaceDefaultTexture | default texture to white
1 | 0x2 | BITFaceDefaultColor | default color to <1,1,1>
2 | 0x4 | BITFaceDefaultAlpha | default alpha to 1.0
3 | 0x8 | BITFaceDefaultTexture_scale | default texture_scale to <1,1,1>
4 | 0x10 | BITFaceDefaultTexture_offset | default texture_offset to <0,0,0>
5 | 0x20 | BITFaceDefaultTexture_rot | default texture_rot to 0.0
6 | 0x40 | BITFaceDefaultShiny | default shiny to none
7 | 0x80 | BITFaceDefaultBump | default bump to none
BITSetTextAtrib if RGB is set to 0,0,0 and A is not set then A will automaticly set to 1. If you don't want this, set a value for A. Detecting this situation isn't easy, because of size constraints we don't bother.

Multiple commands can be concatenated together, although it is more efficient to compress everything in one command (takes less characters for the same result). Multiple commands addressed to different prims can be joined on seperate lines (seperate them with a "\n" character).
Note: TLTP distinguishes between the various data types it can transport by prepending a letter. In the case of TLML, that letter is "T". If you pass multiple TLML lines you should prepend "T" to each line.

Example command:
/0/U!Page!1!66864f3c-e095-d9c8-058d-d6575e6ed1b8/8201F/<1,1,0>/<.25,.25,0>/<.75,.75,0>
- "/" = the command uses this character as seperator for the remainder of the command string.
- "0" = this command targets the first child prim of the browser client.
- U!Page!1!66864f3c-e095-d9c8-058d-d6575e6ed1b8 = URL of the page requested when the target child prim is clicked
- 8201F = 0x1+0x2+0x4+0x8+0x10+0x2000+0x80000 = bits 0, 1, 2, 3, 4, 13 and 19 are set = the command contains a color and corners
- <1,1,0> = color parameter (yellow)
- <0.25,0.25,0.0> = lower left corner coordinates
- <0.75,0.75,0.0> = top right corner coordinates
This command thus turns the first child prim into a yellow centered opaque rectangle covering about half the screen.

Multiple formats may be chained together; just concatenate each format definition with its attributes, this is useful for setting different textures on different faces of a prim. This can be used to emulate the functionality of XyText though there is an XyText interface built in.
That's cool, but there still is a limit on the character number that can be passed in a chat line or an email...
Very true which is why this protocol wastes as little space as possible
If no server key is specified in the URL then the browser simply sends the request to the server that generated the prim.

Notecard texture key interface - The notecard texture key interface is a simple interface for using notecards to store lists of texture keys. While it could be given it's own script it has been merged into XyTextMod script to keep the script count down. The details of the interface will be released shortly.
How does the new TLTP v0.3 transport layer affect this new interface ? If you need a seperate data type feel free to add it to the list on the TLTP page.
This format won't need a new codes but will need a new url code, the simplified notecard page format will need it's own code and url code

XyTextMod (XTM hence forth) is a unification between the two versions of XyText and eliminates the need for remaps and escape codes.
There are two ways to configure XTM through the TLTP feature_mask and though XTM own interface. XTM must be first setup though TLTP (setting XyText_key, XyText_line, XyText_BlockPosition) then with further configuration though XTM (font, version, fading). The format for the two methods of configuration are identical, the only difference is that some attributes can only be set with a particular interface. XyTextMod uses XyTextMod Protocall (XTMP hence forth).

Bit | Hex | TLML | XTMP | Effects | Description
0,1,2 | 0x7 | Yes | Yes | XyText_version | Version Number (0x1 is 3 letters per prim, 0x2 is 4 letters per prim)
3 | 0x8 | Yes | No | XyText_key | If bit 4 is not set, use the current pages URL
4 | 0x10 | Yes* | Yes | XyText_key | XyText_key is used to restrict a XTM block to a specific page
5 | 0x20 | Yes | Yes | XyText_line | This allows for multiple independant lines of text on a page
6 | 0x40 | Yes | No | XyText_BlockPosition | This tells the prim which block of text it should display from the string
7 | 0x80 | Yes | Yes | XyText_font | The font texture to use.
8 | 0x100 | No | Yes | XyText_fading | Enables fade in and fade out
9 | 0x200 | No | Yes | XyText_fade_delay | Period before text should fade out
10 | 0x400 | Yes | Yes | XyText_max_alpha | Sets the Alpha & Max Alpha used when fading
11 | 0x800 | Yes | Yes | XyText_color | Sets the color
12 | 0x1000 | Yes | Yes | Full Bright | Sets full bright on the xytext faces, this takes a paramater
16,17 | 0x30000 | Yes | Yes | XyText Text | Sets the text on the prims

Version | Description
0 | Recycle
1 | 4 characters per face
2 | 5 characters per face
3 | 8 characters per face
4 | 10 characters per face
5 | reserved
6 | reserved
7 | reserved

About bits 3 & 4:
When a XTMP command is recieved first check is to see if the prim is a part of the group that message is inteded for via XyText_key, XyText_key is the source url for the page the message originated from. After that it checks XyText_line to see if they match; if they do it executes the command.

About bits 12 & 13:
  • When an XTM command is sent via TLML and is bridged to XTM:
    • Bit 12 tells XTM that it's a group of text and it is to use it's BlockPosition to get it's text.
      • If Bit 13 is also set along with Bit 12 then XTM propogates that text to all other prims in the group.
    • If just Bit 13 is set then XTM ignores BlockPosition and displays the first block of text in the string
  • When an XTM command comes from the TLTP parser for a group it acts just like above except Bit 13 does not propogate the message.

Example XTMP config:
llMessageLinked(LINK_SET, 3100, ",0x411,471a92ff-50f4-2858-2f36-deab4cc50276:next,0.5", "471a92ff-50f4-2858-2f36-deab4cc50276:home")
This allows the page next write text into home and also sets the max alpha to 0.5.

Because the scripts have grown in size and quantity, the scripts have been split across multiple pages.

exchangeTLTPBrowser
exchangeTLMLRenderer
exchangeTLMLGenerator

Changes between TLML 0.5 & 0.6:
  • Mask split into two two masks, prim and face.
  • Buggy recycle code thown out, totaly rewriten, defaulting is much cleaner.
  • TLML -99 cleans up more efficiantly (the script is reset and calls "|-|+|1F|+")
  • BITSetTextAtrib no longer supports two paramaters, either a vector <r,g,b> or rotation <r,g,b,a>
    • if the vector form is used, the alpha is recycled. At clear it is set to <1,1,1,1>.
  • Lots of stuff

While writing TLML and it supporting scripts i've been driven to make TLML as efficiant as possible. With the creation of the the generator scripts and it's subsequant updates a glaring problem exists in TLML. Because of the design flaw, TLML should be reworked as it will aid in speeding up the script and making TLML more user friendly.

The problem is in how the data is transmitted. With 0.5 (and previous versions) BITprim_type & BITprim_type_chained are close to last to be applied. This is a problem. Take for example that the prim starts out as a basic sphere and the user wants to apply a texture to face 2. A basic sphere only has one face. The command will be ignored. I propose in 0.6 that BITprim_type & BITprim_type_chained be moved to the front. Another issue is we are running out of BITs (currently there are 28 or 32 allocated). The current method for allocating BITs isn't scalable. Most flags make little sence to repeat more then once. Who really needs to call BITprim_type or BITparticle more then once?

TLML 0.6 Proposal B

Proposed Command Layout

This proposal forces all values that require multiple paramaters to use TightLists. This will result in a few characters of extra overhead but will overall reduce errors when writing TLML pages (a good thing, no more weird notecard errors). After more consideration i've rekindled the idea of a config/recycle mask. The reason for this is to keep the main masks clog free.


exchangeTLTP

Touch-Link Transfer Protocol (TLTP for short)

Change Log:
Version 0.33e

Main changes since 0.32a
Support for TLML-L pages, re-added scanned in the background.

Main changes since 0.31
- support for TLML-Light pages

Main changes from 0.25:
- types of data transmitted are now described by the first character sent
- types of data: animation, sound, TLML, URL, XTMP, notecard key, chat request, RPC
- TLTP codes migrated to TLML
- TLTP-RPC
- URL format revised

Main changes from 0.2:
- codes for XTMP
- code for remote animation playing
- code for remote sound playing
- added stub for future TLTP-RPC

Main changes from 0.1:
- both modes of communication are now stateless and connectionless, although the TLTP-over-chat method is still filtered on the server->client way, by channel number
- chat-capable servers now advertise on -9, instead of clients advertising
- new -06 error code for server-initiated redirections

Return to protocol exchange
Description of TLML
Sample server and client code

What is this ?

TLTP is a protocol between a HUD attachment or stand-alone object acting as a programmable interface and an object either local or remote for exchanging data.
The data sent can be TLML-formatted prim-pages that can link to another TLML page, effectively making this a kind of HTTP-for-SL. It can also be chat commands, link messages to send, animation or sounds to play, etc... enabling it to serve as transport layer for anything in SL.

TLTP

The protocol supports three modes for the transfer of the TLML data: chat functions or llEmail functions for a direct transmission of the data, or the dataserver event for reading the data from a notecard.

The chat way:
Service Discovery:
The server advertises itself on channel -9, saying its default URL (containing the server's channel number, see URL format below). The client listens for this and stores it temporarily.

Browsing
The client then just has to open a listener on a random channel to request a page, by saying an URL composed of the index of the page requested, the chat communication method identifier "0" and the number of the random channel on which to send the page, on the server's channel. This URL can also include the name of the client.
Example:
- Server advertises the URL "|0|-671|Bob's Server|homepage", which stands for "feel free to browse Bob's Server on private channel -671, starting at the prim-page of index 'homepage' ".
- Client says "U|homepage|0|76711|My browser" on channel -671 to request the default page.
- Server then sends the TLML page on channel 76711 so that only the client that requested this page will receive it.

To send the prim-page back, the server simply says the TLML commands composing this page, on the same client-specified channel.
Example continued:
- Server says "T/0/U!nextpage!1!66864f3c-e095-d9c8-058d-d6575e6ed1b8/8201F/<1,1,0>/<0.25,0.25,0.01>/<0.75,0.75,0.01>" on channel 76711, which is TLML code for a centered opaque yellow rectangle covering half the screen, that links to the URL "U!nextpage!1!66864f3c-e095-d9c8-058d-d6575e6ed1b8" when clicked.

The client then usually extracts the string that trails the starting "T/0" to its first child prim, which interprets it and changes appearance according to the parameters it contains.

The email way:
Service Discovery:
There is no automatic discovery method. However browser scripts should support bookmarks, and chat servers can redirect to email servers.
This leaves some space for developping a DNS-like service.

Browsing:
The client simply sends an email of subject "TLTP" to the server and message containing an URL composed of the index of the requested page, the communication method identifier "1" and its key (and optionnally its name), and the server replies with a stream of emails of subject "TLTP" and of message containing one or more TLML command line(s) for this page. Because of the slow speed of this transfer method, it's best to limit it to simple pages of 2 or 3 prims.

URL Format:
All URLs should be strings starting with "U" when sent over TLTP, followed by a TightList composed of:
string | Index of the page
integer cast as string | Optional. Communication method: 0 for chat, 1 for email This will support more methods in the future.
key or integer (cast as string) | Optional, set only if previous field is set. Key of the server for email, or server's channel for chat
string | Optional, set only if previous two fields are set. Name of the server
Example URLs: U!bestiary (when passed, the browser will request the page "bestiary" to the last called server)
U!Forward!0!128 (the browser will ask for the page "Forward" on channel 128, this can be used to make remote controllers easily)
U!Store!1!66864f3c-e095-d9c8-058d-d6575e6ed1b8!Jesrad Seraph.The Bestiary (Points at the page "Store" on server The Bestiary owned by Jesrad Seraph, of key 66864f3c-e095-d9c8-058d-d6575e6ed1b8, via email)

URL Type | Method | Format
Chat | 0 | Index, 0, channel [, server_name]]
Email | 1 | Index, 1, address [, server_name]
Joint notecard | 2 | Index, 2 [, line, name]
The name of the server that can be added to URLs is meant to be used in Bookmarks and for future systematic resolution of names ;)

Data types:

TLTP Servers and clients are not limited to exchanging URLs and TLML data, they can send any of these types of data:
First character | Data | Description
U | TightList | URL see section above
T | string | TLML command
t | notecard_key | key of a notecard containing data This is just a page stored in a notecard, the entire card will be read
x | string | XTM command
X | string | XTMP command
J | TightList([begin, end, offset, (visible-begin, (visible-end))]) | all integers, last 2 optional, can only be called from within a TLML-L page
C | TightList([(channel,) text]) | channel is optional, if left out, llSay is used otherwise it uses llShout
c | string | uses llOwnerSay to say string
s | stops all sounds playing
S | TightList([sound(, volume)]) | llTriggerSound(sound, volume) sound can be a name (but must be in browsers inventory) or key, volume is optional, defaults to 0.5
L | TightList([sound(, volume)]) | llPlaySound(sound, volume) sound can be a name (but must be in browsers inventory) or key, volume is optional, defaults to 0.5
l llToLower("L") | TightList([sound(, volume)]) | llLoopSound(sound, volume) sound can be a name (but must be in browsers inventory) or key, volume is optional, defaults to 0.5
D | TightList([start(, end)]) | both integers, end is optional, if left out only start is cleared.
R | TightList([scriptname] + parameters ) | composed of the name of the RPC script, and the necessary parameters
N | TightList([difference, notecard_texture_key, subgroup]) | difference is how far and direction to move the current position on the viewers that corespond to notecard_texture_key with regaurds to subgroup
W | float | duration to wait before interpreting further commands Useful for mimicking Flash ;)
K | no idea | Reserved for keys
k | no idea | Reserved for keys
p | integer | Request permissions, discard existing permissions
P | integer | Request permissions, keep existing permissions
A | string | name of an animation to play llStartAnimation Use the name for built-in animations
a | string | name or key of animation to stop llStopAnimation
M | TightList | TightList of multiple TLTP commands chained together

Sample Code

TLTP Server:
Works
// TLTP Server
// version 0.33e
// Author: Jesrad Seraph
// Modify and redistribute freely as long as you allow free modification and redistribution

// This example server supports both chat and email transport methods
// The pages are sent as keys for server performance

// Currently, the default page is the first page cached.

// TODO: support crossing the transport methods (listen->email, email->shout)

integer cur_emailer = 0;
integer max_emailer;

string indexname;        // contains the index page name

integer channel = 777;        // private server channel for accepting requests
integer handle;            // listener handle for getting requests
float advert_rate = 5.0;    // timerate for announcing the server
string tltp_str = "TLTP";    // saving memory
//string eof = EOF;
key ind;
integer index;

list TLML_L;

// courtesy of Strife Onizuka
list TightListParse(string a)
{
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}
string TightListDump(list a, string b)
{
    string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) == -1)
            jump end;
    integer d = -llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b);
}

sendEmail(string a, string s, string m)
{
    if (max_emailer > 0)
    {
        llMessageLinked(LINK_THIS, cur_emailer, TightListDump([a + "@lsl.secondlife.com", s, m], "!"), "");
        cur_emailer = (cur_emailer + 1) % max_emailer;
    } else llEmail(a + "@lsl.secondlife.com", s, m);
}

default
{
    state_entry()
    {
        if (llGetInventoryNumber(INVENTORY_NOTECARD) <= 0)
        {
            llOwnerSay("No pages found.");
        } else state ready;
    }

    changed(integer c)
    {
        if (c & CHANGED_INVENTORY) { llSleep(1.0); llResetScript(); }
    }
}

state ready
{
    state_entry()
    {
        index = llGetListLength(TLML_L);
        while(index)
            if(llGetInventoryType(llList2String(TLML_L, --index)) == INVENTORY_NONE)
                TLML_L = llDeleteSubList(TLML_L,index,index);
        indexname = llGetInventoryName(INVENTORY_NOTECARD, index = 0);
        integer a = llGetInventoryNumber(INVENTORY_NOTECARD);
        while(index < a)
            if(llListFindList(TLML_L, [llGetInventoryName(INVENTORY_NOTECARD, index)]) + 1)
                ++index;
            else
            {
                ind = llGetNotecardLine(llGetInventoryName(INVENTORY_NOTECARD, index), 0);
                a = index;
            }
        max_emailer = llGetInventoryNumber(INVENTORY_SCRIPT) - 1;
        handle = llListen(channel, "", "", "");
        llOwnerSay("Server ready on channel " + (string)channel);
        llSetTimerEvent(advert_rate);
        llOwnerSay((string)llGetKey() + "@lsl.secondlife.com");
        if (llGetObjectDesc() == "") llSetObjectDesc("Server");
    }

    dataserver(key queryid, string data)
    {
        if(ind == queryid)
        {
            if(llList2String(TightListParse(data),-1) == "TLML-L")
                if(llListFindList(TLML_L, [data = llGetInventoryName(INVENTORY_NOTECARD, index)]) == -1)
                    TLML_L += data;
            if(++index < llGetInventoryNumber(INVENTORY_NOTECARD))
                ind = llGetNotecardLine(llGetInventoryName(INVENTORY_NOTECARD, index), 0);
        }
    }

    changed(integer c)
    {
        if (c & CHANGED_INVENTORY) { llSleep(1.0); state default; }
    }

    timer()
    {
        llShout(-9, "U" + TightListDump([indexname, 0, channel, llGetObjectDesc()], "#") );
        llGetNextEmail("", "");
    }

    listen(integer ch, string n, key id, string msg)
    {
        string a = llGetSubString(msg, 0, 0);
        if (a != "U") return;
        list info = TightListParse(llDeleteSubString(msg, 0, 0));
        if (llGetListLength(info) < 3) return;    // simple sanity check

        string req = llList2String(info, 0);
        key thepage = llGetInventoryKey(req);
        string m = llList2String(info, 1);
        integer c = (integer)llList2String(info, 2);

        if (m != "0") return;    // not supporting cross transport methods right now

        if (thepage == NULL_KEY)
        {
            llShout(c, "T" + TightListDump(["-4", indexname], "!"));
        } else {
            integer type = llGetInventoryType(req);
            if (type + 1)
            {
                if (type == INVENTORY_NOTECARD) {
                    if(llListFindList(TLML_L, [req]) + 1)
                        req = "U" + TightListDump([(string)thepage, 2, 0, llGetKey()], "!");
                    else
                        req = "t" + (string)thepage;
                    llShout(c, req);
                } else
                if (type == INVENTORY_ANIMATION) { llShout(c, "A" + (string)thepage); } else
                if (type == INVENTORY_SOUND) { llShout(c, "S" + TightListDump([(string)thepage, "1.0"], "!")); } else
                if (type == INVENTORY_SCRIPT) { llShout(c, "T" + TightListDump(["-4", indexname], "!")); } else
                {
                    llShout(c, "cDownloading " + req);
                    if((n = llGetOwnerKey(id)) != id)
                        llGiveInventory(n, req);
                    else
                    {
                        llShout(c, "cCannot resolve your key ! Please come to sim " + llGetRegionName());
                    }
                }
            }
            else
            {
                llShout(c, "T" + TightListDump(["-4", indexname], "!"));
            }
        }
    }

    email(string time, string address, string subj, string message, integer left)
    {
        if (subj == tltp_str)
        {
            integer start = llSubStringIndex(message, "\n\n") + 2;
            string a = llGetSubString(message, start, start);
            if (a != "U") return;

            list info = TightListParse(llDeleteSubString(message, 0, start));
            string req = llList2String(info, 0);
            key thepage = llGetInventoryKey(req);
            string m = llList2String(info, 1);
            if (m != "1") return;    // not supporting cross transport methods right now

            if (thepage == NULL_KEY)
            {
            } else {
                integer type = llGetInventoryType(req);
                if (type + 1)
                {
                    // page is not a notecard
                    if (type == INVENTORY_NOTECARD) {
                        if(llListFindList(TLML_L, [req]) + 1)
                            req = "U" + TightListDump([(string)thepage, 2, 0, llGetKey()], "!");
                        else
                            req = "t" + (string)thepage;
                        sendEmail(llList2String(info, 2), tltp_str, req); } else
                    if (type == INVENTORY_ANIMATION) { sendEmail(llList2String(info, 2), tltp_str, "A" + indexname); } else
                    if (type == INVENTORY_SOUND) { sendEmail(llList2String(info, 2), tltp_str, "S" + TightListDump([thepage, "1.0"], "!")); } else
                    if (type == INVENTORY_SCRIPT) { sendEmail(llList2String(info, 2), tltp_str, "T" + TightListDump(["-4", indexname], "!")); } else
                    {
                        sendEmail(llList2String(info, 2), tltp_str, "cDownloading " + req);
                        string id = llGetOwnerKey(llGetSubString(message, 0, 35));
                        if (id != llGetSubString(message, 0, 35)) {
                            llGiveInventory(id, req);
                        } else sendEmail(llList2String(info, 2), tltp_str, "cCannot resolve your key ! Please come to sim " + llGetRegionName());
                    }
                }
                else
                {
                    sendEmail(llList2String(info, 2), tltp_str, "T!-4");
                }
            }
        } else llOwnerSay("Unknown email received: " + subj + ": " + message);
    }
}

Sample RPC plugin (call it MoveTo, use by sending R!MoveTo!<vector>[!time] to a browser that contains this script):
// MoveTo TLTP Plugin
// Sample plugin for RPC demonstration

integer rpc_code = 4400;

list TightListParse(string a)
{
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}

default
{
    link_message(integer part, integer code, string msg, key id)
    {
        if (code != rpc_code) return;
        if (msg != llGetScriptName()) return;

        list l = TightListParse((string)id);
        float delta;

        if (llGetListLength(l) > 1) { delta = (float)llList2String(l, 1); } else
            delta = 0.5;

        vector target = (vector)llList2String(l, 0);

        if (target == ZERO_VECTOR)
        {
            llStopMoveToTarget();
        } else llMoveToTarget(target, delta);
    }
}


exchangeToolDiscovery

Tool Discovery Protocol

This protocol is used by the TrustNet HUD Avatar Scanner (supported starting at version 0.50) to discover "tools", that is, various scripts that can target avatars to perform a function. The idea is to use the scanner to select an avatar from the list by clicking on it, and then pass the selection to an external tool. This is intended to integrate with tools like security orbs, weapons that target a specific avatar, etc.

Protocol

The object that wishes to implement the protocol must listen on channel 0x44470001 (or 1145503745 in decimal).

The text sent on the channel to the object implementing the protocol has a base format of llList2CSV([version, target_key, command]). Additional data is arguments to the command.

Field | Type | Description
version | integer | Protocol version number. Currently always 1.
target_key | key | Object key the message is destined to. If NULL_KEY, then destined to all the objects that hear it.
command | string | Command (see list below)
If the object implementing this protocol supports some sort of access control (for example, can only be used by its owner) it should ignore completely commands coming from objects owned by avatars that won't be able to use it.

Discovery

The first stage of the protocol is the discovery process. The scanner sends the "discover" command, and all the objects wishing to appear on the menu must reply as quickly as possible. The reply to this command must be sent to the specified channel, which is randomly generated.

Field | Type | Description
version | integer | Protocol version number. Currently always 1.
target_key | key | NULL_KEY
command | string | "discover"
reply_channel | integer | Channel where to send the reply. Will be randomly generated.

Discovery Reply

The reply must be in the format of llList2CSV([version, name, priority]) sent to the channel indicated in the "discover" command.

Field | Type | Description
version | integer | Protocol version number. Currently always 1.
name | string | Name of the function the object replying performs. Must 24 characters or less, or the reply will be ignored. Must be unique.
priority | integer | Number from 1 to 10, indicating the importance of this function. Higher numbers have higher priority. If there are enough objects replying that they can't fit in the llDialog list (more than 12), the lowest priority entry will be dropped to make room for a new one. The TrustNet Avatar Scanner internally gives a higher priority to objects owned by the scanner's owner.
The name sent must be unique, as it is what will be displayed on the llDialog button. Only one function per object will be accepted.

Command Execution Request

If the tool is chosen to perform its function, then an execute command will be sent to the channel, with this format:

Field | Type | Description
version | integer | Protocol version number. Currently always 1.
target_key | key | Object's key
command | string | "execute"
avatar_key | key | Key of the avatar that was selected
avatar_name | string | Name of the avatar that was selected
The object should respond to this by performing whatever action it does on the avatar. If multiple actions may be performed, it should present them to the scanner's owner (obtained with llGetOwnerKey in the listen event) using llDialog.

Versioning

In order to make it easier to extend the protocol if needed, all commands are prefixed with a version number. An object implementing the protocol should check the version number it just received, and if it doesn't recognize the version, the preferred action is to ignore the command completely.

Protocol Example

This is the timeline of the events that would happen when the full protocol is played out.

1. Scanner sends llList2CSV([version, NULL_KEY, "discovery", channel_num]) to channel 0x44470001.
2. Object replies with llList2CSV([version, "Test Object", 5]) sent to channel channel_num.
3. If "Test Object" is selected on the dialog, scanner sends llList2CSV([version, objects_key, "execute", avatar_key, avatar_name]) to channel 0x44470001.
4. Object performs the action on avatar avatar_key.

Example Source

This is the source for a script that would be detected by the scanner, and be possible to use with it.

// Example of Tool Discovery protocol, by Dale Glass

// Channel where the announcement request will be sent
integer  g_announce_channel = 0x44470001;

// Name the object will have in the tools list. Must be unique
string   g_name             = "Test";

// Priority, 1-10. Larger number = higher priority
integer  g_priority         = 5;

// Protocol version we speak
integer  g_protocol_version = 1;

default {
    state_entry() {
        llListen(g_announce_channel, "", NULL_KEY, "");
    }

    listen(integer channel, string obj_name, key id, string str) {
        list tmp = llCSV2List(str);

        integer version  = llList2Integer(tmp,0);
        key     dest_key = llList2Key(tmp,1);
        string  command  = llList2String(tmp,2);

        if ( version != g_protocol_version ) return;
        if ( (dest_key != llGetKey()) && (dest_key != NULL_KEY) ) return;

        if ( command == "discover" ) {
            integer reply_chan = llList2Integer(tmp,3);
            llSay(reply_chan, llList2CSV([g_protocol_version, g_name, g_priority]));

            llOwnerSay("Replied to announcement request from " + obj_name);
        } else if ( command == "execute" ) {
            key    av_key  = llList2Key(tmp,3);
            string av_name = llList2String(tmp,4);

            // Perform our action
            llInstantMessage(av_key, "Test!");
            llOwnerSay("Executed command on " + av_name + " (" + (string)av_key + ")");
        } else {
            llOwnerSay("Unknown command: " + command);
        }
    }

}

Protocol Exchange


exchangeTrustNet

TrustNet API

TrustNet is an attempt to replace the ratings system in SL with something better. A complete explanation of how it works can be found here: http://daleglass.wordpress.com/trustnet/

This is the complete API available to other people. Any unexplained functionality (you can tell for example by skipped command numbers) is either unavailable in the API, not stable enough, not generally useful, or deprecated.

HTTP Module

The HTTP module is the actual part doing requests with llHTTPRequest to the TrustNet server. To reduce server load and improve performance using the cache (explained below) is strongly encouraged. This function is done by two scripts, http and http_controller. Both are absolutely required to perform the function. The first one handles the actual HTTP requests, and the other deals with the formatting.

Constant | Integer | String | Key | Description
TN_HTTP_EXPLAIN | 0x44472902 | Agent's name | Agent's key | Returns a human-readable string (don't try to parse it, format is subject to change) that explains why the specified agent has the rating it does. Output may be cut to prevent script crashes due to memory exhaustion.
TN_HTTP_FLUSH | 0x44472904 | none | none | Requests aren't done immediately. The HTTP module will delay a request and attempt to convert multiple commands into one llHTTPRequest. This command requests the HTTP to do the pending requests now. This is useful if you're using TrustNet for something like a door, which runs a check for a single agent, and where waiting for more to appear is most likely useless.
TN_HTTP_GET_RATING | 0x44472906 | llList2CSV([agent_name, rating_type, link_num]) | Agent's key | Retrieves the rating_type rating for the specified agent. The link_num argument specifies to which prim to send the reply. This is provided as convenience for scanner objects that use a prim per agent.
TN_HTTP_LOGIN | 0x44472903 | none | none | Logs into the TrustNet system. This is required before any other commands work. No credentials are required, authentication is done against your key.
TN_HTTP_RATE | 0x4447290b | llList2CSV([agent_name, rating_type, score]) | Agent's key | Submits a rating for the indicated agent. The rating type is a string and a table of them can be found below. The score is an integer. Allowed values are -1, 0 and 1. A rating will remove any previous ones. 0 removes any previous rating.
TN_HTTP_SEARCH | 0x44472907 | rating type | none | Returns a human-readable string (don't parse it) containing a list of avatars with the best ratings of the requested type. This will for example return the best scripters from your net.

List of currently recognized rating types
Rating | Description
behavior | Are they nice to have around or not? A positive rating here implies a certain amount of trust. If you rate somebody's behavior positively, their ratings will affect the scores you get
scripting | Scripting ability
building | Building ability
avatars | Avatar making ability (not appearance)

Cache Module

Since requesting things takes time, and for some purposes the same request might be likely to be made repeatedly, a cache module is provided. Its use is strongly encouraged, unless you absolutely don't needed it. An example of when you could choose not to use it is when you're using TrustNet on something like a door, where checks are made against single agents very infrequently.

Constant | Integer | String | Key | Description
TN_CACHE_FLUSH | 0x44473103 | none | none | Clears the cache completely
TN_CACHE_GET | 0x44473100 | llList2CSV([agent_name, rating_type, link_num]) | Agent's key | Same as TN_HTTP_GET_RATING, only if it's found in the cache you get the result a lot faster
TN_CACHE_SET | 0x44473101 | llList2CSV([rating_type, score]) | Agent's key | Stores a rating in the cache. Overrides a previous rating if any. Ignored if the rating type is wrong (see TN_CACHE_SET_RATING_TYPE below). This is generally not necessary in normal usage.
TN_CACHE_SET_RATING_TYPE | 0x44473106 | rating type | none | The current cache implementation only caches scores of one type. The rest are passed through. If the rating you're primarily working with is not 'behavior' then you should call this to make the cache more useful. Calling it will clear the cache. If this defect is fixed, this command will be silently ignored.
TN_CACHE_STATS | 0x44473102 | none | none | Dumps cache statistics using llOwnerSay. May be helpful during development.

Client Events

These messages are going to be sent to the client script. This is how confirmations, replies, and errors are reported. They are sent to LINK_THIS, that is, the same module where the TrustNet scripts are. Many of them may be ignored.

Constant | Integer | String | Key | Description
TN_CLIENT_CONNECTING | 0x4447300b | none | none | HTTP module is connecting to the server. Will happen after a TN_HTTP_LOGIN. May be ignored.
TN_CLIENT_DOWNLOAD_DONE | 0x4447300c | none | none | Retrieval of ratings is complete. This happens once per ratings "bunch", not per requested rating. May be ignored.
TN_CLIENT_ERROR | 0x44473007 | Error message | none | Error message from the server, in a human-readable format. Will be received if something goes wrong, such as a request being incorrectly formatted.
TN_CLIENT_EXPLAIN | 0x44473008 | Message | none | Reply to TN_HTTP_EXPLAIN, formatted for human readability. Should not be parsed in any way, format may change unpredictably.
TN_CLIENT_LOGGING_IN | 0x4447300a | none | none | HTTP module is performing a login. Will happen after a TN_HTTP_LOGIN. May be ignored.
TN_CLIENT_LOGIN_ERROR | 0x44473006 | Error message | none | Received if a login attempt fails. This may be due to an expired subscription, server problems, or your version being too old.
TN_CLIENT_LOGIN_OK | 0x44473005 | Login message | none | Received when a login is successfully completed. Detecting this is mandatory. Most commands will be silently ignored until a login is completed.
TN_CLIENT_NO_RATINGS | 0x44473011 | none | none | Notification from the server informing the user that they haven't given any ratings yet. That means that no scores can be calculated. For that reason, using commands like TN_HTTP_GET_RATING and TN_CACHE_GET is strongly discouraged until you submit ratings to the server.
TN_CLIENT_NOTICE | 0x44473009 | Notice | none | General message from the server to the user. May contain notices about future downtime, etc. May be received at any time. May be ignored.
TN_CLIENT_RATING | 0x44473000 | llList2CSV(rating_type, rating) | Agent's key | Result of a TN_HTTP_GET_RATING.
TN_CLIENT_SEARCH | 0x4447300f | Message | none | Result of a TN_HTTP_SEARCH query. Formatted for human readability.
TN_CLIENT_STATS_QUEUED | 0x44473001 | Count | none | How many pieces of data (rating requests, rating submissions, etc) are currently waiting to be sent.
TN_CLIENT_STATS_TRANSMITTING | 0x44473003 | Count | none | How many pieces of data are currently being transmitted
TN_CLIENT_SUBSCRIPTION_EXPIRING | 0x4447300e | none | none | Notification from the server informing the user that their subscription is expiring (currently, less than a week left). Will be received during login. It is recommended that you check for this in at least one object to avoid surprises.
TN_CLIENT_UPLOAD_DONE | 0x4447300d | none | none | Ratings were accepted by the server. This happens once per ratings "bunch", not per requested rating. May be ignored.

Code Examples

Explain

This example demonstrates how to make an object that waits for somebody to click it, then does a TN_HTTP_EXPLAIN request for the agent that clicked it, and produces the output.

// Example TrustNet script by Dale Glass
// Demonstrates the TN_HTTP_EXPLAIN TrustNet command

integer TN_HTTP_LOGIN         = 0x44472903;
integer TN_HTTP_EXPLAIN       = 0x44472902;
integer TN_CLIENT_LOGIN_OK    = 0x44473005;
integer TN_CLIENT_LOGIN_ERROR = 0x44473006;
integer TN_CLIENT_EXPLAIN     = 0x44473008;

default {
    state_entry() {
        // Start a login
        llMessageLinked(LINK_THIS, TN_HTTP_LOGIN, "", NULL_KEY);
        llSetText("Logging in...", <0,0,1>, 1.0);
    }

    link_message(integer sender_num, integer num, string str, key id) {
        if( num == TN_CLIENT_LOGIN_OK ) {
            // Login completed, everything went fine
            state running;
        } else if ( num == TN_CLIENT_LOGIN_ERROR ) {
            // Login failed. Here we could perhaps retry after a delay.
            llOwnerSay("Login failed!\n" + str);
            llSetText("Login failed", <1,0,0>, 1.0);
        }
    }
}

state running {
    state_entry() {
        llSetText("Ready. Click me!", <0,0,1>, 1.0);
    }
    touch_start(integer count) {
        llSetText("Requesting...", <0,0,1>, 1.0);

        // Request an explanation of the score from the server
        llMessageLinked(LINK_THIS, TN_HTTP_EXPLAIN, llDetectedName(0), llDetectedKey(0));
    }
    link_message(integer sender_num, integer num, string str, key id) {
        if ( num == TN_CLIENT_EXPLAIN ) {
            // Explanation received
            llWhisper(0, str);
        }
    }
}

Get Rating

// Example TrustNet script by Dale Glass
// Demonstrates the TN_HTTP_GET_RATING TrustNet command.
//
// This is a more complete example with code for handling
// common events.

integer TN_HTTP_LOGIN                   = 0x44472903;
integer TN_HTTP_GET_RATING              = 0x44472906;

integer TN_CLIENT_RATING                = 0x44473000;
integer TN_CLIENT_LOGIN_OK              = 0x44473005;
integer TN_CLIENT_LOGIN_ERROR           = 0x44473006;
integer TN_CLIENT_SUBSCRIPTION_EXPIRING = 0x4447300e;
integer TN_CLIENT_ERROR                 = 0x44473007;
integer TN_CLIENT_NOTICE                = 0x44473009;
integer TN_CLIENT_LOGGING_IN            = 0x4447300a;
integer TN_CLIENT_CONNECTING            = 0x4447300b;

handle_common(integer num, string str, key id) {
    if ( num == TN_CLIENT_NOTICE ) {
        llOwnerSay("Notice: " + str);
    } else if ( num == TN_CLIENT_ERROR ) {
        llOwnerSay("Error: " + str);
    }
}

default {
    state_entry() {
        // Start a login
        llMessageLinked(LINK_THIS, TN_HTTP_LOGIN, "", NULL_KEY);
        llSetText("", <0,0,1>, 1.0);
    }

    link_message(integer sender_num, integer num, string str, key id) {
        if( num == TN_CLIENT_LOGIN_OK ) {
            // Login completed, everything went fine

            if ( str != "" ) {
                // Display login message, if there is one
                llOwnerSay(str);
            }
            state running;
        } else if ( num == TN_CLIENT_LOGIN_ERROR ) {
            // Login failed. Here we could perhaps retry after a delay.
            llOwnerSay("Login failed!\n" + str);
            llSetText("Login failed", <1,0,0>, 1.0);
        } else if ( num == TN_CLIENT_SUBSCRIPTION_EXPIRING ) {
            llOwnerSay("Your TrustNet API subscription is expiring");
        } else if ( num == TN_CLIENT_CONNECTING ) {
            llSetText("Connecting...", <0,0,1>, 1.0);
        } else if ( num == TN_CLIENT_LOGGING_IN ) {
            llSetText("Logging in...", <0,0,1>, 1.0);
        } else {
            handle_common(num, str, id);
        }
    }
}

state running {
    state_entry() {
        llSetText("Ready. Click me!", <0,0,1>, 1.0);
    }
    touch_start(integer count) {
        llSetText("Requesting...", <0,0,1>, 1.0);

        // Request an score from the server
        llMessageLinked(LINK_THIS, TN_HTTP_GET_RATING,
                        llList2CSV([llDetectedName(0), "behavior", llGetLinkNumber()]),
                        llDetectedKey(0));
    }
    link_message(integer sender_num, integer num, string str, key id) {
        if ( num == TN_CLIENT_RATING ) {
            // Explanation received
            list result = llCSV2List(str);
            string rating_type = llList2String(result,0);
            float  score       = llList2Float(result,1);
            llSetText("", <0,0,0>, 0.0);
            llWhisper(0, llKey2Name(id) + "'s " + rating_type + " score is " + (string)score);
        } else {
            handle_common(num, str, id);
        }
    }
}

Protocol Exchange


httpHeaderCrunch

Hi.
Posting this so maybe it helps others out. it's pretty basic.

<?php
    //SL Class.9
    //This class can pull info from SL headers.
    //Currently for http_req

    //TODO: Add Email header parsing when I have a few free mins... If someone else wants to, go ahead.
    //I don't rely on SLEmail as much anymore, so it isn't a priority
    //Find any bugs? Ways to make it better? Please share :)
    //Max Case - July 8, 2006

  class secondlife {
     var $UserAgent;
     var $UserAgentVersion;
     var $Shard; //Production or Testing?

     var $HostNumber;
     var $Region;
     var $LocalPos;
     var $LocalRot;
     var $LocalVel;
     var $ObjectKey;
     var $ObjectName;
     var $OwnerKey;
     var $OwnerName;

 // Class constructor
 function secondlife() {

 }

 // Returns class name
 function GetClassName() {
    return 'secondlife';
 }

 //This take in $_SERVER for args at the moment.
 //Loads up all the vars.

 function ParseHeaders($headers) {
     foreach( $headers as $key => $value )
     {
         //Loop though values

        if($key == "HTTP_USER_AGENT" ) //HTTP_USER_AGENT is Second Life LSL/1.10.0(32) (http://secondlife.com)
        {
            $this->SetUserAgent($value);
            $this->SetUserAgentVersion($value);
        }
        else if($key == "HTTP_VIA")//HTTP_VIA is 1.1 sim480.agni.lindenlab.com:3128 (squid/2.5.STABLE9)
        {
            $this->SetHostNumber($value);
        }
        else if(  strpos($key,"HTTP_X_SECONDLIFE_") !== false )
        {
            $temp = explode("HTTP_X_SECONDLIFE_", $key);
            $headerinfo = $temp[1];

            if($headerinfo == "LOCAL_POSITION")
            {//Parse out ( ) in SetLocalPos
                $this->SetLocalPos($value);
            }
            else if($headerinfo == "LOCAL_ROTATION")
            {
                $this->SetLocalRot($value);
            }
            else if($headerinfo == "LOCAL_VELOCITY")
            {
                $this->SetLocalVel($value);
            }
            else if($headerinfo == "OBJECT_KEY")
            {
                $this->SetObjectKey($value);
            }
            else if($headerinfo == "OBJECT_NAME")
            {
                $this->SetObjectName($value);
            }
            else if($headerinfo == "OWNER_KEY")
            {
                $this->SetOwnerKey($value);
            }
            else if($headerinfo == "OWNER_NAME")
            {
                $this->SetOwnerName($value);
            }
            else if($headerinfo == "REGION")
            {
                $this->SetRegion($value);
            }
            else if($headerinfo == "SHARD")
            {
                $this->SetShard($value);
            }
        }
     }
 }

 function GetAll() {

  return compact(
        $this->UserAgent,
        $this->UserAgentVersion,
        $this->Shard, //Production or Testing?

        $this->HostNumber,
        $this->Region,
        $this->RegionCorners,
        $this->LocalPos,
        $this->LocalRot,
        $this->LocalVel,
        $this->ObjectKey,
        $this->ObjectName,
        $this->OwnerKey,
        $this->OwnerName
    ); //compact it all into array, return it
 }

 function GetUserAgent() {
    return $this->UserAgent;
 }

 function GetUserAgentVersion() {
    return $this->UserAgentVersion;
 }

 function GetShard() {
    return $this->Shard;
 }

 function GetHostNumber() {
    return $this->HostNumber;
 }

 function GetRegion() {
    return $this->Region;
 }

 function GetLocalPos() {
    return $this->LocalPos;
 }

 function GetLocalRot() {
    return $this->LocalRot;
 }

 function GetLocalVel() {
    return $this->LocalVel;
 }

 function GetObjectKey() {
    return $this->ObjectKey;
 }

 function GetObjectName() {
    return $this->ObjectName;
 }

 function GetOwnerKey() {
    return $this->OwnerKey;
 }

 function GetOwnerName() {
    return $this->OwnerName;
 }

 function SetUserAgent($UserAgent) {

    $this->UserAgent = $UserAgent;
 }

//Sets to 0 if Not SL
//Use that as indicator of SL or not sl?
//good for one script which can handle browser req's, or SL req's
 function SetUserAgentVersion($UserAgentVersion)
 {
     if(  strpos($UserAgentVersion,"Second Life") !== false )
     {
         if (preg_match("/LSL\/(.*)/", $UserAgentVersion, $matches))
         {
             $temp = explode("(", $matches[1]);
             $ua_version = $temp[0];
         }

         $this->UserAgentVersion = $ua_version;
     }
     else
     {
         $this->UserAgentVersion = 0;
     }
 }

 function SetShard($Shard) {
    $this->Shard = $Shard;
 }

 function SetHostNumber($HostNumber)
 {
    if (preg_match("/sim(.*)/", $HostNumber, $matches))
    {   //grab region_sim
        $temp = explode(".", $matches[1]);
        $region_sim_number = $temp[0];
    }

    $this->HostNumber = $region_sim_number;
 }

 function SetRegion($Region)
 {
    $foo =  explode(' (', $Region, 2);
    //explode region, and create world corner coords.

    $temp = $this->burst_coords($this->remove_brackets( $foo[1] ));
    $reg_corners = array();
    $reg_corners['x'] = $temp[0];
    $reg_corners['y'] = $temp[1];

    $this->RegionCorners = $reg_corners;
    $this->Region = $foo[0];
 }

 function SetLocalPos($LocalPos)
 {
    $temp = $this->burst_coords($this->remove_brackets( $LocalPos ));
    $position = array();
    $position['x'] = $temp[0];
    $position['y'] = $temp[1];
    $position['z'] = $temp[2];

    $this->LocalPos = $position;
 }

 function SetLocalRot($LocalRot)
 { //returns x,y,z,w
    $temp = $this->burst_coords($this->remove_brackets( $LocalRot ));
    $rotation = array();
    $rotation['x'] = $temp[0];
    $rotation['y'] = $temp[1];
    $rotation['z'] = $temp[2];
    $rotation['w'] = $temp[3];

    $this->LocalRot = $rotation;
 }

 function SetLocalVel($LocalVel)
 {
     $temp = $this->burst_coords($this->remove_brackets( $LocalVel ));
     $velocity = array();
     $velocity['x'] = $temp[0];
     $velocity['y'] = $temp[1];
     $velocity['z'] = $temp[2];

    $this->LocalVel = $velocity;
 }

 function SetObjectKey($ObjectKey) {
    $this->ObjectKey = $ObjectKey;
 }

 function SetObjectName($ObjectName) {
    $this->ObjectName = $ObjectName;
 }

 function SetOwnerKey($OwnerKey) {
     $this->OwnerKey = $OwnerKey;
 }

 function SetOwnerName($OwnerName) {
     $this->OwnerName = $OwnerName;
 }

//Little utility / string cleaner functions.
//This removes brackets, and spaces :>
function remove_brackets($foo)
{
    return trim($foo, " ())");
}
 function burst_coords($foo) //CSV ", "
 {
     return explode( ", ", $foo);
 }
}

/* EXAMPLE */
/*
$testheader = array(
"HTTP_USER_AGENT"=>"Second Life LSL/1.10.0(32) (http://secondlife.com)",
"HTTP_VIA"=>"1.1 sim400.agni.lindenlab.com:3128 (squid/2.5.STABLE9)",
"HTTP_X_SECONDLIFE_LOCAL_POSITION"=>"(34.408112, 159.722198, 68.476143)",
"HTTP_X_SECONDLIFE_LOCAL_ROTATION"=>"(0.000000, 0.000000, 0.000000, 1.000000)",
"HTTP_X_SECONDLIFE_LOCAL_VELOCITY"=>"(0.000000, 0.000000, 0.000000)",
"HTTP_X_SECONDLIFE_OBJECT_KEY"=>"4d97ef9f-3e36-efe7-ffd5-c210a973ec1b",
"HTTP_X_SECONDLIFE_OBJECT_NAME"=>"Flagpole .5",
"HTTP_X_SECONDLIFE_OWNER_KEY"=>"6d869ee0-e19e-4afa-8431-b85705198a0c",
"HTTP_X_SECONDLIFE_OWNER_NAME"=>"Max Case",
"HTTP_X_SECONDLIFE_REGION"=>"Yoyo slsls (254720, 258048)",
"HTTP_X_SECONDLIFE_SHARD"=>"Production"
);

$sl = new secondlife; //Make the object
$sl->ParseHeaders($testheader);

print_r($sl->GetAll());
*/
?>


LSLdotPHP

CyberLeo.Net's LSL.php, LSL emulation library script for PHP.

Current emulated functions:
llSendRemoteData
array llSendRemoteData(key $channel, string $dest, integer $idata, string $sdata)
only varience is the return is a PHP array, structured as follows:

return Array(
    "Channel"=>$channel,
    "StringValue"=>$string,
    "IntValue"=>$integer
);
or
return Array(
    "error"=>"Query error",
    "code"=>$response->faultCode(),
    "message"=>$response->faultString()
);

it otherwise behaves identical.
Default server is SecondLife's own XML-RPC server.

To use:
include("LSL.php");
on CDN servers.


MiniSlogo

Mini Second logo, or Minislogo, is a subset of the Slogo protocol/language. The main differences is that Minislog has a simpler parser and fewer tokens to look for. Unlike other protocols, Minislog isn't meant for any specific task, but instead is to be a general-purpose protocol.

Groupings: Square brackets. [ and ]
Whitespace: Spaces, tabs, and returns.
Tokens: Alphanumeric strings. a-z, A-Z, 0-9, and underscore (_)
Quote: "
Note that greater than and less than ( < and > ) are not used, nor is parenthesis ( ), curly brackets { }, pipes |, slashes \ /, colons (:;), or math symbols (*&^%$#@!-+=) as functions. Slogo has uses for these, which the minislog parser does not handle.

Tokens are broken subcategorized into numeric literals (3, 5.2, 4E6, -4), String literals ("Hello, "World, "Thisobject, "f35cc46b-de75-9437-c5bf-067d16964a84 ) and commands (Make, Tell, Ask, DoAction, llPizza). Named variables and constants (x, y, ORDER_PIZZA_HUT, ) are not implemented, as the parser does not have a name lookup table. Do note that the minus is allowed in string literals, necessary for UUIDs. Also note that all string literals have a quote preceding the text, to differentiate from a command, but do not have a trailing quote. This is a carryover from logo. Finally, logo, and thereby, Slogo and Minislog are case insensitive.

Tokens are grouped into lists by brackets, separated by white space. No commas are used. Unlike LSL, Lists may contain sublists. Indeed, sometimes, that's the only way. A vector is simply a list of three numeric literals. A rotation is simply a list of 4 numeric literals.

Minislog, therefore is a series of commands, string literals, numeric literals, and lists thereof.

There are two essential commands that should be supported by all minislog-compatible devices.

Tell is a function/command with two arguments, a list of what to do, and string literal of who should do it. For example, if there is a object called Dominos that can be told to deliver, the command:
Tell "Dominos [Deliver]
will do. It is legal to instruct an object by its UUID, replacing "Dominos with the quotes followed by the hexidecimal UUID. However, for our samples, easily readable text will be used. Furthermore, one of the advantages of Slogo is to instruct several similarly-named objects at the same time, or to make programming easy. Because the what is a list, you can send multiple commands, or pass arguments to the individual commands. The brackets indicate to minislog (Or more importantly, to other programs on the same channel who are NOT Domino, so they know how much to skip) which are commands to be told.
Tell "Dominos [Wait 5 Deliver] Tell "Pizza_Hut [GiveOut "Dominos 5 NewPizza]
In the above example, it is likely that Pizza_Hut, as instructed, will offer its own command to Dominos before performing the NewPizza command. Also, it is likely you want to adjust some variables or properties of the instructed code. While one could use another custom function, it is best to use logo conventions.
Tell "Dominos [make "couponvalue 5 make "target [45 78.2 92.6]]

Future plans
One design that hasn't been implemented or fully speced out is the attributes, or more, what the string literals will apply.
Tell "Dominos [make "pos [23 45.6 341.56] make "rot [0 0 0 1] make "shape "box]
And possibly, like llSetPrimitiveParams, there will be a way to make several at once.
Tell "Dominos [make "attr ["pos [23 45.6 341.56] "rot [0 0 0 1] "shape "box]]
Alternatively, logo does not use make for the position of the turtle, instead using the functions pos and setpos :x :y. So it might be that properties that require actions when set will use wrapper function/tokens reflecting LSL.
Tell "Dominos [setpos [23 45.6 341.56] setrot [0 0 0 1] setattr ["shape "box "color [0.4 0.5 1.0]]]

Additionally, Wait in logo causes a program to delay for n 60ths of a second. So to instruct a minislog-compliant primative to change random colors twice could go like:
Tell "Color-Ball [make "color [.5 0 1] wait 180 make "color [1 .5 0]]
Obviously, thought is needed on how to specify which side (Or which prim!) the color is applied to. Furthermore, logo uses the literals "true and "false to handle boolean logic, while LSL uses integers. A well-behaved minislog-compliant script should adhere to "true and "false where applicable.

Finally, while Tell and Make have been defined and will be implemented in the first iteration, other commands are reserved for future use: Ask, Reply, Authenticate, Decrypt


PetProtocol032

Back to PetProtocol main page

version 0.32
* If you make 2 pets they will attempt to give each other commands. For debugging purposes, the pets will say what command they hear.

(note)
pet protocol

The goal is to develop a protocol that would allow pets to communicate and interact with other pets and avatars.

1) easy to understand and modify
2) open source which anyone can copy for free from the forum.
- Please do not post your ideas to this thread if you don't want other people to use it. =]
- It is completely acceptable if you wish to use any or all of the code in this post in your pets and sell them for profit.

We need some scale to show where the animal fits into the food chain and show what other animals would be concidered a threat, prey, or playmate.

food chain 0.0 - 1.0
*This scale is off the top of my head and Very likely to be changed.*
0.0 grass
0.1 sheep
0.15 birds, mice
0.2 house cats
0.3 dogs
0.7 hippo
0.8 shark
0.9 lion
1.0 godzilla

A pet will chat it's status on a the introduction channel (9000) once every 10 seconds.

(If 2 pets decide they want to chase each other they agree to communicate more often on some other channel.. need to think about that more.)

Here are the attributes to be chatted every 10 seconds.
attribute example
fight 0.3 what would the pet approach
flee 0.4 what would the pet run from
type dog
subtype beagle
action chase, hide, beg, eat
target sylvester/grass/bp (use target's key instead of name?)
offsetR relative offset from target. <-5,0,0> would follow 5m behind target
offsetA absolute offset from target. <-5,0,0> would follow 5m west of target
- - - -
inferred info (can be detected when the pet chats)
name spot (pet's name = object name)
key a0deec52-f289-7608-7064-a8bcf480e6d8
distance distance in meters, often a radius

now to list some behaviors or "action" (a + sign indicates a target is required - such as another pet or avatar)
+ eat
+ chase
+ avoid
+ follow/swarm/flock
+ bump/push
+ ride/perch
+ mimic
play_dead
speak
sit
stay
sleep
+ jump_over
dig
+ circle
+ play_tag
+ beg
find
give/deliver
retrieve/fetch
meet
face/look

example introduction string:
"introduction + fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|owner + offsetR| <-5,0,0> + group|6500"

example command string:
"command + action|follow + target|mykey + offsetR|<-5,0,0>"

[ question + knowncommands|1 ]

[ answer + knowcommands|1 + follow| + sit| ]

.pet (script)

string gBehavior_0; // this pet's behavior
string gBehavior_1; // one other pet's behavior
list glBehaviors_MRU = [0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0]; // last 10 pet keys and their fight/flee parameters "ID, fight, flee"
// string gCurrentAction; delete?
list gActions; // Action probabilities
list gObey; // obey values for threat, neutral, prey, owner, other avatar
list glCurrentMessage; // the most recently received message
float gTime = 0; //sum of all action probabilities
integer gL1; // listen handler for owner
integer gL2; // listen handler for pet introduction channel
integer gL3; // listen handler for group channel
integer gL4; // listen handler for pet's private channel
integer gIntroduction_channel = 9000;
integer gGroup_channel = 1; //this value should be defined in the read_behavior function;
integer gPet_channel;
key gOwner;

// get_channel grabs the numbers in the last 9 digits of this pet's key. We will use this integer as this pet's private chat channel.
integer get_pet_channel(key ID_key)
{
    integer temp_ID = (integer)llDumpList2String(llParseString2List(llGetSubString(ID_key, 28, 37), ["-","a","b","c","d","e","f"], []), "");
    return temp_ID;
}

initialize()
{
gOwner = llGetOwner();
llListenRemove(gL1);
llListenRemove(gL2);
llListenRemove(gL3);
//integer llListen(integer channel, string name, key id, string msg);
gL1 = llListen (0, "", gOwner,"");
gL2 = llListen (gIntroduction_channel, "", "", "");
gL3 = llListen (gGroup_channel, "", "", "");
gPet_channel = get_pet_channel(llGetKey());

// llWhisper(0, (string)gPet_key + " "+ (string)llGetKey());
gL4 = llListen (gPet_channel, "", "", "");
gTime = 0;
llSetTimerEvent(10); //move this to a better place
read_behavior();
}

//string get_param_value (string)
//{

read_behavior()
{
// read behavior from notecard, email, or XML-RPC
read_notecard();

gBehavior_0 = "fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|master + group|6500";
llWhisper(0,(string)gBehavior_0);

gActions = [
    40,"sleep",
    1,"speak",
    20,"follow + target|owner",
    10,"evade + target|owner + offsetR| <-0,0,0> + distance|4.5",
    30,"command + action|eat + target|owner",
    10,"command + action|stay",
    50,"command + action|follow + target|mykey + offsetR|<-5,0,0>"
    ];

// the following sums the probabilities of the possible actions
    integer i;
    integer max = llGetListLength(gActions);
    for (i=0;i<max;i+=2)
    {
        gTime = gTime + llList2Float(gActions, i);
//        llWhisper(0,(string)gTime);
    }

gObey = [1.0, 0.5, 0.1, 0.9, 0.5];

}

read_notecard()
{
// Read description
// Read actions list
}

write_behavior() {// IM's or emails owner the current behavior
}

//////////////////////////////////////////////////////////////////////////
// CHOOSE BEHAVIOR
// mod: mood modifier:
//      < 1 will only do the first mod% of the behaviors
//      = 1 will do behaviors exactly as specified (will do a behavior every time)
//      > 1 has increasing chance of doing nothing
// time: the sum of all 'chances' much faster to calculate this ahead of time then as you go
// behaviors: A list that looks like [chance, "behavior_command", chance, "behavior_command"
//        ex: [0.4, "bark", 0.2, "sit"] or [4, "bark", 2, "sit"]
// Num and target are sent with the link message.  Perhaps always call this with the key of
// any currently targeted animal?  Will work fine if 0 and "" are used respectively.
//
// Example call, although behavior should be a list stored in a variable made from reading a notecard...
// choose_behavior(1,0.6,[0.4,"sleep",0.2,"bark"],0,"");
choose_behavior(float mod, float time, list behaviors, integer num, key target)
{
    float choice = llFrand(time * mod);
//    llWhisper(0,"time = "+(string)time);
//    llWhisper(0,"choice = "+(string)choice);
    integer i;
    integer max = llGetListLength(behaviors);
    for (i=0;i<max;i+=2)
    {
        if (choice < (float)llList2String(behaviors,i))
        {
            //Do this action!
            llMessageLinked(LINK_SET,num,llList2String(behaviors,i+1),target);
//            llWhisper(0,llList2String(behaviors,i+1));
llSetText(llList2String(behaviors,i+1), <0.5,0.5,1>,1);
            return;
        }
        else
        {
            choice -= llList2Float(behaviors,i);
        }
    }
}

introduce()
{
    llSay(gIntroduction_channel, "introduction + "+gBehavior_0);
}

//////////////////////////////////////////////////////////////////////////
// OBEY function
// Any incoming commands are sent to this function. This script determines
// if the command will be obeyed based on values from the pet's behavior
// setup. If the command will be obeyed, it will be sent out to the other
// scripts in the pet.
// There are 5 parameters for the obey variable,
// (threat, neutral, prey, owner, other avatar),
// each ranging from 0 to 1.
// If the parameter is 0, the pet will never obey a command from something
// in that class, if it's 1 the pet will always obey that command.
integer obey ( string command, string string_this_pet, key commander )
{
//gBehavior_0 = "fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|master + group|6500";
    list list_this_pet = llParseString2List(string_this_pet, ["|", "+", " "], []);
llSay(0, "list_this_pet is "+llDumpList2String(list_this_pet, ","));
//integer llSubStringIndex(string source, string pattern)
    float this_pet_flee = llList2Float(list_this_pet, (llListFindList(list_this_pet, ["flee"])+1));
    float this_pet_fight = llList2Float(list_this_pet, (llListFindList(list_this_pet, ["fight"])+1));
//check that we actually have a flee & fight value for commander in our MRU. if not, assume commander has fight of 0, flee of 1.
    float commander_flee = 1;
    float commander_fight = 0;
//NEED COMMANDER'S KEY
//THEN SEARCH THE MRU FOR THE KEY
integer commander_in_MRU = llListFindList(glBehaviors_MRU, [(string)commander]);
    if ( commander_in_MRU != -1 )
    {
llSay(0, "commander_in_MRU is "+(string)commander_in_MRU);
//IF THE COMMANDER'S KEY IS IN OUR MRU
//READ THE FIGHT AND FLEE FROM OUR MRU
        commander_flee = llList2Float(glBehaviors_MRU, (commander_in_MRU+1));
llSay(0, "llListFindList(list_this_pet) is "+(string)(llListFindList(list_this_pet, ["flee"])+1));

        commander_fight = llList2Float(glBehaviors_MRU, commander_in_MRU+2 );
//***to do*** make sure numbers are between 0.0 and 1.0
    }
    integer obey = 0;

llSay(0, "glBehaviors_MRU is "+llDumpList2String(glBehaviors_MRU, ","));
llSay(0, "this_pet_flee is "+(string)this_pet_flee+" & this_pet_fight is "+(string)this_pet_fight);
llSay(0, "commander_flee is "+(string)commander_flee+" & commander_fight is "+(string)commander_fight);

// test if commander is a threat
    if ( commander_fight > this_pet_flee )
    {
llSay(0, (string)commander_fight +">"+ (string)this_pet_flee);
        if (llList2Float(gObey,0) > llFrand(1)) // calculate chance of obeying the command
        {
            llMessageLinked(-4, 0, command, NULL_KEY);
            obey = TRUE;
        }
    }
    // neutral
//    if ( commander.fight > this_pet.flee )
    {
//        if (llList2Float(obey,2) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    // prey
//    else if ( this_pet.fight > commander.flee )
    {
//        if (llList2Float(commander,3) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    // owner
//    else if ( other == gOwner )
    {
//        if (llList2Float(commander,4) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    //other
//    else if ( other != gOwner )
    {
//        if (llList2Float(commander,5) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    return obey;
}

default
{
    state_entry()
    {
        initialize();
    }

    on_rez(integer start_param)
    {
        initialize();
    }

   listen(integer channel, string name, key id, string message)
   {
//  if id = gOwner
//  if id = pet_key
//  if id = pet_group
//    list lPet_key = [(string)get_pet_channel(id)];
//***to do***do a bounds check before converting to list?
    list glCurrentMessage = llParseString2List(message, ["|", "+", " "], []);
//gBehavior_1 =
llSetText(llKey2Name(id)+": "+(string)message, <1,0,0>,1);
llSleep(0.01);
llSetText(llKey2Name(id)+": "+(string)message, <1,1,1>,1);
//llWhisper(0,(string)message);
//llSay(0, llList2String(glCurrentMessage, 0));
if (llList2String(glCurrentMessage, 0) == "command")
    obey(message, gBehavior_0, id); // decide if this pet should obey the command
// (respond to requests)
// (respond to questions)
// (make note of introductions)
if (llList2String(glCurrentMessage, 0) == "introduction")
{
// glBehaviors_MRU = [ "a",  "0",  "0",  "0",  "0",  "0",  "0",  "b",  "9916", "h" ];
    integer Pet_key_in_MRU = llListFindList(glBehaviors_MRU, [(string)id]); // find where this pet is in our contact list
//llWhisper(0,"lPet_key "+(string)lPet_key+" is at '"+(string)Pet_key_in_MRU+"'");

//==================
//llSay(0, "glBehaviors_MRU is "+llDumpList2String(glBehaviors_MRU, ","));
//==================

// 1) See if the pet is already in the list
// 2) If they are in the list, remove them from the list and add them to the front of the list
// 3) if they are not in the list, add them to the front of the list and delete whatever is at the end of the list
    if (Pet_key_in_MRU == -1)  // if the pet is not in our contact list, add it and it's fight/flee parameters to the front of the list and delete the last pet on the list
    {
            // find the fight/flee parameters
            list temp_list = llParseString2List(message, ["|"," + "], []);
float fight_value = llList2Float(temp_list, (llListFindList(temp_list, ["fight"])+1));
float flee_value =  llList2Float(temp_list, (llListFindList(temp_list, ["flee"])+1));
//        llInstantMessage(llGetOwner(), "fight_value = '"+(string)fight_value+"'");
//        llInstantMessage(llGetOwner(), "flee_value = '"+(string)flee_value+"'");
glBehaviors_MRU = [(string)id, fight_value, flee_value]+glBehaviors_MRU;

glBehaviors_MRU = llDeleteSubList(glBehaviors_MRU, 27, 29);

//glBehaviors_MRU = ["A", "5", "orange"]+glBehaviors_MRU;
//    llInstantMessage(llGetOwner(), "glBehaviors_MRU = '"+(string)glBehaviors_MRU+"'");
//        llInstantMessage(llGetOwner(), "lPet_key = '"+(string)llget_param_value("fight|")+"'");
    }
    else
    {
            // find the fight/flee parameters
    list temp_list = llParseString2List(message, ["|"," + "], []);
    float fight_value = llList2Float(temp_list, (llListFindList(temp_list, ["fight"])+1));
    float flee_value =  llList2Float(temp_list, (llListFindList(temp_list, ["flee"])+1));
glBehaviors_MRU = llDeleteSubList(glBehaviors_MRU, Pet_key_in_MRU, Pet_key_in_MRU+2);
glBehaviors_MRU = [(string)id, fight_value, flee_value]+glBehaviors_MRU;

    }

    }

    }

    changed(integer change)
    {
// read_notecard;
    }

    timer()
    {
// if not in the middle of behavior??
introduce();
//choose_behavior(float mod, float time, list behaviors, integer num, key target)
choose_behavior(1,gTime,gActions,0,"");

// stay_close_to_owner();
    }

}

beagle behavior (note)
May want to use a delimiter other than "," for vectors and quats. Maybe "/" or "*" ?

// default description
// [fight (0-1), flea (0-1), type, subtype, default action, target, rotation, group channel]
// example: 0.3, 0.4, "Dog", "Beagle", "sitting", "", <0,0,0>, 6500;

description = "fight|0.3 + flee|0.4+ type|dog + subtype|beagle + action|watching + target|owner + group|6500"

// next are the actions this pet will do and their percentage change.
// they don't need to add up to 1. they are relative to their total.

sleep, 0.3;
chase, 0.2;

// next are the actions this pet will do if specific pet types are detected.

cat, chase, 1.0;
mouse, evade, 1.0;

// next is the percentage chance that this pet will obey commands
// the 1st number is chance to obey a command from a pet with a higher fight number than this pet, ie. a pet concidered a threat.
// the 2nd number is chance to obey a command from a pet that is neither a threat nor prey
// the 3rd number is chance to obey a command from a pet that is prey
// the 4th number is chance to obey a command from its owner
// the 5th number is chance to obey a person other than the owner
// obey, threat, neutral, prey, owner, other_avatar
obey, 1.0, 0.5, 0.1, 0.9, 0.5;
follow (script)

// follow script
//

startup()
{
    llSensorRemove();
    llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y, TRUE);
    llSetStatus(STATUS_ROTATE_Z, TRUE);
    llSetStatus(STATUS_PHANTOM, TRUE);
    llSensorRepeat( "", llGetOwner(), AGENT, 100, PI, 0.5 );
}

default
{
    state_entry()
    {
        startup();
    }
    on_rez(integer param)
    {
        startup();
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "follow")
            llWhisper(0, "follow");

                llSensorRepeat( "", id, AGENT, 100, PI, 0.5 );

    }

    no_sensor()
    {
        llSetTimerEvent(30); // the ride may be lost, die in 30 seconds
    }
    timer(){
        llWhisper(0, "cannot find target ");
        //llDie();
    } // haven't detected the user for 30 seconds, so delete the ride
    sensor( integer total_number )
    {
        vector vTarget = llDetectedPos(0);
        rotation rTarget = llDetectedRot(0);
        vector vOffset = <-2.0,0,-0.2>; // offset from the agent's center

        vector vDestination = vTarget + vOffset * rTarget;
//        llLookAt(vDestination, 2, 0.2);

        llMoveToTarget(vDestination, .1);

        llLookAt(vTarget + <0,0,vOffset.z>, 1, 0.1);

//        llMoveToTarget(vTarget, 0.5);
        llSetTimerEvent(0.0); // turns off the die timer
    }
    touch_start(integer total_number)
    {
        llSay(0, "stopit.");
    }
}

speak (script)

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "speak")
            llWhisper(0, "bark");
    }
}

COMMAND (script)

// this script is untested

// COMMAND script (output)
// If chosen action is to command other pets to do an action
// this script replaces constants with values, and sends it out.

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (llGetSubString(str, 0, 6) == "command")
        {
//            llWhisper(0, "outgoing command is " + str);
            integer wordBegin;
            wordBegin = llSubStringIndex(str, "|owner");
            if (wordBegin != -1)
            {
                str = llDeleteSubString(str, wordBegin+1, wordBegin+5);
                str = llInsertString(str, wordBegin+1, (string)llGetOwner());
//                llWhisper(0, "modified command is " + str);
            }

            wordBegin = llSubStringIndex(str, "|mykey");
            if (wordBegin != -1)
            {
                str = llDeleteSubString(str, wordBegin+1, wordBegin+5);
                str = llInsertString(str, wordBegin+1, (string)llGetKey());
//                llWhisper(0, "modified command is " + str);
            }
            llWhisper(9000, str); // whisper the command on channel 9000.
            //***to do*** this channel needs to be picked more intelligently
        }
    }
}


SNTP

SecondNet Transmissions Protocol (SNTP)

Version 0.1

Overview

SNTP is a communications standard for interobject and intersim communication throughout Second Life.
It is designed to be similar to TCP but simpler for a few reasons;
- Packets do not have error correction as chat messages in SL always come out clean and email uses TCP so there is already error correction
- DNS is not expected to be implemented until at least Version 2.0
- There are no IP classes. When a host requests an IP address from the router it's connected to, the host gets what it is given. This makes NAT impossible without using improper IP addresses or using the 255.0.0.0 section which is reserved for the 255th sim to come online.

Architecture

SNTP is designed to be easy to add new hosts to the network and even easier for objects to communicate.

One of the parts of SecondNet that breaks the rules is that it has a central computer (The RGH Server, at IP 0.0.0.0).
The Rapid Growth Handler (RGH) server retains a table that the BoneLink servers of each sim can reference between the first octet of an IP address and the key of the BoneLink server that the remote host is connected to.

BoneLink servers
A BoneLink server is essentially that sim's connection to the rest of SecondNet.
Just for example lets say that the sim Kyle has a BoneLink server with the IP address 23.0.0.0
If that BoneLink server goes down then Kyle becomes isolated from the rest of SecondNet until the BoneLink server comes back up. All hosts with IP Address 23.*.*.* can talk to other 23.*.*.* hosts and all other hosts outside of 23.*.*.* can talk to hosts outside of 23.*.*.*. This is because the BoneLink server is the only connection to the rest of SL unless you have a backup connection through another sim. BoneLink servers are the only type of SecondNet device that isn't open source.

Routers
A router diverts one single connection to all the connections below it.


Constants

Constants are pre-defined values that do not change--they're constant: TRUE will always equal 1. However, this does not mean that values should be used instead; while there's no technical reason not to, constants exist to simplify your code and make it easier to understand. It's good coding practice to use them.

This page is for general constants; function-specific ones should be documented on the functions page.
You can find an almost complete list of constants & values on LexFile.

float

PI | 3.1415926535897932384626433832795 (pi)
TWO_PI | 6.283185307179586476925286766559 (pi * 2)
PI_BY_TWO | 1.5707963267948966192313216916398 (pi / 2)
DEG_TO_RAD | To convert from degrees to radians (multiply, example: radian = 90 * DEG_TO_RAD;)
RAD_TO_DEG | To convert to radians from degrees (multiply, example: degree = PI_BY_TWO * RAD_TO_DEG;)
SQRT2 | 1.4142135623730950488016887242097 (square root of 2)

integer

TRUE | 1, an integer constant for boolean operations.
FALSE | 0, an integer constant for boolean operations.
DEBUG_CHANNEL | 2147483647, a special chat channel used to print to the Script Error Window.

string

NULL_KEY Indicates an empty key: "00000000-0000-0000-0000-000000000000". This constant is typed as a string not as a key.

EOF, "/n/n/n", Indicates the last line (or a line past this) of a notecard was read (returned in the dataserver event), or that the notecard contained embedded objects.

Substring | Replaced With
\t | four spaces
\n | new line
\" | double quote
\\ | backslash

rotation

ZERO_ROTATION <0.0, 0.0, 0.0, 1.0> (Note that ZERO_ROTATION is not <0.0, 0.0, 0.0, 0.0>! This is important.)

vector

ZERO_VECTOR <0.0, 0.0, 0.0>

object | type
AGENT | 1
ACTIVE | 2
PASSIVE | 4
SCRIPTED | 8

For more info see object types.

Also, see annoyances for a partial reason for explicitly specifying floating-point constants in the vector and rotation definitions. Using integers can cause logical-looking calculations in assignments to fail in ways that can be difficult to notice at first due to the way LSL parses numbers and handles inline math operations using the fastest data type.

Functions | Events | Types


ACTIVE

ACTIVE is a bitflag constant used in the llDetectedType, llSensor, and llSensorRepeat functions, and sensor event, with a value of 2. An "active" object either has a running script in its inventory or is currently being affected by physical forces (i.e. it is in motion).

In my testing, the ACTIVE flag seems unrelated to the presence of a script. The SCRIPTED constant seems to work as one might expect. -ReadyJack
Most scripts aren't constantly processing, so this would be true. They define running as executing commands, not as having an open listener, or events. -KeknehvPsaltery

Andrew Linden, 10/25/05: We used to distinquish between "active scripts" -- scripts that had timers or other events that needed to be polled on a regular basis such as timer() and listen(), and "inactive scripts" -- scripts that only had events that were triggered from outside stimuli such as touch() and collision(). (This is internal LL developer lingo which tends to leak out into our public documents.)

The old "inactive scripts" category is not reported now, so scripts that only toggle phantom or start animations on sit are now included in the "active script" total. As far as I know there are still two lists of scripts in the new scheduler, those that need to be polled and those that don't. The new scheduler is definitely more efficient than the old one.

"Active" can also refer to a user's active group.

Sensor | Constants | Object Type


Not found.


ALL_SIDES

A constant with the value of -1, used to effect all sides of the prim when passed to these functions:

llGetAlpha
llGetColor Returns "average" color on all sides of the prim.
llOffsetTexture
llRotateTexture
llScaleTexture
llSetAlpha
llSetColor
llSetLinkAlpha
llSetLinkColor
llSetPrimitiveParams
llSetTexture
llSetTextureAnim

See side for individual side numbers.

Constants | Side


DEBUG_CHANNEL
DEBUG_CHANNEL is an integer constant that, when passed to llSay, llWhisper, or llShout as a channel parameter, will print text to the Script Warning/Error Window. It has the value '2147483647'.

The Script Warning/Error Window can be viewed by either clicking the script alert symbol over an object containing a script printing to the window, or by clicking "Show Script Warning/Error Window" in the Tools menu of the SecondLife client.

Example Usage:
default {
    state_entry() {
        llSay(DEBUG_CHANNEL, "This is some debug text");
    }
}

This can be used to with a simple flag to turn on specific debug messages in your scripts.

Expanded Example Usage:
integer DEBUG = TRUE;

debugPrint(string message)
{
    if (DEBUG == TRUE)
    {
        llSay(DEBUG_CHANNEL, message);
    }
}

default
{
    state_entry()
    {
        debugPrint("state_entry called.");
    }
}

Constants


DEG_TO_RAD

float DEG_TO_RAD

A constant used to convert from degrees to radians through multiplication.
The constant amounts to PI / 180.

Examples:
float radians = degrees * DEG_TO_RAD;

vector euler_rot = <45, 90, 0> * DEG_TO_RAD;

Compare with RAD_TO_DEG.

Constants | Rotations


EOF

string EOF

This is a constant passed to the dataserver event, when any lines past the end of a notecard are read. Notecards are read using llGetNotecardLine.

EOF is a string that contains three newline ("\n") characters.

integer position;
string notecard = "notecard_name"
key query;

default
{
    state_entry()
    {
        if(llGetInventoryType(notecard) == INVENTORY_NOTECARD)
            query = llGetNotecardLine(notecard, position);
        else
            llOwnerSay(notecard + " is not a notecard in the object's inventory.");
    }
    dataserver(key request, string data)
    {
        if(query == request)
        {
            if(data == EOF)
            {
                //End of notecard!
            }
            else
            {
                //Do something with the data
                //Increase the position variable and
                ++position;
                //Continue reading the next lines.
                query = llGetNotecardLine(notecard, position);
            }
        }
    }
}
Constants | dataserver | llGetNotecardLine


FALSE

An integer constant for boolean comparisons. Has the value '0'.

Constant names should be used in place of actual values to make code more readable and ensure that scripts continue to work should the values change.

See also TRUE.
Constants


Loop

A loop is a type of flow control statement that repeats a code block when a condition is satisfied.

Flow Control


NULL_KEY

NULL_KEY is a constant indicating an empty (null) key: "000000-0000-0000-0000-000000000000".

NULL_KEY should always be used instead of an empty string. Even though the latter works for most LSL functions, it is bad style and can cause hard-to-track-down bugs in your script:

key foo;

foo = "";
if (foo == NULL_KEY) llSay(0, "This is never true");

This is because the empty string "" in a key is a different value from NULL_KEY.

Constant | Hacks


PASSIVE

integer PASSIVE

PASSIVE is a bitflag constant used in llDetectedType, llSensor and sensor. A 'passive' object is one that is not SCRIPTED and either not physical or is at rest if it is physical.

I haven't seen an at rest physical object return PASSIVE. At least when using llDetectedType. Any one have more information on the various interactions that affect the type returned?

Constants / Object Type / sensor / llDetectedType / llSensor


PERMISSION_CHANGE_LINKS

integer PERMISSION_CHANGE_LINKS

This constant represents a permission used with the llRequestPermissions function, to specify whether or not the script has permission to link with other objects and to delink prims from its linkset.

PERMISSION_CHANGED_LINKS can only be granted successfuly by the object owner and must be requested by a script in the object's parent/root prim. When the function is called, a dialog will pop up asking the owner if they wish for the object to be able to link/unlink prims/objects. (Note that the script does not wait for the dialog to be answered. Use run_time_permissions to be alerted when permissions have been changed.) If the user grants the permission, then the script will be able to link the object containing it to other prims/objects (of the same owner) using llCreateLink, or unlink itself (or other prims linked to it) using llBreakLink or llBreakAllLinks.

Constants | Link | Permissions


PERMISSION_DEBIT

integer PERMISSION_DEBIT

This permission is used with the llRequestPermissions function and can only be requested of the object owner. When the function is called, a dialog will pop up asking the owner if they wish for this object to be able to give away money. If the permission is granted then the script will be able to give away the owner's money via the llGiveMoney function.

Q: Does the owner have to be in the same sim to grant this permission?
A: No. After a few tests, PERMISSON_DEBIT seems to have no range limit and can be used on avatars in other sims, though it has a bit of a delay if you are not in the same sim.

Constants | llRequestPermissions | run_time_permissions | llGiveMoney


PI

float PI

3.1415926535897932384626433832795...

1/2 of a circle's circumference in radians (1 radius in radians.)

Constants | PI_BY_TWO | TWO_PI


PI_BY_TWO

float PI_BY_TWO

1.570796326794896619231321691639 (PI / 2)

Constants / TWO_PI


PUBLIC_CHANNEL
PUBLIC_CHANNEL is an integer constant that, when passed to llSay, llWhisper, or llShout as a channel parameter, will print text to the publicly heard chat channel. Has the value '0'.

Example Usage:
default {
    state_entry() {
        llSay(PUBLIC_CHANNEL, "Hello to everyone around me!");
    }
}

Chat | Constants | DEBUG_CHANNEL


RAD_TO_DEG

float RAD_TO_DEG

A constant used to convert from radians to degrees through multiplication.
The constant amounts to 180 / PI.

Examples:
float degrees = radians * RAD_TO_DEG;

vector euler_rot = <PI, PI, 0> * RAD_TO_DEG;

Compare with DEG_TO_RAD.

Constants | Rotations


rotate

See rotation.


Scale

Scale is referencing measurement of one thing against measurement of another thing. The relationship between the 2 things is scale. If the scale of an object is off, it will look too big or small in relation to surrounding objects. Keeping object scales consistent will reduce disorientation and awkwardness while moving among them. Many SL avatars tend to be way too tall (6.5'+). -That seems to be a personal comment there, since people (like myself) are actually over 6'5" in RL :)

Scale, represented as a vector, is another way of saying "stretch" (as in the editor window), "size", or "dimensions", at least in the context of SL.

Functions

Function | Description
llGetPrimitiveParams | get prim scale (among other things)
llSetPrimitiveParams | set prim scale (among other things)
llGetScale | get prim scale
llSetScale | set prim scale
llGetTextureScale | get texture scale
llParticleSystem | set particle start/end scale
llScaleTexture | scale texture
llSetTextureAnim | set texture animation scale (among other things)

Events

changed - can detect when scale (among other things) is changed

Transform


SCRIPTED

integer SCRIPTED

SCRIPTED is a bitflag constant used in the llDetectedType and llSensor functions, as well as the sensor event. A 'scripted' object is one that has at least one currently running script on it.

Object Type / sensor / constants / llDetectedType


SQRT2

float SQRT2

Constant 1.4142135623730950488016887242097.

The square root of 2. The length ratio of the hypotenuse to a side in a right isosceles triangle.

Constants


STATUS_PHYSICS

integer STATUS_PHYSICS

Constant for llGetStatus and llSetStatus.

Enables/disables physics (physical attribute) simulation for a prim/object.

physics | physical


TRUE
An integer constant for boolean comparisons. Has the value '1'.

Constant names should be used in place of actual values to make code more readable and ensure that scripts continue to work should the values change.

See also FALSE.

Constants


TWO_PI

float TWO_PI

2 * PI. Often used because TWO_PI radians describe a full circle (360 degrees).

Constants / PI_BY_TWO / Rotation


ZERO_ROTATION

rotation ZERO_ROTATION

A constant describing the rotation <0.0, 0.0, 0.0, 1.0>. (<0.0, 0.0, 0.0> in Euler representation.)

Note: this is not <0.0, 0.0, 0.0, 0.0>.

Constants | Rotation


ZERO_VECTOR

vector ZERO_VECTOR

The vector <0.0, 0.0, 0.0>.

Q: Is there any reason to use <0.0, 0.0, 0.0> over "<0,0,0>"?
A: Either will work. Integers are implicitly cast to floats by the compiler (which will cost you 2 bytes per float -- if you're watching memory, this is an easy fix that could save you many bytes). But really all you need is a zero and a decimal to indicate a float.

Constants


Events

LSL is an event-driven language. Literally, scripts are divided into blocks of code that are triggered when a certain event happens. Scripts can provide event handlers, like moving_end or touch_start. If a script provides an event handler, and an event of this type occurs, the simulator will add this event to the script's event queue.

The script's event handlers will then be called in the order the events are queued in (FIFO). According to Cory Linden, the event queue can hold up to 64 events. If the event queue is filled, it will silently drop any new events. Remember: LSL is not multi-threaded. Only one event will be run at a time; events cannot interupt each other. There is no way to interact with the event queue directly.

Changing states clears the event queue. Different states can have different event handlers. Be wary of bugs though. For instance, when an event handler is called, all of the arguments for that event handler are passed by value, and thus placed on the script's stack. If a script has insufficient memory to hold all of the arguments, the script is halted with a "stack/heap collision" error and no code in the event handler will run.

Unlike functions, it's not possible for scripters to create our own custom event handlers. If you need to check whether or not something has occurred, and it's not covered by an existing event handler, you'll need to use the timer() event.

The function llMinEventDelay may be of some use in coordinating events.

Event | Triggered
at_rot_target | when a rotational target set with llRotTarget is reached
at_target | when a target set with llTarget is reached
attach | when an object attaches or detaches from agent
changed | when certain aspects of the object are changed (inventory, color, shape, scale, texture, link, ownership)
collision | while task is colliding with another task
collision_end | when task stops colliding with another task, or stops being penetrated while llVolumeDetect is set
collision_start | when task starts colliding with another task, or starts being penetrated while llVolumeDetect is set
control | when one of the controls taken with llTakeControls is pressed/held/released
dataserver | when task receives asynchronous data
email | when task receives email
http_response | when task receives response to request made with llHTTPRequest
land_collision | while task is colliding with land
land_collision_end | when task stops colliding with land
land_collision_start | when task starts colliding with land
link_message | when task receives a link message sent via llMessageLinked
listen | when task is within hearing range of chat matching the critera set in llListen
money | when money is given to task
moving_end | when task stops moving or when task exits a sim.
moving_start | when task begins moving or when task enters a sim.
no_sensor | when a call to llSensor or llSensorRepeat results in no targets sensed
not_at_rot_target | when a rotational target set with llRotTarget is not yet reached
not_at_target | when a target set with llTarget is not yet reached
object_rez | when task rezzes another task using llRezObject
on_rez | when task is rezzed (from inventory or another task, includes attaching directly from inventory)
remote_data | any kind of XML-RPC communication
run_time_permissions | when an agent grants run-time permissions to task that were requested via llRequestPermissions
sensor | Result of llSensor or llSensorRepeat functions
state_entry | on any transition into the state and on startup
state_exit | on any state transition out of the state
timer | in intervals set by llSetTimerEvent
touch | while agent clicks on task
touch_start | when agent starts clicking on task
touch_end | when agent stops clicking on task

Q: Wait, what's a "task"?
A: In LSL parlance, a task is an avatar/agent or an object. Both can collide with an object, though only objects can contain scripts. In the above list, you can generally take "task" to mean "the object containing the script".

Q: How do I get input from one event and act on that in another?
A: Use a global variable to store and recall the data. Global variables can be accessed and redefined from any event or state.

Q: How do I know which event is going to be triggered, and in what order? This is complicated!
A: It is, but you can get the hang of it pretty quickly. Say you have state_entry, on_rez and attach events in a script, and you attach the object from inventory. What's going to happen? Well, state_entry will already have triggered when the script first ran, so it's not that. on_rez always executes first, even if you attach it, so that's what will happen first. attach will happen afterwards, unless on_rez contains a state change, or something else to interrupt the state, such as llResetScript or llDie. See: Example Event Order.

Q: Okay, but what about other events? How do I know when they'll be triggered?
A: As soon as something happens to trigger them, in the order that the event occurs in. So if you click on an object, you'll see touch_start, touch and touch_end, in that order. If the listen event is triggered while you're still touching the object, you'll get that after touch. It can be difficult to structure a script, if you need some input from a listen event, and some from touch_start, but you can use global variables to hold data between events.

Q: What's the different between an "event" and an "event handler"? I've seen both terms used, but they seem to both apply to the same thing.
A: Properly, an "event" is the thing that happens and an "event handler" is the thing that handles it. In practice, most scripters use the terms interchangably, or favour "event" over "event handler". This is technically incorrect, but is very common usage.

Q: Can events interrupt each other?
A: No, events are run to their completion, in the order that they were received. For example, if you have a while(TRUE); statement (a statement that never finishes executing) in your state_entry event, and someone touches the object (triggering a touch_start event) the touch_start event will never run, because the state_entry event never finishes.

Q: Why is there no event handler for "Release Keys" button?
A: Scratch what I said a moment ago. You meant the "Release Keys" button. I think the run_time_permissions event is triggered when permissions change, so just check the "perm" against PERMISSION_TAKE_CONTROLS to make sure if you still have it or not. - Aak
A2: That isn't quite correct, run_time_permissions is not called when permissions are released. If you want to know if permissions have been released you need to call the llGetPermissions - BW

Functions | Constants


at_rot_target

at_rot_target(integer tnum, rotation targetrot, rotation ourrot)

This event is raised when the scripted object comes within a defined angle of the target rotation targetrot (as defined by the llRotTarget library call).

The handle from llRotTarget is supplied in tnum and the current rotation in ourrot.

Compare with the not_at_rot_target() event.

Events | Rotation | Target


at_target

at_target(integer tnum, vector targetpos, vector ourpos)

This event handler is triggered when the scripted object comes within a defined range of the target position targetpos (defined by the llTarget function call). See also the not_at_target() event.

The tnum parameter is the handle of the target that was reached (as returned by llTarget); examine this value when there are multiple targets active. The targetpos parameter gives the location of the target. The ourpos parameter gives the object's current position, which can be anywhere within the range specified in the original call to llTarget.

The following example demonstrates the use of llTarget and the associated event handlers. Note that while this example uses llMoveToTarget and llStopMoveToTarget, there is no requirement to use these functions; anything that moves the object to within range of the target will trigger the at_target event.

integer targetID;

default
{
    touch_start( integer total_number )
    {
        // Become a physical object
        llSetStatus(STATUS_PHYSICS, TRUE);

        llWhisper( 0, "we're on our way" );

        // destination is ten meters above our current position
        vector destination = llGetPos() + <0, 0, 10>;

        // how close is close enough
        float range = 0.5;

        // Ask to be informed whether we're there or not
        targetID = llTarget( destination, range );

        // Start moving towards the destination
        llMoveToTarget( destination, 10 );
    }

    not_at_target()
    {
        // We're not there yet.
        llWhisper(0, "Still going");
    }

    at_target( integer number, vector targetpos, vector ourpos )
    {
        llSay(0, "We've arrived!");

        // Stop notifications of being there or not
        llTargetRemove(targetID);

        // Stop moving towards the destination
        llStopMoveToTarget();

        // Become non-physical
        llSetStatus(STATUS_PHYSICS, FALSE);
    }
}

Events


attach

attach(key id)

This event handler is triggered whenever the object is attached (which makes it an attachment) or detached from the avatar. If it is attached, id contains the key of the avatar it is attached to, otherwise id contains NULL_KEY.

Example:
default
{
    attach(key attached)
    {
        if (attached != NULL_KEY)   // object has been //attached//
        {
            llWhisper( 0, "I'm stuck on you, " + llKey2Name(attached) );
            // etc.
        }
        else  // object has been detached
        {
            llWhisper( 0, "Why hast thou forsaken me?" );
            // etc.
        }
    }
}

Note: if both on_rez and attach are used, on_rez will always be triggered first.

Note: when the object is detached to inventory, it will be given a limited amount of CPU cycles to run its attach event before it is derezed. There is no guarantee that all of the code within the attach event will complete before the derez occurs. In particular, expensive operations such as llSetPos may cause the script to be descheduled early, preventing timely execution of subsequent code.

Does not fire in child prims, only in root of linked objects.

Use the llAttachToAvatar, llDetachFromAvatar and llGetAttached functions to play with attachments.

Events | Agent/Avatar | Attachment


changed

changed(integer change)

The changed event is invoked when operations change certain properties of the prim/object. The change parameter is a bitfield of one or more of the following values:

Constant | Value | Indicates |
CHANGED_INVENTORY | 0x1 | changed object inventory (could include an item being added, removed, or renamed, or a notecard being edited and saved). | Details
CHANGED_COLOR | 0x2 | changed object color or transparency |
CHANGED_SHAPE | 0x4 | changed object shape (box to cylinder, for example), cut, hollow amount/shape, twist, top size, or shear |
CHANGED_SCALE | 0x8 | changed object scale. | Details
CHANGED_TEXTURE | 0x10 | changed object texture: offset, repeats, rotation, and reflection/bump maps (but not transparency -- that's CHANGED_COLOR) |
CHANGED_LINK | 0x20 | linking or delinking (also when an avatar sits on or unsits from the object). | Details
CHANGED_ALLOWED_DROP | 0x40 | An item was dropped (into this object's inventory) that was only allowed by the llAllowInventoryDrop function. This allows the object to identify items dropped by anyone who doesn't have modify permissions on the object. |
CHANGED_OWNER | 0x80 | The ownership of the object changed. This value is passed when an object is deeded to a group, when an object is purchased, or when a newly-purchased object is rezzed. | Details
CHANGED_REGION | 0x100 | The object changed regions/sims. | Details
CHANGED_TELEPORT | 0x200 | The object has been teleported. | Details

These values are bitwise and can be ORed or ANDed together.
Two flags can show up in the same event trigger; it has been seen when resizing an object with the "Stretch Textures" box unchecked so the texture scale (repeats) changes as the prim is resized. Also, the CHANGED_REGION and CHANGED_TELEPORT seem to come in tandem.

With this in mind, always write "if (change & CHANGED_LINK)" rather than "if (change == CHANGED_LINK)" so that the contents of your event will be triggered in all cases. Likewise, using IF/ELSEs (as in "else if (change & CHANGED_SCALE)") could theoretically result in only one of the triggers being counted. If you stick to with IF statements only, your changed event will need to perform a check for each IF statement, but it will catch all triggers in the very rare event of a simultaneous trigger.

Notes:
  • The change parameter describes only what type of change happened, not the difference between the original and changed versions of the object. If you need to determine exactly what changed, the script must store the relevant properties and compare the values from before and after the change.
  • Prim/object position and rotation are not currently detected when changed but can be via a timer and llGetRot and llGetPos. Position changes can also be detected by using the moving_start and moving_end events.
  • When an avatar sits on an object, they seem to become part of the linkset.
  • A changed() event is raised with CHANGED_LINK as the parameter when an avatar sits on an object or un-sits. To find out what happened, use llAvatarOnSitTarget which returns the NULL_KEY if no avatar is sitting on the object, or the key of the avatar that is sitting at the sit target.

Example:
// When an avatar sits on the object containing this script,
// the object will say "Get off!" and then force the avatar to stand up.
default {
    state_entry() {
        llSitTarget(<0, 0, 0.1>, ZERO_ROTATION); // needed for llAvatarOnSitTarget to work
    }

    changed(integer change) { // something changed
        if (change & CHANGED_LINK) { // and it was a link change
           // llSleep(0.5); // llUnSit works better with this delay
            key av = llAvatarOnSitTarget();
            if (av) { // somebody is sitting on me
                llSay(0, "Get off!"); // say in chat when person is remove from prim
                llUnSit(av); // unsit him
            }
        }
    }
}

Details

Constant or Value: changed() is triggered: changed() is not triggered:
CHANGED_INVENTORY
  • when adding any inventory item to the prim's inventory.
  • when deleting any inventory item from the prim's inventory.
  • when changing the name or description of an inventory item inside the prim.
  • when changing asset permissions of an inventory item within the prim.
  • when saving a notecard that already exists within the prim's inventory.
  • when recompiling a script that already exists in the prim's inventory.
  • when inventory is changed in another prim in the linked object.
  • when resetting a script.
  • when a no-copy item is removed (but not deleted!) from the prim by manually dragged back to user inventory.
  • when inventory is added using llAllowInventoryDrop, and the user dropping the new inventory item is not the owner of the prim.
CHANGED_SCALE
  • in a prim, linked or not, when resizing that prim.
  • in the root prim in a linked object when resizing the entire object.
  • in a different prim in a linked object when resizing a separate prim in that object.
  • in a child prim in a linked object when resizing the entire object.
CHANGED_LINK
  • when linking two (or more) prims together.
  • when delinking an object.
  • when delinking a prim from an object.
  • when an avatar sits on an object.
  • when a sitting avatar stands up from the object.
  • when duplicating a linked object.
  • When changing the prim type/shape of a prim in the object.
  • in an attachment when an avatar sits down or stands up.
  • in an attachment when it's attached.
  • in an attachment when another attachment is attached.
  • in an attachment when you sit on an object.
  • in an object when the sitting avatar attaches an object.
  • when duplicating a single prim.
CHANGED_OWNER
  • in the original object when a user buys the object.
  • in the original object when a user buys a copy of the object.
  • in the original object when a user takes the object or a copy of the object.
  • in the newly-rezzed copy when the user rezzes the object for the first time. (on_rez() is still triggered first.) This occurs whether or not the object was copied or purchased.
  • when an object is deeded to a group.
  • in the original object when the user buys the contents of an object. It is still triggered in the copy when the new owner rezzes it.
CHANGED_REGION
  • in the root prim of an attachment when the user teleports to a new sim/region or crosses into a new sim directly.
  • in the root prim of an object that moves into a new region.
  • in a child prim of an attachment when the user teleports to a new region.
  • in a child prim of an object that moves into a new region.
CHANGED_TELEPORT
  • in the root prim of an attachment when the user teleports to a new sim, or a location within the current sim. This happens whether they teleport manually, or using one of the teleport functions.
  • in a child prim of an attachment when the user teleports to a different location.
  • in any prim in an attachment when the user sits on an object that uses the llSitTarget "sit teleport" trick.
  • in any prim in an unattached object.
  • in an attachment, after teleporting to no-script land, not even being entered into the event queue. This means it will not be triggered after teleporting to a region where scripts are allowed, only the event for that teleport will be triggered.
(As of Second Life 1.16.0 (5) this is not accurate. Teleporting into no-script land will cause the event to trigger once you move onto script-enabled land, as though you had just teleported.)

Aside from the above, the changed() event is also not triggered:
  • when an object changes position. (Use moving_start() and moving_end().)
  • when an object changes rotation.
  • when changing the object's name or description.
  • when changing the object's group. (not deeding the object to a group -- that'll trigger CHANGED_OWNER)
  • when toggling "Share with group", "Allow anyone to move", "Allow anyone to copy", setting the object "For Sale", changing any of the sale settings, or setting "Next owner can:".
  • when toggling "Lock", "Physics", "Temporary" or "Phantom".
  • when setting prim material.
  • when using llSetText.
  • when using llParticleSystem.
  • in a worn attachment when the owner attaches or detaches it. (Use attach().)
  • in a worn attachment when the wearer sits on something.
  • when the object is selected/edited.

Q & A

Q: How do I find out when my object has changed position or rotation?
A: You can't, not by using changed(). The moving_start() and moving_end() event handlers are triggered when the object is moved. To find out when the object is rotated, your best option is to use a timer. (Unless, of course, your object is an attachment, in which case, it will never merely rotate, but will change position constantly, triggering moving_start() and moving_end().)

Q: Regarding bitwise operations: it says above that scale and textures "have been seen" changing at the same time. Does that mean that both CHANGED_TEXTURE and CHANGED_SCALE will always be triggered while scaling a texture with an object?
A: Not necessarily, it just means that they occasionally will be triggered together. You can't predict when this will happen, or even if it will. Make your code able to deal with all possibilities. Assumptions will often produce code that fails, with little indication as to why. As it says above, always write "if (change & CHANGED_LINK)" rather than "if (change == CHANGED_LINK)" so that the contents of your event will be triggered in all cases.

Q: Is there a way to detect when a user buys an object?
A: Yes. Specifying CHANGED_OWNER for change will let you filter the results of changed(). See the details section for specifics.
To detect when a user pays an object, you want the money() event handler.

Q: Is there a way to be notified via an event when a script is added or removed from the object's inventory?
A: No. Your only option is to use a timer to regularly compare the current inventory to a list of preexisting scripts.

Q: Can I use the detection functions (llDetected*) with changed()?
A: No, they only work with touches, collisons and sensors.

Q: Can I find out who (or what) caused the object to change?
A: No.

Events | Detection


collision

collision(integer num_detected)

Collisions raise this event when an object or avatar collides with the object the script is attached to. The number of detected objects is passed to the script in num_detected. Information on those objects may be gathered via the llDetected* functions. Collisions are also generated if an avatar bumps into or walks on top of an object.

The collision event is not raised when using llVolumeDetect, only collision_start and collision_end are.

Example:
default {
    collision(integer num_detected) {
        llSay(0, llDetectedName(0) + " is currently colliding with me!");
    }
}

collision() and Linked Objects
In a linked object, place a collision event in the root prim to affect the whole object, not just the root prim.
To prevent collisions on a specific child prim from being relayed to the parent, place an empty collision event in the child.
To pass collisions on to the parent from a child with a collision event, use llPassCollisions.

Compare with the collision_start and collision_end events.

Note:
Its important to understand that collision events are triggered when an object collides with the scripted object. Not when the scripted object collides with an object. So the script goes in for example a target and not in a projectile. Very useful of course.
Events | Collisions


collision_end

collision_end(integer num_detected)

This event handler is raised when another object or avatar stops colliding with the object the script is attached to. The number of detected objects is passed to the script in num_detected. Information on those objects may be gathered via the llDetected* library functions. Collisions are also generated if an avatar bumps into or walks on top of an object.

Example:
default {
    collision_end(integer num_detected) {
        llSay(0, llDetectedName(0) + " has stopped colliding with me!");
    }
}

collision_end() and Linked Objects:
In a linked object, place a collision_end event in the root prim to affect the whole object, not just the root prim.
To prevent collisions on a specific child prim from being relayed to the parent, place an empty collision_end event in the child.
To pass collisions on to the parent from a child with a collision_end event, use llPassCollisions.

Compare with collision and collision_start.

Events | Collisions


collision_start

collision_start(integer num_detected)

This event is raised when another object or avatar starts colliding with the object the script is attached to. The number of detected objects is passed to the script in num_detected. Information on those objects may be gathered via the llDetected* functions. Collisions are also generated if an avatar bumps into or walks on top of an object.

Example:
default {
    collision_start(integer num_detected) {
        llSay(0, llDetectedName(0) + " collided with me!");
    }
}

collision_start() and Linked Objects:
In a linked object, place a collision_start event in the root prim to affect the whole object, not just the root prim.
To prevent collisions on a specific child prim from being relayed to the parent, place an empty collision_start event in the child.
To pass collisions on to the parent from a child with a collision_start event, use llPassCollisions.

Compare with collision and collision_end.

Events | Collisions


control

control(key id, integer held, integer change)

Once a script has permission to take control inputs from the avatar with the key id (see llRequestPermissions), and has taken control of the desired set (see llTakeControls), this event will be used to pass the state of taken controls to the script.

The held parameter is a bitfield indicating which controls are held down. The change parameter is a bitfield indicating which controls changed their state since the last control event. The combination of these two parameters can be used to determine the various states of any taken controls. The easiest way to do this is by comparing the parameters to pre-defined control constants.

Here are the available control constants:

Constant name | Value | Hex Value | Description
CONTROL_FWD | 1 | 0x00000001 | Tests for avatar forward control
CONTROL_BACK | 2 | 0x00000002 | Tests for avatar back control
CONTROL_LEFT | 4 | 0x00000004 | Tests for avatar move left control
CONTROL_RIGHT | 8 | 0x00000008 | Tests for avatar move right control
CONTROL_UP | 16 | 0x00000010 | Tests for avatar up control
CONTROL_DOWN | 32 | 0x00000020 | Tests for avatar down control
? | 64 | 0x00000040 | ?
? | 128 | 0x00000080 | ?
CONTROL_ROT_LEFT | 256 | 0x00000100 | Tests for avatar rotate left control
CONTROL_ROT_RIGHT | 512 | 0x00000200 | Tests for avatar rotate right control
CONTROL_LBUTTON | 268435456 | 0x10000000 | Tests for avatar left button control
CONTROL_ML_LBUTTON | 1073741824 | 0x40000000 | Tests for avatar left button control with the avatar in mouselook

The held and change parameters can each have information on multiple controls in any received event. If a control is pressed down the corresponding bit (matching the control constant) in the held parameter will be set to 1. If the state of the control just changed from up to down or down to up, the corresponding bit in the change parameter will be set to 1.

Since we tend to think more of presses, holds and releases, it's useful to know how they relate. If the bit for a given control is unset (0) in both parameters then it hasn't been touched and it is up. If the bit in both the held and change parameters is set (1), then the control was just pressed. If held is set and change is not, then the button is being held down. As long as the button is held down additional control events will be generated with the control's held bit set. If the held bit is unset and the change bit is set, then the control was just released.

So if a '1' indicates the presence of the desired control constant in the parameters (across the top) for each control state (down the side) then:

 | held | change
button not held | 0 | 0
button press | 1 | 1
button held down | 1 | 0
button release | 0 | 1

Detecting Controls

There are a few ways you can test for controls in your code. Remembering your bitwise operators you'll note that & is the intersection. So only bits that are set on both sides of the operator will remain set in the result. This is handy for seeing if the same bit is set in two values. Or (the 'pipe' operator |) is the union. All bits set in both arguments are set in the result. This can be useful for combining control constants to check for combinations. Be careful though, checking for multiple bits is tricky (see below).

The following code looks for an initial press of the forward button by and'ing held and change (if it's held and changed that's a press) and the desired control constant. Since only a single bit representing forward is set in the CONTROL_FWD constant, and'ing that constant with the two parameters will only result in a TRUE value if that bit is also set in both the held and change parameters (i.e. the button was pressed).

if (held & change & CONTROL_FWD) llOwnerSay("forward pressed");

This code looks for a button release:

if (~held & change & CONTROL_FWD) llOwnerSay("forward released");

The ~ operator is bitwise NOT, i.e. it flips all the bits from 0 to 1 or 1 to 0. If the CONTROL_FWD bit is set in ~held it was unset in the original held, which means it is up (not depressed).

This code looks for a button being held down (and will include the initial press since it's true then as well):

if (held & CONTROL_FWD) llOwnerSay("forward held");

An alternative style that might be easier for some to understand maps more directly to the control state table above and looks like so:

if ((held & CONTROL_LBUTTON) && (change & CONTROL_LBUTTON)) llOwnerSay("mouse press");
if ((~held & CONTROL_LBUTTON) && (change & CONTROL_LBUTTON)) llOwnerSay("mouse release");

If there's a 0 in the row you want for one of the parameters put a tilde ~ in front of it in the if statement. If it's a 1, leave it normal.

Handling Multiple Controls

A single callback to this function can contain information on the status of multiple controls so they can be combined for more detailed actions. This is tricky because you may want to insure that all the bits you're looking for are set if you're looking for a "combo" move of more than one key. A statement like the following won't work as expected:

if (held & change & (CONTROL_FWD | CONTROL_LEFT)) llOwnerSay("forward and/or left"); // Might not be what you want!

The problem is a case where only one of the specified controls is pressed, the forward button for example. Looking at the table at the top, forward has a value of 1. Left has a value of 4 (100 binary). So CONTROL_FWD | CONTROL_LEFT has a value of 5 (101 binary). If the forward key is pressed alone, both held and change will have a value of 1. In this case 1 & 1 & 101 evaluates to 1 because the first bit (1) is set in all 3 arguments and since one is TRUE the combo will be detected.

This is fine if you are looking for any combination of the specified controls, but if you want to make sure that all the desired controls are pressed, your code needs to work like the following:

integer secret_style = CONTROL_DOWN | CONTROL_UP | CONTROL_FWD;

if ((held & change & secret_style) == secret_style) {
    llOwnerSay("five point palm exploding heart technique");
}

This will only evaluate to TRUE if all the bits set in secret_style are present in both held and change. This method allows additional keys
beyond the ones you are looking for to be pressed and the combo will still be detected. If you want to be even more strict use code like this:

if ((held & change) == secret_style) llOwnerSay("very strict");

This will only evaluate to TRUE if exactly the specified controls are pressed and no others.

Asking someone to make sure they press 3 keys at the same instant to get them in one control event may be a bit much. An example that will make sure at least one was pressed and the correct 3 are down looks like:

if ((held & change) && (held == secret_style)) llOwnerSay("ouch");

This is saying, "If any key has been pressed and the keys that are down are exactly the right ones, then cause some pain."

Parameter Conversion

Dealing with held and change can be hard to keep track of in complex scripts. If you'd rather use the ideas of pressed, down and released you can use code like this to figure out all the logic for you:

control(key id, integer held, integer change) {
        integer pressed = held & change;
        integer down = held & ~change;
        integer released = ~held & change;
        integer inactive = ~held & ~change;

        if (pressed & CONTROL_LBUTTON) llOwnerSay("click");
    }

Keep in mind that down will not contain keys that have just been pressed even though they are "down". If you don't want that just use held.

Mouselook Mode vs. Normal Mode

In mouselook, the default movement keys act a bit different compared to "normal" mode. In ML mode the left mouse button triggers CONTROL_ML_LBUTTON instead of CONTROL_LBUTTON. The left/right keys are now used for strafing, so instead of CONTROL_ROT_LEFT and CONTROL_ROT_RIGHT (for turning), CONTROL_LEFT and CONTROL_RIGHT are triggered.

In "normal" mode CONTROL_LEFT and CONTROL_RIGHT can be triggered by holding down shift and using the left/right keys.

Example

default {
    state_entry() {
        llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS); // get permission to take controls
    }

    run_time_permissions(integer perm) { // permissions dialog answered
        if (perm & PERMISSION_TAKE_CONTROLS) { // we got a yes
            llTakeControls(CONTROL_UP | CONTROL_DOWN, TRUE, FALSE); // take up and down controls
        }
    }

    control(key id, integer held, integer change) { // something happened to one of our controls
        if (held & CONTROL_UP) { // the "fly up" key is held
            llSetPos(llGetPos() + <0, 0, 0.25>); // move up
        } else if (change & held & CONTROL_DOWN) { // the "fly down" key was pressed
            llSetPos(llGetPos() + <0, 0, -0.25>); // move down
        }
    }
}

Q: What are the keyboard keys for CONTROL_LEFT and CONTROL_RIGHT? Do they exist?
A: If you hold down the shift key and use left or right when NOT in mouselook, or if you move left or right in mouselook. (Shift-A/D when not in mouselook, A/D in mouselook)
Events | Agent/Avatar | Mouselook


dataserver

dataserver(key queryid, string data)

This event handler is triggered when the requested data is returned to the script. Data may be requested using the llGetNotecardLine, llGetNumberOfNotecardLines, llRequestAgentData, llRequestInventoryData, and llRequestSimulatorData functions.

The queryid parameter is the key returned by any of these functions. The data parameter is the requested information (see the descriptions of the function calls for information on how to interpret it). The dataserver event is raised for all scripts in the prim. This is why it is necessary to check the query id.

Dataserver answers aren't necessarily coming in the order they were requested in. So, if there are multiple pending requests, always use the queryid key to determine which answer is being received. If only one request is processed at a time, and only one script is in the prim that uses the dataserver event, this problem doesn't arise.

Note: Dataserver requests will trigger all dataserver() events in all scripts within the same prim where the request was made. dataserver() events will not be triggered in scripts contained in other prims in the same linked object. This is the same regardless of whether the prim in question is the parent/root prim, or a child.

Remember: Erroneous requests silently fail and never cause a dataserver() event to occur (e.g. if a notecard line is requested, but a key is supplied that isn't pointing to a notecard). A timer could catch these silent failures by checking to see if no dataserver() event has occured after a defined time.

The dataserver is rather slow, so keep this in mind when using the aforementioned functions.

Functions:

Function | Description
llGetNotecardLine | Requests the content of a given line in a notecard.
llGetNumberOfNotecardLines | Requests the total number of lines in a notecard.
llRequestAgentData | Requests an agent's online status, name, account creation date, or rating.
llRequestInventoryData | Currently limited to getting a position from a landmark.
llRequestSimulatorData | Requests status, position, and rating (PG/Mature) of a sim.

Example:
key request;

default
{
    state_entry()
    {
        request = llRequestAgentData(llGetOwner(), DATA_BORN); // request your SL creation date
    }

    dataserver(key queryid, string data)
    {
        if( request == queryid )
            llSay(0, "You were born on " + data);
    }
}

Example of working with multiple requests: KriAyakashi
list avatars;
list requests;

default
{
   state_entry()
   {
       llSensor("", NULL_KEY, AGENT, 96.0, PI);
   }

   sensor(integer num_detected)
   {
      integer i;
      avatars = [];
      requests = [];
      for(i=0; i<num_detected; ++i)
      {
         key avatar = llDetectedKey(i);
         avatars  += [avatar]; // to use less memory use this hack: avatars = (avatars=[]) + avatars + [avatar];
         requests += [llRequestAgentData(avatar, DATA_BORN)]; // to use less memory use this hack: requests = (requests=[]) + requests + [llRequestAgentData(llDetectedKey(i), DATA_BORN)];
      }
   }

   dataserver(key queryid, string data)
   {
       integer idx = llListFindList(requests, [queryid]);
       avatar = llList2Key(avatars, idx); // as we added the requests in the same order as avatars
       llOwnerSay(llKey2Name(avatar)+" was born "+data);
   }
}

Functions | Events


email

email(string time, string address, string subj, string message, integer num_left)

Triggered when a request by llGetNextEmail is answered. Returned are the email's time, sender's address, subject in subj, the message itself and how many more emails are queued for this object in num_left.

time is an UNIX timestamp (integer) typecast as a string, similar to that returned by llGetUNIXTime.

address and subj are limited to 78 characters each.
message is limited to 1000 characters.

The message field is formatted as follows:
Object-Name: object_name\n
Region: region_name (region_corner _coordinates)\n
Local-Position: (local_pos)\n
\n
message_text

Where:
object_name is the name of the sending object. To get the objects key, look at the sender's address.
region_name is the name of sim the object is located in. This is as returned by llGetRegionName.
region_corner_coordinates is the region's Region Corner. This is in the format x,y with no z component. t is not enclosed in <>'s.
local_pos is the sending object's local position. It is in the format x,y,z . It is not enclosed in <>'s.
message_text is the actual text sent in the message string in llEmail.

To remove the headers attached to the beginning of the message, use this line:
message = llDeleteSubString(message, 0, llSubStringIndex(message, "\n\n") + 1);

Notes:
  • Emails outside these limits will not bounce, but are silently discarded instead.
  • Incoming emails are stripped of quoted text ("> bla"), any control characters and sigdashes ("-- \n"), as well as anything below them.
  • As of SL 1.5.8, llGetNextEmail now invokes no sleep penalty. Note: the rate at which emails can be downloaded is limited by the period between a successful llGetNextEmail call and the corresponding email event (from 1 to 3 seconds). There is no reason to poll faster than that, but there is no penalty if you do. For best results, call llGetNextEmail at some descheduled rate (every 5 seconds or longer), then call llGetNextEmail again at the end of the the email event if (and only if) there are more pending emails in the queue.
  • Don't think subject limit is true anymore. I've sent 120 characters on my last test -MaxCase
  • As of SL 1.6.6, the number of pending emails is 100 and bounce messages are received when a queue is full. -Splat1Edison
  • As of SL 1.6.10, if sent from external sources, the subject bounces at >78 characters and message at >1000 characters -AlondriaLeFay
  • Seems the statement about subj outbound character limitations needs to be investigated more before being stated so concisely. I passed up to 110 characters so far, each character over 78 taking slightly longer to be delivered. This concurs with -MaxCase's findings above. Version: SL 1.8.2 (7) Jan. 30, 2006 -SiRiSAsturias
  • As of SL 1.10.3 (4), the message limit of 1000 characters may have been relaxed? I was able to pass approx. 1100 characters from SL to an external email address. -BurnmanBedlam (this is incoming mail, not outgoing, which has a limit of 4096 characters for all parts)

Example:
default {
    state_entry() {
        llEmail((string)llGetKey() + "@lsl.secondlife.com", "Test!", "This is a test message."); // Send email to self.

        llSetTimerEvent(2.5); // Poll for emails. (Yes, that is a retarded way on an event-based system!)
    }

    timer() {
        llGetNextEmail("", ""); // Check for email with any sender address and subject.
    }

    email(string time, string address, string subj, string message, integer num_left) {
        llSay(0, "You got mail! " + (string)num_left + " more mails.");
        llSay(0, llList2CSV([time, address, subj, message]));
        if (num_left == 0) llSetTimerEvent(0.0); // Stop timer when there's no more email.
    }
}

To send an email, use llEmail.

Events | Communications


http_response

http_response(key request_id, integer status, list metadata, string body)

This event handler is invoked when an HTTP response is received for a pending llHTTPRequest request or if a pending request fails or times out.

request_id is set to the same value as the key returned by the llHTTPRequest which initiated the call.

status is set to an HTTP status code either returned in the HTTP response or detailing the error which caused the request to fail. 499 seems to indicate a failure to establish a TCP connection while 0 likely means that the connection was established but the HTTP reply was invalid.

metadata is filled with <key, value> pairs describing the response. Currently, the only key returned is HTTP_BODY_TRUNCATED with a value equal to the point at which the response was truncated in bytes.

body is set to the body of the HTTP response as long as the response includes a "Content-Type" header specifying a text mime type. If a mime type is not specified or the type is not a text type, body is set to "Unsupported or unknown Content-Type." If the "Content-Type" header specifies a character set, body will be set to the body of the response transcoded to UTF-8, or to "Unsupported or unknown character set." if the character set is unsupported. The body is currently limited to 2048 bytes.

Remember that "\n" in LSL is converted to a newline at compile time, and that your server's code may have a different character than \n for newlines. You should check your server's documentation for the proper method to add new lines, your millage may vary. - Ice

Simple script for reading data as it is presented from your web server:
//Ice Brodie's http text output script
//This script requests from our data source (URL) and echoes out what it sends to the owner of the script
//I use this to test my PHP apps personally, it's a simple debugging application so I release it publicly
//You may use, redistribute, modify, copy however you feel would be useful

string URL="http://www.secondlife.com/httprequest/homepage.php";
key http;//This stores the HTTP request we send.

default
{
    touch_start(integer foo)
    {
        http=llHTTPRequest(URL, [] ,"");
    }

    http_response(key id,integer status, list meta, string body)
    {
        if(http==id)
        {
            integer i;
            list lbody=llParseString2List(body,["\n"],[]);
            integer count=llGetListLength(lbody);
            //This turns newline characters char(10) into new lines in chat
            //These are Unix/LSL style end of lines, use \r\n in place of \n for
            //  Windows style end of line from web server.
            for(i=0;i<count;i++)
            {
                llOwnerSay(llList2String(lbody,i));
            }
        }
    }
}
Events | Communications


land_collision

land_collision(vector pos)

This event is raised while the object is colliding with the ground. pos contains the position (in region coordinates) where the collision occured.

Compare with land_collision_end and land_collision_start.

If a script with this event exists in the parent prim of a link set, and a child prim collides with the land, the parent prim's land_collision event will be triggered. However, if the child prim has a script (even one without a land_collision event) in it, land collisions will not be passed unless the child prim's script contains llPassCollisions(TRUE).

Events / Collisions


land_collision_end

land_collision_end(vector pos)

This event is raised when the object stops colliding with the ground. pos contains the position (in region coordinates) where the collision occured.

Compare with land_collision and land_collision_start.

Events / Collisions


land_collision_start

land_collision_start(vector pos)

This event is raised when the object begins to collide with the ground. pos contains the position (in region coordinates) where the collision occured.

Compare with land_collision and land_collision_end.

Example:
default {
    state_entry() {
        llSetStatus(STATUS_PHYSICS, TRUE); // start falling
    }

    land_collision_start(vector pos) {
        llSay(0, "Hit the ground at " + (string)pos);
    }
}

Events | Collisions


link_message

link_message(integer sender_num, integer num, string str, key id)

Triggered in a script when the containing prim receives a link message via llMessageLinked.

sender_num is the link number of the sending object in the set.
num, str, and id are arbitrary values passed to the llMessageLinked function.

Notes:
The link message queue seems to hold 64 messages. Anything past that is discarded.

Also see llGetLinkName and llGetLinkNumber for related functions.

For an example see ExampleLinkMessage.

Events | Link | Communications


listen

listen(integer channel, string name, key id, string message)

The listen() event handler is invoked whenever a chat message matching the constraints passed in the llListen function is heard. The channel the chat was picked up on, the name and id of the speaker and the message are passed.

Channel 0 is the public chat channel all users see as chat text. Channels 1 ? 2,147,483,648, and the negative of that entire range, are private channels that aren?t sent to the SL client, but that other scripts can listen for.

If you want your listen() event handler to be invoked for all messages on the specified channel, don't specify any constraints when you call llListen. In the following example, the listen() event handler examines the id parameter to see if the speaker is the object's owner:

Example 1:
default
{
    state_entry()
    {
        llListen( 0, "", NULL_KEY, "" );
    }

    listen( integer channel, string name, key id, string message )
    {
        if ( id == llGetOwner() )
        {
            llWhisper( 0, "I hear and obey, master." );
            // interpret message
        }
        else
        {
            llWhisper( 0, "You're not the boss of me." );
            // etc.
        }
    }
}

Your listen() event handler can also examine the message parameter to check for a specific command. The following example checks just for a command, no matter who is speaking:

Example 2:
listen( integer channel, string name, key id, string message )
{
    if ( message == "open sesame" )
    {
        llWhisper( 0, "Welcome!" );
        // open door
    }
    else
    {
        llWhisper( 0, "That's not the magic word." );
        // etc.
    }
}

If you want to completely ignore messages that don't meet your constraints, you should specify the constraints in the initial call to llListen, and your listen() event handler won't be invoked to handle undesired messages.

In fact, you should try and constrain all of your llListen() commands as much as possible. Open listens on channel 0 can affect both script and sim performance, particularly in busy areas, since they will trigger the listen() event for each and every line of speech in regular chat.

For example, a much less laggy way to do example 1:
(Example 3)
default
{
    state_entry()
    {
        llListen( 0, "", llGetOwner(), "" );
    }

    listen( integer channel, string name, key id, string message )
    {
        llWhisper( 0, "I hear and obey, master." );

    }
}

Note that a script can listen on more than one channel at once:
(Example 4)
default
{
    state_entry()
    {
        llListen(0,"",llGetOwner(),"");
        llListen(1,"",llGetOwner(),"");
    }

    listen(integer channel, string name, key id, string message)
    {
        if (channel == 0)
        {
            llSay(0, "Channel 0.");
        }

        else if (channel == 1)
        {
            llSay(0, "Channel 1.");
        }
    }
}

However, try to keep the number of calls to llListen to a minimum to reduce lag.

While it's valid to register multiple listeners on different channels, you cannot set multiple listeners on the same channel, even if you specify different filters for them. The one last set will work, overwriting any previous ones on the same channel.

Events | Chat | Communications


money

money(key id, integer amount)

This event is triggered when the agent with the key id gives amount of Linden Dollars to the object.

The existence of a money event (even an empty one) in the current state enables the "Pay" entry in the PieMenu when right-clicking the object and allows users to pay the object's owner. Note that this gives you control of when the "Pay" entry is shown, by switching between states with and without a money event handler.

The amount is chosen via an input field by the giver, or by the amounts specified in the "Fast Pay" buttons (to customize the values of these, use llSetPayPrice).

If the object is deeded to a group, the group receives the money.

Example:
default {
    money(key giver, integer amount) {
        llSay(0, "Thanks for the " + (string)amount + "L$, " + llKey2Name(giver));
    }
}

For an additional example, see ExampleMoney.

Notes:
  • Under some circumstances, a linked object may display the "Pay" option in the pie menu for other prims, even when the script containing the money event has moved to a different state. This is an old bug, but it's not clear on how to predict when this will happen. To avoid this sort of problem, use a single state in the payment script and simply refund the money when the object would otherwise have switched into another state. (See this thread for more.)
  • Money cannot be paid to an attachment; while there is a "Pay" option on the pie menu, paying money to an attachment will not cause the money() event to be triggered.

Q: Someone said they paid my object, but they didn't get anything. I see the transaction in my account's transaction history, though. What gives?
A: Under high database or sim load, there appears to be a bug where the money event is not triggered. The Lindens are aware of the problem, but there's not yet any word on a fix. In the meantime, it can help to add a list to your script containing a log of users (using llDetectedName) to pay your object money (from the script's perspective, at least, meaning the money event was triggered) and the time at which they paid. Then, if you receive a complaint, you can easily check against your log to see if the customer is telling the truth.

Q: Is there a way to find out when money is paid into a user's account?
A: No, the money() event can only be triggered when money is paid directly to the object containing the money() event. There is no way for an LSL script to check a user's money, nor be triggered when it changes.
In theory, one could run a script on an external server that parsed the contents of the SL Account page on the SL website, then coordinated payment data with scripts in SL. This would naturally depend on several factors, such as the rate at which the SL Account page is actually updated, the amount of transactions from different sources you're likely to have, and of course, how much the Lindens do or do not want your bot to hammer their server.

To give money via a script, use the llGiveMoney function.

Events | LindenDollar


moving_end()

This event is triggered when an object (physical or nonphysical) with this script stops moving (and changes position, including when attached) or enters a sim.

Compare with moving_start.

Example:
vector start;

default {
    moving_start() {
        start = llGetPos();
        llSleep(2.0); // this prevents the end event from getting triggered earlier than we want
    }

    moving_end() {
        llSay(0, "You moved me " + (string)llVecDist(start, llGetPos()) + " meters.");
    }
}

Q: Will moving_end be triggered when I change the rotation of my object?
A: No, both moving_start and moving_end are only triggered by a position change. There is no way to trigger on a rotation directly. To do so, you'll need to use a timer. The "exception" to this is if the object is an attachment. When your avatar turns, moving_start and moving_end will be triggered. Why? Because they're moving; their position has changed, because they're not rotating around the avatar's point of rotation, but rather in an arc.

Q: Is there a way to only trigger moving_end when entering a different sim?
A: No, the best you can do is filter your event as in:
string gCurrentSim; // current simulator name

default {
    moving_end(){
        // sim_name is so we only need to call llGetRegionName once.
        string sim_name = llGetRegionName();
        if (sim_name != gCurrentSim) {
            // if we're here, it means we've just entered a new sim.
            llOwnerSay("Entered " + sim_name + ".");
            gCurrentSim = sim_name;
        }
    }
}

Events | Position


moving_start()

This event is triggered whenever an object (physical or nonphysical) with this script starts moving (and changes position, including when attached) or enters a sim.

Compare with moving_end.

Example:
vector start;

default {
    moving_start() {
        start = llGetPos();
        llSleep(2.0); // this prevents the end event from getting triggered earlier than we want
    }

    moving_end() {
        llSay(0, "You moved me " + (string)llVecDist(start, llGetPos()) + " meters.");
    }
}

Q: Will moving_start be triggered when I change the rotation of an object?
A: No, both moving_start and moving_end are only triggered by a position change. There is no way to trigger on a rotation directly. To do so, you'll need to use a timer. The "exception" to this is if the object is an attachment. When your avatar turns, moving_start and moving_end will be triggered. Why? Because they're moving; their position has changed, because they're not rotating around the avatar's point of rotation, but rather in an arc.

Q: Is there a way to only trigger moving_start when entering a different sim?
A: No, the best you can do is filter your event as in:
string gCurrentSim; // current simulator name

default {
    moving_start(){
        // sim_name is so we only need to call llGetRegionName once.
        string sim_name = llGetRegionName();
        if (sim_name != gCurrentSim) {
            // if we're here, it means we've just entered a new sim.
            llOwnerSay("Entered " + sim_name + ".");
            gCurrentSim = sim_name;
        }
    }
}
A2: Instead you can now use changed with CHANGED_REGION
changed(integer change) {
    if (change & CHANGED_REGION) {
        //region change code here
    }
}
Events | Position


no_sensor()

This event is raised when sensors are active (via the llSensor and llSensorRepeat library calls) but are not sensing anything. Called instead of sensor.

Also see sensor.
Events / Sensors


not_at_rot_target()

This event is raised when a target is set via the llRotTarget library call, but the script is outside the specified angle.

See also at_rot_target.

Events


not_at_target()

This event is triggered repeatedly when a target is set via the llTarget library call and the task is outside the specified range.

See also at_target.

Events


object_rez

object_rez(key id)

Triggered when a prim rezzes another object from its inventory via llRezObject. The id is the globally unique key for the object it just rezzed.

This event is raised for all scripts inside the prim that called llRezObject. This allows for having one script do the actual rezzing while another script receives the object_rez events and handles them (for example using llCreateLink to link a newly created prim to the original, root, prim).

Note: object_rez will be triggered only for scripts within the same prim as the script that rezzed the new object. object_rez will not be triggered in scripts contained within other prims of a linked object. It doesn't matter if the rezzer script is contained within the parent or child prims--either will work; object_rez triggers will just be limited to that prim.

object_rez is not to be confused with on_rez which is raised in the newly rezzed object, not the prim that rezzed it.

Example:
default {
    state_entry() {
        llRezObject("Bubu", llGetPos(), ZERO_VECTOR, ZERO_ROTATION, 1);
    }

    object_rez(key id) {
        llSay(0, "I made a bubu and its key is " + (string)id);
    }
}

To keep an object's inventory in rezzed objects (from the forums):

object_rez(key child)
{
    llGiveInventory(child,llKey2Name(child));
}

Events | Inventory | Object | Rez


on_rez

on_rez(integer start_param)

Triggered in an object whenever it is rezzed from inventory or by another object, or in an attachment when its owner logs in while wearing it, or attaches it from inventory. on_rez is not triggered when an object is rezzed after a teleport. For that, use a changed() event instead.

The start_param is the param(eter) supplied by llRezObject or llRezAtRoot in the script that rezzed this object. When rezzed by an agent, start_param is always 0.

start_param can be obtained anywhere in your script by using llGetStartParameter.

Note: on_rez still follows the rules of states. Remember, running scripts save their state when taken into inventory, and that placing them back in-world does not reset the script; it merely resumes the process. In other words, to get a script to execute a specific function regardless of state, on_rez must be present in every state. This means if a llListen is in an on_rez event, it will start a new listener, rather than remove the old one first. Avoid putting llListen in on_rez (or use llListenRemove first).

Example:
// this example can be used in a bullet that is rezzed by a gun
// if rezzed by the gun, supply a value != 0 to llRezObject

default {
    on_rez(integer start_param) {
        if (start_param != 0) { // if rezzed by gun
            llSetTimerEvent(5.0); // set die timer
        } // otherwise do not die after timeout, so we can still be rezzed for editing
    }

    timer() {
        llDie(); // delete this object
    }
}

Note: if using both on_rez and attach, on_rez will always be triggered first. However, if there's not adequate time to run everything in attach prior to detaching the object, the "detaching" part will run prior to on_rez:
default {
    on_rez(integer start_param) {
        llSay(0, "on_rez");
    }

    attach(key id) {
        llSleep(10.0);
        llSay(0, "attach");
    }
}

Don't confuse on_rez with object_rez, which occurs in the object doing the rezzing using llRezObject.

Q: Is the on_rez event guaranteed to complete before any in-world interaction with the object can occur? For example, if a script had several calls within on_rez, could other actions on the object, like a physics event, affect the object before the event completed? Bullets that only change their physical state on rez still seem to be impelled by the force passed to the llRezObject function. That seems to indicate that the physics are turned on in the on_rez event of the bullet before the impulse is applied by the gun script.
A: Probably what is happening is the force applied is given a time-to-live and if this time is exceeded, the force is stopped. If an event is already being executed when an object is being rezzed, then on_rez will be queued.

Q: Is on_rez triggered when duplicating an object by hand with shift-copy? I remember it used to, but it's not doing that at all. The following won't even work with shift-copy.
on_rez(integer bla){
    llOwnerSay("I've been rezzed");
}
A: This happens when you do a shift-copy, because the script resets. It's a new object that's first rezzed, then the script contained within it is started. state_entry is called, because the object has already existed when the script starts. If an object is taken and the owner has copy permission on it, and then rezzes that object via script or from inventory, it will have the same script state as when it was taken, including script permissions.

Q: Is there an event that runs when the object is deleted or taken into inventory?
A: No. The attach() event will be triggered when detaching an attached object, but that's it. If you're wanting to create some sort of cleanup function or to disable your object after use, use on_rez() and some kind of counter.

Q: Is there a way to get the key of the object or agent that rezzed this object?
A: No, not directly. The easiest way is to have the rezzer say something (for example a codeword) after executing llRezObject, and have the script in your rezzed object listen for it in chat. The rezzer's id will then be handed to the rezzed object's listen event handler. You can use a short llSleep delay in the rezzer object if your script isn't hearing it.

Q: How can I retain all the values in my variables and update llGetOwner while avoiding llResetScript?
A: Create a new function that removes and sets the llListen with llGetOwner. reference this function in state_entry and on_rez. This assigns a new owner to the listener but does not remove any data. Unlike llResetScript which resets the data.

Events | Functions | Rez


remote_data

remote_data(integer event_type, key channel, key message_id, string sender, integer idata, string sdata)

Triggered by various XML-RPC calls with event_type specifying the type of data (see table below).

event_type | Value | Description | channel | message_id | sender | idata | sdata
REMOTE_DATA_CHANNEL | 1 | channel (requested via llOpenRemoteDataChannel) was created | the opened channel | NULL_KEY | "" | 0 | ""
REMOTE_DATA_REQUEST | 2 | XML-RPC request is received | channel the message was sent on | NULL_KEY (for now) | "" | integer data | string data (up to 255 characters)
REMOTE_DATA_REPLY currently disabled | 3 | XML-RPC reply is received | channel used in the llSendRemoteData request | return value of llSendRemoteData | "" | integer data | string data (up to 255 characters)

Events / XML-RPC


run_time_permissions

run_time_permissions(integer perm)

This event is invoked either after the user has responded to a permission request or if permissions granted to the script have changed. The integer perm value returned to this event contains the current set of permissions flags so, if perm == 0, then no permissions are set.

A script needs permission from either its owner or the agent it is to act on before it performs certain functions, such as debiting money from its owner's account, triggering an animation on an agent, capturing control inputs, etc. llRequestPermissions is used to request these permissions and the various permissions integer constants can be supplied:

perm Value | Value | Action | Granter
PERMISSION_DEBIT | 2 | permission to take money from agent's account | owner
PERMISSION_TAKE_CONTROLS | 4 | permission to take agent's controls | anyone
PERMISSION_TRIGGER_ANIMATION | 16 | permission to trigger animation on agent | anyone
PERMISSION_ATTACH | 32 | permission to attach/detach from agent | owner
PERMISSION_CHANGE_LINKS | 128 | permission to change links | owner
PERMISSION_TRACK_CAMERA | 1024 | permission to follow camera movement | anyone
PERMISSION_CONTROL_CAMERA | 2048 | permission to use llSetCameraParams | anyone

See FutureRevisions for future additions to perm.

Note: permission requests do not always ask the user for permission directly. In case of the agent sitting on or attaching a scripted object, PERMISSION_TRIGGER_ANIMATION and PERMISSION_TAKE_CONTROLS will always be granted automatically. However, they still need to be requested within the script.

See control for an example.

Q: I see that this function takes an integer bitfield value for perm, but I see that values 1, 8, and 64 are not listed. Any ideas of what these were, are, or will be used for?
A: See FutureRevisions.

Q: Can I ask for permissions that don't have a constant or an unimplemented constant? Can the user grant them?
A: Yes they can be requested and granted; they just won't do anything. See the table below for restrictions.

perm Value | Value | Future Expected Use | Granter
unknown | 1 | unknown | anyone
PERMISSION_REMAP_CONTROLS | 8 | permission to remap agent's controls | anyone
PERMISSION_RELEASE_OWNERSHIP | 64 | (deprecated) permission to release ownership (The concept of public objects has been removed from SL.) | owner
PERMISSION_CHANGE_JOINTS | 256 | permission to change joints | owner
PERMISSION_CHANGE_PERMISSIONS | 512 | permission to change permissions | owner

Events | Agent/Avatar


sensor

sensor(integer num_detected)

This event is raised whenever objects matching the constraints of the llSensor or llSensorRepeat functions are detected. The number of detected objects is passed to the script in num_detected. Information on those objects may then be gathered via the llDetected* library functions.

If no matches are found, no_sensor will be raised instead.

The sensor() event handler will receive a maximum of 16 detected objects within 96m, sorted by target distance.

It seems that avatars in vehicles can be detected up by a sensor from very far distances. The most I've ever seen was over 1,000m away! -SchitsoMonkey

In the following example, clicking the object causes it to scan once for the presence of agents within a radius of 10m in all directions. If any agents are detected, the object whispers their names in order:

Example:
default
{
    touch_start(integer total_number)
    {
        llSensor("", NULL_KEY, AGENT, 10, PI); // scan for agents/avatars within 10 metres.
    }

    sensor(integer total_number) // total_number is the number of avatars detected.
    {
        llWhisper(0, (string)total_number + " avatars detected" );
        // The following 'for' loop runs through all detected avatars and says "Hello Jane Doe",
        // where "Jane Doe" is the name of the current detected avatar.
        integer i;
        for (i = 0; i < total_number; i++)
        {
            llWhisper(0, "Hello " + llDetectedName(i));
        }
    }

    // if nobody is within 10 meters, say so.
    no_sensor() {
        llSay(0, "Nobody is around.");
    }
}

Q: Do sensor areas overlapping simulator boundaries carry over into the other simulator(s)? Are they supposed to?
A: Not in all cases. As of 1.11 or so, llSensor does not detect across sim boundaries at all and llSensorRepeat is limited by only detecting across boundaries approximately every five seconds.

Q: Can a sensor detect child prims within the object that contains the sensor?
A: No. Sensors only detect the root prim in a linkset, and never the object containing the sensor itself. This is a good thing, as otherwise it would be possible to reverse-engineer linked objects and recreate them, completely negating asset permissions.
Likewise, sensors contained within attachments cannot be used to detect the agent to which the attachment is attached.

Q: Is there a way to just check whether an agent is within a sim?
A: Yes. Assuming you know the agent's key, you can use llKey2Name. No sensor is required. If you don't know the key.

Q: Is there a way to get a list of all agents within a sim?
A: No, not without many objects containing sensors and a fairly elaborate communications system. There isn't a way to just get a list.

Q: Where is the sensors origin in link sets?
A: A sensors point of origin in linked objects is from the prim calling the function. Not the root or the center of the linkset.

Q: How to detect more than 16 results in the area? Can i filter the results of 'first sensor' to find 16 different results with a 'second sensor'?
Events | Functions | Sensors


state_entry()

The state_entry event occurs whenever a new state is entered, including script start/reset, and is always the first event handled.

This event is not invoked upon changing to the same state.

This event is not triggered every time the object is rezzed, due to the preservation of states between instances (rezes). Use on_rez for a reliable first event. It is also not called when the sim restarts.

Note: the LSL compiler doesn't recognise if there are two (or more) state_entry event handlers for the same state. Only the last state_entry is executed. All the previous state_entry in the same state are ignored.

Compare with state_exit.

Events


state_exit()

The state_exit event occurs whenever the state command is used to transition to another state. It is handled before the new state's state_entry event.

This event is not invoked upon changing to the same state.

Compare with state_entry.

Events


timer()

The timer event is raised at regular time intervals set by the llSetTimerEvent function (you only need to call this function once).

Note: an event will never run while another event is still executing. The timer event will get queued and be executed after the previous events are dequeued. In other words, llSetTimerEvent only specifies the amount of time that must pass before a timer event is put into the event queue, which is not necessarily the same as the amount of time before the next timer event actually runs.

The timer event interval can be changed randomly each time it's called by putting:
llSetTimerEvent(llFrand(#));
in the timer event itself, where "#" is the upper value. llFrand will return a random result between 0 and #.

default {
    state_entry() {
        llSetTimerEvent(1.0); // generate a timer event every 1 second
    }

    timer() {
        llSetColor(<llFrand(1.0),llFrand(1.0),llFrand(1.0)>,ALL_SIDES); // set a random color
    }
}

Specifics:
When a timer event is specified, it does not immediately fire; rather, it waits the allotted amount of time before triggering the first timer event. So, if llSetTimerEvent(3600); it must wait one hour (60 minutes = 60 seconds x 60 = 3600 seconds) for it to trigger the first event.

To trigger the event once before waiting the time set, create a function with what it is to do, call it, call llSetTimerEvent, and then call the function in the timer event:

neTimed()
{
    llSetColor(<llFrand(1.0),llFrand(1.0),llFrand(1.0)>,ALL_SIDES); // set a random color
}

default {
    state_entry() {
        neTimed();
        llSetTimerEvent(1.0); // generate a timer event every 1 second
    }

    timer() {
        neTimed();
    }
}

The way the timer event works is: wait, do event, wait, do event, wait, ...
The way the above script works is: do event, wait, do event, wait, do event, ...

For the timer to do something different depending on some condition, use an if statement that checks a global variable. The following example uses a touch event to set the global variable that the timer event checks:

integer on = 1; // set "on" flag to 1 (on)
float time = 1; // timer interval

default
{
    state_entry()
    {
        llSetTimerEvent(time); // generate a timer event every 1 second
    }

    timer()
    {
        llOwnerSay("on");
    }

    touch(integer touches)
    {
        if (on == 1)
        {
            on = 0;
            llSetTimerEvent(0);
            llOwnerSay("off");
        }
        else
        {
            on = 1;
            llSetTimerEvent(time);
        }
    }
}
Note: the second if statement is only necessary in a touch event if there is also an if-else in the timer event; it won't work as else otherwise, oddly. However, simply remove the timer's if-else and code as above and this won't be a problem.
Events | Time


touch

touch(integer num_detected)

This event is repeatedly triggered while a user (agent) is clicking on ("touching") the object containing the script.

The total number of touching agents is passed to the script in the num_detected parameter. Information on those agents may be gathered via the llDetected* functions.

Lag/Performance Advisory:
Be aware that the touch_start() event is recommended for virtually all cases where you need to touch an object. The only time you should be using touch() is when you actually need the object to be receiving touch updates several times a second. Additionally, a bug has recently been discovered that causes the touch() event to continue reporting touches after the object has stopped being touched. There is no word yet on a fix. For both these reasons, touch_start() is recommended over touch(), unless absolutely necessary.

Event: | Triggered: | Triggered multiple times:
touch() | while a user is clicking on the object. | YES
touch_start() | when a user starts clicking on the object. | NO
touch_end() | when a user stops clicking on the object. | NO

Notes:
  • "Touch" means "click on", not "collide with"; to deal with collisions, use the collision, collision_start, and collision_end events.
  • Scripts containing a touch, touch_start, or touch_end event may be subject to "prim drift" caused by the user dragging the object when they mean to click it. To avoid this, use llSetStatus(STATUS_BLOCK_GRAB, TRUE) to block grabs.

See also the llSetTouchText and llPassTouches functions.

Events


touch_end

touch_end(integer num_detected)

This event is raised when an agent stops touching the object the script is attached to. The number of touching agents is passed to the script in the num_detected parameter. Information on those objects may be gathered via the llDetected* library functions.

Event: | Triggered: | Triggered multiple times:
touch() | while a user is clicking on the object. | YES
touch_start() | when a user starts clicking on the object. | NO
touch_end() | when a user stops clicking on the object. | NO

Notes:
  • "Touch" means "click on", not "collide with"; to deal with collisions, use the collision, collision_start, and collision_end events.
  • Scripts containing a touch, touch_start, or touch_end event may be subject to "prim drift" caused by the user dragging the object when they mean to click it. To avoid this, use llSetStatus(STATUS_BLOCK_GRAB, TRUE) to block grabs.

See also the llSetTouchText and llPassTouches functions.

Events


touch_start

touch_start(integer num_detected)

This event is raised when an agent first touches the object the script is attached to. The number of touching agents is passed to the script in the num_detected parameter. Information on those objects may be gathered via the llDetected* functions.

Event: | Triggered: | Triggered multiple times:
touch() | while a user is clicking on the object. | YES
touch_start() | when a user starts clicking on the object. | NO
touch_end() | when a user stops clicking on the object. | NO

Notes:
  • "Touch" means "click on", not "collide with"; to deal with collisions, use the collision, collision_start, and collision_end events.
  • A script containing a touch, touch_start, or touch_end event may be subject to "prim drift" caused by the user dragging the object when they mean to click it. To avoid this, use llSetStatus(STATUS_BLOCK_GRAB, TRUE) to block "grabs".

See also the llSetTouchText and llPassTouches functions.

Q: I see a lot of llDetected*(0), which seems like hacky laziness since even though this is a singular event and so unlikely to coincide, it seems possible for two touch starts to be described in a single event. Does it work that way?
A: Yes, multiple touches can be combined into a single event, but since this is rare you can get away with just checking index 0. When you suspect things get clicked a lot and close together, such as perhaps in competitive games, you should use a for or while loop to step though all the avatar references.
Q: Why you can't touch objects attached to somebody else?
A: As of 1.12 you can
Q: how to code so only the touchable object's owner can touch it?
A: Use a For loop and compare llDetectedKey to llGetOwner
Q: How the owner can script the touchable object to only allow specific people to touch it?
A: Use a For loop and use llDetectedName to compare to a list of names using llListFindList
Q: How to prevent inappropriate invisible touchable objects in PG sims?
A: As of V1.12 I don't see any way for a script to check the PG sim setting.

You can also time how long the touch went for with this quick hack:
//By Charlie Rezillo
float time = 0.1;
default
{
    touch_start(integer num_detected)
    {
        llSetTimerEvent(0.1);
    }
    timer()
    {
        time = time + 0.1;
    }
    touch_end(integer num_detected)
    {
        llSetTimerEvent(0.0);
        llInstantMessage(llDetectedKey(0),llDetectedName(0) + " you held down your mouse button for " + (string)time + " seconds.");
        time  = 0.0;
    }
}

Events


Flow Control

Flow control statements control when and where code is executed (how it flows). The following keywords are parts of LSL flow control.

Keyword | Purpose
if-else | Execute some statements if a condition is true, other statements otherwise.
while | Execute some statements as long as a condition is true.
do-while | Execute some statements at least once, and then as long as a contition is true.
for | Execute some statements a set number of times.
jump | Jump to another part of a function or event.
return | Exit a function or event, possibly returning a value.
state | Transition from one script state to another.

Style Guide


for

for Loop

The for loop is used to execute statements while a condition is true, and executes a line of code after each iteration/cycle/step. More commonly, it executes code a certain number of times, and operates a counter to facilitate it.

Format:
for (initialization; test; update)
{
    statements
}

initialization and update are any single LSL statements. test is any LSL statement that returns an integer.

Example:
integer x;
list things = ["apple", "venus", "Volume 1"];
integer length = llGetListLength(things);

for (x = 0; x < length; x++)
{
    llSay(0, llList2String(things, x));
}
This code says every element of the list things in order.

To execute statements max number of times, with x going from 0 to max - 1, use:
for (x = 0; x < max; x++)

To execute statements max number of times, with x going from 1to max, use:
for (x = 1; x <= max; x++)

To execute statements max number of times, with x going from max to 1, use:
for (x = max; x >= 1; x--)

Note: the test statement is executed every iteration of the loop, so if there is some value that does not change while the loop is running, don't do this:
for (x = 0; x < unchangingCalc(); x++)

Instead, do this:
integer calc = unchangingCalc();
for (x = 0; x < calc; x++)

Any for loop can be re-written as a while loop like this:
initialization
while (test)
{
    statements
    update
}

So, the initialization is always executed, then the test is evaluated, and if it is FALSE, the statements and the update are never executed.

For loops and while loops are syntatically equivalent (one can be converted to the other and the result will be exactly the same) but it is traditional to use for loops to run a block of statements a set number of times (for example, 10 times, once for every item in a list, or once for every result from a sensor) and to use while loops to execute statements until some condition is no longer true (for example, waiting for something).

Since the statements that make up a for loop can be arbitrary LSL, there is a huge temptation to try and be clever; for example, by rewriting the above example as:

Bad Example:
integer x;
list things = ["apple", "venus", "Volume 1"];

for (x = 0; x < llGetListLength(things); llSay(0, llList2String(things, x++)))
{
    ;
}

Do not give in to this temptation!!! It makes code harder to read, gains nothing and makes experienced scripters cry. Additionally, the function llGetListLength is needlessly called once for every element in the list.

Better Example:
integer x;
list things = ["apple", "venus", "Volume 1"];
integer length = llGetListLength(things);

for (x = 0; x < length; x++)
{
    llSay(0, llList2String(things, x));
}

Very Bad Example:
list things = ["apple", "venus", "Volume 1"];
integer x;
for (x=-llGetListLength(things); x; llSay(0, llList2String(things, x++)));

Flow Control


Do-While

A do-while loop is similar to a while loop, but the order in which the parts are executed is different.

Format

do
{
statements
} while (condition);

Example

do
{
    llSay(0, "monkey!");
} while (monkeysRemain());

This code will say "monkey!" once, and then continue saying "monkey!" as long as the function monkeysRemain() returns TRUE.

In a do-while loop, the statements are always executed at least once, then the condition is checked, and if it evaluates true, the statements are repeated.

A do-while loop is used to execute the statements one time, and then additional times. For a loop that executes statements only when a specific condition is true, see the while loop.

Finding a time to use this is rare. It's a wrong solution when I use it (so the section ends up getting re-written). -BW

Accoding to Strife replacing a while loop with a do{}while wrapped in an if statement reduces bytecode size. -Chris

It also executes faster than a while loop, (the bytecode savings is 5 bytes) -BW aka Strife

Flow Control | While


If-Else

Execute some statement(s) if a condition is true, else some other statement(s) if it is false.

Format:
if (condition)
        statement
else
        statement

if (condition)
{
    statements
}
else
{
    other statements
}

Notes:
  • The else and its statements are optional.
  • If there's only one statement in the block of code then the encompassing curly braces ('{' and '}') are also optional.

Syntax

There are no semicolons at the end of the conditional statement.

Incorrect Example
if (condition);
{
    statement;
}

if (condition);
{
    statement2;
}

Using a semicolon after the if statement will cause the code inside the curly braces to execute regardless of the result of the conditional statement. The reason being that the semi-colon tricks the compile into thinking that the use wanted a single line conditional result (see below for an example) and not one with braces, the result of this being that the curly braces are treated as a sub-scope unassociated with the conditional.

Example:
if (llFrand(1.0) >= 0.5)
    llSay(0, "heads");
else
    llSay(0, "tails");

This code checks to see if a random number between 0.0 and 1.0 is greater than 0.5. If it is, the script says "heads", if it's not, the script says "tails"

Note: Only up to 23 else statements may be in a conditional chain. (a total of 24 conditionals: 1 if and 23 elses) Any more, and a syntax error will occur when the script is saved/compiled. However, a workaround to this is to nest conditionals, or, if that's not possible, split the conditional chain into a series of distinct if statements: at the end of each if(){} block, put a jump or return as the last statement so that the remaining if statements that would have otherwise been "else if" statements are skipped.

Boolean Operators

By using boolean operators to AND or IF conditions together, code will be more efficient and use less memory than it would otherwise. Suppose a variable i is FALSE and another variable j is TRUE:

Compare this:
integer i = FALSE;
integer j = TRUE;

if (i)
{
    // we will not reach here.
}
else if (j)
{
    llOwnerSay((string)llGetFreeMemory());
}

And this:
integer i = FALSE;
integer j = TRUE;

if (!i)
{
    if (j)
    {
        llOwnerSay((string)llGetFreeMemory());
    }
}

To this:
integer i = FALSE;
integer j = TRUE;

if (!i && j ) // if i equals FALSE AND j equals TRUE.
    llOwnerSay((string)llGetFreeMemory());

See?

Note: In some other languages, one must formally end the IF statement with endif or something similar. LSL does not.

Note: Operators in LSL are not short-circuited.
Example:
// A simple method to say if the method was called.
integer test() {
    llOwnerSay("Test method called!");

    return TRUE;
}

default {
    touch_start(integer total_number) {
        if (FALSE && test()) { // If the test is short-circuit optimized then the test() method should never be called.
            // Will never get here.
        }
    }
}

In a short-circuited language if the left-hand side of a logical AND (&&) is FALSE the right-hand side is never tested since the entire test would always return a FALSE output no matter what the value of the right-hand side. Since LSL is not short circuited, both the left and the right-hand sides are tested all the time.

If your application requires or would greatly benefit from a short-curcuit method, for example if test above was a process intensive function and the code would be called regularly or repeatedly, you can emulate with additional if's, despite the fact it doesn't look pretty or proper.

Example:
default {
    touch_start(integer total_number) {
        if (FALSE) {
            if (test()) // Will never get here.
                // Will never get here either.
        }
    }
}

Script | Flow Control


jump

jump label

A jump is like a goto in other languages. Jumps can be used to alter normal flow control. When a jump occurs, script execution immediately moves to the next statement after the corresponding label definition, skipping intervening code and breaking out of any loops.

Labels are defined by an arbitrary name, prefixed with an @ (at sign) character.

Example:

list gList;
integer scanForThingys(){
   integer i;
   integer len = llGetListLength(gList);
   for (i = 0; i < len; i++){
      if ( isThingy( llList2String(gList, i) ) )
         jump getOut;
   }
   i = -1;
   @getOut; // when the jump is executed this script leaves the FOR loop and continues from here
   return i;
}

Note: a jump only works within the current scope, a global function, or an event handler. Jumps don't work between global functions or event handlers.

Most coders recommend avoiding jumps where possible and you'll find that they are rarely necessary in well-structured code. LSL lacks a "break" feature that is standard in most languages for leaving a loop early so this is one case where a jump can be useful. Careful coders will avoid jumping backwards or too far forwards in their code.

For more on the subject see: GoTo Statement Considered Harmful.

Note: Please see the comments, you can only have a single jump statement going to a label.
Flow Control


Return

A return is a value given when exiting a function or event. The return keyword is used for exiting from functions or events. If a function has a return value, all code paths will need to end in a return statement.

Note: The term "return value" is used when referring to the value retrieved by calling a function.
Also, there is not currently a function to return objects from a land parcel--that's not what this keyword does.

Example:
MyGlobalFunction(string str) {
    llSay(0, str);
}

float WithReturnValue() {
    float    r;

    r = llFrand(10);
    return r;    // also valid: return(r);
}

default {
    touch_start(integer total_number) {
        if ( llDetectedKey(0) != llGetOwner() ) return; // exit this event if touched by stranger

        MyGlobalFunction("Hooray for SL!"); // call a function without return value

        llSay(0, (string)WithReturnValue()); // call function and use the return value

        WithReturnValue(); // discarding the return value is also legal
    }
}
Flow Control | Glossary


statekeyword

state

This keyword is used to transition between states in a script. It's also used to define states, with the exception of the default state. State changes generally only happen in event handlers, although it's possible to hack around this limitation and transition from inside user-defined functions.

Example:
default
{
   touch_start(integer detectedCount)
   {
      llOwnerSay( "Transitioning to the foo state.");
      state foo;
   }
}

state foo
{
   touch_start(integer detectedCount)
   {
      llOwnerSay( "Transitioning back to the default state.");
      state default;
   }
}

The maximum number of states a single script can support is 144 (including the default state). 144 is 128 + 16, both powers of two. Adding any states to a single script beyond 144 will generate a syntax error. To test this, create a script containing the contents below:

//
//
default {state_entry(){}}state state0{state_entry(){}}state state1{state_entry(){}}state state2{state_entry(){}}state state3{state_entry(){}}state state4{state_entry(){}}state state5{state_entry(){}}state state6{state_entry(){}}state state7{state_entry(){}}state state8{state_entry(){}}state state9{state_entry(){}}state state10{state_entry(){}}state state11{state_entry(){}}state state12{state_entry(){}}state state13{state_entry(){}}state state14{state_entry(){}}state state15{state_entry(){}}state state16{state_entry(){}}state state17{state_entry(){}}state state18{state_entry(){}}state state19{state_entry(){}}state state20{state_entry(){}}state state21{state_entry(){}}state state22{state_entry(){}}state state23{state_entry(){}}state state24{state_entry(){}}state state25{state_entry(){}}state state26{state_entry(){}}state state27{state_entry(){}}state state28{state_entry(){}}state state29{state_entry(){}}state state30{state_entry(){}}state state31{state_entry(){}}state state32{state_entry(){}}state state33{state_entry(){}}state state34{state_entry(){}}state state35{state_entry(){}}state state36{state_entry(){}}state state37{state_entry(){}}state state38{state_entry(){}}state state39{state_entry(){}}state state40{state_entry(){}}state state41{state_entry(){}}state state42{state_entry(){}}state state43{state_entry(){}}state state44{state_entry(){}}state state45{state_entry(){}}state state46{state_entry(){}}state state47{state_entry(){}}state state48{state_entry(){}}state state49{state_entry(){}}state state50{state_entry(){}}state state51{state_entry(){}}state state52{state_entry(){}}state state53{state_entry(){}}state state54{state_entry(){}}state state55{state_entry(){}}state state56{state_entry(){}}state state57{state_entry(){}}state state58{state_entry(){}}state state59{state_entry(){}}state state60{state_entry(){}}state state61{state_entry(){}}state state62{state_entry(){}}state state63{state_entry(){}}state state64{state_entry(){}}state state65{state_entry(){}}state state66{state_entry(){}}state state67{state_entry(){}}state state68{state_entry(){}}state state69{state_entry(){}}state state70{state_entry(){}}state state71{state_entry(){}}state state72{state_entry(){}}state state73{state_entry(){}}state state74{state_entry(){}}state state75{state_entry(){}}state state76{state_entry(){}}state state77{state_entry(){}}state state78{state_entry(){}}state state79{state_entry(){}}state state80{state_entry(){}}state state81{state_entry(){}}state state82{state_entry(){}}state state83{state_entry(){}}state state84{state_entry(){}}state state85{state_entry(){}}state state86{state_entry(){}}state state87{state_entry(){}}state state88{state_entry(){}}state state89{state_entry(){}}state state90{state_entry(){}}state state91{state_entry(){}}state state92{state_entry(){}}state state93{state_entry(){}}state state94{state_entry(){}}state state95{state_entry(){}}state state96{state_entry(){}}state state97{state_entry(){}}state state98{state_entry(){}}state state99{state_entry(){}}state state100{state_entry(){}}state state101{state_entry(){}}state state102{state_entry(){}}state state103{state_entry(){}}state state104{state_entry(){}}state state105{state_entry(){}}state state106{state_entry(){}}state state107{state_entry(){}}state state108{state_entry(){}}state state109{state_entry(){}}state state110{state_entry(){}}state state111{state_entry(){}}state state112{state_entry(){}}state state113{state_entry(){}}state state114{state_entry(){}}state state115{state_entry(){}}state state116{state_entry(){}}state state117{state_entry(){}}state state118{state_entry(){}}state state119{state_entry(){}}state state120{state_entry(){}}state state121{state_entry(){}}state state122{state_entry(){}}state state123{state_entry(){}}state state124{state_entry(){}}state state125{state_entry(){}}state state126{state_entry(){}}state state127{state_entry(){}}state state128{state_entry(){}}state state129{state_entry(){}}state state130{state_entry(){}}state state131{state_entry(){}}state state132{state_entry(){}}state state133{state_entry(){}}state state134{state_entry(){}}state state135{state_entry(){}}state state136{state_entry(){}}state state137{state_entry(){}}state state138{state_entry(){}}state state139{state_entry(){}}state state140{state_entry(){}}state state141{state_entry(){}}state state142{state_entry(){}}
//
//

The above script will compile successfully. However, if you add a single additional state to it, the script will generate a syntax error during compilation, despite no "real" syntax error being present, and despite the script being well under the 16k script limit.

Flow Control | state


while

while Loops

The statements in a while loop are executed over and over while a condition is met.

Format

while (condition)
{
    statements
}

Note that if the condition is false initially, the statements will never be executed.

Example

This code will wait until it is at least 3600 seconds past the start of the SL day. ...Mostly. See the page on llGetTimeOfDay.

integer bDone = FALSE;

while (!bDone)
{
    if (llGetTimeOfDay() > 3600)
    {
        bDone = TRUE;
    }
    else
    {
        llSleep(500);
    }
}
llSay( 0, "Good morning!" );

The following would behave the same as the above loop.

while( llGetTimeOfDay() < 3600 )
{
    llSleep(500);
}
llSay( 0, "Good morning!" );

FlowControl


Functions

A function can be thought of as a machine into which input is provided, and which returns output. By using functions, the same blocks of code can be used over and over again, simply by referencing them.

LSL comes with over 310 built-in functions that allow scripts and objects to interact with their environment. All of the built-in functions start with "ll" -- those are lower-case 'L's, for "Linden Library".

The user can define functions (even with return values) as long as the name does not conflict with a reserved word, a built-in constant, or a built-in function.

Example
This example calls the llSay function which is used to send chat text to the specified channel.
llSay(0, "Hello world!");

Note: As SL has matured, several functions have been deprecated. Be sure to use the correct function since deprecated functions are unreliable and may be removed completely at a future date.

Categorical

  • Agent/Avatar
  • Camera
  • Communications
  • Collision
  • Color
  • Controls
  • Dataserver
  • Detection
  • Dynamics/Movement/Physics
  • Group
  • Inventory

  • Land (Ground/Terrain)
  • Light
  • Link
  • List
  • Math
  • Particle
  • Primitive/Object
  • Script
  • Sensor
  • Simulator (Environment/World)

  • Sound
  • String (Text/Name)
  • Teleport
  • Texture
  • Time
  • Transformation: Rotation/Scaling/Translation
  • Vehicle
  • Video
  • Weather
  • XML-RPC

    • Alphabetical

    A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

    A

    llAbs
    llAcos
    llAddToLandBanList (1.8)
    llAddToLandPassList
    llAdjustSoundVolume
    llAllowInventoryDrop
    llAngleBetween
    llApplyImpulse
    llApplyRotationalImpulse
    llAsin
    llAtan2
    llAttachToAvatar
    llAvatarOnSitTarget
    llAxes2Rot
    llAxisAngle2Rot

    B

    llBase64ToInteger (1.5)
    llBase64ToString
    llBreakAllLinks
    llBreakLink

    C

    llCeil
    llClearCameraParams (1.9)
    llCloseRemoteDataChannel
    llCloud
    llCollisionFilter
    llCollisionSound
    llCollisionSprite
    llCos
    llCreateLink
    llCSV2List

    D

    llDeleteSubList
    llDeleteSubString
    llDetachFromAvatar
    llDetectedGrab
    llDetectedGroup
    llDetectedKey
    llDetectedLinkNumber
    llDetectedName
    llDetectedOwner
    llDetectedPos
    llDetectedRot
    llDetectedType
    llDetectedVel
    llDialog
    llDie
    llDumpList2String

    E

    llEdgeOfWorld
    llEjectFromLand
    llEmail
    llEscapeURL (1.7)
    llEuler2Rot

    F

    llFabs
    llFloor
    llForceMouselook
    llFrand

    G

    llGetAccel
    llGetAgentInfo
    llGetAgentSize
    llGetAlpha
    llGetAndResetTime
    llGetAnimation
    llGetAnimationList (1.4)
    llGetAttached
    llGetBoundingBox (1.5)
    llGetCameraPos (1.7)
    llGetCameraRot (1.7)
    llGetCenterOfMass
    llGetCreator (1.5)
    llGetColor
    llGetDate
    llGetEnergy
    llGetForce
    llGetFreeMemory
    llGetGeometricCenter (1.5)
    llGetGMTclock (1.5)
    llGetInventoryCreator (1.6)
    llGetInventoryKey
    llGetInventoryName
    llGetInventoryNumber
    llGetInventoryPermMask (1.6)
    llGetInventoryType (1.7)
    llGetKey
    llGetLandOwnerAt
    llGetLinkKey
    llGetLinkName
    llGetLinkNumber
    llGetListEntryType
    llGetListLength
    llGetLocalPos
    llGetLocalRot
    llGetMass
    llGetNextEmail
    llGetNotecardLine
    llGetNumberOfNotecardLines (1.5)
    llGetNumberOfPrims (1.5)
    llGetNumberOfSides
    llGetObjectDesc (1.5)
    llGetObjectMass
    llGetObjectName
    llGetObjectPermMask (1.6)
    llGetObjectPrimCount (1.13)
    llGetOmega
    llGetOwner
    llGetOwnerKey
    llGetParcelDetails (1.13)
    llGetParcelFlags (1.9)
    llGetParcelMaxPrims (1.13)
    llGetParcelPrimCount (1.13)
    llGetParcelPrimOwners (1.13)
    llGetPermissions
    llGetPermissionsKey
    llGetPos
    llGetPrimitiveParams (1.5)
    llGetRegionCorner
    llGetRegionFlags (1.9)
    llGetRegionFPS
    llGetRegionName
    llGetRegionTimeDilation
    llGetRootPosition (1.5)
    llGetRootRotation (1.5)
    llGetRot
    llGetScale
    llGetScriptName
    llGetScriptState
    llGetSimulatorHostname (1.5.1)
    llGetStartParameter
    llGetStatus
    llGetSubString
    llGetSunDirection
    llGetTexture
    llGetTextureOffset
    llGetTextureRot
    llGetTextureScale
    llGetTime
    llGetTimeOfDay
    llGetTimestamp (1.5)
    llGetTorque
    llGetUnixTime (1.9)
    llGetVel
    llGetWallclock
    llGiveInventory
    llGiveInventoryList
    llGiveMoney
    llGodLikeRezObject
    llGround
    llGroundContour
    llGroundNormal
    llGroundRepel
    llGroundSlope

    H

    llHTTPRequest (1.10)

    I

    llInsertString
    llInstantMessage
    llIntegerToBase64 (1.5)

    K

    llKey2Name

    L

    llList2CSV
    llList2Float
    llList2Integer
    llList2Key
    llList2List
    llList2ListStrided
    llList2Rot
    llList2String
    llList2Vector
    llListFindList
    llListInsertList
    llListRandomize
    llListReplaceList
    llListSort
    llListStatistics (1.9)
    llListen
    llListenControl
    llListenRemove
    llLoadURL
    llLog
    llLog10
    llLookAt
    llLoopSound
    llLoopSoundMaster
    llLoopSoundSlave

    M

    llMakeExplosion (deprecated)
    llMakeFire (deprecated)
    llMakeFountain (deprecated)
    llMakeSmoke (deprecated)
    llMapDestination (1.8)
    llMD5String
    llMessageLinked
    llMinEventDelay
    llModifyLand
    llModPow (1.6.2)
    llMoveToTarget

    O

    llOffsetTexture
    llOpenRemoteDataChannel
    llOverMyLand
    llOwnerSay (1.6)

    P

    llParcelMediaCommandList (1.6)
    llParcelMediaQuery (1.6)
    llParseString2List
    llParseStringKeepNulls (1.5.5)
    llParticleSystem
    llPassCollisions
    llPassTouches
    llPlaySound
    llPlaySoundSlave
    llPointAt
    llPow
    llPreloadSound
    llPushObject

    R

    llRefreshPrimURL (1.7)
    llRegionSay 1.16.0(5)
    llReleaseCamera
    llReleaseControls
    llRemoteDataReply
    llRemoteDataSetRegion
    llRemoteLoadScript (removed)
    llRemoteLoadScriptPin
    llRemoveFromLandBanList (1.8)
    llRemoveFromLandPassList (1.8)
    llRemoveInventory
    llRemoveVehicleFlags
    llRequestAgentData
    llRequestInventoryData
    llRequestPermissions
    llRequestSimulatorData (1.6)
    llResetOtherScript
    llResetScript
    llResetTime
    llRezAtRoot (1.5.6)
    llRezObject
    llRot2Angle
    llRot2Axis
    llRot2Euler
    llRot2Fwd
    llRot2Left
    llRot2Up
    llRotBetween
    llRotLookAt
    llRotTarget
    llRotTargetRemove
    llRotateTexture
    llRound

    S

    llSameGroup
    llSay
    llScaleTexture
    llScriptDanger
    llSendRemoteData
    llSensor
    llSensorRemove
    llSensorRepeat
    llSetAlpha
    llSetBuoyancy
    llSetCameraAtOffset
    llSetCameraEyeOffset
    llSetCameraParams (1.9)
    llSetColor
    llSetDamage
    llSetForce
    llSetForceAndTorque
    llSetHoverHeight
    llSetLinkAlpha (1.5)
    llSetLinkColor
    llSetLinkPrimitiveParams (1.13.4)
    llSetLinkTexture (1.13.4)
    llSetLocalRot (1.5.4)
    llSetObjectDesc (1.5)
    llSetObjectName
    llSetParcelMusicURL (1.4)
    llSetPayPrice (1.7)
    llSetPos
    llSetPrimURL (1.7)
    llSetPrimitiveParams
    llSetRemoteScriptAccessPin
    llSetRot
    llSetScale
    llSetScriptState
    llSetSitText
    llSetSoundQueueing
    llSetSoundRadius
    llSetStatus
    llSetText
    llSetTexture
    llSetTextureAnim
    llSetTimerEvent
    llSetTorque
    llSetTouchText
    llSetVehicleFlags
    llSetVehicleFloatParam
    llSetVehicleRotationParam
    llSetVehicleType
    llSetVehicleVectorParam
    llShout
    llSin
    llSitTarget
    llSleep
    llSound (deprecated)
    llSoundPreload (deprecated)
    llSqrt
    llStartAnimation
    llStopAnimation
    llStopHover
    llStopLookAt
    llStopMoveToTarget
    llStopPointAt
    llStopSound
    llStringLength
    llStringToBase64
    llStringTrim
    llSubStringIndex

    T

    llTakeCamera
    llTakeControls
    llTan
    llTarget
    llTargetOmega
    llTargetRemove
    llTeleportAgentHome
    llToLower
    llToUpper
    llTriggerSound
    llTriggerSoundLimited

    U

    llUnescapeURL (1.7)
    llUnSit

    V

    llVecDist
    llVecMag
    llVecNorm
    llVolumeDetect

    W

    llWater
    llWhisper
    llWind

    X

    llXorBase64StringsCorrect (1.9)
    llXorBase64Strings (deprecated)

    User-Defined Functions | Events


llAbs

integer llAbs(integer val)

Returns the absolute value of val. The absolute value of a number is its positive value. To put it another way, the absolute value is the distance of the number from 0 on the number line.

Examples of arguments:
val | return value
-4 | 4
-3 | 3
2 | 2
1 | 1

Basically, it kills off the negative sign of an integer.

Note: llAbs works for integers. To determine the absolute value of a float, use llFabs.

Functions | Math


llAcos

float llAcos(float val)

Returns the arccosine or inverse cosine in radians of val.

Basically, it is the inverse of cosine (llCos), just as as division is the inverse of multiplication.

Example:
float cosine_return;
float arccosine_return;

cosine_return = llCos( 1 );
arccosine_return = llAcos( cosine_return );

llOwnerSay( "cosine_return = llCos( 1 ) == " + (string)cosine_return );
llOwnerSay( "arccosine_return = llACos( cosine_return ) == " + (string)arccosine_return );

This would result in the following:
cosine_return = llCos( 1 ) == 0.540302
arccosine_return = llACos( cosine_return ) == 1.000000

Compare with llCos.

Functions | Math


llAddToLandBanList

llAddToLandBanList(key agent, float hours)

Adds agent to the parcel ban list for the specified number of hours. A value of 0 for hours will add agent indefinitely. The smallest value hours will accept is 0.01; anything smaller will be seen as 0. When values that small are used, it seems the function bans in 30 second increments (Probably 36 second increments, as 0.01 of an hour is 36 seconds). Residents teleporting to a parcel where they are banned will be redirected to a neighboring parcel.

Q: So, I cannot ban someone unless he's nearby or I know his key, making the land ban mechanism on scripted objects pretty useless, correct?
A: You cannot ban someone unless they are nearby or you know their key - but this doesn't make it useless. See the example script on llHTTPRequest for an example of a Name2Key script.

To remove a user to the land ban list, use llRemoveFromLandBanList.

To add or remove users from the land pass list, use llAddToLandPassList and llRemoveFromLandPassList.

Functions | Teleport | Land


llAddToLandPassList

llAddToLandPassList(key agent, float hours)

Adds agent to the land pass list for the specified number of hours. A value of 0 for hours will add agent indefinitely.

The specified agent receives a message: "Alert: You have been issued a pass good for xx minutes." When the time is nearly elapsed, the agent receives several messages: "Alert: Your pass to this land is about to expire.", and, finally: "Alert: Your pass expired and you have been ejected". The agent is then ejected.

This function is silently ignored unless the object the script is in is owned by the owner of the land. In the case of group-owned land, it does not appear to be sufficient for the owner of the object to be a member or even an officer of the group--nor for the object to be set to group ownership. Instead, the object must first be shared with that group, then have an officer of the group deed the object to the group.

Multiple calls to this function for the same agent are not cumulative. Instead, if the agent already has a timed land pass active, which has less time to run than the hours parameter, the time left to run will be topped up to hours. If the active land pass has more time to run than the hours parameter, it will not be altered and no alert will be given.

Note: while this function also works on the land owner, it will not eject them, so determining the usefulness of adding oneself to one's own land pass list is left as an exercise for the reader.

Q: Will this also remove a user's objects when the pass expires?
A: No.

To remove a user to the land pass list, use llRemoveFromLandPassList.

To add and remove them from the land ban list, use llAddToLandBanList and llRemoveFromLandBanList.

Functions | Agent/Avatar | Land


llAdjustSoundVolume

llAdjustSoundVolume(float volume)

Adjusts the volume of the currently playing attached sound (started with llPlaySound or llLoopSound). volume should be between 0.0 and 1.0.

Has no effect on sounds started with llTriggerSound.

After adjusting the volume, this function delays the script for 0.1 seconds.

Functions | Sound


llAllowInventoryDrop

llAllowInventoryDrop(integer add)

If add is true, all users, even those who do not have modify permissions, can drop inventory items onto the object. If add is false the system forbids all users except those with modify permissions from dropping items onto the object.

The changed event handler is called with the CHANGED_ALLOWED_DROP flag when an item is dropped onto the object by a user that does not have modify permission. CHANGED_INVENTORY is passed to changed if someone with modify permission dropped the item.

To drop an inventory item onto/into an object, hold the Control key while dragging any inventory item onto the object (the object you're about to drop your item onto will be highlighted with a red frame). But for notecard drop, there is no need to hold the control key. Just drag and drop your notecard on which the object will be highlighted with a white frame.

The exception are scripts, attempting to drop a script onto an object which allows inventory drops will cause the object to shout "Not permitted to edit this!".
  • Really, it shouts this? Isn't that... insane? -- Catherine
  • Heh, very much so, it does this also when using Tools > Save Object Back Into Object Contents :-\ -- Chris
  • The exception to the exception is if the scripts are given from one object to another via llGiveInventory, which transfer the scripts in a non-running state -DragonEccleston

default
{
    state_entry()
    {
        llAllowInventoryDrop(TRUE);
    }
    changed(integer mask)
    {
        if(mask & (CHANGED_ALLOWED_DROP | CHANGED_INVENTORY))
            llWhisper(0, "My inventory has changed");
    }
}

Functions / Inventory


llAngleBetween

float llAngleBetween(rotation a, rotation b);

Returns angle, in radians, between rotation a and rotation b.

float AngleBetween(rotation a, rotation b)
{
    return 2 * llAcos((a.x * b.x + a.y * b.y + a.z * b.z + a.s * b.s)
                  / llSqrt((a.x * a.x + a.y * a.y + a.z * a.z + a.s * a.s) *
                           (b.x * b.x + b.y * b.y + b.z * b.z + b.s * b.s)));
}

Functions | Rotation


llApplyImpulse

llApplyImpulse(vector force, integer local)

If an object is physical, this call applies a single push in a specific direction to the object defined by the argument force. If local is true, force is applied along the object's local axis, otherwise it is applied along the global axis.

Note: unlike llApplyRotationalImpulse, llApplyImpulse does work on attachments.

To set the object's velocity using llApplyImpulse():
setVel(vector newVel, integer localAxis)
{
     vector curVel = llGetVel();

     if(localAxis)
     {
          rotation rot = llGetRot();
          curVel /= rot; // Un-rotate curVel.
     }

     newVel -= curVel;
     newVel *= llGetMass();

     llApplyImpulse(newVel,localAxis);
}

Compare with llApplyRotationalImpulse.

  • In order to give an object a specific speed and direction, use the momentum equation: p=m*v where v is the target velocity vector and m is the mass of the object.

llApplyImpulse(llGetMass()*<0,0,10>,FALSE);
//10 meters per second upwards
//Gravity will slow the object down immediately and make it fall back

To overcome gravity effects, use a force which, unlike an impulse, is constant over time:

llSetForce(<0,0,9.8>*llGetMass(),FALSE);
// will cancel the effects of gravity

To stop a moving object dead in its tracks, apply an impulse to counter its current impulse:

llApplyImpulse(-llGetMass()*llGetVel(),FALSE);
//This will cancel the speed of an object that has no other forces set on it
//and will cause it to stop in place relative to the ground

Limitations

The actual impulse may be less than requested for two reasons.
  • The push vector magnitude is limited to 20,000. Anything over this is scaled back to a magnitude of 20,000.
  • The push vector is always scaled by the energy of the object. For relatively small objects (say, smaller than an avatar), the object's energy is likely to be 1.0, in which case there will be no effect. However, for large objects, or objects that are being manipulated a lot, the energy may be anywhere from 0.0 to 1.0, and the effect can be very sizeable. See energy for more details on how energy is calculated.

Functions | Dynamics | Force | Impulse


llApplyRotationalImpulse

llApplyRotationalImpulse(vector force, integer local);

Applies a rotational impulse. This movement is relative to the axes created by the object's rotation (seen with "Local Axis" enabled in edit mode) if local == TRUE. Otherwise, it is applied relative to the task's region's axes (seen with "Local Axis" unchecked in edit mode).

Note: this function only works on physical objects. It does not work on attachments.

Compare with llApplyImpulse.

Does not seem to factor in the mass of the object.

Functions | Dynamics | Physics | Impulse


llAsin

float llAsin(float val)

Returns the arcsine in radians of val.

Basically, it is the inverse of sine (llSin), just as as division is the inverse of multiplication.

Example:
float sine_return;
float arcsine_return;

sine_return = llSin( 1 );
arcsine_return = llAsin( sine_return );

llOwnerSay( "sine_return = llSin( 1 ) == " + (string)sine_return );
llOwnerSay( "arcsine_return = llAsin( sine_return ) == " + (string)arcsine_return );

This would result in the following messages:
sine_return = llSin( 1 ) == 0.841471
arcsine_return = llAsin( sine_return ) == 1.000000

Compare with llSin.

Functions | Math


llAtan2

float llAtan2(float y, float x)

Returns the arctangent in radians of y/x.

Compare with llTan.

Functions | Math


llAttachToAvatar

llAttachToAvatar(integer attachment)

Attaches the scripted object at the specified attachment point to the avatar it has PERMISSION_ATTACH for. Even though users other than the owner can grant this permission, attaching to them will fail with the message "Script trying to attach to someone other than owner!".

An object must have permission to attach itself. Call llGetPermissions to see if the object has the PERMISSION_ATTACH permission; if it doesn't, llRequestPermissions must first be called to request permissions from the user.

The attach event is called with the avatar's key.

Notes:
  • Once an object is attached to the avatar, certain script functionality is disabled. See attachment for more details.
  • If there is already an attachment at the attachment point, it will be detached and replaced with the new one.
  • The attachment points are (usually) centered on the body part. If an object has not been attached before, it attaches at <0,0,0> offset from the attachment point. If the object has been attached before, it remembers its last attachment offset.
  • Attachment points are relative to the object's skeleton, not its surface. This can cause some attachments to look weird. ATTACH_RPEC and ATTACH_LPEC are examples of attachment points with high levels of surface/skeleton drift.

The attachment parameter can be one of the following constants (also integers):

Constant | Value | Comment
? | 0 | attach to last used attachement spot, like when you select Wear (default is right hand)
ATTACH_CHEST | 1 | chest/sternum - ATTACH_LBOOBIE and ATTACH_RBOOBIE don't exist, though there's ATTACH_RPEC and ATTACH_LPEC
ATTACH_HEAD | 2 | head
ATTACH_LSHOULDER | 3 | left shoulder
ATTACH_RSHOULDER | 4 | right shoulder
ATTACH_LHAND | 5 | left hand
ATTACH_RHAND | 6 | right hand
ATTACH_LFOOT | 7 | left foot
ATTACH_RFOOT | 8 | right foot
ATTACH_BACK | 9 | back
ATTACH_PELVIS | 10 | pelvis
ATTACH_MOUTH | 11 | mouth
ATTACH_CHIN | 12 | chin
ATTACH_LEAR | 13 | left ear
ATTACH_REAR | 14 | right ear
ATTACH_LEYE | 15 | left eye
ATTACH_REYE | 16 | right eye
ATTACH_NOSE | 17 | nose
ATTACH_RUARM | 18 | right upper arm
ATTACH_RLARM | 19 | right lower arm
ATTACH_LUARM | 20 | left upper arm
ATTACH_LLARM | 21 | left lower arm
ATTACH_RHIP | 22 | right hip
ATTACH_LHIP | 23 | left hip
ATTACH_RULEG | 24 | right upper leg
ATTACH_RLLEG | 25 | right lower leg
ATTACH_LULEG | 26 | left upper leg
ATTACH_LLLEG | 27 | left lower leg
ATTACH_BELLY | 28 | belly/stomach/tummy
ATTACH_RPEC | 29 | right pectoral
ATTACH_LPEC | 30 | left pectoral - as noted above, ATTACH_RPEC and ATTACH_LPEC don't always look like they're attached to the right and left side of the chest, and will appear to float around a little.
? | 31 | HUD Center 2
? | 32 | HUD Top Right
? | 33 | HUD Top
? | 34 | HUD Top Left
? | 35 | HUD Center
? | 36 | HUD Bottom Left
? | 37 | HUD Bottom
? | 38 | HUD Bottom Right

Q: Does this have any purpose when attaching from inventory or only for attaching on a touch event or similar thing? Seems like going from inventory doesn't require PERMISSION_ATTACH either.
A: llAttachToAvatar is used for objects that already exist in-world. Objects in your inventory can be thought of as being in an inert, suspended state. They don't actually "exist" until you rez them.

Q: If I attach an object from inventory, will it have PERMISSION_ATTACH right away, or do I have to request it? And what about an object rezzed by an attached object? (For example, if I need to have an attached object rez a HUD element.)
A: You always must request permissions, but some permissions are auto-granted upon request for attachment or sit. BW

Example:
default {
    touch_start(integer num_detected) {
        llRequestPermissions(llDetectedKey(0), PERMISSION_ATTACH);
    }

    run_time_permissions(integer perm) {
        if (perm & PERMISSION_ATTACH)  {
            llAttachToAvatar(ATTACH_RHAND);
        }
    }
}

Q: Can an attachment go directly to another point on the body? i.e. from left hand to chest?

Functions | Agent/Avatar | Attachment


llAvatarOnSitTarget

key llAvatarOnSitTarget()

If an avatar is sitting on the sit target, this function will return the avatar's key; NULL_KEY otherwise. This will only detect avatars sitting on sit targets defined with llSitTarget. At some point prior to SL 1.6.5, it would apparently work without, but has apparently not been the case for some time. Scripts compiled prior to the change will function correctly, but recompiling them will cause them to break.

Agents that sit on the object, are linked to the object and are added to the end of the link set. See llGetNumberOfPrims for example functions.

Example:
default {
    state_entry() {
        llSitTarget(<0.0, 0.0, 0.1>, ZERO_ROTATION); // needed for llAvatarOnSitTarget to work
        // Note that if both the vector and the rotation are zero,
        // the SitTarget is removed instead of set and the following will not work:
    }

    changed(integer change) { // something changed
        if (change & CHANGED_LINK) { // and it was a link change
            llSleep(0.5); // llUnSit works better with this delay
            if (llAvatarOnSitTarget() != NULL_KEY) { // somebody is sitting on me
                llSay(0, "Get off!");
                llUnSit(llAvatarOnSitTarget()); // unsit him or her
            }
        }
    }
}

Q: It's only returning NULL_KEY! What's up with that?
A: llAvatarOnSitTarget will only work if llSitTarget has been set.

Agent/Avatar | Functions | sit | target


llAxes2Rot

rotation llAxes2Rot(vector fwd, vector left, vector up);

Returns rotation represented by coordinate axes fwd, left, and up. These axes should be orthogonal to each other.

Note: Technically, only two non-parallel vectors are needed to define this rotation which can be done by calling llAxes2Rot(fwd, left, fwd % left);

functions | rotation


llAxisAngle2Rot

rotation llAxisAngle2Rot(vector axis, float angle);

Returns rotation described by rotating angle radians about axis.

functions | rotation


llBase64ToInteger

integer llBase64ToInteger(string str)

Little endian decode the 8 character Base64 string str into an integer.

Use llIntegerToBase64 to encode.

This function currently has a bug and if you pass it an invalid base64 string the result will be a random integer

Functions / Base64


llBase64ToString

string llBase64ToString(string str)

Converts a Base 64 string to a conventional string. If the conversion creates any unprintable characters, they are converted to question marks (this behavior is different then llUnescapeURL).

Compare with llBase64ToInteger, llStringToBase64 and llIntegerToBase64.

Raw Printable Character Ranges
Decimal | Hex
10 | 0xA
32 -> 127 | 0x20 -> 0x7F
Functions | Crypto | Communications | Strings


llBreakAllLinks();

Delinks all prims in the link set (Requires permission PERMISSION_CHANGE_LINKS be set).

Q: Can I delink a prim from an attachment?
A: No.

Q: Do I need to call llBreakAllLinks from the root prim in order for this to work?
A: No, as long as it has PERMISSION_CHANGE_LINKS set, any script in any prim may break the link. You cannot grant permissions to one script and have another act upon those permissions, however. Scripts must request their own permissions.

To delink a single prim from the linkset, use llBreakLink.

Functions | Link


llBreakLink

llBreakLink(integer linknum)

Delinks the object with the given link number linknum (requires the permission PERMISSION_CHANGE_LINKS be set).

Remaining link children in the link set will be renumbered, so if llBreakLink(5) will cause the previous child prim 6 to become 5, child 7 will become 6, and so on.

If you want the prim that was delinked to do something after delinking, use a changed event, as in this example:
changed(integer change) {
    if (change & CHANGED_LINK) // linked/unlinked/sat on
        if (llGetLinkNumber() == 0) // now i'm a single prim linked to nothing
            llDie(); // so i die
}

Q: Can I delink a prim from an attachment?
A: No. If you want to make a prim in an attachment invisible, you can use llSetAlpha to hide it, but it will remain linked to the rest of the attachment.

Q: Do I need to call llBreakLink from the root prim in order for this to work?
A: No, as long as it has PERMISSION_CHANGE_LINKS set, any script in any prim may break the link. You cannot grant permissions to one script and have another act upon those permissions, however. Scripts must request their own permissions.

Q: Can I delink a prim from a no mod object?
A: Yes.
A: I'm uncertain when this functionality changed but my testing shows that if you attempt to break a link from a no-mod object, it will fail with the error message "Delink failed because you do not have edit permission".

Q: Can I use this function to force an avatar to get off the prim?
A: No. Use llUnSit instead.

To break all links in a linked object, use llBreakAllLinks.

Functions | Constants | Link


llCeil

integer llCeil(float val);

Returns the smallest integer value >= val (rounds up).

The usage of these functions can be tricky. For examples of potential issues see llFrand.

Compare with llFloor and llRound.

Functions | Math


llClearCameraParams()

Resets all camera parameters to default values and turns off scripted camera control.

Requires llRequestPermissions(key agent, PERMISSION_CONTROL_CAMERA). The agent (avatar camera) this function targets is the agent that gave PERMISSION_CONTROL_CAMERA to the script. (The UUID of which is returned by llGetPermissionsKey.)

For scripted camera control, see llSetCameraParams.

Functions | Camera | Agent/Avatar


llCloseRemoteDataChannel

llCloseRemoteDataChannel(key channel)

Closes XML-RPC channel.

This function does not appear to actually do anything; remote_data events will still fire if a request is received even after llCloseRemoteDataChannel is called, and it doesn't throw any sort of error even if you pass it a string that's clearly not a key. The only way to actually close an XML-RPC channel appears to be to cause the script to be recompiled.

Note: This function delays the script for 1.0 second.

Functions / XML-RPC


llCloud

float llCloud(vector offset)

Returns the cloud density at the current position + offset.

How clouds work:

  • SL features a cellular automata-based weather system.
  • There are 16x16 square cloud "cells" per sim (which means each is 16x16m in size). Does it need to be stated that there are boundry cloud cells in each sim, as is stated on Weather? -MalarthiBehemoth
  • The cloud density data is sent from the server to the client once every time the agent connects to a simulator via logging in or teleporting into a simulator.
  • The client attempts to render clouds from the cloud algorithm results sent from the server, which is why different clients can see different cloud formations and de-synchronises cloud view from the server over time.
  • The Z value in the vector is irrelevant (clouds are always between about 180-200m absolute height).
  • It returns a value between 0.0 and 1.0, with 0.0 being no clouds, and 1.0 very dense clouds.
  • Values returned above 1.0 - 2.0 indicate rainfall, but the option for rain is not currently implemented.
  • Values are no longer capped at 1.0, but nothing notable happens when 1.0 is exceeded.
  • Clouds and wind are related.

The cloud seed/growth/disipation algorithm uses a solenoidal vector field, which is a vector field with zero-divergence: nabla dot v = 0

This condition is satisfied whenever v has a vector potential, because if v = nabla times A then nabla dot v = nabla dot (nabla times A) = 0.

Functions | Simulator | Weather


llCollisionFilter

llCollisionFilter(string name, key id, integer accept)

If accept is TRUE, then the script only triggers collision events when colliding with the object or avatar specified by name and id. Otherwise, the script only accepts collisions with objects or avatars that are not name and id. Either parameter is optional, "" can be passed for name to specify objects or avatars with any name, and NULL_KEY can be passed for id to specify objects or avatars with any UUID.

Stop filtering:
llCollisionFilter("", NULL_KEY, TRUE);

Filter out all collisions:
llCollisionFilter("", NULL_KEY, FALSE);

This can be very useful if you only care about collisions from a specific avatar, such as the current player of a game--it can prevent other people from disturbing the gameplay.

Notes:
  • It appears as though this function only filters whatever the last filter specified was. This makes it useless if you only care about accepting collisions from individual avatars or objects (plural). It's only useful if you only care about a single object OR avatar--one or the other, never both or multiples.-- LuccaKitty
  • It appears that if you use this function with llVolumeDetect, llSetDamage will NOT generate a damage collision. Conversely, it will also not prevent damage when llVolumeDetect is set to FALSE. I think this has something to do with the specific way llCollisionFilter and llSetDamage operate--this is probably a bug. -JarhynWilde (have you reported it? -LexNeva )
  • This does not make the object ignore collisions, it just filters the collision event. -KyrahAbattoir
  • This function is reset at state changes, so call it again in the state_entry() event in all states where it is required. -JesradSeraph

Functions | Detection Functions | Collisions


llCollisionSound

llCollisionSound(string impact_sound, float impact_volume)

Suppresses the default collision sounds and replaces them with impact_sound (a sound in the object's inventory or a key) played at the volume impact_volume. Supply an empty string to suppress collision sounds. Oddly, an impact_volume of 0 will disable collision particles.

Question: I'd like to play the default sound, but at a quieter volume, say 0.5. What do I use for impact_sound to still use the default sound?
Answer: That varies, depending on the material used and its sound keys.

Compare with llCollisionSprite.

Functions | Sound | Collisions


llCollisionSprite

llCollisionSprite(string impact_sprite)

Replaces the default collision sprites (the orange particles you see when an object bumps into something/is bumped into) with a specified texture from the object's inventory or any texture's key. impact_sprite may also be a key. If a key is specified, the texture does not have to be in the object's inventory.

To suppress the default collision sprites, use an empty string for impact_sprite or set llCollisionSound's impact_volume to 0. (Yes, llCollisionSound affects llCollisionSprite.)

Compare with llCollisionSound.

Functions | Collisions | Sprite


llCos

float llCos(float theta)

Returns the cosine of theta radians.

Given a point on a circumference, it is the horizontal distance from the center, measured in radians.

____
|    \
|     B
|    /|\
|   / | \
|  /  |  \
| /   |   |
A/____C___|D
not a good circle, but measures the distance AC where DB is the number of radians (usually between 0 and TWO_PI) you must go around the circumference.

Radians | Angle | Cosine
0 | 0 | 1
0.392699 | 22.5 | 0.923880
0.785398 | 45 | 0.707107
1.178097 | 67.5 | 0.382683
1.570796 | 90 | 0
1.963495 | 112.5 | -0.382684
2.356194 | 135 | -0.707107
2.748893 | 157.5 | -0.923880
3.141593 | 180 | -1
3.534292 | 202.5 | -0.923880
3.926991 | 225 | -0.707107
4.319690 | 247.5 | -0.382684
4.712389 | 270 | 0
5.105088 | 292.5 | 0.382684
5.497787 | 315 | 0.707107
5.890487 | 337.5 | 0.923880

The following will rez 16 objects around the object containing the script in a circle. It will use the first object in its inventory as the object to rez.
default
{
    state_entry()
    {
        // name of first object in inventory
        string name = llGetInventoryName(INVENTORY_OBJECT, 0);

        // number of objects to rez
        float count = 16;

        // size of circle from center to the edge
        float radius = 5.0;

        // distance in radians between each object
        float step = (1.0 / count) * TWO_PI;

        // position on circumference to place object
        float radians;

        // move along circumference
        for(radians = 0.0; radians < TWO_PI; radians += step)
        {
            // get x/y position of circumference
            float x = llCos(radians) * radius;
            float y = llSin(radians) * radius;

            // translate relative to objects current position
            vector pos = llGetPos();
            pos.x += x;
            pos.y += y;

            // rez the object
            llRezObject(name, pos, ZERO_VECTOR, ZERO_ROTATION, 0);
        }
    }
}

Compare with llAcos.

Functions | Math


llCreateLink

llCreateLink(key target, integer parent)

Attempts to link the object containing the script and target (requires that permission PERMISSION_CHANGE_LINKS be set). If parent == TRUE, the object calling this function will be the parent prim of the new link set, and the target prim will become that parent's first child (inserted before any children the parent might have already had).

Notes:
  • This function delays the script for 1 second.
  • Attached (worn) objects cannot be linked/unlinked.
  • Attempts to link avatars to an object will fail with the message "Link failed -- avatar sitting on an object being linked."

The target doesn't require any script permission at all but does require that both objects' owners be the same and each object has modify permission.

Once the target has been linked, its changed event will be triggered with CHANGED_LINK. The target will then also be able to receive link_message events.

Q: What happens if the target is itself a linked object? Do links form trees, or are all the children of the target treated as children of the current object?
A: No, links are not trees. When one object links another, the new prims are added in the order in which they were added to the original object. Suppose we have Object A, consisting of three prims, [A1, A2, A3], and Object B, consisting of [B1, B2, B3]. Upon linking, the new object's link order would be [A1, B1, A2, A3, B2, B3]. This is the case with both linking via llCreateLink and the editing tools.
A: This may have changed recently. Currently, it appears that when linking two sets like those above, the resulting link-order will be [A1, B1, B2, B3, A2, A3], or more generally, [A1, (set of B in order), (rest of A in order) ].

Q: So is there a way to link two objects, with a resulting object's link order of [A1, A2, A3, B1, B2, B3]?
A: Not directly. Not only can you not specify where prims will end up in the resulting object, but you also can't selectively set a prim's position in the linkset. To accomplish this, you'd have to either break all links in the second object (and link its prims individually), or link it, delinking and relinking them individually. This second option is likely better, despite the extra step, as it could be more easily automated, and doesn't necessarily require the second object contain any scripts at all.

To break a link, use llBreakLink. To break all links, use llBreakAllLinks.

Functions | Link


llCSV2List

list llCSV2List(string src)

Create a list from a string of comma separated values (CSV) specified in src.

All list items will be of type string, regardless of their contents (llGetListEntryType will always return TYPE_STRING). This should be kept in mind if you intend to sort such a list using llListSort or transmit it via llMessageLinked.

llList2CSV performs the inverse function.

One thing to be aware of is that llCSV2List has a built in escaping mechanism. Anything inside angle brackets (like <0, 5, 10>) remains unparsed no matter if it is a vector, rotation or just garbage.

If you need more flexibility, such as separators other than comma, use llParseString2List instead.

Heres an example showing the difference between a list before and after using llCSV2List:

string list2stringtype(list input) { // converts list to a CSV string with type information prepended to each item
    list        output;
    string      s;
    integer     i;

    for (i = 0; i < llGetListLength(input); i++) {
        if (llGetListEntryType(input, i) == TYPE_INTEGER) s = "(integer)";
        else if (llGetListEntryType(input, i) == TYPE_FLOAT) s = "(float)";
        else if (llGetListEntryType(input, i) == TYPE_STRING) s = "(string)";
        else if (llGetListEntryType(input, i) == TYPE_KEY) s = "(key)";
        else if (llGetListEntryType(input, i) == TYPE_VECTOR) s = "(vector)";
        else if (llGetListEntryType(input, i) == TYPE_ROTATION) s = "(rotation)";
        else if (llGetListEntryType(input, i) == TYPE_INVALID) s = "(INVALID)";

        output += [s + llList2String(input, i)];
    }

    return llList2CSV(output);
}

default {
    state_entry() {
        list        l;

        l = [4, 4.4f, "banana", "15.3", <1,2,3>, <4,5,6,7>]; // this list has proper type information
        llSay(0, list2stringtype(l));

        l = llCSV2List(llList2CSV(l));  // this list is converted to a CSV string and back
        llSay(0, list2stringtype(l));   // causing it to loose all type information
    }
}

And here are two functions to safely transport lists through strings (as used in llMessageLinked) while retaining their type information:

string List2TypeCSV(list input) { // converts a list to a CSV string with type information prepended to each item
    integer     i;
    list        output;
    integer     len;

    len=llGetListLength(input); //this can shave seconds off long lists
    for (i = 0; i < len; i++) {
        output += [llGetListEntryType(input, i)] + llList2List(input, i, i);
    }

    return llList2CSV(output);
}

list TypeCSV2List(string inputstring) { // converts a CSV string created with List2TypeCSV back to a list with the correct type information
    integer     i;
    list        input;
    list        output;
    integer     len;

    input = llCSV2List(inputstring);

    len=llGetListLength(input);
    for (i = 0; i < len; i += 2) {
        if (llList2Integer(input, i) == TYPE_INTEGER) output += (integer)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_FLOAT) output += (float)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_STRING) output += llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_KEY) output += (key)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_VECTOR) output += (vector)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_ROTATION) output += (rotation)llList2String(input, i + 1);
    }

    return output;
}

default {
    state_entry() {
        list    l;
        string  s;

        l = [4, 4.4, "banana", "15.3", <1, 2, 3>, <4, 5, 6, 7>]; // a list with nice type information

        s = List2TypeCSV(l); // convert to CSV string with type information prepended to each item

        l = TypeCSV2List(s); // convert back to list with proper type information

        if (llGetListEntryType(l, 5) == TYPE_ROTATION) llSay(0, "Hooray!"); // it worked
    }
}

A variation using the newer dump/parse methods can be found at ExampleListConversion.

Functions | Lists | llList2CSV


llDeleteSubList

list llDeleteSubList(list src, integer start, integer end)

Returns a list equal to src without the slice defined by start and end. The start and end are inclusive, so 0, llGetListLength(src) - 1 would cause the function to return an empty list, and 0, 0 would cause the function to return a list equal to src without its first element. Using negative numbers for start and/or end causes the index to count backwards from the length of the list, so 0, -1 would cause the function to return an empty list. If start is larger than end the list returned is the exclusion of the entries, so 6, 4 would cause the function to return only src's 5th list entry.

IMPORTANT NOTE: This function does not manipulate src directly. To manipulate the list you pass as src, you must set src equal to the return value of this function.

Example:
list src = ["A", "B", "C"];
llDeleteSubList(src, 1, 1);
// ^^^ After this line, src is still ["A", "B", "C"] because the return value is discarded

src = llDeleteSubList(src, 1, 1);
// ^^^ After this line, src is ["A", "C"] because the return value is assigned to it

Compare with llListInsertList.

Lists | Functions


llDeleteSubString

string llDeleteSubString(string source, integer start, integer end)

Returns a copy of source with the indicated substring removed. The source is unaltered.

The start and end are inclusive, so 0, length - 1 would return an empty string and 0,0 would delete the first character. Using negative numbers for start and/or end causes the index to count backwards from the length of the string, so 0, -1 would delete the entire string. If start is larger than end the substring is the exclusion of the entries, so 6, 4 would delete the entire string except for the 5th character.

Compare to llGetSubString.

Example:
string test = "Example";

llSay(0, llDeleteSubString(test, 2, 5)); // outputs "Exe"

Functions | String


llDetachFromAvatar()

Detaches the scripted object from the avatar it's attached to. Requires PERMISSION_ATTACH (via llRequestPermissions).

After detaching, the attach() event hander is called with the value NULL_KEY.

Calling this function from a child-prim will fail silently.

Clicking on an object in-world that calls llAttachToAvatar moves that object into your inventory (also highlights it and labels it 'worn'). You can see it there as soon as it's attached. Calling this function will leave the item in your inventory. This can make for a novel version of 'take' that only relies on the left mouse button and provides a confirmation dialog via the permissions request.

Q: Is there a way to delete an attachment instead of just detaching it?
A: Not directly. llDie is the function used to delete objects, but it doesn't work on attachments.
Instead of simply calling llDetachFromAvatar on its own, your best option is to switch states to a "dead" state -- one where the attach() event handler calls llDetachFromAvatar, causing the attachment to automatically detach, (with PERMISSION_ATTACH granted, of course) and where on_rez() contains llDie. This will cause the object to be deleted, should it be rezzed on the ground. You can also stick llSetObjectName in the "dead" state's state_entry(). This would rename it to something like "Expired Attachment - Delete Me", indicating to the inventory's owner that they're free to get rid of it.

Functions | Agent and Avatar | Attachment | Constants


llDetectedGrab

vector llDetectedGrab(integer number)

Returns the grab offset of detected object number (returns <0,0,0> if number is not a valid sensed object).

This is the direction the object (must be moveable) is being dragged in.

If the object is moving on its own (see dynamics), and this function is triggered, it returns <0,0,0>.

As of SL 1.8.2(7), this function works on both physics-enabled and non-physical objects.

Example:
default
{
    touch(integer num_detected)
    {
        llOwnerSay((string)llDetectedGrab(0));
    }
}

Note: Unlike the other llDetected* functions, llDetectedGrab will only return a meaningful value in the touch() event.

HUD Attachments
In the case of HUD attachments, llDetectedGrab will always be relative to the direction the avatar is facing. This means that you can produce a script that will allow you to use llDetectedGrab in HUD attachments; however, it will not perform correctly if you move the camera around. Regardless of where they appear to be on your screen, HUD attachments are still attachments, which are fixed to the avatar's rotation, despite appearances to the contrary.

Q: I can't get this to work. What's going on?
A: Is llDetectedGrab inside your touch() event? If it's within touch_start() or touch_end(), it won't work.

Functions | Detection


llDetectedGroup

integer llDetectedGroup(integer number)

Returns TRUE if detected object or agent number has the same user group active as this object. It will return FALSE if the object or agent is in the group, but the group is not active.

Note: It will return TRUE if both the object or avatar detected and its object have no group set!
Note: this function refers to groups of users, not linked objects.

Note: The llDetected* functions will only return a meaningful value in the collision(), collision_start(), collision_end(), sensor(), touch(), touch_start(), or touch_end() events.

See also llSameGroup.

Functions | Detected Functions


llDetectedKey

key llDetectedKey(integer number)

Returns the key of detected object number (returns NULL_KEY if number is not valid sensed object).

Note: The llDetected* functions will only return a meaningful value in the collision(), collision_start(), collision_end(), sensor(), touch(), touch_start(), or touch_end() events.

Q: Is this a 0-based or 1-based index?
A: 0-based; This function starts counting at 0. -Chris

Functions | Detection | Sensors


llDetectedLinkNumber

integer llDetectedLinkNumber(integer number)

Returns the link position (0 for a non-linked object, 1 for the root (parent) of a linked object, 2 for the first child, etc.) of the object the touch or collision event is triggered for.

See also llPassTouches and llPassCollisions.

This is most useful for finding out which prim was touched without having to put a script into each prim.

Note:
As of SL 1.6.7, llDetectedLinkNumber will only ever return 0 when used with llVolumeDetect, even when used in a multi-prim object. This may be caused by the fact that llVolumeDetect was added some time after the original detected functions. There is not yet any word on when this might be fixed.

Note to Note
Actually, I just tried it and when the llDetectedLinkNumber is in a touch_start event, and in an unlinked prim, it returns 0.
Functions | Detected Functions | Link


llDetectedName

string llDetectedName(integer number)

Returns the name of detected object number (returns an empty string if number is not valid sensed object).

If the detected target has left the region or ceased to exist, this function will return the key of the object/agent instead of the name.

Note: The llDetected* functions will only return a meaningful value in the collision(), collision_start(), collision_end(), sensor(), touch(), touch_start(), or touch_end() events.

Q: How do I figure out what to use for number? I'm confused.
A: For most uses, if you're using a one-off event like collision or touch_start, you can use 0, the first object number. If you're using a sensor, try a for loop, with number incrementing, to cycle through all detected objects.

Functions | Detection


llDetectedOwner

key llDetectedOwner(integer number)

Returns the key of the owner of detected object number (returns NULL_KEY if number is not valid sensed object).

Note: The llDetected* functions will only return a meaningful value in the collision(), collision_start(), collision_end(), sensor(), touch(), touch_start(), or touch_end() events.

Note: llDetectedOwner returns a NULL_KEY if the detected object is deeded to a group. This is the only way to discover that a sensed object is deeded and a dataserver request with llRequestAgentData for more info will definitively time out. Nevertheless llGetOwnerKey returns a unique key for a group owned object.

Functions | Detected Functions


llDetectedPos

vector llDetectedPos(integer number)

Returns the position of detected object number (returns <0,0,0> if number is not valid sensed object).

Note: The llDetected* functions will only return a meaningful value in the collision(), collision_start(), collision_end, sensor, touch, touch_start, or touch_end events.

Functions | Detection


llDetectedRot

rotation llDetectedRot(integer number)

Returns the rotation of detected object or agent number. (Returns ZERO_ROTATION (<0,0,0,1>) if number is not a valid sensed object.)

Agents that are not in mouselook can only turn around the world Z-axis (unless click-dragging the mouse cursor on the avatar), while those that are in mouselook can rotate in all three directions (the rotation returned here is actually that of the head, i.e. where the avatar is facing). This makes it possible to usually determine if a user is in mouselook or not (the chance that a user manages to look straight and level in mouselook is pretty small).

Example:
// determine if someone is in mouselook or not
// this method is incorrectly reporting someone to be out of mouselook when they
// reset their camera by pressing escape, then enter mouselook and hold their mouse still
default {
    state_entry() {
        llSensorRepeat("", llGetOwner(), AGENT, 50.0, PI, 0.5); // sensor for the scripts owner
    }

    sensor(integer num_detected) {
        rotation rot = llDetectedRot(0); // the owner's rotation

        if (llVecMag(<rot.x, rot.y, 0>) > 0.001) {
            // yup, we're in mouselook
            llSetColor(<0, 0, 1>, ALL_SIDES);
        } else {
            // nope, not in mouselook
            llSetColor(<1, 1, 1>, ALL_SIDES);
        }
    }
}

Note:
  • A more reliable method of determining whether a given agent is in mouselook is to use llGetAgentInfo.
  • The llDetected* functions will only return a meaningful value in the collision, collision_start, collision_end, sensor, touch, touch_start, or touch_end events.

Functions | Detection Functions | Sensors | Mouselook


llDetectedType

integer llDetectedType(integer number)

Returns the detected object type (AGENT, ACTIVE, PASSIVE, SCRIPTED) number (returns 0 if number is not a valid sensed object; other values are on sensor).

Note: The return value is a bitfield, so comparisons need to be an AND check:
if (llDetectedType(0) & AGENT)
{
    // do stuff
}

Note: The llDetected* functions will only return a meaningful value in the collision(), collision_start(), collision_end(), sensor(), touch(), touch_start(), or touch_end() events.

Functions | Detection | Object Type


llDetectedVel

vector llDetectedVel(integer number)

Returns the velocity of detected object number (returns <0,0,0> if number is not valid sensed object).
The velocity returned is relative to the global axes.

Note: The llDetected* functions will only return a meaningful value in the collision, collision_start, collision_end, sensor, touch, touch_start, or touch_end events.

Functions | Detection | velocity


llDialog

llDialog(key id, string message, list buttons, integer chat_channel)

Shows the user specified by id a popup dialog box (in the upper right corner of their SecondLife window) containing message and buttons (and an "Ignore" button). Selecting any of the buttons will cause the avatar to say the text of that button on channel chat_channel.

Even though the chat will originate with the user's name and key, the chat position will be centered at the object calling llDialog. This ensures the object will be always within listening range of the answer.

Using DEBUG_CHANNEL as the chat_channel will cause avatar to say the button name on the public channel, rather then on the debug channel as one might expect.

Limits to message:
  • message must be less than 512 characters and may not be empty, though it can contain a single space which will give the appearance of an empty message. If message is empty, the object will say "llDialog: must supply a message".
  • If message is greater than 512 characters, the object will say "llDialog: message too long, must be less than 512 characters" and the dialog box will not appear.
  • Newline "\n" and tab "\t" escape sequences are valid for formatting the message. Messages with more than 8 lines (including the result of text wrap) will cause a vertical scrollbar to be displayed inside the dialog. There isn't a way to change the actual size of the dialog box.

Limits to buttons:
  • buttons must be a list of strings; other types are not accepted. Attempting to pass a list containing other types will result in the object saying "llDialog: button list must contain only strings".
  • buttons must have 12 or fewer entries and can be empty. An empty buttons will simply show the message with an "OK" button.
  • Supplying a space (" ") for an item in buttons will result in an empty button. Clicking the button will still cause the client to say " ".
  • Empty strings ("") will cause the the object will say "llDialog: all buttons must have label strings" and will not display the dialog box.
  • Entries in buttons cannot be longer than 24 characters. Attempting to pass llDialog a list with entries longer than 24 characters will result in the object saying the message "llDialog:Button Labels can not have more than 24 characters.", and then fail to show the dialog box.
  • While actual button width is fixed (three buttons to a line), the amount of text that will fit on a button is limited and varies with character width. ("O" is wider than "i", for example.) If the button label is too long to be viewed, it will be cut off and "..." will be appended. However, the chat output will still contain the full string.

If the user selects "Ignore", the script will not receive an answer, so implement a timeout (recommended) or allow the dialog to be restartable from anywhere.

Notes

  • llDialog will only work if the user identified by id is in the same region or if they have been to that region and left but have not yet logged out.
  • This function delays the script for 1 second.
  • Buttons are drawn left to right, bottom to top. Supplying the list ["1","2","3","4","5"] to buttons will result in "4", "5", a space, and on the following line, "1", "2", "3". (see code below for a fix)
  • As of SL 1.8.2(9), the system automatically prepends the string "<Object Owner Name>'s <Object Name>:\n\n" to message before it is displayed.

Example

// when touched, present a dialog with four color choices

integer CHANNEL = 42; // dialog channel
list MENU_MAIN = ["Sit", "Stand", "Fly", "Cheat", "Options..."]; // the main menu
list MENU_OPTIONS = ["Cherry", "Blueberry", "Vinegar", "Slime", "Chips", "Salad", "...Back"]; // a submenu

default {
    state_entry() {
        llListen(CHANNEL, "", NULL_KEY, ""); // listen for dialog answers (from multiple users)
    }

    touch_start(integer total_number)
    {
        llDialog(llDetectedKey(0), "What do you want to do?", MENU_MAIN, CHANNEL); // present dialog on click
    }

    listen(integer channel, string name, key id, string message)
    {
        if (llListFindList(MENU_MAIN + MENU_OPTIONS, [message]) != -1)  // verify dialog choice
        {
            llSay(0, name + " picked the option '" + message + "'."); // output the answer
            if (message == "Options...")
                llDialog(id, "Pick an option!", MENU_OPTIONS, CHANNEL); // present submenu on request
            else if (message == "...Back")
                llDialog(id, "What do you want to do?", MENU_MAIN, CHANNEL); // present main menu on request to go back
            // here you have the name and key of the user and can easily verify if they have the permission to use that option or not
            else if (message == "Sit")
                llSay(0, "This is where stuff would happen if this wasn't just an example");
        } else
            llSay(0, name + " picked invalid option '" + llToLower(message) + "'."); // not a valid dialog choice
    }
}

//Compact function to put buttons in "correct" human-readable order ~ Redux
list order_buttons(list buttons)
{
    return llList2List(buttons, -3, -1) + llList2List(buttons, -6, -4) + llList2List(buttons, -9, -7) + llList2List(buttons, -12, -10);
}

//Function that puts buttons in "correct" human-readable order without relying on intelligent higher order list functions ~ Redux
list order_buttons(list buttons)
{
        integer offset;
        list fixt;
        while((offset = llGetListLength(buttons)))
        {
            fixt += llList2List(buttons, (offset = -(offset%3 + 1)), -1);
            buttons = llDeleteSubList(buttons, offset, -1);
        }
        return buttons;
}

Q & A
Q: What is the equivelant range of llDialog (whisper, say, shout, regionsay)?
A: Responding to the dialog will work as if the object were to use llSay(channel,message), where 'channel' is the channel specified by llDialog and 'message' is the text that was on the button choosen. This means the range is a 20-meter radius centered on the object.

Q: Is there a way to extend (or narrow) the range?
A: Not directly. You could use the listen event trigger a llShout or llRegionSay when it hears a dialog choice, but it doesn't appear to be possible to narrow the range unless you do something equivalent to an if/else and llVecDist.

Functions | Agent/Avatar | Chat | Communications | User-Interface


llDie();

Deletes the object that contains the script. That means if the prim containing this script is part of a linkset, all the prims will be deleted, regardless of whether or not the prim is the root or a child in the linkset. To delete just one prim from a linkset, first detach it with llBreakLink.

llDie will not work for attachments. Use llDetachFromAvatar instead.

Warning: Objects deleted via llDie are NOT returned to the owner's inventory (not even to their "Trash" folder). There is no way to get them back.

Q: How do I delete another object?
A: You can't. Your best option is to place a script inside the object you wish to delete and then use chat to tell it when to do so.
Alternatively, llGiveInventory can be used to give a copy of a script to another object, but the script will always be turned off within the recipient object. Scripts can only be turned on by manually activating them.
llRemoteLoadScriptPin may be useful, but if you can already get into the object that way, you can likely put a script containing llDie in it already.

Q: How do I make my object delete itself after a certain period of time?
A: Use llSleep or the timer event.

Q: How do I make my object delete itself if it goes off my land?
A: Use a timer and llGetLandOwnerAt(llGetPos()).

Q: How do I make my object delete or return itself when it leaves the sim?
A: See llSetStatus and the STATUS_DIE_AT_EDGE and STATUS_RETURN_AT_EDGE constants.

Q: Is there an event that runs when the object is deleted or taken into inventory?
A: No. The attach() event will be triggered when detaching an attached object, but that's it. llDie will not do anything within an attachment, however.
If you're wanting to create some sort of cleanup function or to disable your object after use, (to create a demo version, or something that expires, for instance) use on_rez() and some kind of counter.

Functions | Rez


llDumpList2String

string llDumpList2String(list src, string separator)

Concatenates all items in the list src into a single string, placing the specified separator between values.

llParseString2List performs the opposite function.

Example:
list foo = ["a", "b", 5, ZERO_VECTOR];

llSay(0, llDumpList2String(foo, " + ")); // outputs: "a + b + 5 + <0.000000, 0.000000, 0.000000>"

See also llList2CSV and ExampleListConversion.

Lists | String


llEdgeOfWorld

integer llEdgeOfWorld(vector pos, vector dir)

Returns TRUE if the line along the direction dir from the position pos hits the edge of the world in the current simulator. Returns FALSE if that line crosses into another simulator.

Basic Example:
// returns TRUE if you're in a sim with no neighbor to the north, otherwise FALSE.
llEdgeOfWorld(llGetPos(), <0, 1, 0>)

Useful Example:
You're probably more interested in knowing if, while travelling from here to there, you would fall off the world. This function will tell you:

integer wouldGoOffWorld(vector here, vector there)
{
    if (there.x < 0 || there.x > 256 || there.y < 0 || there.y > 256)
    {
        return llEdgeOfWorld(here, there - here);
    }
    else
    {
        return FALSE;
    }
}

Functions | Simulator


llEjectFromLand

llEjectFromLand(key user)

Ejects user from land that you own-- user will be automatically unsat if sitting.

Ejecting means pushing upwards and away from your land. This is not hindered by objects; ejected users will go right through them.

Note: when dealing with group land, the object owner must be the group. It won't work if the object owner is a member or officer in the group; the object itself must be deeded.

Compare with llUnsit and llTeleportAgentHome.

This can be a possible cure to grief or just cause more grief. Be careful with this function!

Q: Hey, I am the creator of Dayton Home Security, and have been selling it no problem for months. Just this week new buyers have been IMing me and saying that Lindens are shuting them off. People have been saying that it's ejecting them even when they aren't on the owner's land. I used no llPushObject functions in this security, and llEjectFromLand has no power over land that is not owned by its owner. Anyone know what's going on?!? Beware. - Ryan Dayton
A: Ejecting people from land as they innocently fly over it, sometimes at high altitude, is griefing and is against the TOS. You need to build in some kind of delay to your system, or something that doesn't send people home just because they had the nerve to glance or accidentally fly over the person's land for a few moments. Sometimes these people are in vehicles, and they lose their vehicles with systems like this. - Hank Ramos

functions | agent/avatar | land


llEmail

llEmail(string address, string subject, string message)

Sends email to address with the specified subject and with message in the body.

These emails will always have the sender [key]@lsl.secondlife.com where [key] is the asset-ID of the prim containing the script sending the email (see llGetKey). Example sender: a311ce4d-e755-f148-7999-56f289ca3f3b@lsl.secondlife.com. Emails sent to such an address can be read using llGetNextEmail.

The length of the entire email is limited to 4096 characters, including the subject, message, and other headers (such as address), as well as CR + LF and the prepended lines to the body.

In the message, the "\n" escape sequence is translated into "\r\n" (CR + LF) in order to generate RFC 2821 compatible line terminators. If you don't include them yourself, such a line terminator is automatically inserted after every 998 (exact number) characters (as required by the SMTP standard). Do note the email event has its own restrictions.

As of 1.6.10 - The entire email message, including headers (email, subject, etc) and message (including the pre-pended text) is 4096. Thus, the message length can only be 4096 - <headers/subject> - <prepended text>

After sending the email, this function delays the script for 20 seconds.

Unlike IM sessions with people, there is no need to first send a message before being able to receive one. Note that agents who send you an IM while you're offline have a sender of [session]@im.secondlife.com.

Note that scripts compiled prior to 1.3.5 must be recompiled, as older scripts were compiled with a bug that caused the the "\n" escape sequence to be incorrectly translated to CR (^M) instead of CR + LF (^M^J) as required by RFC 822 (and its successor RFC 2822). This bug is now fixed.

The message body will always have four lines prepended before your actual message, containing the object name, the region name and grid location, the object's position in region coordinates and an empty line before message.

Email example:
Object-Name: Object
Region: Gibson (254976, 256000)
Local-Position: (117, 129, 50)

The real message starts here.

For an example of llEmail in action, see the email entry.

Q: What is the range of IP addresses I need to open up on my server to be able to receive email from SL?
A: The following netblocks:
66.150.244.0/23
69.25.104.0/23
72.5.12.0/22
-Francis

Q: What is the best way to cut out the headers in LSL if another object is receiving this email, and we only want the message?
A: Can't say if it's the best way, but I use:
string MessageWithoutHeaders = llDeleteSubString(OriginalMessage, 0, llSubStringIndex(OriginalMessage, "\n\n") + 1)
-Prio

Q: How many emails can you send before LL decides you've sent enough and blocks it?
A: It's more a volume issue >.> one of our heightmapper URL domains got killed when I had rezzed about... 15 heightmappers... LL determined it was an attack and quietly blackholed the e-mail address, I never heard back, and since designed my system to not be near as bad, but it's slow now >.< -- Ice
-- Well, I think it is between 50 - 100 emails / min before they do something like that.. I had something similar that sent around 120 emails / min and I was fine, but they where small, around 200 characters. Alpha
Data in this A has to be out of date... throttling seems to be handled by the server itself as of 1.6, as the high volume mailer I have would just stop recieving e-mails from the sim at one point, further tests are needed. - Ice
-- It is, there has been changes in the email system recently through my experiments. Through a linden, I was able to understand that the limit is 1 email / second for an extended amount of time. You will then be throttled until the day's email rate for you has slowed down to that rate, thus, at 12:00 AM PST, the throttling list is cleared out. This amount of time is yet unknown, but it seems to be proportional to the rate. Ex: If you send an email every 10th of a second, you will be throttled faster than if you sent every half second. Emails are throttled by the hostname of the destination server, not the domain as many believed in the past. If the email is sent from an object in SL to another object in SL, the sender's key is throttled. I have to still do some more experiments to conclude this result. If the version is 1.9.1 or later, use llHTTPRequest instead, it is much faster and can send up to the script's memory in data at once. -- Alpha

Q: How do I CC: an email to multiple recipients?
A: Despite being part of the email standard, you can't. Use multiple llEmail calls. (Note: llMessageLinked may come in handy here.)

1.6 Released the other day to find some unsuspecting results with llEmail. If no subject is used(empty string), the email will not send. this can be fixed with a simple space as the subject. Those that rely on emails with no subjects might need to do some script revision ~_< -MingChen

LindenLab uses PostFix.

Compare with llGetNextEmail.

Functions | Communications


llEscapeURL

string llEscapeURL(string url)

Returns the string that is the URL-escaped version of url (replacing spaces with %20, etc).
This function returns the UTF-8 encoded escape codes for selected characters.

Only numbers and ASCII letters are not escaped: [^0-9a-zA-Z]

As of 1.9.0(21) this function only returns a string with 254 characters, this is unacceptable. If all your characters need escaping then this will only allow the user to work with 84 bytes. If those 84 bytes represent 6byte characters (top of the UTF-8 range) then you will only be able to decode 14 characters.- BW

See llUnescapeURL.

Functions


llEuler2Rot

rotation llEuler2Rot(vector vec)

Returns the rotation (a quaternion) represented by Euler angle vec (in radians).

The inverse function is llRot2Euler.

Example:
vector eul = <0, 0, 45>; //45 degrees around the z-axis, in Euler form
eul *= DEG_TO_RAD; //convert to radians rotation
rotation quat = llEuler2Rot( eul ); //convert to quaternion
llSetRot( quat ); //rotate the object

Functions | rotation | rotation math library


llFabs

float llFabs(float num)

Returns the absolute value of a float. The absolute value of a number is its "distance" from 0. To put it another way, if val is positive or 0, llFabs returns val. If val is negative, llFabs returns val * -1.

Examples of arguments:
num | return value
-4.0 | 4.0
-3.75 | 3.75
2.34 | 2.34
1.203 | 1.203

Basically, it kills off the negative sign of a float. To have this functionality for integers, it is recommended using llAbs instead.

Functions | Math


llFloor

integer llFloor(float val);

Returns the largest integer value <= val (rounds down).

llFloor(val) is equivalent to (integer)val if val >= 0. (integer)val is preferred, because it is faster.

The usage of these functions can be tricky. For examples of potential issues see llFrand.

Compare with llCeil and llRound.

Functions | Math


llForceMouselook

llForceMouselook(integer mouselook)

After calling this function with mouselook set to TRUE, any agent sitting down on the prim will be forced into mouselook.
Just like llSitTarget, this changes a permanent property of the prim (not the object) and needs to be reset by calling this function with mouselook set to FALSE in order to disable it.

Note: This only affects agents who sit on the object after the function has been called. Any current sitters remain unaffected until they resit.

The agent is forced into mouselook upon sitting on such a prim, but can exit mouselook on their own afterwards. It's useful for vehicles and the like.

Q: Great, but how do I force someone out of mouselook?
A: Unfortunately, you can't.

Functions | Mouselook


llFrand

float llFrand(float max)

Returns a random float x from the interval [0.0, max), that is, 0.0 <= x < max. Ideally, the values returned would be uniformally distributed, but it has been reported that they are not very uniform.

To get a random float from [min, max) use something like

float randBetween(float min, float max)
{
    return llFrand(max - min) + min;
}

Integers

There are many cases where a random integer value would be useful: lists, channels, constants, etc. Try something like
integer randInt(integer n)
{
     return (integer)llFrand(n + 1);
}

integer randIntBetween(integer min, integer max)
{
    return min + randInt(max - min);
}

It should be noted, that llFrand can only ever return 24 random bits.

Rounding

There are a couple different functions for converting a float to an integer, in addition to casting. They will not yield evenly distributed results. The correct solution is to use an integer typecast (integer).

//right way
integer random = (integer)llFrand(4.0);//will return an integer 0 to 3, all with equal probabilities (more or less).

A first idea might be to use llRound but that has problems with the distributions. For example:

//wrong, do not do this.
integer random = llRound(llFrand(4.0));

In this case the values will range from 0 to 4 which is more intuitively obvious than the example below. However, the values returned by llFrand that will result in the integer 0 range from 0.0 to 0.5, and the values that will result in the integer 1 will be 0.5000001 to 1.5. As you can see the range for integer 1 is twice as large. That means a 1 will be "randomly" generated twice as often as a 0 which is probably not desired. The same effect happens at the far end with the integer 4.

Using llCeil is a bit safer, but still wrong:

//still wrong, do not do this (unless the input to llFrand is negitive, at which point it will do the same thing as an integer typecast)
integer random = llCeil(llFrand(4.0));

By rounding up it seems like this would result in a straight forward range from 1 to 4. It's unlikely, but possible, that llFrand will return a pure 0.0 value. In that case llCeil will not round up and it will return anomalous 0 values in very disproportionate frequency to the other values. The 0 is actually stealing that bit of probability from the 4, so an integer 4 will be infinitesimally less likely to occur than the others.

//still wrong, do not do this (unless the input to llFrand is positive, at which point it will do the same thing as an integer typecast)
integer random = llFloor(llFrand(5.0));

Using 5.0 for the input isn't obvious but results in float numbers between 0.0 and 4.99999. The floor function truncates the decimal. This results in values of 0 through 4. This is a perfect distribution of probability and the numbers drop right into list usage being zero indexed. So think of the input number as how many possible integers desired and, remember, they start with 0. But if input to llFrand is negitive this will have the same problem as llCeil

Solutions

The correct solution is to use the (integer) typecast. For example, to get a random integer ranging from 0 to 4:

integer random = (integer) llFrand (5.0);

Functions | Math


llGetAccel

vector llGetAccel()

Returns the current acceleration (in m/s²) exhibited by an object.

Example: If you create a cube, check its physics checkbox, and drop it from a height, it will initially return <0,0,-9.8> (the value for gravity in SL) while falling. This value will decrease, because some funky wind resistance seems to be at work to allow falling objects to achieve terminal velocity.

Compare with: llGetVel

Functions | Dynamics


llGetAgentInfo

integer llGetAgentInfo(key id)

Returns a bitfield of id agent information:

Constant | Represents | Value
AGENT_FLYING | Returned if agent is flying. | 0x0001
AGENT_ATTACHMENTS | Returned if agent has attachments. | 0x0002
AGENT_SCRIPTED | Returned if agent has scripted attachments. | 0x0004
AGENT_MOUSELOOK | Returned if agent is in mouselook. | 0x0008
AGENT_SITTING | Returned if agent is sitting. | 0x0010
AGENT_ON_OBJECT | Returned if agent is sitting on an object. | 0x0020
AGENT_AWAY | Returned if agent is in "away" mode. | 0x0040
AGENT_WALKING | Returned if agent is walking. | 0x0080
AGENT_IN_AIR | Returned if agent is in the air (hovering). | 0x0100
AGENT_TYPING | Returned if agent is typing. | 0x0200
AGENT_CROUCHING | Returned if agent is crouching. | 0x0400
AGENT_BUSY | Returned if agent is in "busy" mode. | 0x0800
AGENT_ALWAYS_RUN | Returned if agent has running ("Always Run") enabled. | 0x1000

From what I've found, this function only returns meaningful values (or anything, really) if your own object calls it while it's on your own land. Basically, if you own the object and own the land, it will work. Otherwise, I haven't seen it work properly (or at all). - HawksterWestmoreland

Notes on the above:
  • Agents are IN_AIR and not FLYING when they are jumping, falling or kept away from the ground by means of a script.
  • Agents can have ATTACHMENTS that are not SCRIPTED
  • Agents can be SITTING on the ground, which means they are not sitting ON_OBJECT. (When sitting on an object, they are both SITTING and ON_OBJECT.)
  • The agent defined by id must be in the same simulator as the script calling this function.

Example:
// when touched, check if the agent is flying and say so.
default
{
    touch_start(integer total_number)
    {
        // we use a FOR loop here because two people can click the object at the same time.
        integer i;
        for (i = 0; i < total_number; i++)
        {
            // llGetAgentInfo returns a bitfield. Use & instead of == for comparisons.
            if (llGetAgentInfo(llDetectedKey(i)) & AGENT_FLYING)
            {
                llSay(0, llDetectedName(i) + ", you're flying.");
            }

            else
            {
                llSay(0, llDetectedName(i) + ", you're not flying.");
            }
        }
    }
}

Also compare with llRequestAgentData.

Q: I can't find 'Always Run' in the client, and it seems to be off. Is this deprecated?
A: It's in the World menu. It also has a hotkey, ctrl + r.

Q: Is there a way to find out additional information about attachments found with AGENT_ATTACHMENTS or AGENT_SCRIPTED?
A: No. There's no way to determine how many attachments an avatar has, nor where they're attached, or what they are.

Q: Is there a way to find the key of the object the agent is sitting on?
A: No.

Q: How long does it take for the agent to enter AFK mode from the last time they did anything?
A: 10 minutes. The agent can still be acted upon by scripts and objects within SL, but if there's no mouse or keyboard input, they'll go AFK after 10 minutes.

Q: What is crouching? How does an avatar crouch?
A: By pressing the DOWN Button when not flying.

Functions | Agent/Avatar


llGetAgentSize

vector llGetAgentSize(key id)

If id is in the same sim region as the script calling this function, it returns a vector representing the size of the avatar. This is useful for approximating the height of the avatar. This function returns a ZERO_VECTOR when the key is not an agent or the agent is not in the sim.

The returned vector holds the size of the avatar's bounding box, of which only the height (z) varies. Width (x) and depth (y) are constant. (0.45m and 0.6m respectively). While the size returned by this function does not change when the avatar sits down (whether on an object or on the ground), the bounding box returned by llGetBoundingBox will change its size to include the object the avatar is sitting on.

If using this to determine avatar height, remember that the bounding box is not the same as the actual avatar height. Keep in mind that this is an approximation. The actual avatar height can be taller or shorter than the returned measurement based on factors like hair, shoes, avatar height, torso length and leg length. You can eyeball the exact height for a specific avatar with a prim if you need precision.

Avatars in SL tend to be tall. The average is probably around 2 meters.

Example:
default {
    touch_start(integer num_detected) {
        vector size;

        size = llGetAgentSize(llDetectedKey(0));
        llSay(0, "You are " + (string)size.z + "m tall.");
    }
}

Q: Is there a way to automatically trigger a script when an avatar's size changes?
A: No. To do so, you'll need to use a timer, or some other kind of regular check. This can be laggy.

Functions | Agent/Avatar


llGetAlpha

float llGetAlpha(integer face)

Returns the alpha (transparency) of the given face.

If face is ALL_SIDES, the value returned is the sum of alphas on all faces. To obtain the mean value, use the following:

float meanAlpha = llGetAlpha(ALL_SIDES) / ((float)llGetNumberOfSides());

Contrary to setting up the alpha of objects using edit mode, "alpha" in scripts is defined by a normalized float between 0 and 1 where 1 would be completely opaque, and 0 would be completely transparent.

Alpha can be set using llSetAlpha.

See also llGetPrimitiveParams.

Q: I have a 100% alpha texture that I applied to my object, but llGetAlpha is still reporting 1. What gives?
A: This isn't actually a bug. Rather, it's displaying the texture as you've applied it at 100% visible. The fact that the texture itself is completely alpha doesn't change that fact. Regardless of how it appears on the client, the sim itself has no idea what a texture looks like.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture


llGetAndResetTime

float llGetAndResetTime()

Gets the time in seconds since the last script reset (or the last call to this function or llResetTime) and resets the time to zero. This offers greater precision than calling llGetTime and llResetTime separately.

Example:
default
{
    touch_start( integer total_number )
    {
        float elapsed = llGetAndResetTime();
        float deadline = 10.0;

        if ( elapsed >= deadline )
            llWhisper( 0, "I'm sorry, you were too slow: "
                          + (string)elapsed
                          + " seconds" );
        else
            llWhisper( 0, "Yay!  You beat the deadline by "
                          + (string)(deadline - elapsed)
                          + " seconds" );
    }
}

Functions | Time


llGetAnimation

string llGetAnimation(key id)

Returns the current basic animation state for avatar id.

The basic animation states are the simplest description of the avatar's posture.

Here's a list of basic animations states returned by this function:

Crouching
Hovering
Hovering Down
Hovering Up
Jumping
PreJumping
Running
Sitting This is returned even when the avatar is sitting on something that stopped the generic sit animation and plays its own instead.
Sitting on Ground
Standing
Striding
Flying
FlyingSlow
Falling Down
Standing Up After falling a long distance, the Landing animation state is skipped and the Standing Up state is used. -EyanaYohkoh
Landing
Soft Landing
CrouchWalking
Turning Left
Turning Right
Walking

Note: This function does not return the specific animations currently played with llStartAnimation. See llGetAnimationList.

Example:
// make a list of all animations states
list l;

default {
    state_entry() {
        llSetTimerEvent(0.25); // poll current state (there is no event for these)
    }

    timer() {
        string s;

        s = llGetAnimation(llGetOwner()); // get the current animation state
        llSetText(s, <1, 1, 1>, 1.0); // display it as floating text
        if (llListFindList(l, [s]) == -1) l += [s]; // if it's not already in the list, add it
    }

    touch_start(integer num) {
        if (llDetectedKey(0) != llGetOwner()) return; // ignore everyone but the owner

        llInstantMessage(llGetOwner(), llList2CSV(l)); // IM the list to the owner
    }
}

This function may seem slow in updating animation states. For example, in things like animation overrides, waiting until llGetAnimation returns "Standing" can cause the walk animation to continue for a moment after the avatar has stopped. If this affects you, try checking llGetVel instead, to see if you have stopped moving. --DirtyMcLean

Functions | Agent/Avatar | Animation


llGetAnimationList

list llGetAnimationList(key id)

Returns a list of keys of all playing animations for avatar id.

llStopAnimation or llStartAnimation may be useful here.

Example:
// stops all currently running animations when clicked
default {
    touch_start(integer num_detected) {
        llRequestPermissions(llDetectedKey(0), PERMISSION_TRIGGER_ANIMATION);
    }

    run_time_permissions(integer perm) {
        if(perm & PERMISSION_TRIGGER_ANIMATION)
        {
            list anims = llGetAnimationList(llGetPermissionsKey());        // get list of animations
            integer len = llGetListLength(anims);
            integer i;

            llSay(0, "Stopping " + (string)len + llGetSubString(" animations",0,-1 - (len == 1)));//strip the "s" when there is only 1 to stop.
            for (i = 0; i < len; ++i) llStopAnimation(llList2Key(anims, i));
            llSay(0, "Done");
        }
    }
}

Functions | Agent/Avatar | Animation


llGetAttached

integer llGetAttached()

Returns the object's attachment point (or 0 if not attached) to the avatar.

Valid values are:
ATTACH_CHEST | 1 | chest (roughly the sternum)
ATTACH_HEAD | 2 | top of head (skull)
ATTACH_LSHOULDER | 3 | left shoulder
ATTACH_RSHOULDER | 4 | right shoulder
ATTACH_LHAND | 5 | left hand
ATTACH_RHAND | 6 | right hand
ATTACH_LFOOT | 7 | left foot
ATTACH_RFOOT | 8 | right foot
ATTACH_BACK | 9 | spine
ATTACH_PELVIS | 10 | pelvis
ATTACH_MOUTH | 11 | mouth
ATTACH_CHIN | 12 | chin
ATTACH_LEAR | 13 | left ear
ATTACH_REAR | 14 | right ear
ATTACH_LEYE | 15 | left eye
ATTACH_REYE | 16 | right eye
ATTACH_NOSE | 17 | nose
ATTACH_RUARM | 18 | right upper arm
ATTACH_RLARM | 19 | right lower arm
ATTACH_LUARM | 20 | left upper arm
ATTACH_LLARM | 21 | left lower arm
ATTACH_RHIP | 22 | right hip
ATTACH_RULEG | 23 | right upper leg
ATTACH_RLLEG | 24 | right lower leg
ATTACH_LHIP | 25 | left hip
ATTACH_LULEG | 26 | left upper leg
ATTACH_LLLEG | 27 | left lower leg
ATTACH_BELLY | 28 | belly/stomach
ATTACH_RPEC | 29 | right pectoral/breast
ATTACH_LPEC | 30 | left pectoral/breast
 | 31 | HUD Center 2
 | 32 | HUD Top Right
 | 33 | HUD Top
 | 34 | HUD Top Left
 | 35 | HUD Center
 | 36 | HUD Bottom Left
 | 37 | HUD Bottom
 | 38 | HUD Bottom Right

Q: Can I use this to find out where a different object is attached?
A: No. You need to use a seperate script in the other object for that.

Functions | Agent/Avatar | Attachment


llGetBoundingBox

list llGetBoundingBox(key object)

Returns the bounding box around the object or avatar with the key object (including any linked prims) relative to the root prim, in a list: [ (vector) min_corner, (vector) max_corner ]

Note: when an avatar sits on an object, it becomes linked to it--and the avatar's bounding box then includes the object it is sitting on.

If the object defined by object does not exist, the list will consist of ZERO_VECTOR.

Example:
// Ezhar's bounding box visualizer

default {
    state_entry() {
        llListen(3, "", llGetOwner(), ""); // listen for a target
    }

    listen(integer channel, string name, key id, string message) {
        llSensor(message, NULL_KEY, AGENT | ACTIVE | PASSIVE, 10, PI); // find the target
    }

    sensor(integer num_detected) {
        list bb;

        vector    min;
        vector    max;

        vector    offset;

        bb = llGetBoundingBox(llDetectedKey(0)); // get the bounding box
        min = llList2Vector(bb, 0); // min corner
        max = llList2Vector(bb, 1); // max corner

        offset = (min + max) / 2; // calculate the offset from the targets root prim center
        offset *= llDetectedRot(0); // rotate it by the targets rotation

        llSetText((string)min + "\n" + (string)max, <1, 1, 1>, 1.0); // display the bounding box corners as text

        llSetScale(-min + max); // change to the size of the bounding box
        llSetPos(llDetectedPos(0) + offset); // move to the target
        llSetRot(llDetectedRot(0)); // and match the targets rotation
    }
}

Functions | Bounding Box | Center | Prim | Object


llGetCameraPos

vector llGetCameraPos()
Returns the position of the camera of the user that granted the script PERMISSION_TRACK_CAMERA. If no user has granted the permission, it returns ZERO_VECTOR.

Q: Is this in region coordinates?
A: Yes.

To determine where the camera is pointing, see llGetCameraRot.

Functions | Camera


llGetCameraRot

rotation llGetCameraRot()

Returns the rotation of the camera of the user who's granted this script PERMISSION_TRACK_CAMERA. If no user has granted the permission, it returns ZERO_ROTATION. The key of the user whose camera is being tracked can be obtained using llGetPermissionsKey.

See llGetCameraPos.

Functions | Camera


llGetCenterOfMass

vector llGetCenterOfMass()

Returns an object's center of mass in region coordinates. This position is affected by cutting, hollowing, shearing, etc prims.

When executed from the root of a linked set, it returns the overall center of mass of all linked objects. When executed from a child object, it returns just the center of mass of the child object itself.

functions | physics | mass


llGetColor

vector llGetColor(integer face)

Returns the color of face (side). If face is ALL_SIDES, the color returned is the mean average of the colors on all sides. This could concievably be used to make a color "blending" script.

Color component values range from 0.0 to 1.0. See color for more info.

Color information can also be retrieved using the llGetPrimitiveParams function.

To set the color use llSetColor or llSetPrimitiveParams.

To access individual color components the vector 'dot' feature can be used:

vector color = llGetColor(ALL_SIDES);
float red_component = color.x;
float blue_component = color.y;
float green_component = color.z;

See vector for more info.

Note: This function will cause the script to fail silently if it is supplied with a face that does not exist: e.g, if a loop goes around 10 times reading the color of faces 0 to 9, and if the script is an unmodified cube, then it will fail on the 7th iteration (as a cube only has 6 sides). There is currently no way to know that this has happened so be careful using loops!

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture | Color


llGetCreator

key llGetCreator()

Returns the key of the object's original creator. Similar to llGetOwner.

Example:
default {
    state_entry() {
        llRequestAgentData(llGetCreator(), DATA_NAME); // get the creator's key and request their name
    }

    dataserver(key queryid, string data) {
        llSay(0, "I was created by " + data);
    }
}

Compare with llGetInventoryCreator.

Q: Is there an equivilent function to llGetOwnerKey?
A: No, at this point, only a script within an object can find out who created it. Scripts can't determine the creator of an arbitrary object.

Functions | Creator


llGetDate

string llGetDate()

Returns the current UTC date as YYYY-MM-DD (with padding zeros).

See also llGetTimestamp.

Example
This function returns the current date in PST. It works just like the llGetDate function by returning a string of the PST date in YYYY-MM-DD format, so once this is added to a script, it can be used instead of llGetDate. (Note: this does not observe Daylight Savings Time. For a script that does that, see this forum thread.) <-This note is wrong, since llGetWallclock observes DST this script will correctly observe DST.

string GetPSTDate()
{
    string DateToday = "";
    string DateUTC = llGetDate();
    if (llGetGMTclock() -  llGetWallclock() < 0) //if it's negative then the date has changed.
    {
        integer year = (integer)llGetSubString(DateUTC, 0, 3);
        integer month = (integer)llGetSubString(DateUTC, 5, 6);
        integer day = (integer)llGetSubString(DateUTC, 8, 9);

        if (day == 1) // if day is the 1st of a month, fix the date
        {
            if (month == 1) // if it is January
            {
                year = year - 1; // wind back the year
                month = 12; // set the month to December
                day = 31; // set to last day of December
            }
            else
            {
                month = month - 1; // wind back one month
                if(month == 2)
                    day = 28 + !(year % 4) - !(year % 100) + !(year % 400);
                else
                    day = 31 - (month == 4 || month == 6 || month == 9 || month == 11);
            }
        }
        else
            day = day - 1;
        if(month < 10)
            DateToday = "0";
        DateToday+=(string)month + "-";
        if(day < 10)
            DateToday += "0";
        DateToday+=(string)day;
        return (string)year + "-" + DateToday;
    }
    return DateUTC;
}
Functions | Time | Simulator


llGetEnergy

float llGetEnergy()

Gets the energy of the object the script is in. The return value is a normalized float between 0.0 and 1.0.

Energy decreases with calls to:

llMoveToTarget
llApplyImpulse
llSetPos
llSetForce
llSetTorque
llApplyRotationalImpulse
llPushObject

When energy reaches 0, calls to these functions have no effects.

When enough time passes (where no calls to these functions are made) energy is replenished to 1.0 again). This function is object-specific, not script-specific, so if a script calls one of the energy-draining functions, another script on the same object can "see" that energy is being drained.

Functions | Energy


llGetForce

vector llGetForce()

Returns the current force (in Newtons) being applied on a physical object relative to the global axis.

This seems to only return the force that was previously set with llSetForce, but doesn't give any hint in which orientation (local or global) it is in.

Compare with llGetTorque.

Functions | Dynamics | Force


llGetFreeMemory

integer llGetFreeMemory()

Returns the free memory available, in bytes, to the script calling it. Free memory means 16kb (the maximum each script gets) minus the size of the script's bytecode, stack, and heap.

NOTE: The above statement I have found to be untrue & should be ammended to exactly as LL has it stated in the client: "returns the available heap space for the current script". SiRiSAsturias

As of SL 1.10.6 (July 16, 2006), this function remains broken!
llGetFreeMemory will not take into account memory that has been freed, which can result in far less free memory reported than actually exists. What it will report is the historic free memory--the smallest free memory up until that point. So, in theory, your script could have nearly 16kb free, but report only several bytes free. Depending on what you need, this isn't necessarily a bad thing, but if you're relying upon llGetFreeMemory to know when a script is "full", you may run into some problems.

The historic free memory problem could potentially cause a situation where your script has several kilobytes of memory free, but "thinks" it only has a few bytes available. Other than to suggest that you attempt to design your script in such a way that this can't happen, there isn't a workaround for this situation.

Q: Does llResetScript also reset the historic low memory mark?
A: Yes.

Functions | Memory


llGetGeometricCenter

vector llGetGeometricCenter()

Returns the geometric center of the linked set the script is in relative to the object's position (the position of the root prim of a linked set). To get the object's position, use llGetPos.

llGetGeometricCenter works perfectly for complex objects with two prims, but any more than two and it's off significantly. Even three basic cubes offset along three axes with no rotation gives a problem. I'm really trying to conserve prims by using auto rezzing, but it's still very frustrating. The other few functions that came with SL 1.5 seem about as useful. -ReadyJack

Functions | Object


llGetGMTclock

float llGetGMTclock()

Gets the time in seconds since midnight in GMT/UTC.

GMT time is very useful when synchronizing with systems outside of SL via email or RPC. It is an absolute and international measure of time that does not change throughout the year. It isn't so great when trying to make a wall clock for Second Life that observes Daylight Saving Time. Creating local time from this function is a matter of adding the correct offset to GMT, a requirement complicated by yearly and national differences of when Daylight Saving Time goes into and out of effect.

The following function gets a clock for any time zone in the world, with a military time toggle.
(by DanielLuchador)
(note by CeeqLaborde - I actually think this breaks bad when timezone takes the gmt time over 23:59:59)

string clock(float timezone, integer military)
{
    integer raw = (integer)(llGetGMTclock() + (timezone * 3600));
    integer shiftraw = raw;

      //  3600 = seconds in an hour
      // 86400 = seconds in 24 hours
    if((timezone * 3600) + raw > 86400)
    {
        shiftraw = raw - 86400;
    }
    else if((timezone * 3600) + raw < 0)
    {
        shiftraw = raw + 86400;
    }

    integer hours = shiftraw / 3600;
    integer minutes = (shiftraw % 3600) / 60;
    integer seconds = shiftraw % 60;
    string ampm;

    //non-military time adjustments
    if(!military)
    {
          // 43200 = seconds in 12 hours
        if(shiftraw < 43200)
        {
            ampm = " AM";
        }
        else
        {
            ampm = " PM";
            hours -= 12;
        }
    }

    string shours = (string)hours;
    string sminutes = (string)minutes;
    string sseconds = (string)seconds;

    //add zeros to single digit minutes/seconds
    if(llStringLength(sminutes) == 1)
    {
        sminutes = "0" + sminutes;
    }
    if(llStringLength(sseconds) == 1)
    {
        sseconds = "0" + sseconds;
    }

    string time = shours + ":" + sminutes + ":" + sseconds + ampm;

    return time;
}

For a monotonic time source with seconds precision use llGetUnixTime

See also: llGetWallclock (returns time in seconds in the PST/PDT time zone).

Functions | Time | Simulator


llGetInventoryCreator

key llGetInventoryCreator(string item)

This function returns the UUID of the creator of item. If item is not found in inventory, the object says "No item named 'name' ".

Compare with llGetCreator.

Q: Will it work if a UUID of an inventory item is passed to the function?
A: No, item must be the inventory item's name. Passing a key will return an error, even if the key refers to a valid inventory item.

Functions | Inventory


llGetInventoryKey

key llGetInventoryKey(string name)

Returns the key of the inventory item name, or NULL_KEY if name not in object's inventory, or if the object owner does not have modify, copy and transfer permissions on that inventory item.

NOTE: If you intend to use llRezObject and/or llRezAtRoot with the inventory object, be aware that the key returned, is the key of the item within the inventory. Using llRezObject and/or llRezAtRoot, if the inventory object has copy permission, will result in a new copy of the inventory item being rezzed, with a new key issued to the new item. However, if the inventory item doesn't have copy permission, the original will be rezzed and the key will be unchanged. - DavidSoon

This can't be stressed enough: if the object owner does not have modify, copy and transfer permissions on the inventory item being requested, then the function returns the NULL_KEY. Everything else may have partial permissions, including the object and the script, and the function will work but the inventory item being requested MUST have full permissions. Do not attempt to use llGetInventoryKey to verify the existence of an inventory item. For that (and for checking its type), you want llGetInventoryType.

Compare with llGetInventoryName and llGetInventoryNumber.

Functions | Inventory


llGetInventoryName

string llGetInventoryName(integer type, integer number)

Gets the name of the inventory item number of type. Items are numbered starting from zero. If no item of the specified type is found an empty string is returned.

Note: The inventory is returned in alphabetical order.

Use the following constants to specify type:
Constant | Value | Look At | Hex
INVENTORY_ALL | -1 | all inventory items |
INVENTORY_ANIMATION | 20 | animations | 0x14
INVENTORY_BODYPART | 13 | body parts | 0x0D
INVENTORY_CLOTHING | 5 | clothing | 0x05
INVENTORY_GESTURE | 21 | gestures | 0x15
INVENTORY_LANDMARK | 3 | landmarks | 0x03
INVENTORY_NOTECARD | 7 | notecards | 0x07
INVENTORY_OBJECT | 6 | objects | 0x06
INVENTORY_SCRIPT | 10 | scripts | 0x0A
INVENTORY_SOUND | 1 | sounds | 0x01
INVENTORY_TEXTURE | 0 | textures | 0x00

Note: Inventory constants are not bitflags and thus should not be used with Bitwise operators.

Example:
llSay(0, "I have a notecard named " + llGetInventoryName(INVENTORY_NOTECARD, 0) + " in my contents.");
//
//put here for firefox- it was collapsing the window
//

Q: Is there a way to directly check if an item exists in inventory without having to run a for loop?
A: Yes, you can use llGetInventoryType. All inventory functions are case sensative.

Compare with llGetInventoryKey and llGetInventoryNumber.

To get the name of the current script, use llGetScriptName.

Functions | Inventory


llGetInventoryNumber

integer llGetInventoryNumber(integer type);

Get the total number of items of type in the prim's inventory.

Use the following constants to specify type:
INVENTORY_ALL | -1 | Look at all inventory items |
INVENTORY_TEXTURE | 0 | Only look at textures | 0x00
INVENTORY_SOUND | 1 | Only look at sounds | 0x01
INVENTORY_LANDMARK | 3 | Only look at landmarks | 0x03
INVENTORY_CLOTHING | 5 | Only look at clothing | 0x05
INVENTORY_OBJECT | 6 | Only look at objects | 0x06
INVENTORY_NOTECARD | 7 | Only look at notecards | 0x07
INVENTORY_SCRIPT | 10 | Only look at scripts | 0x0A
INVENTORY_BODYPART | 13 | Only look at body parts | 0x0D
INVENTORY_ANIMATION | 20 | Only look at animations | 0x14
INVENTORY_GESTURE | 21 | Only look at gestures | 0x15

Note: Inventory constants are not bitflags and thus should not be used with Bitwise operators.

Example:
llSay(0, "I contain " + (string)llGetInventoryNumber(INVENTORY_OBJECT) + " objects.");

Compare with llGetInventoryKey and llGetInventoryName.

Functions / Inventory


llGetInventoryPermMask

integer llGetInventoryPermMask(string item, integer mask)

Returns the requested permission mask for the inventory item defined by item. If item is not in the object's inventory, llGetInventoryPermMask returns 0 and causes the object to say "No item named '<item>'", where "<item>" is item. If this is used to determine whether or not an inventory item exists within the object, it will have the side effect of spamming chat.

To determine the existence of an item in the inventory without spamming chat, use llGetInventoryType.

Base permissions are the permissions the object has when it was given to the current owner. In theory, no other mask can have more permissions than the base. If they do then this is a bug in permissions and should be reported.

Mask | Value | Description
MASK_BASE | 0 | Get the object's base permissions
MASK_OWNER | 1 | Get the permissions the current owner has.
MASK_GROUP | 2 | Get the permissions the object's active group has.
MASK_EVERYONE | 3 | Get the permissions everyone has on the object.
MASK_NEXT | 4 | Get the permissions the next owner has.
These are not bit flags. They cannot be OR'ed.

Permissions | Value (hex) | Value (integer) | Description
PERM_ALL | 0x7FFFFFFF | 2147483647 | Move/Modify/Copy/Transfer permissions
PERM_COPY | 0x00008000 | 32768 | Copy permission
PERM_MODIFY | 0x00004000 | 16384 | Modify permission
PERM_MOVE | 0x00080000 | 524288 | Move permission
PERM_TRANSFER | 0x00002000 | 8192 | Transfer permission
Do note that ((PERM_COPY | PERM_MODIFY | PERM_MOVE | PERM_TRANSFER) is not the same as PERM_ALL). This is because there may (read as: will) be more permissions in the future.

Compare with llGetObjectPermMask.

Q: How do I set perm masks?
A: As of SL 1.6.1, you can't. There's a function to do so, llSetInventoryPermMask, but it hasn't been completed for security reasons. At some point, it will probably be completed, but there has been no word on when that might happen.

Functions | Inventory | Permissions | Asset Permissions


llGetInventoryType

integer llGetInventoryType(string name)

Returns the type of the inventory item name. Remember, like the other inventory functions, llGetInventoryType is case-sensitive.

This function can be used to check if an inventory item exists in the containing object's inventory.

If the item name is not present in the object's inventory, INVENTORY_NONE is returned and, unlike the other inventory functions, no error message is generated or displayed. This is deliberate.

Flag | Value | Inventory Type | Hex Value
INVENTORY_NONE INVENTORY_ALL | -1 | Item does not exist | -1
INVENTORY_TEXTURE | 0 | texture | 0x00
INVENTORY_SOUND | 1 | sound | 0x01
INVENTORY_LANDMARK | 3 | landmark | 0x03
INVENTORY_CLOTHING | 5 | clothing | 0x05
INVENTORY_OBJECT | 6 | object | 0x06
INVENTORY_NOTECARD | 7 | notecard | 0x07
INVENTORY_SCRIPT | 10 | script | 0x0A
INVENTORY_BODYPART | 13 | body part | 0x0D
INVENTORY_ANIMATION | 20 | animation | 0x14
INVENTORY_GESTURE | 21 | gesture | 0x15

Example:
// Says the type of the inventory item named "New Script"

default {
    state_entry() {
        string item_name = "New Script"; // the name of the item we're going to look for.

        // this is a list of all the possible inventory types, as constants.
        list list_types = [INVENTORY_NONE, INVENTORY_TEXTURE, INVENTORY_SOUND, INVENTORY_LANDMARK,
        INVENTORY_CLOTHING, INVENTORY_OBJECT, INVENTORY_NOTECARD, INVENTORY_SCRIPT,
        INVENTORY_BODYPART, INVENTORY_ANIMATION, INVENTORY_GESTURE];

        // this list is of the string names corresponding to the one above.
        list list_names = ["None", "Texture", "Sound", "Landmark", "Clothing", "Object", "Notecard",
        "Script", "Body Part", "Animation", "Gesture"];

        integer detected_type = llGetInventoryType(item_name); // look up which type this object is.
        integer type_index = llListFindList(list_types,[detected_type]); // where in list_types is this type?
        string type_name = llList2String(list_names, type_index); // get the corresponding entry in the names list.
        llSay(0,"The inventory item " + item_name + " is a " + type_name + "."); // say the output.
    }
}

Q: Is there a way to make this not case-sensitive?
A: No, your best option in that case would be to use a loop and check each inventory name individually as in the following example:

string InventoryName(string name, integer type)
{//finds an item in a case insensitive fashion of the given type and returns its true name.
    integer a = llGetInventoryType(name);
    if(!~a)//a == INVENTORY_NONE
    {//it should be noted that INVENTORY_NONE == INVENTORY_ALL == -1; which is why '!~a' works.
        string lc_name = llToLower(name);
        a = llGetInventoryNumber(type);
        while(a)
        {//(a = ~-a) is equivalent to --a, but runs faster.
            if(llToLower(name = llGetInventoryName(type, a = ~-a)) == lc_name)
            {//we found a match ^_^
                return name;
            }
        }
    }
    else if((a == type) ^ (!~type))//return name, aslong as a == type or type == INVENTORY_ALL
    {//we already know that a != INVENTORY_NONE, but just incase we use xor instead of or.
        return name;
    }
    return "";//no match ~_~
}

integer InventoryExists(string name, integer type)
{//only included to show how this type of check could be done if the value of 'type' is not constant and could be INVENTORY_ALL.
    return (llGetInventoryType(name) == type) ^ (!~type);
}//Since INVENTORY_ALL == INVENTORY_NONE, the extra part on the end is required to invert the result.

Functions | Inventory


llGetKey

key llGetKey()

Get the current key of the prim containing this script.

Each time an object is rezzed, each of its primitives receives a new, different, and probably unique key. It is probably worth thinking of llGetKey's result as the key associated with the prim's current existence in the world (or identifying its current session with the world) rather than as a key "for" the prim as such. This behavior is the case whether you have copy permission over the object or not. It almost goes without saying that different copies of the "same" object each have different keys for each of their constituent prims.

Teleporting from one place to another causes attachments to be derezzed and rezzed at the new location. This implies that when teleporting, all attachment scripts will receive an on_rez event, and each attachment's prims will be given a new key. Scripts making use of llGetKey should update the key in their on_rez event. None of the above applies to the short distance "teleport hack" using llSitTarget, however.

Email can only be sent to an object (either from the outside world or using llEmail) using a prim's key as part of the email address. Therefore, to get a long-lasting in-world target for email, either redirect it through a relay outside Second Life or make very sure that the target object will never be derezzed.

Modifying a prim (for example, by editing the scripts in it) does not change its key. Resaving a script does give the script itself a new key, but a script's key is retrieved with llGetInventoryKey, not llGetKey.

Compare with llGetOwnerKey, llGetOwner and llGetInventoryKey.

Functions | Key


llGetLandOwnerAt

key llGetLandOwnerAt(vector pos)

Returns the key of the land owner at pos or NULL_KEY if the land is public. (Public land still technically exists, but is almost never seen. If you see any, it's probably a bug since public land is supposed to automaticly revert to Govenor Linden)

Note: since land can also be owned by groups, this function doesn't always return a reference to an agent, presumably the key returned is the group key. In theory, this could be used llRequestAgentData and the dataserver event could be used to try and correlate this to a given agent, then timer run to automatically time out if the reference isn't found in a reasonable amount of time. (llRequestAgentData doesn't return an error if it attempts to look up a key that doesn't belong to an agent.) Unfortunately, this isn't foolproof or fast, and may result in false negatives. There is no way for a script to determine the actual details of the group, regardless.

This is affected by the same relative/absolute bug as llGround, in that when you attach an object with the function, it'll report from the southwest corner (0,0) instead of relative to the object's location. -Ice

Preliminary tests reveal that this function's argument is always interpreted as an absolute position now. -St. Psaltery

Also useful: llOverMyLand.

Functions | Simulator | Land


llGetLinkKey

key llGetLinkKey(integer linknum)

Get the key of the prim with the link number linknum in linked object.

Functions | Link


llGetLinkName

string llGetLinkName(integer linknum)

Gets the name of linknum in link set.

Compare with llGetLinkNumber.

Returns NULL_KEY if not in set.

Q: Weird. Shouldn't it return "" (empty string)?
A: Well, it shouldn't return NULL_KEY, that's for sure. It does though. Crazy, huh?

Functions | Link


llGetLinkNumber

integer llGetLinkNumber()

Returns what number in a link set the script is attached to (0 means no link, 1 the root/parent, 2 for first child, etc).

Links are numbered in the reverse order in which they were linked -- if you select a box, a sphere and a cylinder in that order, then link them, the cylinder is 1, the sphere is 2 and the box is 3. The last selected prim has the lowest link number.

Compare with llGetLinkName.

Functions / Link


llGetListEntryType

integer llGetListEntryType(list src, integer index)

Returns the type of the variable at index in src. One of the following constants is returned:

Constant | Value | Function
TYPE_INTEGER | 1 | Indicates that the list entry is holding an integer.
TYPE_FLOAT | 2 | Indicates that the list entry is holding a float.
TYPE_STRING | 3 | Indicates that the list entry is holding a string.
TYPE_KEY | 4 | Indicates that the list entry is holding a key.
TYPE_VECTOR | 5 | Indicates that the list entry is holding a vector.
TYPE_ROTATION | 6 | Indicates that the list entry is holding a rotation.
TYPE_INVALID | 0 | Indicates that this wasn't a valid list entry. Occurs when passing an index greater then the length of the list or less then the negative length of the list.

Example at llCSV2List.

Note that it's possible to have a list entry be a string consisting of "<1,1,1>". This will not return TYPE_VECTOR; it will return TYPE_STRING. The same is true of other types.

Functions | Lists | Types


llGetListLength

integer llGetListLength(list src)

Returns the number of elements in the list src.

Example:
list foo = ["bar", "baz"];
llOwnerSay((string) llGetListLength(foo));

This example will make the object say "2" to the object owner.

llGetListLength returns the number of elements in a list, not the maximum index in a list. To loop through all the elements in a list, use this:

list MyList = ["a","b","c"];
integer length = llGetListLength(MyList);
integer i;
for (i = 0; i < length; ++i) {
    llOwnerSay(llList2String(MyList, i));
}

Note: In the above example, the length integer is declared, calling llGetListLength a single time, rather than calling it within the for loop. As this forum thread discusses, doing it this way results in a much faster loop.

HACK:
list MyList = ["a","b","c"];
integer length_slow = llGetListLength(MyList); //Slow
integer length_fast = (MyList != []); //Fast
integer neg_length_fast = ([] != MyList); //Fast & negative

Functions | List


llGetLocalPos

vector llGetLocalPos()

Gets the position of a child object in object-local coordinates, which are relative to the child's parent object.

If the script is inside the parent, or in an unlinked object, it will return the same value as llGetPos would.

ExampleGetRootPos shows an example of its use.

As of SL 1.7, llGetLocalPos will work with attachments, including HUD attachments. The value returned (in the root prim) is relative to the attachment point and not the position of the avatar.

There is no llSetLocalPos() function; instead, llSetPos works fine.

To get the local rotation of a prim, use llGetLocalRot().

Functions | Dynamics | Position | Link


llGetLocalRot

rotation llGetLocalRot()

Returns the local rotation of the (child) object relative to the root/parent object's rotation.

If this is called on the root (parent) object, or an unlinked object, this returns llGetRot() instead. If the object is attached then it returns the rotation of the object relative to the attach point.

ExampleGetRootRot shows an example of its use.

Use llSetLocalRot to set the local rotation.

Compare with llGetRot.

Functions | Rotation | Link


llGetMass

float llGetMass()

Returns the scripted object's mass. When called from a script in a linkset, the parent will return the sum of the linkset weights, while a child will return just its own mass. When called from a script inside an attachment, this function will return the mass of the avatar it's attached to, not its own.

Mass appears to be measured in a unit analogous to kilograms.
There is speculation that the mass unit could be a "lindogram".

See llGetObjectMass to get the mass of other objects.

Q: Is there a way to set an object's mass?
A: No, short of linking additional prims to it.

For an example use, see the example in llSetForce.

Mass | Physics


llGetNextEmail

llGetNextEmail(string address, string subject)

This function requests the next waiting email with sender address and subject. You can leave these empty to match any address and/or subject in the queue.

If a matching email is found in the queue, llGetNextEmail will remove it from the queue and trigger an email() event in one to three seconds. If no matching email is found, this function has no effect. This function will trigger the email() event in all scripts in the prim.
  • Not sure where the 1-3 second statistic comes from, but after sending and checking 1000 emails using llResetTime() before every llGetNextEmail() then llGetTime() in the email() event the longest delay observed was 0.379707s and the average time was 0.216751s. That was under heavy email load, emails recieved a bit less frequently can take less than 0.05s to retrieve. -DragonEccleston

The queue seems to hold 101 emails. Any emails sent after that are bounced.
  • As of SL 1.6.6, the number of pending emails is 100 and bounce messages are received when a queue is full. -Splat1Edison
  • The max value returned by num_left in the email() event is 100, plus the email just received is 101. -DragonEccleston

The address for sending email to an object is <objectkey>@lsl.secondlife.com. So for example, an object which returned "9dd0db80-d7f0-cf7d-699d-5afb6eed0640" on a call to llGetKey would be reachable as 9dd0db80-d7f0-cf7d-699d-5afb6eed0640@lsl.secondlife.com.

This will allow you to receive email from within a script from the outside world. Works by checking a global list of pending incoming emails, and returning the next matching one if there are any. This mail spool persists across script resets.

llGetNextEmail differs from llListen in that it must be called every time you want to check your mail, you can't just set it and have incoming emails trigger the email() event. If you want to check for email at regular intervals, use a timer.

Note: When you teleport, your attachments are derezzed and re-rezzed at the new location. This means that they will all receive new keys (see llGetKey) and that emailing scripts in attachments is therefore a bit tricky.

To send an email, use llEmail.

Functions | Communications


llGetNotecardLine

key llGetNotecardLine(string name, integer line)

This function fetches line number of notecard name and returns the data through the dataserver event. The line count starts at zero. If the requested line is past the end of the notecard, the dataserver event will return the constant EOF ("End Of File") string. You can get the number of lines in a notecard with llGetNumberOfNotecardLines. The key returned by llGetNotecardLine is a unique identifier which will be supplied to the dataserver event as the queryid parameter.

name can be either a string containing the name of a notecard in the object's inventory, or a key pointing to a notecard (that can be anywhere and doesn't have to reside in the object). In the latter case. you should keep in mind that any time you edit and save such a notecard, it will get a new key.

If name is not a valid notecard, or if the notecard is empty, the object will say, "Couldn't find notecard NAME" where "NAME" is the name of the invalid notecard.

Note that lines longer than 255 characters will be truncated without any indication that this has been done.

Requests from notecards that contain embedded inventory items will always result in EOF, regardless of the line being requested.

Note: This function delays the script for 0.1 seconds.

Example:
// Read out a complete notecard from the object's inventory.
string gName;    // name of a notecard in the object's inventory
integer gLine = 0;        // current line number
key gQueryID; // id used to identify dataserver queries

default {
    state_entry() {
        gName = llGetInventoryName(INVENTORY_NOTECARD, 0); // select the first notecard in the object's inventory
        gQueryID = llGetNotecardLine(gName, gLine);    // request first line
    }

    dataserver(key query_id, string data) {
        if (query_id == gQueryID) {
            if (data != EOF) {    // not at the end of the notecard
                llSay(0, (string)gLine+": "+data);    // output the line
                ++gLine;                // increase line count
                gQueryID = llGetNotecardLine(gName, gLine);    // request next line
            }
        }
    }
}

/////
//  Generic Multi Notecard reader by Brangus Weir
//  Given freely and published on wiki.secondlife.com
//
// This script will read three note cards and store the results into 3 lists.
// It can be modified and extended to as many cards as you'd like to read.
//
list gOneCard;  // All the lines from from the first card
list gTwoCard;    // All the lines from from the second card
list gThreeCard;   // All the lines from from the third card

string gsCardOneName = "One";  //Set thse to the name of the invetory iten.
string gsCardTwoName = "Two";
string gsCardThreeName = "Three";

//Temporary variables for processing
string g_sNoteCardName; // Name of the card to be read.
list g_lTempLines;  // The reslulting data pushed into a list
integer g_iLine;    // The line count for the card reader
key g_kQuery;       // The key of thge card being read

initialize(string _action) {
    //Due to the execution order when using dataserver, this function sets the first card to
    //be read, and the excetuion finishes when called again with the _action set to "finish".
    if (_action == "") {
        loadNoteCard(gsCardOneName);
    } else if (_action = "finish") {
        // All cards have been read into the lists... now you can do any kind of string
        // maniulations to get the data you need to set your script.
        // But here we will prove that the cards have been read with a loop
        integer end = llGetListLength(gOneCard);
            // Don't be a noob scripter - evaluate end once
                     //  for (i=0; i< LLGetListLength(gOneCard); i++)
                     // is just so wrong and laggy! THINK and reduce lag.
        integer i;
        for (i = 0; i< end; i++)
            llSay(0, llList2String(gOneCard,i));
        end = llGetListLength(gTwoCard);
        for (i = 0; i< end; i++)
            llSay(0, llList2String(gTwoCard,i));
        end = llGetListLength(gThreeCard);
        for (i = 0; i< end; i++)
            llSay(0, llList2String(gThreeCard,i));
    }

}

loadNoteCard( string _notecard ) {
    g_lTempLines = []; //clear the temp lines
    g_sNoteCardName = _notecard;
    g_iLine =0;
    g_kQuery = llGetNotecardLine(g_sNoteCardName, g_iLine);

}

notecardFinished(string _notecard){
    // Called at the end of each notcard as it is read. The temp results are stored
    // and the next card is commanded to be read.
    if (_notecard == gsCardOneName) {
        gOneCard = g_lTempLines;
        loadNoteCard(gsCardTwoName);
    } else if (_notecard == gsCardTwoName) {
        gTwoCard = g_lTempLines;
        loadNoteCard(gsCardThreeName);
    } else if (_notecard == gsCardThreeName) {
        gThreeCard = g_lTempLines;
        initialize("finish");  // Finally pass exection to finish the initialization.
    }
}

default
{
    state_entry()
    {
    }

    touch_start(integer num_det){
        initialize("");
    }

    dataserver(key _query_id, string _data)
    {
        if (_query_id == g_kQuery) {
            // this is a line of our notecard
            if (_data != EOF) {
                // increment line count
                g_lTempLines += [_data];
                //request a next line
                g_iLine++;
                g_kQuery = llGetNotecardLine(g_sNoteCardName, g_iLine);
            } else {
             //The notecard has been read
             //notify end of read
             notecardFinished(g_sNoteCardName);
            }
        }
    }
}

Q: How do I write to a notecard?
A: You can't. The design of the asset system means that every time you change an asset, be it an object in-world, or an inventory item, you effectively create a new one, with a new key. This means that every time a change was made to a notecard, the asset server would have to create a new asset.

The reason this is bad is simple: load. When the database's load becomes too high, the entire system basically fails. The Lindens don't want that. Changing the asset system has been looked at, but it would evidently necessitate a fairly major rewrite of the entire database, and is not something that's likely to happen.

Fortunately, there are plans to create a seperate database storage system for scripts, though there isn't yet any projected release date for such a feature. Of course, data storage is not the only reason one might want to write to a notecard. The other potential use -- output to a notecard -- is not something the Lindens have plans for.

Q: What happens if I create a notecard, don't edit and save it, but place it in the object and then try to run llGetNotecardLine on it?
A: The same thing as if you passed an invalid name: the object will say, "Couldn't find notecard NAME" where "NAME" is the name of the invalid notecard. Nothing will be returned to the script, and it will be as if llGetNotecardLine was never called.

Q: So what do I do then? How does my script know what to do if there's no way to tell it whether or not the notecard is empty?
A: Try adding a timeout using a timer, or use a second object containing a listener to reply with a message indicating the notecard was empty or not present.

Q: Is there some way to get around the 255-character line limit?
A: No. The best you can do is to check the length of the resulting string in the dataserver event using llStringLength to see if it reaches the maximum, then from that, assume the line has been truncated. The problem with doing this is that your script can't actually do anything about it, save for suggesting to the user that they may wish to examine the line in question to determine if anything important is in there. Further, there's the very real possibility of false positives in non-truncated lines that just happen to be 255 characters long.

Q: So I guess that means there's no way to conclusively determine whether a line's been truncated, huh?
A: Nope, sorry.

Functions | Dataserver | Inventory


llGetNumberOfNotecardLines

key llGetNumberOfNotecardLines(string name)

Returns number of lines in the notecard name via the dataserver event. (Cast the value returned by the dataserver to an integer.)

The key returned is a query id for identifying the dataserver reply.

If name is not a valid notecard, the object will say, "Couldn't find notecard NAME" where "NAME" is the name of the invalid notecard. The dataserver event will not be triggered, and there will be no message returned. llGetNumberOfNotecardLines will still return a query id.

If name refers to a notecard containing embedded inventory items, dataserver will still be triggered, though it will not return the actual number of lines, but rather, the End-Of-File constant. (EOF)

Empty notecards, even ones that have been renamed from "New Note", will not be able to be read, as they don't "exist" in the same way that other assets do. Notecards can have nothing in them, but they can't just be "New Note", renamed. There needs to actually be something saved.

This call can often be avoided by checking if the data requested by llGetNotecardLine is EOF to determine when the end of the notecard has been reached.

Note: This function delays the script for 0.1 seconds.

default {
    state_entry() {
        llGetNumberOfNotecardLines("somenotecard");
    }

    dataserver(key queryid, string data) {
        llSay(0, "This notecard has " + data + " lines."); // note how the returned value is already a string
        // if you want to use it as a number, you will have to cast it to an integer:
        integer    lines = (integer)data;
    }
}

Q: What happens if I create a notecard, don't edit and save it, but place it in the object and then try to run llGetNumberOfNotecardLines on it?
A: The same thing as if you passed an invalid name: the object will say, "Couldn't find notecard NAME" where "NAME" is the name of the invalid notecard. Nothing will be returned to the script, and it will be as if llGetNumberOfNotecardLines was never called.

Q: So what do I do then? How does my script know what to do if there's no way to tell it whether or not the notecard is empty?
A: Try adding a timeout using a timer, or use a second object containing a listener to reply with a message indicating the notecard was empty or not present.

Also see llGetNotecardLine.

Functions | Dataserver | Notecard


llGetNumberOfPrims

integer llGetNumberOfPrims()

Returns the number of prims in (and avatars seated on) the object the script is in.

Note: this function works regardless of whether the script is in the parent or a child prim in a linked set.

//Returns the number of agents on the object
integer GetNumberOfAgents()
{//ignores avatars.
    integer a = llGetNumberOfPrims();
    integer b = a;
    if(1 < a)
        while(llGetAgentSize(llGetLinkKey(a)))
            --a;
    return b - a;
}

//Returns the number of prims in the object
integer GetNumberOfPrims()
{//ignores avatars.
    integer a = llGetNumberOfPrims();
    if(1 < a)
        while(llGetAgentSize(llGetLinkKey(a)))
            --a;
    return a;
}

Functions | Object | Prim | Link


llGetNumberOfSides

integer llGetNumberOfSides()

Returns the number of sides of the prim containing this script.

Each face of a primitive can be referenced to, from 0 to n - 1, where n is the value returned from this function. Each face can have a different texture, color, and opacity (alpha).

Examples:
// Indicates number of sides using llSetText.
default
{
    changed(integer change)
    {
        if(change & CHANGED_SHAPE)
        {
            integer sides = llGetNumberOfSides();
            llSetText((string)sides + llList2String([" side"," sides"],(sides != 1)),<1,1,1>,1);
        }
    }
}

// this rotates the texture of each side to match its sidenumber in degrees
// after running this script, you can select each texture (using "Select Texture")
// in the edit dialog and read it's side number in the "Rotation (degrees)" field.
default {
    state_entry() {
        integer    i;
        integer    sides;

        sides = llGetNumberOfSides();
        for (i = 0; i < sides; i++) {
            llRotateTexture(i * DEG_TO_RAD, i); // rotate by side number degrees
        }
    }
}

Functions | Texture | Side


llGetObjectDesc

string llGetObjectDesc()

Returns the description of the scripted object/prim. You can set the description using llSetObjectDesc.

The description is limited to 127 characters.

Note: Prims in linked objects each have their own descriptions. Setting the description in the parent will not change that of any child prims, and vice-versa. There is no way to get or set the description of a different prim in the linkset; to do that, you need to use link messages.

See also llGetObjectName.

Q: How do I get the description of another object?
A: You can't, unless the other object is set up in such a way that you can ask it using llSay().

Note: when an object's description is a blank string (), and you take it into inventory, SL automatically replaces the description with the string, "(No Description)". You can go into the object's properties in your inventory and change it back to blank () and it will stay that way. - Gistya

Functions | Object | Prim


llGetObjectMass

float llGetObjectMass(key id)

Gets the mass of the object or avatar corresponding to id.

Returns 0.0 if id is not in the sim. Because of this, llGetObjectMass should not be used to determine if a key is in the sim, as it is possible to have extremely low-mass prims. Use llKey2Name instead.

functions | object | avatar | mass


llGetObjectName

string llGetObjectName()

Returns the first 256 characters of the scripted prim's name.

Use llSetObjectName to change the prim's name.

An object's description field can be modified and read with llSetObjectDesc and llGetObjectDesc.

To find other objects' names, see llKey2Name and llDetectedName.

To get the name of the script itself, use llGetScriptName.

To get the name of a linked prim, use llGetLinkName.

If the script is not in the root prim of an object, it returns the name of the linked prim that the script is in. In this case use llGetLinkName with a parameter of 1 to obtain the name of the actual multi-prim object.

Functions | Object | Prim


llGetObjectPermMask

integer llGetObjectPermMask(integer mask)

Returns the requested permission mask for the root object the task is attached to.

Base permissions are the permissions the object has when it was given to the current owner. In theory, no other mask can have more permissions then the base. If they do, then this is a bug in permissions and should be reported.

Mask | Value | Description
MASK_BASE | 0 | Get the object's base permissions
MASK_OWNER | 1 | Get the permissions the current owner has.
MASK_GROUP | 2 | Get the permissions the object's active group has.
MASK_EVERYONE | 3 | Get the permissions everyone has on the object.
MASK_NEXT | 4 | Get the permissions the next owner has.
These are not bitflags! They can not be OR'ed.

Permissions | Value (in hex notation) | Value (integer) | Description
PERM_ALL | 0x7FFFFFFF | 2147483647 | Move/Modify/Copy/Transfer permissions
PERM_COPY | 0x00008000 | 32768 | Copy permission
PERM_MODIFY | 0x00004000 | 16384 | Modify permission
PERM_MOVE | 0x00080000 | 524288 | Move permission
PERM_TRANSFER | 0x00002000 | 8192 | Transfer permission

Note: ((PERM_COPY | PERM_MODIFY | PERM_MOVE | PERM_TRANSFER) != PERM_ALL). This is because there may (read as: will) be more permissions in the future.

Example:
default
{
    state_entry()
    {
        integer perm_mask = llGetObjectPermMask( MASK_NEXT);
        if(perm_mask & PERM_MODIFY) llSay(0,"The next owner will have modify permissions.");
        if(perm_mask & PERM_COPY) llSay(0,"The next owner will have copy permissions.");
        if(perm_mask & PERM_TRANSFER) llSay(0,"The next owner will have transfer permissions.");
    }
}

Compare with llGetInventoryPermMask.

Q: How do I set perm masks?
A: As of 1.6.1, you can't. There's a function to do so, llSetObjectPermMask, but it hasn't been completed for security reasons. At some point, it will probably be completed, but there has been no word on when that might happen.

Functions | Permissions | Asset Permissions


llGetObjectPrimCount

integer llGetObjectPrimCount(key id)

Returns the prim count for any object id in the same sim.

Introduced in SL 1.12.4; released in 1.13.0

Functions | Simulator | Primitive | Object | Link


llGetOmega

vector llGetOmega()

Returns the current rotational velocity (in radians/second) of task, relative to the simulator axes.

The rotation equivalent to llGetVel. The rotational velocity can be changed using llApplyRotationalImpulse, llSetTorque, or llSetForceAndTorque.

To find the velocity relative to the direction the object is rotating, try this:
vector global_omega = llGetOmega();
vector local_velocity = global_omega / llGetRot();

functions | dynamics


llGetOwner

key llGetOwner()

Returns the key of the owner of the scripted object.

Note: a script will only check to see its owner when llGetOwner is called. This can be annoying if you transfer ownership of the object to another user. If you're using llGetOwner in llListen or llRequestPermissions, consider putting llResetScript in the on_rez event, or even llListen in on_rez. Remember to remove your old listens with llListenRemove or they'll pile up! Depending on what you have in state_entry, llResetScript is usually the best method.

You can retrieve the owner of a specific object other than the one containing the script with llGetOwnerKey.

Examples:
default
{
    state_entry()
    {
        if (llDetectedOwner(0) == llGetOwner())
        {
            //do stuff
        }
    }
}

default {
    state_entry() {
        llListen(0, "", llGetOwner(), ""); // listen to my owner
    }

    listen(integer channel, string name, key id, string message) {
        llSay(0, name + " said: " + message); // repeat owner's text
    }

    on_rez(integer start_param) {
        // every time we're rezzed, reset the script
        // this ensures that if we're transferred to a new owner, we're listening to them and not still to the previous one
        llResetScript();
    }
}

Q: How do I get the owner's name instead?
A: If the owner is in the same sim as the object at the time it's called, (IE, if your script is within an attachment) use llKey2Name. Otherwise, you need to use llRequestAgentData and the dataserver event, which takes longer and is more complicated.

Functions | Keys


llGetOwnerKey

key llGetOwnerKey(key id)

Returns the owner of object with the key id.

This function does not work if the target object is in a different simulator. It returns the target object's key (i.e., it returns id) instead of the owner's key. If an avatar key is passed to this function, the result is the avatars key.

Instead of using llGetOwnerKey(llGetKey()), the owner of the object containing the script can be retrieved using llGetOwner.

Compare with llGetKey.

Example:
// this shows the owner of a scripted object that is outputting public chat
default {
    state_entry() {
        llListen(0, "", NULL_KEY, ""); // listen to all public chat
    }

    listen(integer channel, string name, key id, string message) {
        key ownerid;
        string ownername;

        if ( llGetAgentSize(id) != ZERO_VECTOR ) return; // sneaky trick: objects have no agent size, because they're no agents
        // so we only continue if the chat is from an object

        ownerid = llGetOwnerKey(id); // the the owner of the chatting objects
        ownername = llKey2Name(ownerid); // get their name (if the owner is in the same sim)
        // it'd be much better to get the name via llRequestAgentData instead, but i'm trying to keep this example short

        llSay(0, name + " belongs to " + ownername);
    }
}

Q: How do I get the owner's name instead?
A: If the owner is in the same sim as the object at the time it's called, (if your script is within an attachment) use llKey2Name. Otherwise, you need to use llRequestAgentData and the dataserver event, which takes longer and is more complicated.
A2: One can also use the huge w-hat name.cache file (http://w-hat.com/name2key) to find the name of someone's key by using the llHTTPRequest function.

Functions | Key | owner


llGetParcelDetails

list llGetParcelDetails(vector pos, list details)

Returns of a list of the details in the order they are in the details list, for the parcel at pos. pos is in absolute region coordinates.

Values for details:
Detail | Value | Meaning | Max Length | Return Type
PARCEL_DETAILS_NAME | 0 | The name of the parcel. | 63 Characters | string
PARCEL_DETAILS_DESC | 1 | The description of the parcel. | 127 Characters | string
PARCEL_DETAILS_OWNER | 2 | The parcel owner's key. | (36 Characters) | key
PARCEL_DETAILS_GROUP | 3 | The parcel group's key. | (36 Characters) | key
PARCEL_DETAILS_AREA | 4 | The parcel's area, in sqm. | (5 Characters) | integer
Max Lengths expressed with parentheses around represent how many characters required when it is typecast to a string.

This script will return PARCEL_DETAILS_NAME. To change the data returned, you would replace the 0 in lstParcelDetails to any of values, or any number seperated with comas.
default
{
    state_entry()
    {
        llSay(0, "Ready!");
    }

    touch_start(integer total_number)
    {
        list lstParcelDetails = [PARCEL_DETAILS_NAME];

        list lstParcelName=llGetParcelDetails(llGetPos(),lstParcelDetails);

        llSay(0, (string)lstParcelName);
    }
}

Parse All Parcel Details:
default
{
    state_entry()
    {
        llSay(0, "Touch Me!");
    }

    touch_start(integer total_number)
    {
        // Grab Parcel Info
        list lstParcelDetails = llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_NAME, PARCEL_DETAILS_DESC, PARCEL_DETAILS_OWNER, PARCEL_DETAILS_GROUP, PARCEL_DETAILS_AREA]);

        // Set Parcel Variables
        string ParcelName = llList2String(lstParcelDetails, 0);     // Parcel's Name (63 Characters Max)
        string ParcelDesc = llList2String(lstParcelDetails, 1);     // Parcels Description (127 Characters Max)
        key ParcelOwner = llList2Key(lstParcelDetails, 2);          // Parcel Owners Key (AV Or Group Key If Group Owned)
        key ParcelGroup = llList2Key(lstParcelDetails, 3);          // Parcel's Group Key (NULL_KEY Unless Group Set Or Owned By Group)
        integer ParcelArea = llList2Integer(lstParcelDetails, 4);   // Parcel's Size (In Meters Squared. ie: 512, 1024...)

        // Put Your Code Here
    }
}

Added in SL 1.12.4; released in 1.13.0

Functions | Land


llGetParcelFlags

integer llGetParcelFlags(vector pos)

Returns a bitfield specifying the parcel flags (PARCEL_FLAG_*) for the parcel under the position pos.

Flag | Hex | Value | Description
PARCEL_FLAG_ALLOW_FLY | 0x1 | 1 | find if a parcel allows flying
PARCEL_FLAG_ALLOW_SCRIPTS | 0x2 | 2 | find if a parcel allows outside scripts
PARCEL_FLAG_ALLOW_LANDMARK | 0x8 | 8 | find if a parcel allows landmarks to be created
PARCEL_FLAG_ALLOW_TERRAFORM | 0x10 | 16 | find if a parcel allows anyone to terraform the land
PARCEL_FLAG_ALLOW_DAMAGE | 0x20 | 32 | find if a parcel allows damage
PARCEL_FLAG_ALLOW_CREATE_OBJECTS | 0x40 | 64 | find if a parcel allows anyone to create objects
PARCEL_FLAG_USE_ACCESS_GROUP | 0x100 | 256 | find if a parcel limits access to a group
PARCEL_FLAG_USE_ACCESS_LIST | 0x200 | 512 | find if a parcel limits access to a list of residents
PARCEL_FLAG_USE_BAN_LIST | 0x400 | 1024 | find if a parcel uses a ban list, including restricting access based on payment info
PARCEL_FLAG_USE_LAND_PASS_LIST | 0x800 | 2048 | find if a parcel allows passes to be purchased
PARCEL_FLAG_LOCAL_SOUND_ONLY | 0x8000 | 32768 | find if a parcel restricts spatialized sound to the parcel
PARCEL_FLAG_RESTRICT_PUSHOBJECT | 0x200000 | 2097152 | find if a parcel restricts llPushObject
PARCEL_FLAG_ALLOW_ALL_OBJECT_ENTRY | 0x8000000 | 134217728 | find if a parcel allows objects owned by any user to enter
PARCEL_FLAG_ALLOW_GROUP_OBJECT_ENTRY | 0x10000000 | 268435456 | find if a parcel allows with the same group to enter

Example Usage:
// Parcel flags for land the object is currently over.
integer parcelFlags = llGetParcelFlags(llGetPos());
if (parcelFlags & PARCEL_FLAG_ALLOW_FLY)
    llOwnerSay("This parcel allows flying.");

Example test script to see that this function works in the current version of Second Life:
// Show all the parcel flags for land the object is currently over.
default
{
    state_entry ()
    {
        integer Parcel_Flags = llGetParcelFlags (llGetPos ());
        if (PARCEL_FLAG_ALLOW_FLY & Parcel_Flags)
        {
            llOwnerSay ("Parcel allows flying.");
        }
        if (PARCEL_FLAG_ALLOW_SCRIPTS & Parcel_Flags)
        {
            llOwnerSay ("Parcel allows outside scripts.");
        }
        if (PARCEL_FLAG_ALLOW_TERRAFORM & Parcel_Flags)
        {
            llOwnerSay ("Parcel allows terraforming.");
        }
        if (PARCEL_FLAG_ALLOW_DAMAGE & Parcel_Flags)
        {
            llOwnerSay ("Parcel allows damage.");
        }
        if (PARCEL_FLAG_ALLOW_CREATE_OBJECTS & Parcel_Flags)
        {
            llOwnerSay ("Parcel allows creating objects.");
        }
        if (PARCEL_FLAG_USE_ACCESS_GROUP & Parcel_Flags)
        {
            llOwnerSay ("Parcel limits access to a group.");
        }
        if (PARCEL_FLAG_USE_ACCESS_LIST & Parcel_Flags)
        {
            llOwnerSay ("Parcel limits access to a list of residents .");
        }
        if (PARCEL_FLAG_USE_BAN_LIST & Parcel_Flags)
        {
            llOwnerSay ("Parcel uses a ban list.");
        }
        if (PARCEL_FLAG_USE_LAND_PASS_LIST & Parcel_Flags)
        {
            llOwnerSay ("Parcel allows passes to be purchased.");
        }
        if (PARCEL_FLAG_LOCAL_SOUND_ONLY & Parcel_Flags)
        {
            llOwnerSay ("Parcel restricts spatialised sound to the parcel.");
        }
        if (PARCEL_FLAG_RESTRICT_PUSHOBJECT & Parcel_Flags)
        {
            llOwnerSay ("Parcel restricts llPushObject.");
        }
    }
    touch_end (integer n)
    {
        llResetScript ();
    }
}

Also see llGetRegionFlags.

Functions | Land


llGetParcelMaxPrims

integer llGetParcelMaxPrims(vector pos, integer sim_wide)

Returns the maximum number of prims allowed on the parcel at pos.

If sim_wide is TRUE, it returns the maximum number of prims allowed in the sim.
If sim_wide is FALSE, it returns the maximum number of prims this individual parcel supports.

Added in SL 1.12.4; released in 1.13.0

This function seems to return the value without taking the Region object bonus factor into account. -- AtteAderdeen

Functions | Land


llGetParcelPrimCount

integer llGetParcelPrimCount(vector pos, integer category, integer sim_wide)

Returns the number of prims used on the parcel at pos which are in category.

If sim_wide is TRUE, it returns the number of objects for the entire sim in the category specified.
If sim_wide is FALSE, it returns the number of objects on this specific parcel in the category specified

Values for category:
Category | Value | Meaning
PARCEL_COUNT_TOTAL | 0 | All prims on the parcel(s). Does not include temp on rez objects.
PARCEL_COUNT_OWNER | 1 | Prims owned by the parcel owner.
PARCEL_COUNT_GROUP | 2 | Prims not owned by the owner, but set to or owned by the group of the parcel.
PARCEL_COUNT_OTHER | 3 | Prims not set to group or owned by the owner.
PARCEL_COUNT_SELECTED | 4 | All prims selected or sat on.
PARCEL_COUNT_TEMP | 5 | All temp on rez objects.

Added in SL 1.12.4; released in 1.13.0

This function seems to work only for agents who could see this information anyways. Can someone who owns land please test this and fix anything which is missing or incorrect? -- KeknehvPsaltery

Functions | Land


llGetParcelPrimOwners

list llGetParcelPrimOwners(vector pos)

Returns a strided list of keys and integers of up to 100 agents who own objects in the parcel at pos. The list is formatted as [ key agentKey1, integer agentCount1, key agentKey2, integer agentCount2, ... ], sorted by agent key.

The integers are counts of the number of prims (not objects) owned by the corresponding agents.

Sleeps the script for 2.0 seconds.

Function only works when the object owner is in the sim (likely the reason it doesn't work when deeded to group).

"It seems that it will work even if the prim owners aren't there. The function is turned on when the owner of the parcel is in the sim, and doesn't turn off until a while after the owner leaves the sim." -- BirosTiros

This function seems to work only for agents who could see this information anyways (i.e. land owners). Can someone who owns land please test this and fix my assumptions? -- KeknehvPsaltery

It seems to be controlled by the owners permissions in that group regardless if it is set to the group.-- GrumbleLoudon

Introduced in SL 1.12.4; released in 1.13.0

Currently it seems this command is bugged if you deed the containing object to group, causing the returned list to be empty, minimizing it's usefulness in security systems on group owned land --DragonEccleston

"I just tested this on land owned by a group, in which I am a member. It returned the same result (full list) when it was set to no group, set to a different group, set to the landowning group, and deeded to land. Worked perfectly in ALL situations. Tested on 8th May 2007" -- WarKirby

See also

llRequestAgentData(key id, DATA_NAME) to get the owner's name.

Functions | Land


llGetPermissions

integer llGetPermissions();

Returns which permissions have been granted. The return value will be a bitmask of the following values:

Constant: | Result | Allows
PERMISSION_DEBIT | Permission to take money from agent's account. | Use of llGiveMoney.
PERMISSION_TAKE_CONTROLS | Permission to take agent's controls. | Use of llTakeControls and llReleaseControls
PERMISSION_TRIGGER_ANIMATION | Permission to trigger animation on agent. | Use of llStartAnimation and llStopAnimation.
PERMISSION_ATTACH | Permission to attach/detach from agent. | Use of llAttachToAvatar and llDetachFromAvatar
PERMISSION_CHANGE_LINKS | Permission to change links. | Use of llCreateLink, llBreakLink, and llBreakAllLinks
PERMISSION_TRACK_CAMERA | Permission to track the agent's camera position and rotation | Use of llGetCameraPos and llGetCameraRot
PERMISSION_CONTROL_CAMERA | Permission to control the agent's camera | Use of llSetCameraParams and llClearCameraParams

To compare the resulting number to these constants, remember that it is necessary to use bitwise operators! That means doing if (llGetPermissions() & PERMISSION_ATTACH), not if (llGetPermissions() == PERMISSION_ATTACH). The latter may work some of the time, but not in all cases.

Example:
integer perm = llGetPermissions();

if (perm & PERMISSION_DEBIT) // using & is important! perm is a bitmask!
{
    // debit code goes here
}

Q: How does this know what agent to get the permissions from?
A: The last person who requested permissions. Use llGetPermissionsKey to find out the key of the agent.

Q: Is there a way to find out what permissions are already granted for a different agent, or do I need to keep a list of my own?
A: Yes, if you wanted a script to get permissions from more than one person, you would have to keep a list of your own.

Q: It's returning a value that I check against the constants listed in the table above, and nothing happens. What's up with that?
A: Are you sure you're using bitwise operators in your IF statement? (For instance, & instead of ==?) The value returned by llGetPermissions is a bitfield, and will not equal any of the above constants if more than one have been granted and are returned.

Compare with llRequestPermissions and llGetPermissionsKey.

Functions | Agent/Avatar


llGetPermissionsKey

key llGetPermissionsKey()

Returns avatar key for which permissions have been granted or NULL_KEY if none have been granted.

Compare with llRequestPermissions and llGetPermissions.

Functions | Agent/Avatar


llGetPos

vector llGetPos()

Returns the prim's position in region coordinates, which are relative to the simulator's southwest corner.

When called from within a child prim in a linked set, it returns the position of the child in region coordinates, not the parent's position. To get the position of a child object relative to its parent, use llGetLocalPos. To get the position of the parent from a script within a child prim, use llGetRootPosition.

When used in an object attached to an avatar, it will always return the position of the avatar's center in region coordinates, though child objects in attachments will return the position of the child, as stated above.

Example:
// say location when touched
default {
    touch_start(integer num_detected) {
        llSay(0, "I'm at " + (string)llGetPos());
    }
}

Q: As of SL 1.6.7, the editor tooltip for llGetPos says "gets the position (if the script isn't physical)". Is that true? Does this only work on non-physical objects?
A: No, it's not true. The tooltip is in error. (It's misleading as well. A script can't be physical; only objects can.) llGetPos will return the current position regardless of physics status.

Q: Is llGetPos().x valid? (I can get a single component of a vector like so: vector_name.x.)
A: No, in LSL, you can only act upon a component of a vector that exists as a variable. You need to declare a vector first.

Use llSetPos to set the object's position.

See also llGetPrimitiveParams.

Functions | Dynamics | Position


llGetPrimitiveParams

list llGetPrimitiveParams(list params)

Returns primitive parameters specified in the params list.

The resulting list can then be used for parameters of llSetPrimitiveParams (but not without modification since the list is missing the parameters - you have to build a new list based on the params list and your own knowledge of how many arguments each parameter has). A function at the bottom of the page encapsulates llGetPrimitiveParams so that the output can be directly used with llSetPrimitiveParams.

Notes:
  • This function delays the script for .2 seconds.
  • As of SL 1.6.5, llGetTexture and llGetPrimitiveParams now return NULL_KEY when attempting to get the texture on an object without full asset permissions.
  • There is a major difference when using ALL_SIDES in a request instead of a face number. Instead of returning one value set with the mean average of the values on all sides (like for example llGetColor does), a strided list is returned containing llGetNumberOfSides strides (with each stride being a set of the specified return values).

Parameters

Query | Description Alternate Function Call | Return Value(s) | Example Query Example Result
PRIM_BUMP_SHINY, integer face | bumpmap and shininess of a face | integer shiny, integer bump (PRIM_SHINY_* and PRIM_BUMP_*) | [PRIM_BUMP_SHINY, 2] [2, 0]
PRIM_COLOR, integer face | color and alpha of a face llGetColor, llGetAlpha | vector color, float alpha | [PRIM_COLOR, 1] [<0.000000, 0.250980, 1.000000>, 1.000000]
PRIM_FLEXIBLE | flexible properties of the prim | integer TRUE/FALSE, integer softness, float gravity, float friction, float wind, float tension, vector force | [PRIM_FLEXIBLE] [1, 3, 0.300000, 2.000000, 0.000000, 1.000000, <0.000000, 0.000000, 0.000000>]
PRIM_FULLBRIGHT, integer face | full bright attibute | integer TRUE/FALSE | [PRIM_FULLBRIGHT, 1] [1]
PRIM_MATERIAL | material of the prim | integer material (PRIM_MATERIAL_*) | [PRIM_MATERIAL] [3]
PRIM_PHANTOM | phantom property of the object llGetStatus | integer phantom (TRUE/FALSE) | [PRIM_PHANTOM] [0]
PRIM_PHYSICS | physics property of the object llGetStatus | integer physics (TRUE/FALSE) | [PRIM_PHYSICS] [0]
PRIM_POINT_LIGHT | light properties of the object | integer (TRUE/FALSE), vector color, float intensity, float radius, float falloff | [PRIM_POINT_LIGHT] [1, <1.000000, 1.000000, 1.000000>, 1.000000, 10.000000, 0.750000]
PRIM_POSITION | position of the object llGetPos | vector position | [PRIM_POSITION] [<166.558548, 113.733795, 22.803015>]
PRIM_ROTATION | rotation of the object llGetRot | rotation rot | [PRIM_ROTATION] [<0.000000, 0.000000, 0.000000, 1.000000>]
PRIM_SIZE | scale of the object llGetScale | vector size | [PRIM_SIZE] [<0.500000, 0.500000, 0.500000>]
PRIM_TEMP_ON_REZ | temporary on rez property of the object | integer temponrez (TRUE/FALSE) | [PRIM_TEMP_ON_REZ] [1]
PRIM_TYPE | basic prim type | integer primtype, ... these parameters vary with primtype, see PRIM_TYPE table at llSetPrimitiveParams | [PRIM_TYPE] [2, 0, <0.000000, 1.000000, 0.000000>, 0.000000, <0.000000, 0.000000, 0.000000>, <0.000000, 0.000000, 0.000000>, <0.000000, 0.000000, 0.000000>]
PRIM_TEXGEN, integer face | texture mapping mode | integer texgen (PRIM_TEXGEN_DEFAULT, PRIM_TEXGEN_PLANAR) | [PRIM_TEXGEN, 4] [1]
PRIM_TEXTURE, integer face | texture properties of a face llGetTexture, llGetTextureScale, llGetTextureOffset, llGetTextureRot | ( string name OR key uuid ), vector repeats, vector offsets, float rotation (in radians) | [PRIM_TEXTURE, 2] ["Steel Plate", <1.000000, 1.000000, 0.000000>, <0.000000, 0.000000, 0.000000>, 0.000000]

Refer to llSetPrimitiveParams for information about PRIM_SHINY_*, PRIM_BUMP_* and PRIM_MATERIAL_*.

Example:
llGetPrimitiveParams([PRIM_PHANTOM, PRIM_TYPE, PRIM_COLOR, ALL_SIDES]);
Returns:
// note that it will not show the actual constants, but instead their values
[FALSE, // not phantom
// next the type of prim and the hole shape, as well as the parameters:
// vector cut, float hollow, vector twist, vector topsize, vector topshear
PRIM_TYPE_PRISM, PRIM_HOLE_DEFAULT, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <0.0, 0.0, 0.0>, <0.0, 0.0, 0.0>,
<0.000000, 0.250980, 1.000000>, 1.000000, // pairs of: vector color, float alpha
<0.000000, 0.250980, 1.000000>, 1.000000, // one for each side of the prim
<0.000000, 0.250980, 1.000000>, 1.000000, // because the query had ALL_SIDES as face
<0.000000, 0.250980, 1.000000>, 1.000000,
<0.000000, 0.250980, 1.000000>, 1.000000]

Example:
// get the prim type and everything that defines its form, except the scale
list params = llGetPrimitiveParams([PRIM_TYPE]);

// use this to restore the prim type and form (except scale)
llSetPrimitiveParams([PRIM_TYPE] + params);

Encapsulated (the ouput can be used as the input of llSetPrimitiveParams):
list GetPrimitiveParams(list input)
{
    list output;
    integer c = (output != input);//same as -llGetListLength(input);
    if(c)
    {
        list t = output;
        list special = [PRIM_BUMP_SHINY, PRIM_COLOR, PRIM_TEXTURE, PRIM_FULLBRIGHT, PRIM_TEXGEN];
        do
        {
            integer flag = llList2Integer(input, c); //peak the stack
            if(~llListFindList(special, [flag]))
            {
                if(++c)//adjust stack position, make sure it's valid.
                {
                    integer side = llList2Integer(input, c); //peak the stack
                    if(side == ALL_SIDES)
                    {
                        side = llGetNumberOfSides();
                        do
                            output += t + llGetPrimitiveParams(t = [flag, --side]);
                        while(side); //we return the sides in reverse order, easier to code; runs faster.
                    }
                    else
                        output += t + llGetPrimitiveParams(t = [flag, side]);
                    jump test;//by using a jump here we speed the code up a bit.
                }
                llOwnerSay("malformed input");
                jump end;
            }//else //not needed because of jumps.
            output += flag + llGetPrimitiveParams([flag]);
            @test;
        }while(++c);//adjust stack position, make sure it's valid.
    }
    @end;
    return output;
}//Writen by Strife Onizuka

Functions | Prim | Parameter


llGetRegionCorner

vector llGetRegionCorner()

Returns the RegionCorner of the simulator containing the task.

Note that the coordinates returned by llGetPos are relative to the simulator region's southwest corner. You can thus add the vectors returned by llGetPos and llGetRegionCorner to get an object's GlobalCoordinates.

You can also divide these coordinates by the size of a sim (256x256m) to get the grid position of the sim on a 2D grid.

Functions | Simulator | RegionCorner


llGetRegionFlags

integer llGetRegionFlags()

Returns a bitfield specifying the region flags (REGION_FLAG_*) for the region the object is in.

Flag | Hex | Value | Description
REGION_FLAG_ALLOW_DAMAGE | 0x1 | 1 | if a region is entirely damage enabled
REGION_FLAG_FIXED_SUN | 0x10 | 16 | if a region has a fixed sun position
REGION_FLAG_BLOCK_TERRAFORM | 0x40 | 64 | if a region terraforming disabled
REGION_FLAG_SANDBOX | 0x100 | 256 | if a region is a sandbox
REGION_FLAG_DISABLE_COLLISIONS | 0x1000 | 4096 | if a region has disabled collisions
REGION_FLAG_DISABLE_PHYSICS | 0x4000 | 16384 | if a region has disabled physics
REGION_FLAG_BLOCK_FLY | 0x80000 | 524288 | if a region blocks flying
REGION_FLAG_ALLOW_DIRECT_TELEPORT | 0x100000 | 1048576 | if a region allows direct teleports.
REGION_FLAG_RESTRICT_PUSHOBJECT | 0x400000 | 4194304 | if a region restricts llPushObject

Parcel settings may override the region's settings. llGetParcelFlags can be used to determine these settings on a parcel-by-parcel basis.

Example:
integer region_flags = llGetRegionFlags();
if (region_flags & REGION_FLAG_ALLOW_DAMAGE)
   llOwnerSay("This region is damage enabled!");

if (region_flags & REGION_FLAG_FIXED_SUN)
   llOwnerSay("This regions sun doesn't move!");

if (region_flags & REGION_FLAG_BLOCK_TERRAFORM)
   llOwnerSay("This region does not allow terraforming!");

if (region_flags & REGION_FLAG_SANDBOX)
   llOwnerSay("This region is a sandbox!");

if (region_flags & REGION_FLAG_DISABLE_COLLISIONS)
   llOwnerSay("This region currently blocks non-agent collisions!");

if (region_flags & REGION_FLAG_DISABLE_PHYSICS)
   llOwnerSay("This region currently blocks all physics!");

if (region_flags & REGION_FLAG_BLOCK_FLY)
   llOwnerSay("This region blocks flying!");

if (region_flags & REGION_FLAG_ALLOW_DIRECT_TELEPORT) {
   llOwnerSay("This region follows P2P rules!");
} else {
   llOwnerSay("This region uses a telehub!");
}

Functions | Simulator


llGetRegionFPS

float llGetRegionFPS()

Returns the number of frames per second (FPS) the simulator is running at. As of SL 1.7, this will never be higher than 45 frames per second, the same rate as the physics framerate though, according to Lindens, the sim and physics framerates are not intrinsically linked despite being capped at the same value. As before, low framerates (relative to 45fps) mean that a script will not be granted much execution time.

To find out how many script instructions are being executed per second, in the Ctrl-Shift-1 statistics bar, it's "Script Perf"[ormance] under "Simulator".

Compare with llGetRegionTimeDilation.

Functions | Simulator | Lag


llGetRegionName

string llGetRegionName()

Returns the current region (sim) name, for example "Gibson".

To request information about a region try llRequestSimulatorData.

For a list of simulators see SimulatorNames.

Functions / WorldFunctions


llGetRegionTimeDilation

float llGetRegionTimeDilation()

Returns the time dilation of the current region (simulator) as a float between 0 and 1.

Time dilation occurs when the sim can't keep up with the processing of its tasks even after reducing the time allocated to scripts and physics. Avatars will experience this as slowed-down (slow-motion, "bullet-time") movement.

If you have many scripts running in a sim (or physical objects), it would be considerate to pause them when the time dilation drops below a certain threshold (0.8 - 0.9).

See llGetRegionFPS and lag.

This is a script that will automatically pause all scripts once dilation drops below 0.65:

integer running;

string giv(integer type,integer i) {
    return llGetInventoryName(type,i);
}

default
{
    on_rez(integer s) { llResetScript();} Habit - llGetOwner() has a habit of returning the old owner's name when given to another person.

    state_entry()
    {
        llSetTimerEvent(0.3); //Repeat every 3/10th of a second.
        running = TRUE;
    }

    timer() {
        float dilation = llGetRegionTimeDilation(); //Get the dilation
        if(dilation < 0.65 && running) {  //If the dilation is less than 65%
            llOwnerSay("Time Dilaton is critical! Pausing scripts!"); //Say to the owner that message
            integer x;
            for(x=0;x<llGetInventoryNumber(INVENTORY_SCRIPT);x++) {
                if(giv(INVENTORY_SCRIPT,x) != llGetScriptName()) { //If the script number x is not this script
                    llSetScriptState(giv(INVENTORY_SCRIPT,x),FALSE); //Pause it
                }
            }
            running = FALSE;
            llOwnerSay("Done.");
        }
        else
        if (!running)
        { //If the dilation is more than 65%
            integer x;
            for(x=0;x<llGetInventoryNumber(INVENTORY_SCRIPT);x++) {
                if(giv(INVENTORY_SCRIPT,x) != llGetScriptName()) {
                    llSetScriptState(giv(INVENTORY_SCRIPT,x),TRUE); //Enable all scripts
                }
            }
            running = TRUE;
        }
    }
}

Functions | Simulator | Time | Lag


llGetRootPosition

vector llGetRootPosition()

Gets the position (in region coordinates) of the root/parent prim of the object containing the script. This is used to allow a child prim to determine where the root is.

Compare with llGetPos and llGetLocalPos.

Note: there is not currently a way to set the root prim's position from a child prim. To do so, a script is needed in the root to recieve a link message

When called in the child prim of an attachment, the function returns the position of the avatar, as llGetPos would in the parent prim.

Actually, you can set the root prim at the position of the geometric center of the object using a simple function:
root2Centre() //puts the root prim at the geometric centre of the object
{
    vector myPos = llGetPos();
    vector rootPos = llGetRootPosition();
    vector rootOffset = rootPos - myPos;
    llSetPos(myPos - rootOffset);
}
You could also make a parameter for the function to set the root prim at a specified distance:
setRootPos(vector pos) //puts the root prim at the specified position
{
    vector destPos = pos;
    vector myPos = llGetPos();
    vector rootPos = llGetRootPosition();
    vector rootOffset = rootPos - myPos;
    llSetPos(destPos - rootOffset);
}
- Haz

Functions | Root | Position | Link


llGetRootRotation

rotation llGetRootRotation()

Gets the global rotation of the root object of the object script is attached to.

Functions / ChildRotation


llGetRot

rotation llGetRot()

Returns the object's rotation. If called from a script that is attached to an avatar, it will return the avatar's rotation. To get an attachment's rotation relative to the attach point use llGetLocalRot.

Note: the tooltip in the LSL editor erroneously states "gets the rotation (if the script isn't physical)". Aside from the fact that the the script itself can't be physical (they obviously mean the object containing the script), that line is wrong anyway. llGetRot does work if the object is physical.

Compare with llGetLocalRot and llGetRootRotation. See also llGetPos and llGetPrimitiveParams.

Functions | Rotation | Dynamics


llGetScale

vector llGetScale()

Returns the scale (size) of the object.

Object scale can be set using llSetScale or llSetPrimitiveParams.

See also llGetPrimitiveParams.

Functions


llGetScriptName

string llGetScriptName()

Returns the name of this script (in the object inventory), and not the name of the object in which this script resides.

To get the name of the object, use llGetObjectName.

Q: I'm trying to use this to differentiate between several otherwise identical scripts, but something's going horribly wrong! Why is this happening and what do I do to fix it?
A: If you do just a copy & paste, I believe that you get always the name of the "first" script, because the UUID of the script is always the same. Each time you save a script it gets a new asset key. The quickest way to get a new key is to simply edit each script and save it, generating a new key. One way to ensure they all have unique keys is to add a unique comment to the top of each script.

Q: Is there a way to determine the number of the script? If I do a for loop and run through all scripts by name, the scripts are in order. How do I find out where a given script is in the list?
A: You can't directly. Scripts will always have a unique name in an object, so you can check using llGetInventoryNumber and a for loop containing llGetInventoryName, then comparing to the results of llGetScriptName.

Note: In SL 1.9.0, llGetScriptName correctly returns numbered names (e.g. "New Script", "New Script 1", etc).
Functions | Inventory | Script | Name


llGetScriptState

integer llGetScriptState(string name)

Returns TRUE if the script identified by name is running. This allows one script to check on another.

Currently this will not report FALSE for a script that encountered a run-time error (like a stack-heap collision or a bounds-check error), which greatly reduces the usefulness of this function.

See llRemoteLoadScriptPin and llResetOtherScript for more fun things that can be done with this.

Also see llSetScriptState.

Functions | Inventory | Script | State


llGetSimulatorHostname

string llGetSimulatorHostname()

Returns the host name (server) of the sim in which the scripted object is located. For example, "sim225.agni.lindenlab.com".

Note: this function delays the script for 10 seconds.

Functions | Simulator


llGetStartParameter

integer llGetStartParameter()

Returns the start parameter passed to llRezObject or llRemoteLoadScriptPin. If the object was created from agent inventory, this function returns 0.

This is the same value that is passed to the on_rez event. If the script is reset (using llResetScript or other means), this value is set to 0, unless llRemoteLoadScriptPin was used; then the start parameter becomes static until taken into inventory.

Functions | Parameter


llGetStatus

integer llGetStatus(integer status)

Returns the value of status, which will be one of the following constants:

Flag | Value | Default | Description
STATUS_BLOCK_GRAB | 64 | FALSE | If TRUE, object can't be grabbed and physically dragged.
STATUS_DIE_AT_EDGE | 128 | FALSE | If TRUE, objects that reach the edge of the world just die rather than returning to the owner's inventory.
STATUS_PHANTOM | 16 | FALSE | If TRUE, object doesn't collide with other objects.
STATUS_PHYSICS | 1 | FALSE | If TRUE, object moves physically.
STATUS_RETURN_AT_EDGE | 256 | FALSE | If set, objects created with llRezObject will be returned to inventory instead of deleted when going off-world; overrides STATUS_DIE_AT_EDGE. (128)
STATUS_ROTATE_X | 2 | TRUE | If FALSE, object doesn't rotate around local X axis.
STATUS_ROTATE_Y | 4 | TRUE | If FALSE, object doesn't rotate around local Y axis.
STATUS_ROTATE_Z | 8 | TRUE | If FALSE, object doesn't rotate around local Z axis.
STATUS_SANDBOX | 32 | FALSE | If TRUE, object can't cross region boundaries or move more than 10 meters from its start location.

Note: Trying to get multiple status flags at the same time by OR'ing them will return only the value of the status in the most significant bit requested, ignoring the values in lesser bits. If you need more than one, do it one at a time.

Example:
integer physics_on = llGetStatus(STATUS_PHYSICS); // this will return TRUE if STATUS_PHYSICS == TRUE.

See also llSetStatus and llGetPrimitiveParams.

Functions | Constants | Prim


llGetSubString

string llGetSubString(string source, integer start, integer end)

Returns a copy of source containing the indicated substring. source is unaltered.

The start and end are inclusive, so 0, length - 1 would capture the entire string and 0, 0 would capture the first character. Using negative numbers for start and/or end causes the index to count backwards from the length of the string, so 0, -1 would capture the entire string. If start is larger than end the substring is the exclusion of the entries, so 6,4 would give the entire string except for the 5th character.

This behavior is similar but opposite to that of the llDeleteSubString function.

To determine the length of a string, use llStringLength.

Example:
string source = "I never killed a man I didn't like.";
string result = llGetSubString(source, 0, -1); // gets the entire string.
string result = llGetSubString("abcd", 1, -2); // gets "bc"
string result = llGetSubString("abcd", 1, 3); // gets "bcd"

Functions | String


llGetSunDirection

vector llGetSunDirection()

Returns a normalized vector that points toward the sun's current position (and angle of the sim's directional light source). This vector is updated once every 10 seconds.

The moon is directly opposite the sun at all times, so the negation of the vector returned by this function points towards the moon. (This implies that the moon is always full in SL, which is rather strange, but given the orbital oddities below, this is fairly ignorable.)

A Second Life day is 4 Earth hours long. 0:00 is midnight and 2:00 is high noon. Sunrise is at approximately 0:30 and sunset around 3:30, which results in 3 hours of daylight and only 1 hour of night-time. Refer to the chart below for PST, PDT and GMT times. There is no guarantee this data is accurate:

Phase | PST | PDT | GMT
Sunset | 2:30 AM | 3:30 AM | 10:30
Midnight | 3:00 AM | 4:00 AM | 11:00
Sunrise | 3:30 AM | 4:30 AM | 11:30
Sunset | 6:30 AM | 7:30 AM | 14:30
Midnight | 7:00 AM | 8:00 AM | 15:00
Sunrise | 7:30 AM | 8:30 AM | 15:30
Sunset | 10:30 AM | 11:30 AM | 18:30
Midnight | 11:00 AM | 12:00 PM | 19:00
Sunrise | 11:30 AM | 12:30 PM | 19:30
Sunset | 2:30 PM | 3:30 PM | 22:30
Midnight | 3:00 PM | 4:00 PM | 23:00
Sunrise | 3:30 PM | 4:30 PM | 23:30
Sunset | 6:30 PM | 7:30 PM | 2:30
Midnight | 7:00 PM | 8:00 PM | 3:00
Sunrise | 7:30 PM | 8:30 PM | 3:30
Sunset | 10:30 PM | 11:30 PM | 6:30
Midnight | 11:00 PM | 12:00 AM | 7:00
Sunrise | 11:30 PM | 12:30 AM | 7:30

Effectively, the sun moves faster at night than during the day. However, an equivalent model is that the sun's orbit is uniform and circular but not centered on the world (yes, this implies that in Second Life the sun orbits the world rather than the other way around). The true center of the sun would then be at a point well above the horizon, such that the ratio of day-to-night is approximately 3:1. The normal of the sun's orbital plane would appear to be tilted about 45 degrees on the global x-axis, although it varies more than +/- 5 degrees over a Second Life year (which is approximately 10 days long).

For all intents and purposes, the sun can be considered infinitely far away compared to the scale of the world. That is, its direction (and light) is uniform not only over the entire simulator but over the entire world. The exception is that owners of private islands can fix the position of the sun so that its direction may be different from the direction seen in the majority of the world.

Although a rare occurrence, it is possible for the Lindens to override the phase offset of the sun's orbit, and to even lock it in place. Since this happens worldwide, it is likely that the sun's location is actually controlled from a central source rather than by a deterministic model that is duplicated on each simulator.

One possible application for this function is to determine whether it's day- or nighttime. The following script simply uses the z-axis of the sun's vector to find out if the sun is above or below the horizon:

integer night=0; // 0 = daytime, 1 = nighttime

default
{
    state_entry()
    {
        llSetTimerEvent(300); // Check every 5 minutes
    }
    timer()
    {
        vector sun = llGetSunDirection();
        if (sun.z <= 0) night = 1; // Sun is below the horizon
        else if (sun.z > 0) night = 0; // Sun is above the horizon
    }
    touch_start(integer total_number)
    {
        if (night == 1) llSay(0, "It's nighttime.");
        else if (night == 0) llSay(0, "It's daytime.");
    }
}

If the complete script is to behave different at night, an additional script state may be a better solution than checking a variable:

default // daytime state
{
    state_entry()
    {
        llSetTimerEvent(180); // Check every 3 minutes
    }
    timer()
    {
        vector sun = llGetSunDirection();
        if (sun.z < 0) state night;
    }
    touch_start(integer total_number)
    {
        llSay(0, "It's daytime.");
    }
}

state night
{
    state_entry()
    {
        llSetTimerEvent(180); // Check every 3 minutes
    }
    timer()
    {
        vector sun = llGetSunDirection();
        if (sun.z > 0) state default; // Change back to daytime state
    }
    touch_start(integer total_number)
    {
        llSay(0, "It's nighttime.");
    }
}

Q: Can I set the sun position with a script? Debug mode lets you move the sun.
A: No. Debug mode only changes what you see, not what other people see, or what llGetSunDirection sees. If you own a private island, the estate tools can let you change the sun position within your island, but you can't do that with a script either. llGetSunDirection will get the correct sun position in a private sim.

There was once a footnote here claiming that a Linden said the sun and moon actually do move independently but that an eclipse would not occur for at least 500 RL years. It turns out that this is incorrect; the sun and moon are, in fact, always directly opposite each other.

Functions | Simulator | Weather | Time | Light


llGetTexture

string llGetTexture(integer side)

Returns the texture of side. If this texture is in the inventory, it returns the name of this texture in the inventory. If it is not in the inventory, it returns the key of this texture.

Note: If side == ALL_SIDES, side 0 is used instead.

Compare to llSetTexture, llGetPrimitiveParams.

As of 1.6.5, llGetTexture and llGetPrimitiveParams now return NULL_KEY when attempting to get the texture on an object without full asset permissions.

Q: How do I determine which side of an object this applies to?
A: See side.

Q: How do I get the key of a texture that is no-modify?
A: You don't. LSL doesn't allow you to bypass Second Life's DRM model. Asset permissions still apply.

Q: If I can't get the key of a texture because my object is set to no-modify, is there any way to compare the texture against a given key?
A: No, not yet. There doesn't appear to be a reason stopping the Lindens from adding something like integer llCompareTexture(integer side, key src) which would compare the key of side to that supplied as src, returning either TRUE or FALSE, but this isn't something that exists. Unfortunately, there's currently no way around it. Design your scripts accordingly.

Functions | Texture


llGetTextureOffset

vector llGetTextureOffset(integer side)

Returns the texture offset of side in the x and y components of the returned vector.

Note: If side == ALL_SIDES, side 0 is used.

Compare to llOffsetTexture, llGetPrimitiveParams.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture


llGetTextureRot

float llGetTextureRot(integer side)

Returns the texture rotation of side in radians.

Note: If side == ALL_SIDES, side is treated as 0.

Compare to llRotateTexture and llGetPrimitiveParams.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture


llGetTextureScale

vector llGetTextureScale(integer side)

Returns the texture scale of side in the x and y components of the returned vector.

Note: If side == ALL_SIDES, side 0 is used.

Compare to llScaleTexture, llGetPrimitiveParams.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture | Scale


llGetTime

float llGetTime()

Gets the time in seconds since the last script reset, or since the time was last reset using llResetTime or llGetAndResetTime. It has been reported that it can also reset when the server reboots or other unusual events. Because of this, it works best for measuring short intervals (e.g. "Has it been over three seconds since I was last clicked?"), and may be unreliable over long intervals (e.g. "Has is been over three days since I was last clicked?").

Note: llGetTime, llGetTimeOfDay, llGetWallclock, and llGetGMTclock do different things.

The following script reports the script's lifetime each time the object is touched:
default
{
    touch_start( integer total_number )
    {
        float lifetime = llGetTime();
        llWhisper( 0, "This script has been running for "
                      + (string)lifetime
                      + " seconds" );
    }
}

Functions | Time


llGetTimeOfDay

float llGetTimeOfDay()

Gets the Second Life time. Time in Second Life passes at the same speed as in the real world, but the day is normally only 4 hours long. See llGetSunDirection for a detailed description of the Second Life day.

When a sim is rebooted, the value of llGetTimeOfDay is set to zero. It then proceeds to increase by 1 as every second passes so that, initially, it expresses the number of seconds that have passed since the sim was rebooted. The value of llGetTimeOfDay is also set to zero when the sun passes its midnight position (see llGetSunDirection). This means, in normal circumstances, llGetTimeOfDay will show time since reboot for up to four hours and, thereafter, show time since sim midnight. Assuming that a sim is not rebooted very often, llGetTimeOfDay will therefore usually return the number of seconds since sim midnight, but cannot be relied on to do so.

Owners of private islands have the ability to lock the sun's position; in these locations, llGetTimeOfDay will never be reset by the sun passing its midnight position, and llGetTimeOfDay will always show time since sim reboot, increasing without limit. The Lindens also have the ability to lock the sun over the whole world and, in SL 1.5.1, the sun was accidentally locked in one position for several days.

If you are trying to determine whether it is night- or daytime (to activate a light source at night only, for example), you could simpy test if the result of llGetTimeOfDay is larger than 3600 (1 hour in seconds). But the function's behaviour after each sim reboot makes it too unreliable. Using llGetSunDirection provides a more accurate way (see the script example at the bottom of the llGetSunDirection page).

Note: llGetTimeOfDay, llGetTime, and llGetWallclock do different things.

Functions | Time


llGetTimestamp

string llGetTimestamp()

Returns the current time-and-date (a timestamp) in the format YYYY-MM-DDThh:mm:ss.ff..fZ, for example: 2004-08-27T00:56:21.785886Z
The letter Z stands for Zulu, the military time zone name for UTC. T is just a separator between date and time.

This format allows exact times and easy sorting, but is not as suited for comparing time as a libc like timestamp. Nevertheless, it is still possible. (See below.)

This format is one of the options defined by International Standard ISO 8601 (summary). It is also used to represent a specific instant in time in the XML Schema standard's dateTime data type, which is "inspired" by ISO 8601. As such, this is a format for storing in databases and log files, not display to users or computation.

To get the number of seconds elapsed since 00:00 hours, Jan 1, 1970, use llGetUnixTime. This function is better suited to comparing time than llGetTimeStamp.

The following example shows how hours, minutes, and seconds can be extracted from the timestamp.
//A function that returns a vector in the form of <hours,minutes,seconds> using llGetTimestamp()
//  Keknehv Psaltery 06/15/05
vector time()       //Returns a vector with the current time in hours, minutes, and seconds
{                   //In the format <hours,minutes,seconds>
    list timestamp = llParseString2List( llGetTimestamp(), ["T",":","Z"] , [] );
    return <    llList2Float( timestamp, 1 ),
                llList2Float( timestamp, 2 ),
                llList2Float( timestamp, 3 ) >;
}

AlexanderDaguerre disagrees with the comment about this being an unsuitable format for comparing time; this is one of the things that this format was intentionally designed to support and is the reason that sorting works. Comparing two llGetTimestamp strings should give you the same results as comparing the two instants in some high precision integer format (higher precision than Unix timestamp format, too). Of course, with that decimal point in there you shouldn't compare two such strings for equality for the same reasons that you should never expect two floating-point values to compare equal. AlexanderDaguerre agrees with Ezhar that this format is pretty hard to use for time difference as opposed to comparison.

Ezhar would like an easy way to tell the time difference between two timestamps, which is very simple in UNIX timestamps but not in this format (at least not using LSL).
I just scraped together a function that might do what you ask Ezhar... there may be a couple bugs with it, namely in dealing with leap-years and varying days in months, but that's because I wrote it in 10 mins. I would fix them, but its 3AM and Im tired :) -Chris
// float secsBetween(string, string):
// A very rough way of getting the difference in seconds
// between two timestamps in the form returned by llGetTimestamp.
// Note that this function currently has two problems:
//    1. No accurate leap-year correction.
//    2. No accurate month-day correction (30 days, 31 days, etc)
// Both are fixable, but Im currently too sleepy :)
// Written by:     Christopher Omega
// Date:        9/4/04
float secsBetween(string t1, string t2) {
    // Parses the timestamps into a two-element list consisting of
    // [date, time] where date is a string in the form "YYYY-MM-DD"
    // and time is a string in the form "hh:mm:ssZ" (Note: ss is a float value)
    list parsed1 = llParseString2List(t1, ["T"], []);
    list parsed2 = llParseString2List(t2, ["T"], []);

    // Date1 and Date2 are lists in the form of [year, month, day]
    list date1 = llParseString2List(llList2String(parsed1, 0), ["-"], []);
    list date2 = llParseString2List(llList2String(parsed2, 0), ["-"], []);

    // Time1 and Time2 are lists in the form of [hours, minutes, seconds]
    // Taking into account the one-letter "Z" suffix on the end of the time element
    // by deleting it.
    list time1 = llParseString2List(llDeleteSubString(llList2String(parsed1, 1), -1, -1), [":"], []);
    list time2 = llParseString2List(llDeleteSubString(llList2String(parsed2, 1), -1, -1), [":"], []);

    // Stamp* lists are in the form [year, month, day, hour, min, sec]
    list stamp1     = date1 + time1;
    list stamp2     = date2 + time2;

    // SECONDS_CONVERSION is a list containing the number of seconds in:
    // one year, one month, one day, one hour, one minute and one second,
    // respectively.
    // TODO: Provide accurate leap-year and month-day-count correction.
    // Im assuming 365.25 days in a year and 30 days in a month :(
    list SECONDS_CONVERSION = [31557600, 2592000, 86400, 3600, 60, 1];

    float diffInSecs = 0;
    integer i;
    for (i = 0; i < 6; ++i) {
        // TODO: See if extracting elements as floats here is a reasonable
        // accuracy tradeoff.
        // Thought: Perhaps we could multiply the seconds
        // value by some power of ten at the beginning of this function
        // and simply work with it like you would an integer, since its the only
        // floating point value.
        float element1             = (float) llList2String(stamp1, i);
        float element2             = (float) llList2String(stamp2, i);
        float conversionFactor     = (float) llList2String(SECONDS_CONVERSION, i);

        diffInSecs += llFabs(element1 - element2) * conversionFactor;
    }

    return diffInSecs;
}

I was researching information on dates here and noticed that the statement below was innacurate. Every 100 years is /NOT/ a leap year, except for every 400 years, which /ARE/ a leap year. See (http://mathforum.org/dr.math/faq/faq.calendar.html) for more information. - RichardSolvang

My bad - Ice

Here's another stab at a timestamp-difference function. It doesnt use any kind of zero reference point, so as long as the two times are close enough to one another, the function should handle them fine. It works like the difference operator, subtracting the second parameter from the first - so if the second timestamp is farther into the future then the first (has a larger time value), then it will return a negative value. It should handle month-days and leap years much better then my previous try, but to do so, the code grew a great number of lines. Be careful though, I havent rigorously tested it. -Chris
// Scroll down...
integer min(integer a, integer b) {
    if (a > b)
        return b;
    return a;
}

integer isLeapYear(integer year) {
    return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
}

integer daysInMonth(integer month, integer year) {
    if (month < 9) {
        if (month != 2) {
            // Before September, odd numbered months
            // are 31 days long.
            if (month % 2 != 0) {
                return 31;
            } else {
                return 30;
            }
        } else {
            // February is the odd man out.
            if (isLeapYear(year)) {
                return 29;
            } else {
                return 28;
            }
        }
    } else {
        // After August, odd numbered months
        // are 30 days long.
        if (month % 2 != 0) {
            return 30;
        } else {
            return 31;
        }
    }
}

// timestampDiff(string, string)
// Written by Christopher Omega 10/21/05
// Returns the difference between the two timestamps
// passed to it, subtracting the second parameter from
// the first.
float timestampDiff(string t1, string t2) {
    if (t1 == t2)
        return 0;

    float diff = 0;
    integer year1 = (integer) llGetSubString(t1, 0, 3);
    integer year2 = (integer) llGetSubString(t2, 0, 3);
    integer i;
    if (year1 != year2) {
        integer minYear = min(year1, year2);
        integer maxYear = year1;
        if (minYear == year1)
            maxYear = year2;
        float secsBetween;
        secsBetween += 31536000 * (maxYear - minYear);
        for (i = minYear; i < maxYear; ++i) {
            if (isLeapYear(i))
                secsBetween += 86400; // Add extra day.
        }
        if (year2 == maxYear)
            secsBetween *= -1;
        diff += secsBetween;
    }

    integer month1 = (integer) llGetSubString(t1, 5, 6);
    integer month2 = (integer) llGetSubString(t2, 5, 6);
    if (month1 != month2) {
        integer month1Secs;
        for (i = 1; i < month1; ++i) {
            // 1 day = 24 hrs * 60 mins * 60 secs
            month1Secs += daysInMonth(i, year1);
        }
        month1Secs *= 86400; // Convert days into secs.
        integer month2Secs;
        for (i = 1; i < month2; ++i) {
            month2Secs += daysInMonth(i, year2);
        }
        month2Secs *= 86400;
        diff += month1Secs - month2Secs;
    }
    // Whew! Now everything past this point should be clear cut
    // conversion directly into seconds.
    integer days1 = (integer) llGetSubString(t1, 8, 9);
    integer days2 = (integer) llGetSubString(t2, 8, 9);
    diff += (days1 - days2) * 86400;
    integer hours1 = (integer) llGetSubString(t1, 11, 12);
    integer hours2 = (integer) llGetSubString(t2, 11, 12);
    diff += (hours1 - hours2) * 3600; //1 hr = 60 mins * 60 secs.
    integer minutes1 = (integer) llGetSubString(t1, 14, 15);
    integer minutes2 = (integer) llGetSubString(t2, 14, 15);
    diff += (minutes1 - minutes2) * 60;
    float secs1 = (float) llGetSubString(t1, 17, -2); // Skip last char.
    float secs2 = (float) llGetSubString(t2, 17, -2);
    diff += secs1 - secs2;
    return diff;
}

The scripts at LibraryTimeElapsed use this function to compare points in time with high accuracy.

Functions | Time | Timestamp


llGetTorque

vector llGetTorque();

Returns the torque currently being applied on a physical object.

Compare with llGetForce and llSetTorque.

Q: Is the value returned relative to the axes formed by the object's current rotation, or is it relative to the simulator's axes? The latter makes more sense. -Chris
A: If I recall properly it depends on how the torque was applied. If it was a global torque then it is returned relative to the sim's axis, otherwise it is local. Isn't that crazy? -BW

Functions | Dynamics | Torque


llGetUnixTime

integer llGetUnixTime()

Returns the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC from the system clock. In UNIX terms, time_t. This is great for a monotonic source of time that ticks once a second.

Compare with llGetWallclock, llGetGMTclock, and llGetTimeStamp.

Functions | Time | Timestamp


llGetVel

vector llGetVel()

Returns the current velocity of an object in meters/second, relative to the simulator axes.

To find the velocity relative to the direction the object is pointing, try this:
vector global_velocity = llGetVel();
vector local_velocity = global_velocity / llGetRot();

Q: How would one go about calculating the actual speed of an object as a float?
A: Calculate the length (or magnitude) of the velocity vector with llVecMag(llGetVel()).

Functions | Dynamics | Physics


llGetWallclock

float llGetWallclock()

Gets the time in seconds since midnight in the simulator server's time zone. The local time zone is either PST or PDT, depending upon the time of the year, as California observes Daylight Saving Time. The time that it returns is the same as the in-SL clock. llGetWallclock tracks that change and returns Pacific time in either PST or PDT, whichever is appropriate.

To get the current time in GMT, use llGetGMTclock.

How to convert this into hours, minutes and seconds:
integer    t;

integer    hours;
integer    minutes;
integer    seconds;

t = (integer)llGetWallclock(); // seconds since midnight

// one hour has 3600 seconds
hours = t / 3600; // get hours (integer division chops off the decimals)

// the modulo operator % gets the remainder of a divison
// in this case, the remaining seconds after removing the full hours
minutes = (t % 3600) / 60; // divide by 60 because we want minutes, chops off decimals again

seconds = t % 60; // get the seconds that didn't fit into full minutes

Compare with llGetTimeOfDay, llGetTime and llGetGMTclock.

Functions | Simulator | Time


llGiveInventory

llGiveInventory(key destination, string inventory)

Gives the named inventory item to the agent or object with the key destination. Objects have to be in the same sim but agents can receive inventory regardless of where they are. If the user isn't online, they will receive a dialog box when they next log in, asking them whether they would like to accept or decline the object's inventory offer. If offline IMs are enabled, they will also receive an email notification saying they have been offered inventory.

If the recipient is an agent, the user then follows the normal procedure of accepting or denying the offer. If the recipient is an object, the same permissions apply as if a user dragged an inventory item onto the object manually, i.e. if llAllowInventoryDrop has been called with TRUE, any other object can pass items to its inventory. No-copy objects can not be given from attachments.

If the recipient is an object, and the object already has something in its inventory with this name, then Second Life will check to see if the object being given differs from the object already existing in the recipient's inventory, or if it is another copy of the same object (ie, the object is being given multiple times). If it is the same object being given multiple times, nothing happens at all, and the recipient's inventory does not change. If the object is deemed to be a different object with the same name, it will create a new copy with an incremented number, as if a user manually placed an item with a duplicate name into an object's inventory. This differs from what happens if llRemoteLoadScriptPin places a script with a duplicate name into an object's inventory.

If you give a script the transfer will be done on the asset server but the script VM won't be loaded into the sim containing the object. This will cause the script to be unable to be started by normal means. To start the script you must either recompile it (forcing the sim to load the new VM) or pick the object up, rez it in world again, then the script may be set to running by normal means (check box in script window, set all scripts in selection to running via the tools menu, or by using llSetScriptState from another script. If you need to give a running script to an object, use llRemoteLoadScriptPin. A recipient that has recieved a non-running script from an llGiveInventory call cannot start that script running within itself, but it can still give that script to a further object using llRemoteLoadScriptPin, and the script will still be started on the recipient of the llRemoteLoadScriptPin if the parameters to that call specified that it should be.

A script may give itself.

If you attempt to give a large number of objects within a short period of time (such as using multiple scripts to increase speed) or you rez a self-replicating object that attempts to give objects, you may run into the Grey Goo Fence, which attempts to slow grid attacks.

Note: This function delays the script for 3 seconds if the target is an avatar. Object to object transfers seem to have no delay, and the delay text has been removed from the SL tooltip.

Q: How do I just make a script that just gives someone the only object/notecard/texture in inventory?
A: Simple, do this:
default {
    touch_start(integer total_number) {
        // to give a different inventory item type,
        // replace "INVENTORY_OBJECT" with "INVENTORY_NOTECARD", etc.
        llGiveInventory(llDetectedKey(0),llGetInventoryName(INVENTORY_OBJECT, 0));
    }
}

Q: Is there a way to have an object give itself?
A: No, but it's possible to put a copy of the object into itself that it can then give, which is sufficient for many uses. Also, here's a feature suggestion thread asking for it. This sort of thing is sometimes used in self-replicating objects.

Q: Can an object refuse an item based on a conditional statement?
A: No, but if you keep track of the inventory your object contains, you can remove unwanted items using llRemoveInventory when the changed event is called with CHANGED_INVENTORY in its integer parameter.

Q: How can I confirm the intended recipient actually recieved the item?
A: You can't, not automatically. If giving an item to an object, the recipient object could contain a script with a changed event, allowing it to llSay a "received item" message. When giving an item to an agent, you're probably out of luck.

Q: Can I choose which folder the inventory item goes into?
A: Not with llGiveInventory. Instead, use llGiveInventoryList, with the list containing the single item (to an agent in the same sim).

Q: When I try to give an inventory item, the item disappears from the original object's inventory. What's going on?
A: This is happening because you don't have copy permission on the item you're trying to give. Consequently, the script gives the only copy of the item in your object. To determine whether or not you have permission to give the objects specified, use llGetInventoryPermMask.

Q: Can my object give inventory to an object owned by another user?
A: Yes, but only if the receiving object has llAllowInventoryDrop set to TRUE. See CHANGED_ALLOWED_DROP under the changed event.

Q: Can all prims in a linked object be given inventory items?
A: No, though they can each give inventory.

Also see llGiveInventoryList to give several items at once, or to give items in a folder.

Functions | Inventory | llGiveInventoryList


llGiveInventoryList

llGiveInventoryList(key destination, string category, list inventory)

Give the list of named inventory items to the keyed agent (anywhere in the world--see notes below) or object (in the same simulator as the giver) destination. If the recipient is an agent, the user follows the normal procedure of accepting or denying the offer. The offered inventory is then placed in a folder named category in the recipient's inventory. If the recipient is an object, the same permissions apply as if the user dragged inventory onto the object by hand, i.e., if llAllowInventoryDrop has been called with TRUE, any other object can pass items to its inventory. If the recipient is an object, the category parameter is ignored--an object cannot contain folders in its inventory.

Notes:
  • llGiveInventoryList can only give items which the object owner has copy permission on. To determine whether or not the object owner has permission to give the objects specified, use llGetInventoryPermMask.
  • llGiveInventoryList does not consistently deliver the objects to an agent in another sim. From test results, it appears it will deliver only if the agent is a child agent to the sim with the giver or if the agent has been recently in the sim that the giver is. Do not rely on this functionality!
  • Group owned (deeded) objects are not permitted to give inventory. The function sends an error message to the "Script Errors/Warning" dialog: "Group owned objects cannot give inventory.".
  • If inventory is empty, or if the object contains none of the items specified in inventory, or if the object owner does not have copy permission on any of the items specified in inventory, llGiveInventoryList will cause the object to say "Unable to give inventory list: No items passed filter".
  • If inventory contains even one item that exists in the object and is copyable, llGiveInventoryList will give it, in the specified folder, but will not return an error. The items that could not be transferred will be ignored.
  • llGiveInventoryList delays the script for 3 seconds.
  • If you attempt to give a large number of objects within a short period of time (such as using multiple scripts to increase speed) or you rez a self-replicating object that attempts to give objects, you may run into the Grey Goo Fence, which attempts to slow grid attacks.

Example:
default
{
    on_rez(integer nevermind)
    {
        llSetText("Touch me to unpack\nyour new "+llGetObjectName()+".",<1,1,1>,1.0);
    }
    touch_start(integer total_number)
    {
// give all items in a prim to the owner, as folder (with the name of the prim)
// Ezhar Fairlight <efairlight@gmail.com>

// user-friendly additions by Mechanique Thirty (egypt@urnash.com)
// Script adjusted to run faster by Strife Onizuka.

        list        inventory;
        string      name;
        integer     num = llGetInventoryNumber(INVENTORY_ALL);
        string      text = llGetObjectName() + " is unpacking...\n";
        integer     i;
        key         user = llGetOwner();//Set to llDetectedKey(0); to allow anyone to use

        for (i = 0; i < num; ++i) {
            name = llGetInventoryName(INVENTORY_ALL, i);
            if(llGetInventoryPermMask(name, MASK_OWNER) & PERM_COPY)
                inventory += name;
            else
                llOwnerSay("Cannot give asset \""+name+"\", owner lacks copy permission");
            llSetText(text + (string)((integer)(((i + 1.0) / num) * 100))+ "%", <1, 1, 1>, 1.0);
        }

        //chew off the end off the text message.
        text = llGetObjectName();

        //we don't want to give them this script
        i = llListFindList(inventory, [llGetScriptName()]);
        if(~i)//if this script isn't found then we shouldn't try and remove it
            inventory = llDeleteSubList(inventory, i, i);

        if (llGetListLength(inventory) < 1) llSay(0, "No items to offer."); else
        {
            llGiveInventoryList(user, text, inventory);
            llSetText("All done!\nYou can delete this now!",<1,1,1>,1);
            name = "Your new "+ text +" can be found in your inventory, in a folder called '"+ text +"'. Drag it onto your avatar to wear it!";
            if(user == llGetOwner())
                llOwnerSay(name);
            else
                llInstantMessage(user, name);
        }
    }
}

Q: Is there a way to give items into an existing folder or subfolder in a user's inventory?
A: No. The recipient will recieve the items in a new folder with the name specified in category within their inventory's root. There's no way to create a folder within their 'Clothing' folder, for example.

Q: So can I use llGiveInventoryList to give items that I can copy, but that have been set to prohibit other users from copying?
A: Yes. llGiveInventoryList will only prevent a script from giving an items if its owner does not have permissions copy permission. See llGetInventoryPermMask for more details on the difference between a "no-copy" item the creator can copy, and a no-copy item after it's been transferred to another user.

Q: What if the user I'm trying to give the objects to is offline?
A: If their offline buffer isn't full, and they don't have the object giving the items muted, they will be able to accept the transfer when they log on.

I seriously doubt if this is true. It gives me a "Cannot find destination" error -- FuRaz

Functions | Inventory | llGiveInventory


llGiveMoney

llGiveMoney(key destination, integer amount)

Transfers amount of Linden dollars from the script owner to destination. Requires the PERMISSION_DEBIT run-time permission.

destination can only be an avatar, not an an object or a group. The recipient does not need to be in the same sim as the object. If amount is less than or equal to zero, the object will say "Invalid parameter in llGiveMoney()." This looks pretty unprofessional, so if you're writing a vendor script that gives change (for example), you'll probably want to avoid it.

Older versions of the LSL documentation stated that llGiveMoney returned an integer. If the owner has insufficient funds to cover the llGiveMoney call, an error will appear on the user's screen, but there will be no data returned to the script itself. The return integer from llGiveMoney is always zero.

This example script gains the PERMISSION_DEBIT permission on startup and then gives L$1 to anyone who touches it.

default
{
     state_entry()
     {
          llRequestPermissions(llGetOwner(),PERMISSION_DEBIT);
     }

     touch_start(integer num_detected)
     {
          llGiveMoney(llDetectedKey(0),1);
     }
}

For an additional example, see ExampleMoney.

Q: What happens if the owner doesn't have enough funds to satisfy the llGiveMoney request?
A: The script pops up an error dialog stating "Insufficient funds". Because there isn't any return value from llGiveMoney, you need to keep track of money inside your script. This can be difficult to accomplish. There's no direct way to get your script to know whether or not it's out of money, but it'll keep telling the owner that it's out.
As KellyLinden explains: "It is not possible for llGiveMoney to return a meaningful value, the data is simply not available when and where it would be needed to make that happen."

Q: Wait, it returns zero? Are you sure it doesn't actually return nothing? Maybe you're doing it wrong.
A: Nope, it definitely returns zero, and it does so every time.

When llGiveMoney fails due to "Insufficient Funds", the script is not delayed: The error appears to the owner and noone else, and the script continues to function without any delay or conflict.

To receive money, use the money event.

Functions | Agent/Avatar


llGround

float llGround(vector offset)

Returns the ground height at the script's current position in the simulator + offset.

So to get the ground height at the scripted object's current location, use: llGround( <0.0, 0.0, 0.0> )

To actually change the ground height, use llModifyLand.

More details at land.

Also see llGroundContour, llGroundSlope, llGroundNormal, and llSetHoverHeight.

Functions | Simulator | Land


llGroundContour

vector llGroundContour(vector offset);

Returns the contour of the ground at the object's position + offset.

The ground contour is the direction in which there is no change in height.

Example: Imagine taking a horizontal "slice" of the sim's land at the height of the ground being sampled. The ground contour would point in the direction of the outline created.

Compare to llGroundNormal and llGroundSlope.

Functions | Simulator


llGroundNormal

vector llGroundNormal(vector offset);

Returns the normal vector to the ground at the object's position + offset.

The normal vector is orthogonal (perpendicular) to the surface of the ground and is always orthogonal to llGroundSlope.

For example, on a perfectly flat, level area of land, the ground normal would be <0,0,1> (straight up; the z-axis).

Compare to llGroundContour and llGroundSlope.

*this is offset isn't local?! ....data indicates that!;
Functions | Simulator


llGroundRepel

llGroundRepel(float height, integer water, float tau);

Critically damps the object to height (if the object is within height * 0.5 of ground level) over damp tau seconds. The height is above ground level if water is FALSE or above the height of land and water if water is TRUE.

Compare with llSetHoverHeight.

Functions | Land


llGroundSlope

vector llGroundSlope(vector offset);

Returns the slope of the ground at the object's position + offset.

The slope of the ground is direction the land is 'laying'. It is always orthogonal to llGroundNormal.

Example: A 'cliff' would have a slope of approximately <0, 0, -1> (pointing down).

Compare to llGroundContour and llGroundNormal.

Functions | Simulator


llHTTPRequest

key llHTTPRequest(string url, list parameters, string body)

Sends HTTP request to url with the specified body and parameters.

url must always be a valid HTTP or HTTPS URL, pointing to a location outside the Linden Lab and Second Life servers.

parameters is a list of <integer key, string value> pairs:
parameter | Value | Type | Default | Description
HTTP_METHOD | 0 | string | "GET" | "GET", "POST", "PUT" and "DELETE"
HTTP_MIMETYPE | 1 | string | "text/plain;charset=utf-8" | text/* MIME types should specify a charset. To emulate HTML forms use application/x-www-form-urlencoded. This allows you to set the body to a properly escaped (llEscapeURL) sequence of <name,value> pairs in the form var=value&var2=value2 and have them automatically parsed by web frameworks (PHP will allow you to retrieve them from $_POST) (Supported in version 1.10.4)
HTTP_BODY_MAXLENGTH | 2 | integer | 2048 | (Setting HTTP_BODY_MAXLENGTH is not yet supported)
HTTP_VERIFY_CERT | 3 | integer | TRUE | If TRUE, the server SSL certificate must be verifiable using one of the standard certificate authorities when making HTTPS requests. If FALSE, any server SSL certificate will be accepted. (Supported in version 1.10.4)

Although the HTTP_AUTHORIZATION parameter is not available, you may still login to websites requiring basic authentication by formatting your url appropriately:
string username = "Your Name";
string password = "Your Password";
string url = "http://" + llEscapeURL(username) + ":" + llEscapeURL(password) + "@domain.com/page.php";

body specifies the body of the HTTP request and is only used when HTTP_METHOD is POST or PUT. The body is only limited to the amount of available free memory in the script (before the script has a stack/heap collision.) (As of 10/29/06 this seems to be limited to 2048 characters, and cuts off anything further - Seifert)

The key returned by llHTTPRequest uniquely identifies the request and is passed to the http_response() event handler along with the request results when the request completes.

HTTP requests made using llHTTPRequest are throttled based on the script owner and region. Requests are throttled to a maximum of 20 requests per 100 seconds. See this thread (or this page) and this thread (or this page) for more details.
NOTE: This has been changed to 100 requests per 100 seconds now.

If any errors are found in the parameters given to llHTTPRequest, or the HTTP request cannot be made as the owner has exceeded the allowed request rate, then the returned key will be set to NULL_KEY and error messages will be sent to the debug channel.

The following headers are added to HTTP requests made by llHTTPRequest:

Header | Value | Description
Accept | text/* |
Accept-Charset | utf-8;q=1.0, *;q=0.5 |
User-Agent | Second Life LSL/VERSION (http://secondlife.com/) | simulator version making the request
X-SecondLife-Shard | SHARD | "Production" if the HTTP request is made from SL; otherwise, "Testing"
X-SecondLife-Object-Name | NAME | object name making the HTTP request
X-SecondLife-Object-Key | KEY | object UUID making the HTTP request
X-SecondLife-Region | NAME(X,Y) | region containing the object making the request; X,Y is its grid location
X-SecondLife-Local-Position | (X,Y,Z) | object region coordinates making the request
X-SecondLife-Local-Rotation | (X,Y,Z, W) | object quaternion rotation making the request
X-SecondLife-Local-Velocity | (X,Y,Z) | object velocity making the request
X-SecondLife-Owner-Name | NAME | object owner name making the HTTP request This will be group if the object is deeded to group
X-SecondLife-Owner-Key | KEY | object owner key making the HTTP request This will be group if the object is deeded to group
Note, some languages may export the X-SecondLife-* variables differently, however the HTTP headers send it as X-SecondLife-Shard and similar, with an ASCII hyphen, and no prefix before X; these are also not LSL constants in the variables and conform to HTTP header specifications, not LSL specifications. - Ice

This lets some very useful things be done, such as getting keys from key databases. Example, using the w-hat.com database:

//Quick hack to find and display resident's keys from the w-hat name2key database
//  Keknehv Psaltery, 5/5/06

key requestid;
string resident;

default
{
    state_entry()
    {
        llListen(1,"","","");
    }
    listen( integer chan, string name, key id, string msg )
    {
        list names = llParseString2List(msg,[" "],[]);
        resident = llDumpList2String(names," ");
        requestid = llHTTPRequest("http://w-hat.com/name2key?name="+llDumpList2String(names,"+"),[HTTP_METHOD,"GET"],"");
    }
    http_response(key request_id, integer status, list metadata, string body)
    {
        if (request_id == requestid)
        {
            integer i = llSubStringIndex(body,resident);
            if ( i != -1 )
                llSay(0,llGetSubString(body,i,i+llStringLength(resident)+36));
            else
                llSay(0,"No resident named \""+resident+"\" found in the w-hat name2key database");
        } else
            llSay(0,(string)status+" error");
    }
}

Note: Requests currently come from

64.129.40.0/23
66.150.244.0/23
69.25.104.0/23
72.5.12.0/22

Source

An example of using llHTTPRequest/http_response with your own PHP Server

Functions | Communications


llInsertString

string llInsertString(string dst, integer position, string src)

Inserts src into dst at position and returns the result.

Unlike most other string/list functions, this one does not support using a negative position to count backwards from the end of the string.

Example:
llInsertString("abcdef", 3, "123"); // returns "abc123def"

ARGH!!! What an evil little ambiguity! I kept fussing with this function, trying to get it to count backwards... what a waste of time.
Channeling this anger, I created this little code snippet that will provide the backwards counting: -Chris
string insertString(string destination, integer position, string str) {
    if (position < 0)
        position += llStringLength(destination) + 1;

    return llInsertString(destination, position, str);
}
Passing -1 as position will provide the same functionality as appending str to the end of destination with the += operator.

I don't think passing -1 should append to the end, if this behaivor is wanted a very big integer should be passed instead. Using -1 this way, makes negative indexes work differently with this function then the other LSL functions. Using large integers (like 0x7FFFFFFF) has not been shown to effect performance -BW
string insertString(string destination, integer position, string str) {
    if (position < 0)
        position += llStringLength(destination);
    return llInsertString(destination, position, str);
}

str_replace by Chad Statosky - The list method can be nasty.
str_replace entirely rewriten by BW to be more robust.
string str_replace(string from, string to, string str)
{
    integer e = llStringLength(from) - 1;
    if(e > -1)
    {
        string  b = str;
        integer c = -1;
        integer f = llStringLength(to) - 1;
        @loop;//instead of a while loop, saves 5 bytes (and run faster).
        integer d = llSubStringIndex(b, from) + 1;
        if(d)
        {
//            c+=d;
//            str = llInsertString(llDeleteSubString(str,c,c+e), c, to);
//            c += f;
//            b = llGetSubString(str, c + 1, 0x4000);
            b = llGetSubString(str = llInsertString(llDeleteSubString(str,c+=d,c+e), c, to), (c += f) + 1, 0x4000);
            jump loop;
        }

    }
    return str;
}
Functions | String


llInstantMessage

llInstantMessage(key user, string message)

Sends an IM to user containing message and delays the script for 2 seconds. This delay exists to prevent IM spamming.

To message the object owner, llOwnerSay(messsage) is the best option as it does not delay the script.
To send a short message in a popup dialog box, consider using llDialog instead.

Objects can send, but not receive IMs. See communications for methods for communicating with other scripts.

This call does not open a separate tab in the IM window as user-to-user IMs do. Instead, it prints to the chat history but is a private message that other agents within close range do not receive.

If the user has configured their account to send offline IMs to email, (Edit > Preferences > General, check "Send IM to E-mail") they will get offline messages sent to their email account. When the user logs in, they will recieve the message text. This will be the case regardless of whether or not they have the "Send IM to E-mail" option turned on.

Note: If message exceeds 1023 bytes (excluding null), it will be truncated. String length can be determined with llStringLength.

For a multi-line message, use new lines:
"This is on one line.\nThis is on another line."
However, bear in mind that this will display as a single line if the chat history window is closed, but will show up appropriately if it's open.

Example:
// when touched, IM the owner with the name of the touching agent
default {
    touch_start(integer num_detected) {
        llInstantMessage(llGetOwner(), llDetectedName(0) + " touched me.");
    }
}

The time penalty can be worked around by using link messages and to send IMs. Cycling through these allows for very short delays, decreasing with the number of scripts in the cycle. However, abusing this for TOS violations can lead to a warning/suspension/banning.

Compare with llOwnerSay, llWhisper, llSay, llShout, and llMessageLinked

Q: Hey, I'm getting some kind of weird delay when I use llInstantMessage. What's that about?
A: llInstantMessage has a built-in delay of 2 seconds. If you're communicating with the object's owner and the communications can be limited to a single sim, (IE, if it's an attachment) you can replace llInstantMessage(llGetOwner(),message) with llOwnerSay(message) without any delay. If communicating with other users, you might consider using llMessageLinked in your main script and having a second script actually send the instant messages.

Functions | Communications | Agent/Avatar


llIntegerToBase64

string llIntegerToBase64(integer number)

Encodes number as an 8-character Base64 string.

Use llBase64ToInteger to decode such a string.

Functions | Base64


llKey2Name

string llKey2Name(key id)

If the object or agent identified by the key id is in the same simulator as the object containing the script, this function returns their name. Otherwise, an empty string ("") is returned. This can be misleading, because it cannot be distinguished from an object with an empty name ("").

Only works on rezzed objects and avatars. Scripts, animations, and textures will return an empty string ("").

See llRequestAgentData for a (much slower) way to use the dataserver to get the name of an agent even outside the sim or offline.

Note: there is no llName2Key function. To get a key from a name, use sensors or touch events and the llDetected* functions.

Alternatively, you can use the following external sources for llName2Key functionality:
  • name2key - w-hat
  • Moopf Murray :: name2key Services
  • Name2key - libsecondlife
  • www.Ulrikasheim.org - name2key

Note: This function works if someone can see the sim from an adjacent sim. V1.9
Functions | Key


llList2CSV

string llList2CSV(list src)

Create a string of comma separated values (CSV) from list src.

Example:
llWhisper(0, llList2CSV(["string", 1, 5.2]));
The output will be "string, 1, 5.2". Note the space after each comma.

See also llDumpList2String and llCSV2List.

Lists | Functions


llList2Float

float llList2Float(list src, integer index)

Returns the float at index from the list src.

Example:
llList2Float(foo, 0); // returns the first element in the list.
llList2Float(foo, 1); // returns the second element in the list.

Compare with llList2Integer, llList2Key, llList2List, llList2ListStrided, llList2Rot, llList2String and llList2Vector.

Functions | Lists


llList2Integer

integer llList2Integer(list src, integer index)

Returns the integer at index from the list src.

Example:
llList2Integer(foo, 0); // returns the first element in the list.
llList2Integer(foo, 1); // returns the second element in the list.

Compare with llList2Float, llList2Key, llList2List, llList2ListStrided, llList2Rot, llList2String and llList2Vector.

Lists | Functions


llList2Key

key llList2Key(list src, integer index)

Returns the key at index from the list src.

Example:
llList2Key(foo, 0); // returns the first element in the list.
llList2Key(foo, 1); // returns the second element in the list.

Compare with llList2Float, llList2Integer, llList2List, llList2ListStrided, llList2Rot, llList2String and llList2Vector.

Lists | Functions


llList2List

list llList2List(list src, integer start, integer end)

Copy the slice of the list from start to end from the list. start and end are inclusive, so 0, length - 1 would copy the entire list and 0, 0 would capture the first list entry.

Using negative numbers for start and/or end causes the index to count backwards from the length of the list, so 0, -1 would capture the entire list.

If start is larger than end the list returned is the exclusion of the entries, so 6, 4 would give the entire list except for the 5th entry.

Example:
list    foo = ["a", "b", "c", "d", "e", "f"];

llList2List(foo, 0, 0); // returns ["a"]
llList2List(foo, 0, 5); // returns ["a", "b", "c", "d", "e", "f"]
llList2List(foo, 2, 5); // returns ["c", "d", "e", "f"]
llList2List(foo, 4, -1); // returns ["e", "f"]
llList2List(foo, 4, 1); // returns ["a", "b", "e", "f"]

Compare with llList2Float, llList2Integer, llList2Key, llList2ListStrided, llList2Rot, llList2String and llList2Vector.

Q: When did llList2List(["singleton"], 1, -1) start returning ["singleton"] instead of [] ?
A: llList2List has always returned the endpoints, -1 would be the first entry in a single entry list. Use 0x8000 or some other large number for the end point (instead of -1). Or just use llDeleteSubList(["singleton"], 0, 0) instead. --BW

Lists | Functions


llList2ListStrided

list llList2ListStrided(list src, integer start, integer end, integer stride)

Copies the strided slice of the list from start to end.

llList2ListStrided returns a list consisting of every stride'th element from src that is between element start and element end.

list src = ["a", 1, 2, "b", 3, 4, "c", 5, 6, "d", 7, 8];

llList2ListStrided(src, 1, 7, 3);

The result is: [ "b", "c" ]

Probably the most useful application would be:
llList2ListStrided(src, 0, -1, 3);

Which gives the result: [ "a", "b", "c", "d" ]

Q & A:

Q: What's a "stride"?
A: See stride.

Q: How do you get a list of the anything but the first items in the stride? I've tried various combinations of parameters and never got quite what I expected. If I want the whole sub-list I'd expect passing 0 as start would give me a list of the first elements, 1 would give the second elements and so forth, but that's not what happens like an offset into the stride.
A: That is not possible with this function - it can only access the first element in a stride. Perhaps it isn't working as it was originally intended, as the more reasonable way for it to work would indeed be to select elements from the stride based on start.
However, there is actually a workaround for this. To produce a list of all the second elements, just chop the first element off the list with llList2List or llDeleteSubList. This effectively causes llList2ListStrided to offset where it counts the start of the stride from.

So, to get every second element...
llList2ListStrided(llDeleteSubList(src, 0, 0), 0, -1, 2));
and to get every third element...
llList2ListStrided(llDeleteSubList(src, 0, 1), 0, -1, 3));

(For more on removing the first element from the stride, see this forum thread.)

Compare with llList2Float, llList2Integer, llList2Key, llList2List, llList2Rot, llList2String and llList2Vector.

Functions | Lists


llList2Rot

rotation llList2Rot(list src, integer index)

Returns the rotation at index from the list src.

Example:
llList2Rot(foo, 0); // returns the first element in the list.
llList2Rot(foo, 1); // returns the second element in the list.

Note: llList2Rot will not automatically cast a string to a rotation. Thus, if you attempt to retrieve a list element consisting of a rotation cast to a string like "<123,456,789,012>", llList2Rot will return ZERO_ROTATION or <0,0,0,1>. This can be avoided by using llList2String and casting directly, as in:
rotation foo = (rotation)llList2String(bar,0);

Compare with llList2Float, llList2Integer, llList2Key, llList2List, llList2ListStrided, llList2String and llList2Vector.

Lists | Functions


llList2String

string llList2String(list src, integer index)

Returns the string at index from the list src.

Example:
llList2String(foo, 0); // returns the first element in the list
llList2String(foo, 1); // returns the second element in the list

Unlike the other llList2Xxx() functions, llList2String() has some useful default behaviors:
1. If the index refers to a non-existant element, then the empty string is returned. This works if either the index >= the length, or if index < 0 - length.
2. If the item in the list at the index isn't a string, it is cast to a string.

Note that because llList2Vector and llList2Rot will not cast a string to a vector or rotation, you'll need to use llList2String and then typecast the result directly.

Compare with llList2Float, llList2Integer, llList2Key, llList2List, llList2ListStrided, llList2Rot, llList2Vector and llGetListEntryType.

Functions | Lists


llList2Vector

vector llList2Vector(list src, integer index)

Copy and return the vector at index in the list src.

Compare with llList2Float, llList2Integer, llList2Key, llList2List, llList2ListStrided, llList2Rot and llList2String.

Example:
llList2Vector(foo, 0); // returns the first element in the list.
llList2Vector(foo, 1); // returns the second element in the list.

Note: llList2Vector will not automatically cast a string to a vector. Thus, if you attempt to retrieve a list element consisting of a vector cast to a string like "<1,2,4>", llList2Vector will return ZERO_VECTOR or <0,0,0>. This can be avoided by using llList2String and casting directly, as in:
vector foo = (vector)llList2String(["<1,2,4>"], 0);

Functions | Lists


llListen

integer llListen(integer channel, string name, key id, string msg)

Sets a filter for listening to the specified chat channel. If a message is heard that meets the specified criteria, the object's listen() event handler is invoked.

The name, id, and msg arguments specify the filtering criteria. You can pass the empty string (or NULL_KEY for id) for these to set a completely open filter; this causes the listen() event handler to be invoked for all chat on the channel. To listen only for chat spoken by a specific object or avatar, specify the name and/or id arguments. To listen only for a specific command, specify the (case-sensitive) msg argument. You can also use all the arguments to establish the most restrictive filtering criteria.

This function returns a handle that can be used to deactivate or remove the specific channel filter using llListenControl and llListenRemove. An object should remove any filters it has established when it no longer needs them. Listen filters DO NOT persist and are automatically removed on state changes. The returned handles start at 1, so you can set your handle variable to 0 (zero) when you deactivate a channel and test for that elsewhere when you need to know if it's active or not.

Using many instances of llListen can have a visible effect on sim performance and cause lag. If there is a viable alternative communication method such as llMessageLinked, consider using that instead. Especially avoid listening on channel 0 whenever possible. If you must listen to regular chat conversation, use a filter if possible.

If a script with an llListen "hears" a string longer than the script's amount of available free memory, it will halt with a "stack/heap collision" error before your code in the listen() event handler has a chance to begin executing. As of SL v1.13, a single chat string on any channel can be up to 1023 bytes in length.

Examples

Establishes a completely open filter for avatar chat:
integer handle  = llListen( 0, "", NULL_KEY, "" );

Sets a filter that listens only for chat spoken by the object's owner:
integer handle = llListen( 0, "", llGetOwner(), "" );

Sets a filter that listens on channel 96 for objects named "button":
integer handle = llListen( 96, "button", NULL_KEY, "" );

This could be used, for example, in an elevator that responds to button objects on different floors of a building. Each button object would have the same name, but could identify its floor through the specific message it sends.

To set up to 64 filters on different channels, call llListen repeatedly with different criteria. All messages found by any filter are passed to the same listen event handler. If llListen is called again with the identical criteria, the same listener handle is returned, and the listen() event will only be called once.

If a script goes over its limit of around 64, it crashes with a run-time "Too Many Listens" error.

As of SL 1.5.4, the following does work correctly:
llListen(0, "Froggy", "", "");
llListen(0, "", llGetOwner(), "");

To listen on a single channel, for both chat from the owner and from an object with a certain name, (or any other combination of logic) the best option is to use a single llListen function and an if-else in the listen event.

Selling scripts which contain listeners can be problematic if the id parameter is set to llGetOwner. Be sure to update listen filters or use llResetScript when it changes owners if a script's listeners are set to only listen to their owner.

Example

default
{
    state_entry()
    {
        // listen on channel zero for any chat spoken by the object owner.
        llListen(0,"",llGetOwner(),"");
    }

    listen(integer channel, string name, key id, string message)
    {
        // check if the message corresponds to a predefined string.
        // llToLower converts the message to lowercase.
        // This way, "HELLO", "Hello" or "HeLLo" will all work the same way.
        if (llToLower(message) == "hello")
        {
            // if the message from the owner is "hello", respond with "Hello.".
            llSay(0,"Hello.");
        }
    }
}

Potential Issues

Crosstalk

One common problem that is easy to forget about when creating a fancy new scripted widget is crosstalk. This occurs when objects get confused about whose messages are whose. This can be two identical objects which mistake each other's messages or other people's objects which happen to use the same channel and may send problematic (partially compatible and invalid) messages.

There are a number of ways to deal with this problem. A few are listed here:

  • Make filters as tight as possible. For example, use llGetOwner for the id filter.
  • For scripts that only need to talk to themselves (dialogs for example) use a random channel. An example can be found on the llFrand page. This strategy can also be utilized when establishing communication with a rezzed object (pass the random integer via llRezObject's integer parameter).
  • For private channels across multiple objects use a deterministic equation that results in a unique value yet is repeatable. For example, base the channel on the integers contained in the owner's key. This way two users can both use their copies of the same object in close proximity without difficulty.
  • Pass additional data (like a unique command word or the key of the sender or intended recipient) in the message that clues the listener in on the intended recipient or lets the listener know the message isn't from an object they're interested in.
  • Avoid channels below 100. If you don't need humans to type the channel and listen on anything below 10 your object will be spammed. Negative channels cannot be used by avatars without an object intermediary, so these are the most ideal for object-object chat.
  • Do a llGetOwnerKey(id) == llGetOwner() check in your listen event to filter out chat emitted by objects not owned by the same owner.

This is a common issue and the solution can vary widely based on the requirements of a particular problem.

FAQ

Q: I can see how the millions of scripts out there listening to channel 0 would collectively create lots of lag. But, with the callback mechanism and a listener on a private channel, it seems like the only time resources would be used is when an actual message is present. Is this the case?
A: Well, the real server-side problem is the filter. When a script listens on channel 0, it calls the filtering methods every time it hears something. This doesn't create a lot of lag, but tons of these without good use of callback can create some effect on server resources.

Q: Can a wildcard be used for the msg argument? I only want to monitor text that begins with "ls" but can be anything after it ("on", "off", "silence", etc).
A: Wildcards are not supported. You'll want to use llGetSubString or llSubStringIndex in your listen() event.

Q: How close to my listener do I have to be for it to hear me?
A: When saying chat, under 20m. When shouting, 100m. See chat for specifics with dealing with the other chat functions.

Functions | Events | Chat | Communications


llListenControl

llListenControl(integer number, integer active)

Sets the listen event with the handle number as active (active = TRUE) or inactive (active = FALSE).

Example:
integer Handle;

default {
    state_entry() {
        Handle = llListen(0, "", NULL_KEY, ""); // start listening
    }

    touch_start(integer total_number) {
        // when touched...
        llListenControl(Handle, TRUE); // ...enable listen
    }

    listen(integer channel, string name, key id, string message) {
        // when told "off"...
        if (message == "off") llListenControl(Handle, FALSE); // .. disable listen

        else llSay(0, llList2CSV([name, message])); // repeat message
    }
}

Functions / Chat / Communications


llListenRemove

llListenRemove(integer number)

Removes the listen event callback with the handle number. The handle is the value returned when calling llListen.

Invalid handles are ignored, which means the following example works fine:
integer Channel;
integer Handle;

Init() {
    llListenRemove(Handle);
    Handle = llListen(Channel, "", NULL_KEY, "");
}

default {
    state_entry() {
        Channel = 2; // first we listen on channel 2
        Init();
    }

    touch_start(integer total_number) {
        Channel = 4; // after a touch we listen on channel 4 instead
        Init();
    }

    listen(integer channel, string name, key id, string message) {
        llSay(0, llList2CSV([channel, name, message]));
    }
}

Remember, listeners are automatically removed when exiting a state. This means that llListenRemove is only necessary when removing a listen inside a state. If your script design permits it, using multiple states instead of a llListenRemove may be more convenient.

Functions | Chat | Communication | llListen | listen


llListFindList

integer llListFindList(list src, list test)

Returns the position (index) of the first instance of test in src (or -1 if test isn't in src).

When using this function, please remember that the type for src and test have to be the same, so typecast values in test to what they are expected to be in src if they're variables. Remember, this does include keys and strings!

If unsure what type the variables may be, use a for loop to check the output of llList2String() against the string to test for.

Example:
default {
    state_entry() {
        list src = ["The", "Quick", "Brown", "Fox", "Jumped", "Over", "The", "Lazy", "Dog"];
        list test;

        test = ["Brown"]; // What we want to find.
        integer foxColorIndex = llListFindList(src, test); // Returns 2.

        test = ["Jumped", "Over"];
        integer whatFoxDidIndex = llListFindList(src, test); // Returns 4.

        test = ["Quick", "Fox"];
        integer thereIsNoFox = llListFindList(src, test); // Returns -1.
    }
}

To do this with a string, use llSubStringIndex.

Q: How do I figure out the indexes of ALL instances of test?
A: There isn't a direct function to do this. Your best method is probably to do something like find out how many entries the list has using llGetListLength, then use a FOR loop, cutting out every instance of test, then replacing them after they've all been removed. If you can at all avoid it, use llListFindList to simply make sure you don't have any duplicates to begin with.
A2: Alternatively, if your list's order isn't important, you could use llListSort to put your list into alphabetical order, use llListFindList to get the index of the first entry, then simply incrementing by one until you stop finding matches. Obviously, if list order is important, this won't work.
A3: You can also use multiple instance of the llListFindList instead of doing a FOR loop for multiple choices.

Lists | Functions


llListInsertList

list llListInsertList(list dest, list src, integer pos)

Returns the list created by inserting src into dest at pos.
Note that if pos is greater then the length of dest, the list returned is src appended onto the end of dest. To avoid that behavior, create a wrapper function that appends pos - llGetListLength(dest) empty elements onto the end of dest before passing dest to llListInsertList.

IMPORTANT NOTE: This function does not directly manipulate the list you pass as dest. To manipulate dest, set dest equal to the return value of this function.

Example:
list dest = ["A", "B", "C"];
list src = ["X", "Y", "Z"];
llListInsertList(dest, src, 1);
// ^^^ After this line, dest is still ["A", "B", "C"].

dest = llListInsertList(dest, src, 1);
// ^^^ After this line, dest is ["A", "X", "Y", "Z", "B", "C"];

Speed tip:
If you need to insert elements into a list at the beginning of the list, here's a speed tip:
list myList = ["A", "B", "C"];
list newElements = ["9"];

// Instead of: myList = llListInsertList(myList, newElements, 0);
// Use this:
myList = newElements + myList;

Compare with llDeleteSubList.

Lists | Functions


llListRandomize

list llListRandomize(list src, integer stride)

Returns src randomized into blocks of size stride.

If stride is less then one it is treated as 1.

Example:
// This shuffles each list item around.
list foo = ["LSL", "is", "teh", "sucks"];
list bar = llListRandomize(foo, 1);
llSay(0, llList2CSV(bar));
// This might make the object say "is, LSL, sucks, teh" or "LSL, sucks,
// teh, is" or any of the other 22 combinations of possible lists.

// This breaks the list up into two blocks and moves them around.
list foo = ["LSL", "is", "teh", "sucks"];
list bar = llListRandomize(foo, 2);
llSay(0, llList2CSV(bar));
// This might make the object say "LSL, is, teh, sucks" or "teh, sucks, LSL, is",
// but nothing else would be possible.

// This does nothing.
list foo = ["LSL", "is", "teh", "sucks"];
list bar = llListRandomize(foo, 4);
llSay(0, llList2CSV(bar));
// Object says "LSL, is, teh, sucks"

For most purposes, setting stride to 1 is probably what you want. This will just randomize all the items in the list.

Compare with llListSort.

Lists | Functions


llListReplaceList

list llListReplaceList(list dest, list src, integer start, integer end)

Returns a list replacing the slice of the list dest from start to end with the list src. start and end are inclusive, so 0, 1 would replace the first two entries and 0, 0 would replace only the first list entry.

Using negative numbers for start and/or end causes the index to count backwards from the length of the list, so 0, -1 would replace the entire list.

If start is larger than end, the list returned is the list between start and end followed by src, so 6, 4 would return the 5th entry with src after that.

Example of Slices:
// (Note that this is not real code.)
list    foo = ["a", "b", "c", "d", "e", "f", "g"];
list    bar = ["0", "1"];
list    zot;
zot = llListReplaceList(foo, bar, 2, 5); // returns ["a", "b", "0", "1", "g"]
zot = llListReplaceList(foo, bar, 4, -1); // returns ["a", "b", "c", "d", "0", "1"]
zot = llListReplaceList(foo, bar, 4, 1); // returns ["c", "d", "0", "1"]

Note:
llListReplaceList does not manipulate dest directly. The list returned by this function is the modified version of the dest. See the following example:

Functional Example:
default {
    touch_start(integer total_number) {
        list foo = ["a", "b", "c", "d", "e", "f", "g"];
        list bar = ["0", "1"];
        foo = llListReplaceList(foo, bar, 2, 5); // to really return ["a", "b", "0", "1", "g"]
        llOwnerSay(llList2CSV(foo));
    }
}

To replace a stride in the list:
list UpdateSubListStrided(list src, list stride, integer start, integer stride_len) {
    return llListReplaceList(src, stride, start, start + stride_len - 1);
}

Question: Does the slice definied by start and end need to be the same length as src?
Answer: No, you can replace a slice with a longer or smaller src.

Question: I don't get it. It's not replacing my list.
Answer: No, llListReplaceList doesn't actually manipulate dest directly, rather it returns a new list containing the results. To manipulate a list directly, you need to do something like the functional example above. Alternatively, you can pass the result to a new list, and keep the original the same.

Compare with llList2List, llList2ListStrided, llListInsertList, llDeleteSubList, llList2Float, llList2Integer, llList2Key, llList2Rot, llList2String and llList2Vector.

Functions | Lists


llListSort

list llListSort(list src, integer stride, integer ascending)

Sorts src into blocks of stride, in ascending order if ascending is TRUE, descending order if ascending is FALSE, and returns a new list. src is not changed. If ascending is not equal to TRUE then it is considered FALSE (not typical in LSL).

Note that llListSort only works between items of the same type.

Setting stride to either 0 or 1 sorts each item in the list.

Strides

"strides" are groups of related data. For example, if you are storing information about vegetables, and you want to store the vegetable name, its color, and how many of them you have, you might have a list like this:

list vegetables = ["lettuce", "green", 20, "carrots", "orange", 11, "onions", "yellow", 0];

The stride for this list is 3, because there are 3 pieces of data in a block. llListSort sorts on the first bit of data in each block, in this case, the vegetable name. This command:

vegetables = llListSort(vegetables, 3, TRUE);

...returns this list:

["carrots", "orange", 11, "lettuce", "green", 20, "onions", "yellow", 0]

"carrots", "lettuce" and "onions" are now sorted into alphabetical order.

If data types do not match, llListSort still returns a modified list, but the results are strange. Note that while the above example uses a list containing both strings and integers, because the stride is set to 3, llListSort only sorts the list according to the string containing the name of the vegetable.

I've been doing a bit of research into this function and here is how it sorts mixed lists. It sorts the list by each type, then it recombines the sorted result by type, so if the first position was a key, then in the output the first position is a key. So...
llListSort([3, "a",1,"c", 2, "b"], 1, TRUE) == [1, "a", 2, "b", 3, "c"]
//I'm using the == operator here as pseudo-code, it does not work this way in LSL.
I am having problems imagining how this could be useful. -- BW

Functions | Lists


llListStatistics

float llListStatistics(integer operation, list input)

This function allows a script to perform a statistical operation as defined by operation on a list composed of integers and floats.

This function returns a float, so using llRound, llCeil, or llFloor might be more desirable in order to provide a cleaner output from this function.

Statistical Operation | Value | Effect
LIST_STAT_RANGE | 0 | Returns the range.
LIST_STAT_MIN | 1 | Retrieves the smallest number.
LIST_STAT_MAX | 2 | Retrieves the largest number.
LIST_STAT_MEAN | 3 | Retrieves the mean (average).
LIST_STAT_MEDIAN | 4 | Retrieves the median number.
LIST_STAT_STD_DEV | 5 | Calculates the standard deviation.
LIST_STAT_SUM | 6 | Calculates the sum.
LIST_STAT_SUM_SQUARES | 7 | Calculates the sum of the squares.
LIST_STAT_NUM_COUNT | 8 | Retrieves the amount of float and integer elements. Theoretically similar to llGetListLength, except LL states that this function ignores all non-integer and non-float elements. Therefore, this is a useful tool for mixed lists of numbers and non-numbers.
LIST_STAT_GEOMETRIC_MEAN | 9 | Calculates the geometric mean. All numbers must be greater than zero for this to work, according to LL.
Functions | List | Mathematics


llLoadURL

llLoadURL(key avatar_id, string message, string url)

llLoadURL displays a dialog box to the user offering to load a web page using the default web browser.

The URL given must begin with "http://" or "https://"; it will not work with other protocols such as ftp, telnet, secondlife, etc. If a user mutes an object, they will not recieve any URL offers from that object.

Notes:
The url is truncated to 255 characters and message is truncated to 254 characters.
This function delays the script for 10 seconds.
This function only works on a user within the same sim as the object calling it.

Example:
default
{
    touch_start(integer total_number)
    {
        llLoadURL(llDetectedKey(0), "Flame on!", "http://forums.secondlife.com");
    }
}

Q: Can I use this with a secondlife:// URL?
A: No, use llMapDestination to do that.

Functions | Communications | HTTP


llLog

float llLog(float val)

Returns the base e (natural) logarithm of val (value).

If val is less than or equal to 0, it will always return 0.

Compare with llLog10.

Functions | Math


llLog10

float llLog10(float val)

Returns the base 10 (common) logarithm of val (value).

If val is less than or equal to 0, it will always return 0.

Compare with llLog.

Functions | Math


llLookAt

llLookAt(vector target, float strength, float damping)

Causes the object to point the up (Z) axis toward target. Good strength values are around half the mass of the object and good damping values are less than 1/10th of the strength. Asymmetrical shapes require smaller damping. Calling llStopLookAt cancels the function.

This function works for both physical and non-physical objects.

Unlike llSetRot this function will rotate the object around its center of gravity.

Note: within the LSL editor, the tooltip for llLookAt reads "llLookAt(vector target, F32 strength, F32 damping)". F32 refers to a 32-bit float. In LSL, all floats are 32-bit.

Just in case you need an axis other then the up axis pointed at a target, here's a useful code snippet:
// AXIS_* constants, represent the unit vector 1 unit on the specified axis.
vector AXIS_UP = <0,0,1>;
vector AXIS_LEFT = <0,1,0>;
vector AXIS_FWD = <1,0,0>;

// getRotToPointAxisAt()
// Gets the rotation to point the specified axis at the specified position.
// @param axis The axis to point. Easiest to just use an AXIS_* constant.
// @param target The target, in region-local coordinates, to point the axis at.
// @return The rotation necessary to point axis at target.
// Created by Ope Rand, modifyed by Christopher Omega
rotation getRotToPointAxisAt(vector axis, vector target) {
    return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}

// Strength and damping are values used to control how llRotLookAt and llLookAt move, these values are tunable.
float strength = 1.0;
float damping = 1.0;

default {
    state_entry() {
        vector target = <10, 20, 30>; // A vector to look at.

        // These two lines are equivalent, and point the up (Z) axis at the target:
        llRotLookAt(getRotToPointAxisAt(AXIS_UP, target), strength, damping);
        llLookAt(target, strength, damping);

        // This line points the fwd (X) axis at the target:
        llRotLookAt(getRotToPointAxisAt(AXIS_FWD, target), strength, damping);

        // This line points the left (Y) axis at the target:
        llRotLookAt(getRotToPointAxisAt(AXIS_LEFT, target), strength, damping);
    }
}
A strange example to have here since the getRotToPointAxisAt stuff is used for the llRotLookAt function and the llLookAt is only there for a comparison. I'll make use of it anyway. ;-)

Compare with llRotLookAt.

Functions | Dynamics | Rotation


llLoopSound

llLoopSound(string sound, float volume)

Loops sound indefinitely. sound is a UUID of a sound file, or the name of a sound file if the sound file is in task's inventory. volume is a float between 0 and 1 which defines the volume to play the sound at. If volume is > 1, it is set to 1.

llLoopSound can be stopped using llStopSound(). The volume of sounds started with llLoopSound or llPlaySound can be adjusted using llAdjustSoundVolume.

Compare with llLoopSoundSlave.

Functions | Sound


llLoopSoundMaster

llLoopSoundMaster(string sound, float volume)

Behavior is identical to llLoopSound, with the addition of marking the source as a "sync master", causing "slave" sounds played with llLoopSoundSlave to sync to it. If there are multiple masters within a viewer's interest area, the most audible one (a function of both distance and volume) will win out as the master. The use of multiple masters within a small area is unlikely to produce the desired effect.

Functions | Sound


llLoopSoundSlave

llLoopSoundSlave(string sound, float volume)

Loops sound indefinitely. sound is a UUID of a sound file, or the name of a sound file if the sound file is in task's inventory. volume is a float between 0 and 1 which defines the volume to play the sound at. If volume is > 1, it is set to 1.

llLoopSoundSlave() can be stopped using llStopSound().
The volume of sounds started with llLoopSoundSlave can be adjusted using llAdjustSoundVolume.

Behavior is identical to llLoopSound, unless there is a "sync master" present. If a sync master is already playing (using llLoopSoundMaster) the slave sound will begin playing from the same point the master is in its loop synchronizing the loop points of both sounds. If a sync master is started when the slave is already playing, the slave will skip to the correct position to sync with the master.

Note: this does not cause sound to play once every time the sync master plays; it only causes sound to start looping at the beginning of a sync master loop, sound then loops normally (that is, as soon as it ends, it starts again).

Functions | Sound


llMakeExplosion

This function has been deprecated. See DeprecatedFunctions.

llMakeExplosion(integer particles, float scale, float velocity, float lifetime, float arc, string texture, vector offset);
Make a round explosion of particles using texture from the object's inventory.


llMakeFire

This function has been deprecated in favour of llParticleSystem.

llMakeFire(integer particles, float scale, float velocity, float lifetime, float arc, string texture, vector offset);
Make fire particles using texture from the object's inventory.

Note: This function delays the script for 0.1 seconds.

Functions / Particles / DeprecatedFunctions


llMakeFountain

This function has been deprecated in favour of llParticleSystem.

llMakeFountain(integer particles, float scale, float velocity, float lifetime, float arc, string texture, vector offset);
Make fire particles using texture from the object's inventory.

Note: This function delays the script for 0.1 seconds.

Functions / Particles / DeprecatedFunctions


llMakeSmoke

This function has been deprecated in favour of llParticleSystem.

llMakeSmoke(integer particles, float scale, float velocity, float lifetime, float arc, string texture, vector offset);
Make fire particles using texture from the object's inventory.

Note: This function delays the script for 0.1 seconds.

Functions / Particles / DeprecatedFunctions


llMapDestination

llMapDestination(string simname, vector position, vector lookat)

Shows a given location on the map.
Works in attachments, or during touch events. Currently the function will open the map window whenever it is called. There is no way to simply set the map position without opening the window.

The vertical (.z) axis of position is constrained to the 0 - 1000 range.
The lookat value is currently not in use, but will be in a later version.

GlobalMapDestination(vector position, vector lookat)
{
    vector c = llGetRegionCorner();
    llMapDestination(llGetRegionName(), position - c, lookat - c);
}

key request;
string name;
string sim_name;
vector pos;

default
{
    state_entry()
    {
        llAllowInventoryDrop(1);
        if(llGetInventoryNumber(INVENTORY_LANDMARK))
        {
            name = llGetInventoryName(INVENTORY_LANDMARK,0);
            request = llRequestInventoryData(name);
        }
        else
            llWhisper(0,"Please drop a landmark on me");
    }
    dataserver(key id, string data)
    {
        if(id == request)
        {
            pos = (vector)data;
            sim_name = llGetRegionName();
            llSetText("Touch to show \""+name+"\" on the map.",<1.0,1.0,1.0>,1.0);
        }
    }
    touch_start(integer a)
    {
        if(name != "")
            llMapDestination(sim_name, pos, pos);
    }
    changed(integer a)
    {
        if(a & (CHANGED_INVENTORY | CHANGED_ALLOWED_DROP))
            if(llGetInventoryNumber(INVENTORY_LANDMARK))
                request = llRequestInventoryData(name = llGetInventoryName(INVENTORY_LANDMARK,0));
    }
}

Functions | Teleport


llMD5String

string llMD5String(string src, integer nonce)

Performs a RSA Data Security, Inc. MD5 Message-Digest Algorithm on string with nonce (also known as salt). Returns a 32-character hex string. (128-bit in binary.)

The actual string used for calculating the MD5 checksum is src + ":" + (string)nonce. Take note of this if you need to check your MD5 hash outside of SL, such as within a server-side script!

Example:
llSay(0, llMD5String("Hello, Avatar!", 0)); // returns 112abd47ceaae1c05a826828650434a6

Same string on a completely different system returns the same checksum (when used with the same nonce):
$ echo -n 'Hello, Avatar!:0' | openssl md5
112abd47ceaae1c05a826828650434a6
Example usage, with php:
$nonce = 0;
encoded = md5('Hello, Avatar!'.':'.$nonce);
echo encoded;

Q & A

Q: Wait, isn't 32 hex characters 512 bits in binary? (32 * 16 = 512)
A: No, because hex is Base 16, and binary Base 2, meaning a hex character is 4 bits. (See the hexadecimal entry.)

Q: What the hell's a "nonce"?
A: In computer security, a "nonce" is a 'number used once'. It is often a random or pseudo-random number issued in an authentication protocol to ensure that old communications cannot be reused in 'repeat attacks'. So if you wanted to have two scripts talking to each other with secure communications, you might want to consider doing something with llFrand (don't use something easily predictable like the time).
Coincidentally, "nonce" is also a good old word that means 'right now' or 'temporarily', as in, "Are we seriously going to hold it together with duct tape and bubble gum?" "For the nonce!" The etymology of the computer security term is unrelated.

Q: What encoding is used?
A: The result is the MD5 hash of the UTF-8 encoded form of the string.

Q: What happens if UTF-16 characters are passed to it?
A: UTF-16 is an encoding, not a type of character. The elements of LSL strings are Unicode characters. When passed to this call (and also presumable to the base64 encoding calls), the characters are encoded as UTF-8. UTF-8 can represent all Unicode characters. For example the INFINITY symbol is Unicode code point U+221E and is encoded in UTF-8 as the three byte sequence 0xE2 0x88 0x9E.

Functions | Communications | Crypto | MD5


llMessageLinked

llMessageLinked(integer linknum, integer num, string str, key id)

Sends num, str, and id to linknum (the specific prim in the linked set).

The value for linknum can be the link number of the desired prim (use llGetLinkNumber to get the prim's link number containing the script) or one of the constants for linked sets.

Constants

Constant | Value | Description
LINK_ROOT | 1 | root prim in linked set (but not in a single prim, which is 0)
LINK_SET | -1 | all prims in object
LINK_ALL_OTHERS | -2 | all other prims in object besides prim function is in
LINK_ALL_CHILDREN | -3 | all child prims in object
LINK_THIS | -4 | prim script is in

In objects with only 1 prim, linknum is 0. In objects with multiple prims, the linknum count starts at 1. (So in a 2-prim object, llGetLinkNumber would return 1 in the parent and 2 in the child.

num can be any valid integer. Depending on the filter setup for link_message, num may not be necessary.

str and id may be blank, ("") or as long as desired. str and id are only limited by the amount of available memory.

Notes:
  • Because id is a key, and keys are just special strings, it can be typecast to a string (see hacks).
  • Multiple attachments worn by the same avatar can't communicate using llMessageLinked. (Attachments are still treated as individual objects.) The only way attachments can communicate with each other is via email or chat, chat obviously being the faster and less resource-intensive of the two options. Multiple scripts are still able to communicate within a single attachment, of course.

llMessageLinked triggers the link_message event handler in scripts receiving link messages.

Example:
See ExampleLinkMessage.

Q: I don't get it. Why not just use chat?
A: Aside from the fact that an object can't listen to itself, link messages are much faster than using chat functions as well as being private. They're higher-capacity, avoiding chat's 255-character limitations, and use fewer prims. Also, if you tend to have memory overflow problems in a single script, you can use link messages to communicate between scripts in the same object, thus enabling separate storage or even pseudo-multithreading. And finally, as any type can be cast to a string, link messages can transfer all types of data.

Additional testing on 2/1/2005 has shown link messages to be up to 40% faster than llWhisper calls.
This is because llSay, llShout, and llWhisper all have penalties built into them, llMessageLinked does not. -- Ice

11/21/06
There is no delay on any of the llMessageLinked, llSay, llWhisper, or llShout functions. The "delay" is in the event queuing for the receiving event, which is affected by the sims performance at that given time. The speed ranges from llMessageLinked being the fastest as it's internal, to llShout being the slowest as it causes the most sim performance hit as it's external to the prim with a 100m range. -- SiRiSAsturias

Q: What is the length of the penalty on llSay, etc?
A: I seem to remember the delay being about 0.3 seconds. However I cannot find anything to substantiate it. Most functions give the delay on the mouse over "tooltip" in SL, however llSay, llWhisper, and llShout show no delay at all. Can someone else find any further documentation on the delays for them? - Aak
A: There is no delay in these functions, only in the event queues receiving them. This "delay" is affected by the sims performance at that given time. -- SiRiSAsturias

Q: Can I use llMessageLinked to send a message within only one script?
A: Yes, though there are better ways to communicate within a single script. Note that your link_message event will trigger on all link messages the prim receives, so your script may end up talking to itself if you're not careful.

Q: Do I need to specify a value for num, str, and id, or can I use a null string or key?
A: No, you can give llMessageLinked empty values of 0, "" and NULL_KEY, respectively.

Q: Can I use link messages to communicate between two attachments, or an attachment and the object the avatar is sitting on?
A: No, they're not truly linked in this case. You'll need to use chat instead.

Functions | Communications | Link


llMinEventDelay

llMinEventDelay(float delay)

Sets the minimum amount of time, in seconds, between any event calls. Any delay 0.05 or less has no effect. As an example, it is useful if you would like to increase the amount of time between calls such as touch or collision, thereby decreasing load on server resources.

Q: Will setting llMinEventDelay affect events that have internal timers, such as llSetTimerEvent and llSensorRepeat? If so, how does it affect these events?
A: I did some testing and here's what I got with a freshly compiled script with llMinEventDelay(float delay). These were counted using the highly-scientific "one mississipi" method since I don't have a stopwatch.
  • Touch events (touch, touch_start, touch_end): These will not trigger for the first delay seconds after compile. After a touch triggers, all further touches are ignored and lost until delay seconds have passed. Min delay between touch events is 0.1 seconds.
    • Additionally:
    1. touch: After the initial post-compile delay a single touch event may be triggered every delay seconds. Clicking and holding does not create multiple events every delay seconds.
    2. touch_start: After the initial post-compile delay a single touch event may be triggered every delay seconds.
    3. touch_end: The first touch does not trigger, but after that first touch (which may occur during the initial post-compile delay) a single touch event may be triggered every delay seconds.
  • Collision events (collision, collision_start, collision_end): These will not trigger for the first delay seconds after compile. After a collision triggers, all further collisions are ignored and lost until delay seconds have passed.
    • 1. collision: After the initial post-compile delay a single collision event may be triggered every delay seconds. Maintaining collision creates multiple events every delay seconds.
    2. collision_start: Seems to trigger erratically. As best as I can tell, after the initial post-compile delay a single collision event may be triggered. It appears that after the first collision, subsequent collisions before delay seconds have passed are ignored and lost and add to the delay before another collision event can trigger.
    3. collision_end: After the initial post-compile delay a single collision event may be triggered every delay seconds. The collision events trigger delay seconds after the collision. Subsequent collisions before the collision event triggers seem to restart the delay cycle.
  • Timer events (timer): These will not trigger for the first delay seconds after compile. After this, if delay is less than the period of the timer, the timer event will trigger normally. If delay is greater than the period of the timer, the timer will trigger every delay seconds, and will not stagger or trigger erratically even if the delay and the timer are not evenly divisible.
  • Sensor events using llSensorRepeat (sensor): This appears to behave the same as the timer event.
  • Control events: control event has a min delay of 0.05, and can be triggered twice if the llMinEventDelay is long enough to allow it in one delay period.

At this point my brain melted.

See script delay for more information.

Q: After doing all this testing I question the usefulness of this function in light of the apparent randomness of its effect. Would one of my betters like to comment on how it might be used?
A: It seems to me that for touch or collision events, it might still be useful. You're right though, it's pretty limited.

functions | events | script delay


llModifyLand

llModifyLand(integer action, integer size)

This is a general-purpose terraforming function used to modify the shape of land. It will not work while the agent is offline or outside of the sim containing the script.

action | Value | Description
LAND_LEVEL | 0 | Levels the terrain with each call, raising it up to the current height of the object's center (even if already level). To level terrain at a position lower than the current ground level, the terrain needs to be lowered using LAND_LOWER first, because the object cannot be placed under ground with scripted functions (llSetPos).
LAND_RAISE | 1 | Raises the terrain with each call, up to the current height of the object's center.
LAND_LOWER | 2 | Lowers the terrain with each call, down to zero height.
LAND_SMOOTH | 3 | Smooths the terrain.
LAND_NOISE | 4 | Randomizes (adds noise to) the terrain.
LAND_REVERT | 5 | Revert terrain to state.

size | Value | Description
LAND_SMALL_BRUSH | 1 | small brush (2m x 2m)
LAND_MEDIUM_BRUSH | 2 | medium brush (4m x 4m)
LAND_LARGE_BRUSH | 3 | large brush (8m x 8m)

To measure the height of the ground, use llGround.

Notes:
  • There is no LAND_TINY_BRUSH constant. That means land cannot be modifed at the highest possible resolution. This is a shortcoming of the llModifyLand function. Don't like it? Complain to the Lindens.
  • This function does not work in child prims in a link set.
  • If you use llModifyLand(LAND_RAISE, brushsize) so that the land raises over the center of the object, then the object will "fall off the edge of the world", and get returned to your inventory's 'Lost and Found' folder. To avoid this, put an llDie() after the llModifyLand calls and hope it gets called before the object is detected as being under the ground.

Q: Rather than using llDie in the above example, would it work if you set object status to DIE_AT_EDGE?
A: Yes, setting DIE_AT_EDGE to TRUE and STATUS_RETURN_AT_EDGE to FALSE should make the object die.

Functions | Constants | Land


llModPow

integer llModPow(integer a, integer b, integer c)

Returns a raised to the b power, mod c. ( (a ^ b)%c ). b is capped at 0xFFFF (16 bits).

Note: This function delays the script for 1 second.

Q: So what is this actually useful for?
A: Weak RSA encryption--see the discussion in the Second Life Scripting Forum.

Hewee Zetkin wrote the 31 bit implementation of ModPow:

// NOTE
// ----
// All funcitons beginning with the 'u_' prefix assume their arguments are
// non-negative integers in the range [0, 2^31-1].  Passing values outside
// this range results in unspecified behavior.
//
// All results from these functions will either also be in this range or
// will overflow into two integer words.  In the latter case, the least
// significant integer word will be in the above range (the sign bit will be
// zero).  This means that if 'msw' is the most significant integer word and
// 'lsw' is the least significant word, the full number is actually:
//     msw * 2^31 + lsw
// (notice 2^31, NOT 2^32).

integer SIGN_BIT_MASK = 0x80000000;

// Returns the full product of two unsigned integers.  The first (index 0)
// integer is the most significant integer of the product.  The second
// (index 1) integer is the least significant integer of the product.
list u_intProd(integer i1, integer i2)
{
    integer i1Low = i1 & 0xFFFF;
    integer i1High = i1 >> 16;
    integer i2Low = i2 & 0xFFFF;
    integer i2High = i2 >> 16;

    //llOwnerSay("DEBUG - intProd: i1Low = "+(string)i1Low);
    //llOwnerSay("DEBUG - intProd: i1High = "+(string)i1High);
    //llOwnerSay("DEBUG - intProd: i2Low = "+(string)i2Low);
    //llOwnerSay("DEBUG - intProd: i2High = "+(string)i2High);

    integer lowLow = i1Low*i2Low;
    integer lowHigh = i1Low*i2High;
    integer highLow = i1High*i2Low;
    integer highHigh = i1High*i2High;

    integer lowLowMSW = (lowLow >> 31) & 0x1;
    integer lowLowLSW = lowLow & ~SIGN_BIT_MASK;
    integer lowHighMSW = (lowHigh >> 15) & 0xFFFF;
    integer lowHighLSW = (lowHigh << 16) & ~SIGN_BIT_MASK;
    integer highLowMSW = (highLow >> 15) & 0xFFFF;
    integer highLowLSW = (highLow << 16) & ~SIGN_BIT_MASK;

    //llOwnerSay("DEBUG - intProd: lowLowMSW = "+(string)lowLowMSW);
    //llOwnerSay("DEBUG - intProd: lowLowLSW = "+(string)lowLowLSW);
    //llOwnerSay("DEBUG - intProd: lowHighMSW = "+(string)lowHighMSW);
    //llOwnerSay("DEBUG - intProd: lowHighLSW = "+(string)lowHighLSW);
    //llOwnerSay("DEBUG - intProd: highLowMSW = "+(string)highLowMSW);
    //llOwnerSay("DEBUG - intProd: highLowLSW = "+(string)highLowLSW);

    integer msw = lowLowMSW+(highHigh<<1)+lowHighMSW+highLowMSW;

    integer accum0 = lowLowLSW+lowHighLSW;
    if (accum0 & SIGN_BIT_MASK)
    {
        accum0 = accum0 & ~SIGN_BIT_MASK;
        ++msw;
    }

    integer lsw = accum0+highLowLSW;
    if (lsw & SIGN_BIT_MASK)
    {
        lsw = lsw & ~SIGN_BIT_MASK;
        ++msw;
    }

    //llOwnerSay("DEBUG - i1 = "+(string)i1);
    //llOwnerSay("DEBUG - i2 = "+(string)i2);
    //llOwnerSay("DEBUG - msw = "+(string)msw);
    //llOwnerSay("DEBUG - lsw = "+(string)lsw);

    return [ msw, lsw ];
}

// Returns the sum of a big number (product of integers) and a little number
// (integer).  The first (index 0) integer is the most significant integer
// of the sum, and the second (index 1) integer is the least significant
// integer of the sum.
list u_bigSum(integer msw1, integer lsw1, integer i2)
{
    integer msw = msw1;
    integer lsw = lsw1+i2;

    if (lsw & SIGN_BIT_MASK)
    {
        lsw = lsw & ~SIGN_BIT_MASK;
        ++msw;
    }

    return [ msw, lsw ];
}

// Returns the modulo of a large dividend (product of two integers) and a
// small divisor (integer).
integer u_bigMod(integer dividendMSW, integer dividendLSW, integer divisor)
{
    integer msw = dividendMSW%divisor;
    integer lsw = dividendLSW%divisor;

    //llOwnerSay("DEBUG - bigMod: dividendMSW = "+(string)dividendMSW);
    //llOwnerSay("DEBUG - bigMod: dividendLSW = "+(string)dividendLSW);
    //llOwnerSay("DEBUG - bigMod: divisor = "+(string)divisor);

    // This might be slow, but it WILL finish.  Proof (assume msw > 0):
    //     x = msw * 2^31 + lsw
    //     x % y = (msw * 2^31) % y + lsw % y
    //         = (msw % y) * (2^31 % y) + lsw % y
    //     msw % y <= msw
    //     lsw % y <= lsw
    //     y <= 2^31 - 1
    //     2^31 % y < 2^31 - 1
    // thus:
    //     (msw % y) * (2^31 % y) + lsw % y < msw * 2^31 + lsw
    while (msw != 0)
    {
        //llOwnerSay("DEBUG - msw = "+(string)msw);

        integer wsmod = (0x7FFFFFFF%divisor)+1;

        list prod = u_intProd(wsmod, msw);
        integer prodMSW = llList2Integer(prod, 0);
        integer prodLSW = llList2Integer(prod, 1);

        list sum = u_bigSum(prodMSW, prodLSW, lsw);
        msw = llList2Integer(sum, 0)%divisor;
        lsw = llList2Integer(sum, 1)%divisor;

        //llOwnerSay("DEBUG - bigMod: msw = "+(string)msw);
        //llOwnerSay("DEBUG - bigMod: lsw = "+(string)lsw);
    }

    return lsw;
}

// Returns (a*b)%c.
integer u_modProd(integer a, integer b, integer c)
{
    list prod = u_intProd(a, b);
    integer prodMSW = llList2Integer(prod, 0);
    integer prodLSW = llList2Integer(prod, 1);

    integer result =  u_bigMod(prodMSW, prodLSW, c);

    //llOwnerSay("DEBUG modProd - a = "+(string)a);
    //llOwnerSay("DEBUG modProd - b = "+(string)b);
    //llOwnerSay("DEBUG modProd - c = "+(string)c);
    //llOwnerSay("DEBUG modProd - prodMSW = "+(string)prodMSW);
    //llOwnerSay("DEBUG modProd - prodLSW = "+(string)prodLSW);
    //llOwnerSay("DEBUG modProd - result = "+(string)result);

    return result;
}

// Returns (a^b)%c.
integer u_modPow(integer a, integer b, integer c)
{
    integer result = 1;
    integer currentPow = a;
    integer exp = b;

    while (exp != 0)
    {
        //llOwnerSay("DEBUG - modPow: exp = "+(string)exp);

        if (exp & 0x1)
        {
            result = u_modProd(result, currentPow, c);
        }

        //llOwnerSay("DEBUG - modPow: currentPow = "+(string)currentPow);
        //llOwnerSay("DEBUG - modPow: result = "+(string)result);

        currentPow = u_modProd(currentPow, currentPow, c);
        exp = exp >> 1;
    }

    return result;
}

default
{
    touch_start(integer total_number)
    {
        integer N_TESTS = 10;

        float startTime = llGetTime();

        integer i;
        for (i = 0; i < N_TESTS; ++i)
        {
            integer a = (integer)llFloor(llFrand(0x7FFFFFFF));
            integer b = (integer)llFloor(llFrand(0x7FFFFFFF));
            integer c = (integer)llFloor(llFrand(0x7FFFFFFF));

            llOwnerSay("a = "+(string)a);
            llOwnerSay("b = "+(string)b);
            llOwnerSay("c = "+(string)c);

            integer result = u_modPow(a, b, c);

            llOwnerSay("(a^b)%c = "+(string)result);
        }

        float endTime = llGetTime();

        float timeDiff = endTime - startTime;
        float avgTime = timeDiff / N_TESTS;

        llOwnerSay("Average execution time: "+(string)avgTime+" s");
    }
}

read his explaination here.

Functions | Math


llMoveToTarget

llMoveToTarget(vector target, float tau)

Critically damps a physical object's location to position target over tau seconds. Calling llStopMoveToTarget stops the damping. The target is specified in region coordinates. This function returns immediately.

To know when the object actually reaches its target see the at_target and not_at_target events and the llTarget function.

If the target is too far away from the object's current position (~60 meters), or if tau is too small (0.2 is a good tau), this will silently damp it to the object's current position. The interesting thing is the object is in fact damping to its current position when outside the range as opposed to failing completely, but if you move it back to within 60m it will snap back to the target position.

NOTE:I have tried using llMoveToTarget to move my personal av with the object attached to me, and, there at least, it seems it works until 64.999. It will not do 65, but 64.999999999999....etc will work. I used an integer to save it from the message, don't know if that's got anything to do with it.
Argus Stravinsky

Note: The force created by calling this function persists until llMoveToTarget is called again with another target, is called with a tau of 0.0, or llStopMoveToTarget is called. This can be either helpful or hindersome, especially when using a combination of llApplyImpulse and llMoveToTarget.

This function uses energy at a rate controlled by the tau parameter (even if the object is at the target position). This occurs in parallel with script execution. At around 800+ kilograms mass an object will be too heavy for llMoveToTarget to resist gravity. At that point, gravity will overcome the rate at which energy is replenished and the object will most likely fall to the ground.

Use llSetPos for non-physical objects. llRotLookAt can be used to rotate physical objects.

Example: see at_target

Functions | Dynamics


llOffsetTexture

llOffsetTexture(float horizontal, float vertical, integer side)

Sets an object texture's horizontal and vertical offsets on side. Specifying a side of ALL_SIDES will set all sides in one call. Useful values for horizontal and vertical are between -1 and 1.

Note: This function delays the script for 0.2 seconds.

Compare to llGetTextureOffset.

See also llSetPrimitiveParams.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture | Offset


llOpenRemoteDataChannel()

Creates a channel to listen for XML-RPC calls. Will trigger a remote_data event with type = REMOTE_DATA_CHANNEL and a channel ID (key) once it is available. This channel ID must be referenced in the XML-RPC call to the script (from the outside internet)--so the key must somehow get to the scripter's XML-RPC client, most often via llEmail, llHttpRequest or llLoadUrl.

This function delays the script for 1.0 second.

Q: XML-RPC seems unreliable for continuous and parallel communication with several objects in the same sim, or with objects that move across sim borders.
A: To travel across sim borders, you must call llOpenRemoteDataChannel again to re-establish the link. You will get the same key unless a different expiration condition exists (timeout). Use the changed() event to determine if your object has moved into a different sim.

Q: Is the above behavior a bug or a deliberate change? When was it made? What was the behavior before?
A: This behavior was the way that LL implemented it... they've just never fixed it.

Q: What are the details of the expiration conditions? Under what circumstances will you not get the same key?
A: Extended periods without queries, grid restarts.

Functions | Communications | XML-RPC


llOverMyLand

integer llOverMyLand(key id)

Returns TRUE if id is over land owned by the script owner, otherwise returns FALSE. This also works for group land, assuming the object is deeded to the group. It will not work if the object is not deeded, not even if owned by a member or officer in the group. The object itself needs to be deeded.

Example:
default {
    touch_start(integer num_detected) {
        // check if the touching person is over the script owner's land
        if (llOverMyLand(llDetectedKey(0))) {
            llSay(0, llDetectedName(0) + " is on my land.");
        }    else {
            llSay(0, llDetectedName(0) + " is NOT on my land.");
        }
    }
}

NOTE: This function may return a false positive on an AV that was just ejected from your land into a neighboring sim. (As of June 10, 2006)

NOTE: The false positive condition may be worked around by performing:
if (llOverMyLand(key agent) && llGetAgentSize(key agent) != ZERO_VECTOR)
llGetAgentInfo() could be used in stead of llGetAgentSize() but there is the risk that it will return a false negative.

Also useful: llGetLandOwnerAt.

Functions | Land


llOwnerSay

llOwnerSay(string message)

Says message to only the owner of the object running the script if the owner has been within the object's simulator since logging into Second Life, regardless of where they may be are in-world.

This is useful for replacing llInstantMessage(llGetOwner(),msg) as there is no time penalty with llOwnerSay. This is also useful for replacing llWhisper(0,message) for instances where only the owner needs to be given the message (For example, "Dance Bracelet Initialized").

Please use this function to reduce script spam by using it whenever the script needs to tell only the owner something (status info, interactive help text, etc). Other users tend to get annoyed with unnecessarily chatty scripts.

Remember, as with the other chat functions, message is truncated to 1023 bytes.

Example:
llOwnerSay("Hello Avatar!");
Chat Output (visible only to object owner):
Hello, Avatar!

Q: I don't get it. Why was this added?
A: Many people have asked for a way to send private messages to the owner of an object without the delay penalty incurred by llInstantMessage. Many scripters will accomplish this using a llWhisper though it is not completely private, and may annoy people near the object. llOwnerSay is most useful for attachments where you want to get a lot of data back without spamming local chat with whisper or waiting for the instant messages to trickle in due to the time penalty they incur.

Q: How do I get it to work with someone who isn't the owner?
A: You can't. llOwnerSay is intended primarily for debug purposes or messages that only need the attention of the object owner. If you need to send a message to someone else, use llInstantMessage.

Q: How do I make llOwnerSay work without having been in the sim in which my object resides since logging into SL?
A: You can't. You can either enter the sim once, or use llInstantMessage.

Q: Does llOwnerSay work when I'm offline?
A: No, you need to be online and have been in the sim in which your llOwnerSay object is in since logging into SL. If you need to recieve messages when offline, use llInstantMessage.

Q: Are llOwnerSay chat particles visible to other users? I don't want to spam particles just to avoid the instant message delay.
A: No, llOwnerSay chat particles are only visible to the owner.

Compare with llWhisper, llSay, llShout, llInstantMessage and llMessageLinked

Functions | Communications | Chat


llParcelMediaCommandList

llParcelMediaCommandList(list command_list)

How to Use QuickTime:

  • Own land.
  • Apply a texture to an object from your inventory that will eventually be replaced with the media stream.
  • From "About Land/Options", use the GUI to select the texture above.
  • Enter the URL of your media stream
  • Use the little "Movie Control" box to play the media stream.

Additionally, you can:
  • Create objects you want to use as PLAY, STOP, PAUSE & LOOP (play forever)
  • Attach scripts to each, using the following information:

You are allowed one movie (or "media" resource) per land parcel. The movie will be played by replacing a texture on an object with the movie. Users will only see the movie when they are standing on your land parcel. Otherwise they will see the static texture. Script functions only work for objects owned by the land owner or deeded to the group that owns the land. (Remember to set asset permissions on your script and object as well as sharing it with the group!)

Note: This function delays the script for 2.0 seconds.

Currently supported llParcelMediaCommandList options are:

Flag | Value | Parameter | Description
PARCEL_MEDIA_COMMAND_STOP | 0 | - | stop the media stream and go back to the first frame
PARCEL_MEDIA_COMMAND_PAUSE | 1 | - | pause the media stream (stop playing but stay on current frame)
PARCEL_MEDIA_COMMAND_PLAY | 2 | - | start the current media stream playing and stop when the end is reached
PARCEL_MEDIA_COMMAND_LOOP | 3 | - | start the current media stream playing, loop to the beginning when the end is reached and continue to play
PARCEL_MEDIA_COMMAND_TEXTURE | 4 | key texture | specifies the texture to replace with video Note: If passing the key of a texture, it must be explicitly typecast as a key, not just passed within double quotes.
PARCEL_MEDIA_COMMAND_URL | 5 | string url | specifies the movie URL (254 characters)
PARCEL_MEDIA_COMMAND_TIME | 6 | float time | specifies the time index at which to begin playing
PARCEL_MEDIA_COMMAND_AGENT | 7 | key agent | specifies a single agent to apply the media command to
PARCEL_MEDIA_COMMAND_UNLOAD | 8 | - | unloads the stream. While the stop command sets the texture to the first frame of the movie, unload resets it to the real texture that the movie was replacing.
PARCEL_MEDIA_COMMAND_AUTO_ALIGN | 9 | integer enable | turns on/off the auto align feature, similar to the auto align checkbox in the parcel media properties (NOT to be confused with the "align" function in the textures view of the editor!) Takes TRUE or FALSE as parameter.

Most of the QuickTime media file formats are supported, including:

  • QuickTime movies (.mov)
  • Streamable stored QuickTime movies (.mov)
  • Real time QuickTime streams (rtsp://)
  • MPEG4 movies (.mp4, .mpeg4) (simple profile only)
  • QuickTime VR scenes and objects (.mov)
  • Flash movies (.swf) (only non-interactive, version 5 and earlier)
  • Many more - See QuickTime 7 Supported Video Formats List
  • Note: not all of these may play. A good rule of thumb is if it plays in the QuickTime Media Player, it will play in Second Life.
  • You may also retrieve the current URL and/or texture used for the movie by calling llParcelMediaQuery. See the LSL documentation for more details.

Example:
default
{
    touch_start ( integer total_number )
    {
        llParcelMediaCommandList ( [ PARCEL_MEDIA_COMMAND_LOOP ] );
    }
}

Advanced example:
float START_TIME = 30.0;
float RUN_LENGTH = 10.0;

default
{
    state_entry()
    {
        if ( llParcelMediaQuery([PARCEL_MEDIA_COMMAND_TEXTURE]) == [] )
            llSay(0, "Lacking permission to set/query parcel media. This object has to be owned by/deeded to the land owner.");
        llParcelMediaCommandList( [
            PARCEL_MEDIA_COMMAND_URL, "http://enter_your.url/here",
            PARCEL_MEDIA_COMMAND_TEXTURE, (key) llGetTexture(0) ] );
    }

    touch_start(integer num_detected)
    {
        llParcelMediaCommandList( [
            PARCEL_MEDIA_COMMAND_AGENT, llDetectedKey(0),
            PARCEL_MEDIA_COMMAND_TIME, START_TIME,
            PARCEL_MEDIA_COMMAND_PLAY ] );
        list Info = llParcelMediaQuery([PARCEL_MEDIA_COMMAND_URL, PARCEL_MEDIA_COMMAND_TEXTURE]);
        llSay(0, "Playing '" + llList2String(Info, 0) + "' on texture '" + (string)llList2Key(Info, 1) + "' for agent " + llDetectedName(0));
        llSetTimerEvent(RUN_LENGTH);
    }

    timer()
    {
        llParcelMediaCommandList( [ PARCEL_MEDIA_COMMAND_STOP ] );
        llSetTimerEvent(0.0);
    }
}

Q: It's not working! What's wrong?
A: Are you running it over group-owned land? You need to make sure you own the land, or that the script is deeded to the group that owns the land. When deeding, remember to set all asset permissions and check "Share with Group" beforehand.

If you just want to stream music, use llSetParcelMusicURL.

Functions | Video


llParcelMediaQuery

list llParcelMediaQuery(list query_list)

Queries the texture and/or URL for QuickTime-playable video on the land parcel.

This function will only work if the script is contained within an object owned by the landowner (or if the land is owned by a group, only if the object has been deeded to the group). It will not work for group land if the object owner is a member of the group. The object actually has to be owned by the group.

Note: this function delays the script for 2 seconds.

query_list can include one or more of the following flags:
Flag | Value | Description
PARCEL_MEDIA_COMMAND_TEXTURE | 4 | Returns the key of the texture to be replaced with video.
PARCEL_MEDIA_COMMAND_URL | 5 | Returns the movie URL.

llParcelMediaQuery will return a list with the results in the order the values were specified in query_list.

llParcelMediaQuery will return an empty list under the following conditions:
  • The object owner is not the same as the land owner.
  • query_list does not contain either PARCEL_MEDIA_COMMAND_TEXTURE or PARCEL_MEDIA_COMMAND_URL.

Things you can do but should not:
  • If query_list contains list entries in addition to PARCEL_MEDIA_COMMAND_TEXTURE or PARCEL_MEDIA_COMMAND_URL, llParcelMediaQuery will ignore them. This doesn't mean you should specify great big lists for query_list, however.
  • query_list can contain multiple values of PARCEL_MEDIA_COMMAND_TEXTURE or PARCEL_MEDIA_COMMAND_URL, and will return a list with the results corresponding to the various positions of the the values specified in query_list. Don't do this. Call llParcelMediaQuery once, with PARCEL_MEDIA_COMMAND_TEXTURE and PARCEL_MEDIA_COMMAND_URL as needed.

See llParcelMediaCommandList for an example of usage.

Functions | Video | Land


llParseString2List

list llParseString2List(string src, list separators, list spacers)

Breaks src into a list, splitting at and discarding separators, and splitting at and keeping spacers (separators and spacers must be lists of strings, maximum of 8 each). All instances of each separator will be used to parse the string. Empty strings are not returned by this function - if you need those, look at llParseStringKeepNulls.

Calling:
llParseString2List("one,two,three,'four','five,six' ",[","],[]);

Will produce the list:
["one", "two", "three", "'four'", "'five", "six' "]

Calling:
llParseString2List("one,two,three,'four','five,six' ",[",","'"],[]);

Will produce the list:
["one", "two", "three", "four", "five", "six", " "]

Calling:
llParseString2List("A:B::D", [":"], []);

Will get the list:
["A", "B", "D"]

Calling:
llParseString2List("AllCowsEatGrass", ["A", "C", "E", "G"], []);

Will get the list:
["ll", "ows", "at", "rass"]

Calling:
llParseString2List("AllCowsEatGrass", [], ["A", "C", "E", "G"]);

Will get the list:
["A", "ll", "C", "ows", "E", "at", "G", "rass"]

So, when ["A", "C", "E", "G"] was in separators, it removed them, then split the string at that point. When ["A", "C", "E", "G"] was in spacers, it splits the string before and after each spacer, leaving the spacer intact.

To split a sentence into words by using " " as the separator:
llParseString2List("All Cows Eat Grass", [" "], []);

Returns this list:
["All", "Cows", "Eat", "Grass"]

Useful function to retrieve a first name from an avatar's entire name (excluding group).
string getFirstName(string fullName)
{
     return llList2String(llParseString2List(fullName,[" "],[]),0);
}

Be very careful about using this function on strings that may have been input from untrusted sources (for example, strings heard in open chat via a listen() event). A list with a large number of items can consume a large amount of a script's memory, potentially causing it to halt with a stack/heap collision error; disruptive users may try to crash your script by deliberately speaking strings that will parse to extremely large lists.

llParseStringKeepNulls: keeps null strings between separators or spacers
llDumpList2String: performs the opposite function.

See also llCSV2List and ExampleListConversion.

Functions | Lists | Strings


llParseStringKeepNulls

list llParseStringKeepNulls(string src, list separators, list spacers)

llParseStringKeepNulls works almost exactly the same as llParseString2List, except that if a null is found it will add a null string instead of discarding it like llParseString2List does.
llParseString2List("Blah||Foo", ["|"], []) = ["Blah", "Foo"]
llParseStringKeepNulls("Blah||Foo", ["|"], []) = ["Blah", "", "Foo"]
Question: Is it supposed to produce a list of length 1 when "" is passed as src? llGetListLength(llParseListKeepNulls("", ["]_["], [])) returned 1... is this the correct behavior?
Answer:I believe that to be the correct behavior since it has always answered that way.

llParseString2List
llDumpList2String: performs the opposite function

See also llCSV2List and ExampleListConversion.

Lists | Functions


llParticleSystem

llParticleSystem(list parameters)

Makes a particle system based on the parameter list. The parameters are specified as an ordered list of parameters and values.

The following tables are valid parameters. Basically, each particle system is specified with a list consisting of the flags ORed together ([PSYS_PART_FLAGS, flag | anotherflag]) followed by the pattern ([PSYS_SRC_PATTERN, pattern]) and then pairs of parameters and their values.

Notes:
  • A prim may have at most one particle emitter running at a time. Defining another will supercede the previous one.
  • Directional displays are relative to the prim's z-axis and emanate from the center of the prim.
  • You must explicitly cancel an undefined lifetime particle display by passing llParticleSystem an empty list: llParticleSystem([])
  • Due to Second Life's aggressive LOD settings for small prims and particles (which can't be changed by the end-user), if the emitter prim is not large enough to be seen (rendered), the particle system associated with it will not be displayed (e.g. particles in 0.01 m sphere and agent is far away).
  • Most clients are configured to display 4096 particles within their draw distance (default: 96 m). If the number of particles within your client's draw distance is close to the limit, you may not see your particles or only see it sporadically.

Parameter pairs (always one constant followed by parameter of specified type):
Constant | Type | Description | Decimal
PSYS_PART_START_ALPHA | float | alpha of starting particle | 2
PSYS_PART_END_ALPHA | float | alpha of ending particle | 4
PSYS_PART_START_COLOR | vector | color of starting particle | 1
PSYS_PART_END_COLOR | vector | color of ending particle | 3
PSYS_PART_START_SCALE | vector | Starting size of particle billboard (sprite) in meters (z value is ignored). Maximum 4x4, minimum may be 0 if the same x or y value in the end scale (see below) is at least .03125. Using a texture with a larger masked area can give the appearance of an even smaller particle. | 5
PSYS_PART_END_SCALE | vector | Ending size of the particle billboard in meters (z value is ignored). Maximum 4x4, minimum 0 if the same x or y value in the start scale (see above) is at least .03125. Using a texture with a larger masked area can give the appearance of an even smaller particle. | 6
PSYS_PART_MAX_AGE | float | maximum age of a particle, in seconds (max 30 seconds) | 7
PSYS_SRC_MAX_AGE | float | Length of time, in seconds, from when the source object comes into view, or the particle system's creation, that the object emits particles; after this time period no more particles are emitted. | 19
PSYS_SRC_ACCEL | vector | particle acceleration: minimum seems to be .0078125 before any acceleration occurs (more research needed to verify); maximum seems to be 100 | 8
PSYS_SRC_ANGLE_BEGIN | float | area in radians specifying where particles will NOT be created (for ANGLE patterns) | 22
PSYS_SRC_ANGLE_END | float | area in radians filled with particles (for ANGLE patterns) (if lower than PSYS_SRC_ANGLE_BEGIN, acts as PSYS_SRC_ANGLE_BEGIN itself, and PSYS_SRC_ANGLE_BEGIN acts as PSYS_SRC_ANGLE_END) | 23
PSYS_SRC_BURST_PART_COUNT | integer | number of particles to release every burst | 15
PSYS_SRC_BURST_RADIUS | float | Distance from center of source (in meters) where particles are created; only works when PSYS_PART_FOLLOW_SRC_MASK is not set. | 16
PSYS_SRC_BURST_RATE | float | time interval (how often) to release ("burst") particles (in seconds, 0 fastest) | 13
PSYS_SRC_BURST_SPEED_MIN | float | minimum velocity of each newly created particle; negative values act like 0 | 17
PSYS_SRC_BURST_SPEED_MAX | float | maximum velocity of each newly created particle; negative values act like 0; (if lower than PSYS_SRC_BURST_SPEED_MIN, acts as PSYS_SRC_BURST_SPEED_MIN itself, and PSYS_SRC_BURST_SPEED_MIN acts as PSYS_SRC_BURST_SPEED_MAX) | 18
PSYS_SRC_INNERANGLE | float | deprecated, use PSYS_SRC_ANGLE_BEGIN instead. An area in radians specifying where particles will not be created. | 10
PSYS_SRC_OUTERANGLE | float | deprecated, use PSYS_SRC_ANGLE_END instead. The area that is filled with particles, in radians. | 11
PSYS_SRC_OMEGA | vector | rotation between bursts (in radians) (used only by SRC_PATTERN_ANGLE* patterns) | 21
PSYS_SRC_TARGET_KEY | key | target that particles will move towards (if PSYS_PART_TARGET_POS_MASK has been set). If the key cannot be found, particles will target the emitter prim. Targets seem to have no distance limit (up to 739,104m tested so far)--even across sims. (Target key object needs to have been rezzed in-world by the client, else it acts as not found--thus distance is limited to each client's capabilities.) --Jiffies | 20
PSYS_SRC_TEXTURE | string | inventory item name or key of the particle texture | 12
PSYS_SRC_PATTERN | integer | emission pattern; see pattern table below | 9
PSYS_PART_FLAGS | bitfield (integer) | particle behavior; see flag table below | 0

PSYS_PART_FLAGS followed by an integer bitfield with one or more of the following constants OR'ed together:
Constant | Description | Hex
PSYS_PART_BOUNCE_MASK | particles bounce off object's z-axis height | 0x004
PSYS_PART_EMISSIVE_MASK | particles emit a glow (are prelit); otherwise reflect light | 0x100
PSYS_PART_FOLLOW_SRC_MASK | Particle position is relative to source object's vertical position (rotation still disperses particles); disables PSYS_SRC_BURST_RADIUS. | 0x010
PSYS_PART_FOLLOW_VELOCITY_MASK | rotate particles so vertical axis points towards velocity vector | 0x020
PSYS_PART_INTERP_COLOR_MASK | interpolate color (and alpha) from start to end | 0x001
PSYS_PART_INTERP_SCALE_MASK | interpolate particle scale from start to end | 0x002
PSYS_PART_TARGET_LINEAR_MASK | (Not listed in official documentation.) Seems to send particles in a straight line to target (cancels PSYS_SRC_ACCEL, PSYS_SRC_BURST_RADIUS, and possibly other parameters). | 0x080
PSYS_PART_TARGET_POS_MASK | particles move towards (target) object defined in PSYS_SRC_TARGET_KEY | 0x040
PSYS_PART_WIND_MASK | particle velocity is damped toward the wind | 0x008
PSYS_PART_RANDOM_ACCEL_MASK | unimplemented - random acceleration; use llFrand instead | -
PSYS_PART_RANDOM_VEL_MASK | unimplemented - random velocity; use llFrand instead | -
PSYS_PART_TRAIL_MASK | unimplemented - trails; emit more particles at shorter bursts instead | -

PSYS_SRC_PATTERN followed by one (and only one) of the following constants:
Constant | Description | Hex
PSYS_SRC_PATTERN_ANGLE | Shoot particles across a 2-dimensional area defined by arc radians specified in PSYS_SRC_ANGLE_END. An open area will exist in the arc defined by PSYS_SRC_ANGLE_BEGIN. | 0x04
PSYS_SRC_PATTERN_ANGLE_CONE | Shoot particles across a 3-dimensional cone defined by arc radians specified in PSYS_SRC_ANGLE_END. An open area will exist in the arc defined by PSYS_SRC_ANGLE_BEGIN. | 0x08
PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY | Not listed in official documentation. Inverse of PSYS_SRC_PATTERN_ANGLE_CONE; will shoot particles everywhere except the 3-dimensional hollow cone defined by PSYS_SRC_ANGLE_END and PSYS_SRC_ANGLE_BEGIN. | 0x10
PSYS_SRC_PATTERN_DROP | drop particles at source position with no initial velocity | 0x01
PSYS_SRC_PATTERN_EXPLODE | shoot ("explode") particles out in all directions using burst parameters | 0x02
Remember, like llSetText or physics, llParticleSystem is not caused by the script, which merely turns particles on and off. Simply put, particles won't stop until llParticleSystem([]) is executed, which removes the system. This means particles won't stop when moved onto no-script land. Particle systems are rendered client-side and do not contribute to server load after the initial function call.

Also particle effects don't work on HUD objects.

Example:
// This will make an explosion of red particles, which follow the wind and glow
llParticleSystem([PSYS_PART_FLAGS, PSYS_PART_WIND_MASK | PSYS_PART_EMISSIVE_MASK,
    PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE,
    PSYS_PART_START_COLOR, <1,0,0>]);

Q: How do I get my object to stop emitting particles? I removed the script, but it won't stop.
A: Call llParticleSystem again with an empty list. Particles aren't actually generated by the script; rather, the script only toggles a setting on the prim. This behavior is similar to llSetScale or llSetText--the scale or text are a part of the object, and will only be changed when a new setting is fed to them. Particles work the same way.

Example:
default {
     state_entry()
     {
          llParticleSystem([]);
     }
}

Q: Is it just me or is PSYS_SRC_BURST_RATE not acting at all like it should? Sometimes it emits it in the time I set, sometimes it takes 4x as long, or sometimes it emits fractions of a second after it just last emitted.
A: Check if there are other particles in-view that could be sucking up SL's particle limit set in preferences. Increase the particle limit, move to an area with less particles, and/or reduce PSYS_SRC_BURST_PART_COUNT.

Q: How do I make the rotation of the particle match the source prim with PSYS_SRC_PATTERN_DROP?
A: If you use PSYS_SRC_PATTERN_DROP then all particles are rezzed with the same orientation that ignores the source prim. You can change the rotation using PSYS_PART_FOLLOW_VELOCITY_MASK but this is a one time setting that applies to all particles. This can be faked though by using PSYS_SRC_PATTERN_ANGLE with a <0,0,0> velocity. Using PSYS_SRC_PATTERN_ANGLE causes the particles to match the orientation of the source prim.

See ExampleParticleScript, ExampleParticleScript1, and LibraryKeknehvParticles for better examples.

Particles | Functions


llPassCollisions

llPassCollisions(integer pass)

If pass is TRUE, collisions (both the land and object/avatar type) are passed from child to parent: In a linked object, llPassCollisions controls whether collisions are passed from a child prim on to the parent, or handled locally.

To block collisions from being passed to the parent prim, the child prim must contain a script with a collision_start, collision, or collision_end event handler. In the absence of one of these three events, collisions are automatically passed to the parent, whether or not llPassCollisions(FALSE) has been set.

llPassCollisions is used when you do want collisions to be sent to the parent prim, when the child already contains a script with a collision_start, collision, or collision_end event handler. To accomplish this, use llPassCollisions(TRUE). llPassCollisions(TRUE) is also the only way to get a parent prim to register when a child prim collides with the land (using the land_collision event) when the child prim has a script.

llPassTouches performs the same function, only with touches instead of collisions.

Functions | Link | Collisions


llPassTouches

llPassTouches(integer pass)

In a linked object, llPassTouches controls whether touches are passed from a child prim on to the parent, or handled locally.

To block touches from being passed to the parent prim, the child prim must contain a script with a touch_start, touch, or touch_end event handler.

If the child does not have a touch_start, touch, or touch_end event handler, touches are automatically passed to the parent, whether or not llPassTouches(FALSE) has been set. An empty touch event in the child prim will still allow the prim to be clicked, but will not pass touches on to the parent.

In the absence of one of these three events, all touches will be passed to the parent by default.

llPassTouches is used when you do want touches to be sent to the parent prim, when the child already contains a script with a touch_start, touch, or touch_end event handler. To accomplish this, put llPassTouches(TRUE) in the script in the child prim. This will only cause the prim in question to pass its touches to the parent.

When passing touches, to determine what prim in the linkset has been touched, use llDetectedLinkNumber in the parent prim.

Remember, touches are not collisions! Touches are when the object is clicked. If you want collisions, try llPassCollisions.

Functions | Link | Touch


llPlaySound

llPlaySound(string sound, float volume)

This function plays the sound file defined by sound (which may be either the key or name of the sound file--if it is in the prim's inventory). volume is a normalized float between 0 and 1 (if a volume is specified greater then 1 it's set to 1).

A script can only have one sound playing using llPlaySound; subsequent calls will be ignored until the first sound has stopped playing, unless llSetSoundQueueing has been enabled, in which case one sound will be queued and played as soon as the currently playing sound ends. This function returns immediately.

Sounds started with llPlaySound can be stopped using llStopSound. The volume of sounds started with llPlaySound or llLoopSound can be adjusted using llAdjustSoundVolume.

Note:No they can't. At least in version 1.16, llStopSound will not stop sounds started with llPlaySound. IT does work on llLoopSound, though. - WarKirby Magojiro 05/26/2007

Sounds played with llPlaySound and llLoopSound (and the master/slave versions of these functions) "stick" to the object playing the sound. That is, if a long sound is started with llPlaySound, and then the object moves away, the sound will grow fainter. Compare this to llTriggerSound and llTriggerSoundLimited where the sound stays at the location it was first played.

If llPlaySound is used in an attached HUD object, the sound will only be heard by the user wearing the HUD. Additionally, if the user is over land that is set to restrict sound from entering or leaving the parcel, the sound will not be heard at all. To play a sound from a HUD and have others hear it, use llTriggerSound instead.

Note: A call to llPlaySound() where the sound to be played is unavailable (ie. referenced by name but is not present in the object's inventory, or by a non-valid key) will delay the calling script by one (1.0) second. - Cross Lament 01/19/2007

PlaySound(string sound, float volume)
{//not fool proof but pretty reasonable for avoiding the 1.0 second delay if it does not exist
    if((key)name);
    else if(llGetInventoryType(sound) == INVENTORY_SOUND);
    else
        return;
    llPlaySound(sound, volume);
}//Written by Strife Onizuka

Q: Is there a way of finding out when the playback of a sound started with llPlaySound() ended? That would be useful for playing back playlists of sounds with the sound queue (i.e. queue another sound when one has ended).

A: Unfortunately, the only way to do this is to preload and wait, then use a timer set to the clip length. Sounds are not played globally synced, they preload onto each machine based on ping and available network bandwidth, and without sufficient preloading no two clients will hear the same sound at the same time unless it is already cached.
Functions | Sound | Client Asset Keys


llPlaySoundSlave

llPlaySoundSlave(string sound, float volume)

Behaviour is identical to llPlaySound, unless there is a "sync master" (set with llLoopSoundMaster) present and already playing, the slave sound will not be played until the master hits its loop point and returns to the beginning. This function will play the sound exactly once; if it is desired to have the sound play every time the master loops, either use llLoopSoundSlave, with extra silence padded on the end of the sound, or ensure that this function is called at least once per loop of the master.

Sounds | Functions


llPointAt

CURRENTLY BROKEN

llPointAt(vector pos)

Makes the object owner's avatar point towards position pos. Use llStopPointAt to stop pointing.

Note: This function is still broken as of 1.6. It neither points nor faces pos.

'According to Kelly Linden, llPointAt and llStopPointAt have been deprecated since mid-beta, but remain without a replacement to this day. (SL version 1.6)

Functions | Agent And Avatar


llPow

float llPow(float base, float exp)

Returns base raised to the exp.

Example:
llSay(0,(string)llPow(3.0, 2.0)); // returns 9 (3*3)
llSay(0,(string)llPow(4.0, 5.0)); // returns 1024 (4*4*4*4*4)

Remember, roots can be expressed as powers. For example,
llSqrt(4.0);        //Using the math library's square root function
llPow(4.0, 1.0/2.0);    //Using a power to return a square root

This means that one can manage a cube root, or any other root. A cube root would be expressed as
llPow(27.0, 1.0/3.0);

Functions | Math


llPreloadSound

llPreloadSound(string sound)

Preloads the sound clip defined by sound. If sound is in the task's inventory, then sound can be the name of the sound clip. If not, sound must be the key of the sound clip.

This function delays the script 1 second.

Example:
default
{
    state_entry()
    {
        // preload "mysound" for anyone that is currently in listening range of this object.
        llPreloadSound("mysound");
    }

    touch_start(integer num_detected)
    {
        llPlaySound("mysound", 1.0); // play "mysound" when touched.
    }
}

Functions | Sound


llPushObject

llPushObject(key id, vector impulse, vector angular_impulse, integer local)

Applies impulse and angular_impulse to object or avatar id.
local is a boolean (TRUE/FALSE) value that specifies whether or not the push is relative to id's rotation.

A heavier (more massive) object will move another object more than a lighter object with the same impulse applied. Similarly, a heavier object, when being pushed, will not go as far as a lighter object with the same impulse. To have an object push different-sized objects at the same speed, multiply impulse by the mass of the object being pushed. llGetObjectMass can be used to obtain the mass of an object or avatar anywhere within the same sim.

There is no upper range limitation, but falloff is calculated by 1/r³: the further this object is from id, the weaker the push will be. The push will fall off proportional to the cube of the distance (radius) between the two objects.

Use this to calculate and counteract the falloff on a push:
Power*llPow(llVecDist(llGetPos(), TargetPos), 3);

Q: Does multiplying the velocity by the mass of the target also make up for the difference bettwen small and large pushers?

In practice it looks like vector impulse numbers over 2147483647 will act like 2147483647 (integer maximum -- curious!). I know at some stage in the past LL limited how hard llPushObject pushed; maybe they chose the integer maximum as a cutoff. Of course this doesn't stop you using multiple llPushObjects. ;) -Psyke Phaeton (Here's another hint: Vectors dont use integers, they use floats ;) -Raven)

Note that using this function to grief other users will result in an abuse report being filed. If you want to remove someone from your land, use llEjectFromLand. llPushObject is not to be used for that.

As of version 1.11.1 (1) of Second Life, pushing can be set to "restricted" on a parcel by the land owner. This stops llPushObject from being used upon avatars other than the owner of the script (self-pushing is still allowed). Scripts owned by the land owner can still use llPushObject on any avatar, and pushing of non-avatar objects is unaffected.

Functions | Dynamics | Force | Impulse


llRegionSay

llRegionSay(integer channel, string text)

Broadcasts text on the specified chat channel to the whole region.
This means that any script listening on that channel will hear the text broadcasted by another script, regardless of the distance between both scripts, as long as they both are in the same region.

Notes

  • Fuzionor Engawa Comment: I noticed using this did a repeat of 3x for some reason, but not always.
  • The channel cannot be zero (0, main chat). It can be negative or positive, just not zero.
  • When using DEBUG_CHANNEL, the object containing the script will perform the equivalent to:
    llShout(0, "/me say, " + text)
    Weird, huh? So...
    string msg = "Hello, world!";
// The following two lines accomplish the same result:
llRegionSay(DEBUG_CHANNEL, msg);
llShout(0, "/me say, " + msg);
    The output will be heard on a 100 meter range:
    Object say, Hello, World!
  • However, if YOU use a "/me" with it, the results will end up as a normal shout.
    string msg = "/me likes bananas.";
// The following two lines accomplish the same result:
llRegionSay(DEBUG_CHANNEL, msg);
llShout(0, msg);
    Output:
    Object likes bananas.

Compare with llWhisper, llSay, llShout, llOwnerSay, llInstantMessage and llMessageLinked

Functions | Chat | Communications


llReleaseCamera

llReleaseCamera(key agent)

A FollowCam function that returns the camera to the key agent after permission to change it with llSetCameraParams has been granted via llRequestPermissions(agent_key, PERMISSION_CONTROL_CAMERA).

If NULL_KEY is used this function will silently fail.

See also llGetPermissionsKey for the agent permissions that were last requested for.

Functions | Agent | Camera


llReleaseControls()

Releases control previously taken with llTakeControls.

Note: releasing control will unset PERMISSION_TAKE_CONTROLS. To take control again, re-request the permission with llRequestPermissions.

Q: If you call release controls and PERMISSION_TAKE_CONTROLS is not set will this trigger a script error?
A: No. In fact, it's a good idea to call it immediately before llTakeControls, as this seems to help avoid control issues where controls are being passed on where they should not be.

Q: If llReleaseControls() unsets PERMISSION_TAKE_CONTROLS, can you still use other permissions the script has been granted?
A: Yes, probably. In testing with a script that requests PERMISSION_TAKE_CONTROLS and PERMISSION_TRIGGER_ANIMATION, animations can still be triggered after a call to llReleaseControls().

Functions | Agent/Avatar | Control


llRemoteDataReply

llRemoteDataReply(key channel, key message_id, string sdata, integer idata)

Send an XML-RPC reply to the request with message_id on channel with payload of string sdata and integer idata. The size of sdata is limited to 254 characters.

This function is used to reply to XML-RPC requests received via the remote_data event.

Note: This function delays the script for 3.0 seconds.

Additional notes:
Even though you can specify the channel you are replying to, if you direct a second script (via a link_message) to reply on your behalf (in order to get around the 3-second enforced delay, for example), it does not work. RPC channels are script-specific, and you can only reply to requests received within the same script. --TalarusLuan

Functions / XML-RPC


llRemoteDataSetRegion()

Not working - read below

This was intended for reregistering your XML-RPC channel with the new sim after changing regions - for some reason it wasn't used. Instead of it, you call llOpenRemoteDataChannel again - you'll receive a remote_data event with the same channel id as in the old sim, so requests can continue. Any requests sent while not yet reregistered after changing sims are dropped.

You do not need to worry about this if your object doesn't change regions.

XML-RPC / Communications / Functions


llRemoteLoadScript

llRemoteLoadScript(key target, string name, integer running, integer start_param)

No longer working, as of SL 1.2.13. When attempted to use, you get the message "llRemoteLoadScript has been deprecated. Please use llRemoteLoadScriptPin instead.

Note: This function delays the script for 3.0 seconds.

(Added in 1.2.6)

Deprecated / Functions


llRemoteLoadScriptPin

llRemoteLoadScriptPin(key target, string name, integer pin, integer running, integer start_param)

In SL 1.12 a serious bug was introduced in this function, causing the bytecode to be stripped from the source code and loaded as a seperate, unnamed inventory object that would be named "Old Script". Any scripts that used llRemoteLoadScriptPin() between the deployment of 1.12 on 8/23/2006 to the rolling restart on Friday 8/25/2006 created these... problems. llGetScriptName() is unable to differentiate between the different scripts as they're ALL named "Old Script", and any inventory functions will only work on the first instance in inventory. Future update scripts will need to be written to look for these "Old Scripts" and their uncompiled properly named source stubs and handle them accordingly. -DragonEccleston

If the owner of the object containing this script can modify the object identified by the key target, and if the pin matches the PIN previously set using llSetRemoteScriptAccessPin (on the target prim), then the script name will be copied into target.

If the target prim already contains a script with the same name as name, it will be replaced without any warning.

Note: target must be a different prim than the one containing this script. llRemoteLoadScriptPin cannot copy a script into itself. Also, target must identify a rezzed prim, existing in the world. It cannot be in the inventory of an object.

If running is TRUE, start the copied script with start_param. start_param can be retrieved using llGetStartParameter in the copied script's state_entry.

Note: this function delays the script for 3 seconds.

Example:
llRemoteLoadScriptPin(targetID, scriptName, pin, TRUE, param);

Note: llRemoteLoadScriptPin only works if both prims are in the same sim. This is presumably because of limitations in the way that sims are currently set up, but may be a "political" limitation imposed by the Lindens in an effort to curb intra-grid communications somewhat.

If copying fails, (for instance, if key == llGetKey()) the script will shout (yes, really!) "Unable to add item!" on channel 0. It makes three attempts (check this out--and then what?).

If an incorrect pin is used, the script sends the following error to the DEBUG_CHANNEL: "Object: Task Object trying to illegally load script onto task Other_Object!"

This function replaces the deprecated llRemoteLoadScript.

See llGetScriptState and llResetOtherScript for other fun things you can do with this.

If you need to update scripts for your end users, consider putting the updated scripts inside of an object which is then delivered directly to end users via llGiveInventory. This object, if rezzed in the same sim as the item being updated, can then negotiate a PIN with the object and the scripts can be updated/enabled automatically.
-- DirtyMcLean

See llGetLinkKey to get the key of a child prim. This works, at least as of 1.13.2.11. --DunselHodgson

Loading the current script into another prim works as of 1.13.2.11. --DunselHodgson
Functions | Inventory


llRemoveFromLandBanList

llRemoveFromLandBanList(key avatar)

Remove avatar from the land parcel ban list.

Q: What happens if the user isn't on the ban list?
A: Nothing.

To add a user to the land ban list, use llAddToLandBanList.
To reset the land ban list, use llResetLandBanList.

To add or remove users from the land pass list, use llAddToLandPassList and llRemoveFromLandPassList.

Functions | Land


llRemoveFromLandPassList

llRemoveFromLandPassList(key avatar)

Remove avatar from the land parcel pass list.

Q: What happens if the user isn't on the pass list?
A: Nothing.

To add a user to the land pass list, use llAddToLandPassList.

To add and remove them from the land ban list, use llAddToLandBanList and llRemoveFromLandBanList.

Functions | Land


llRemoveInventory

llRemoveInventory(string inventory)

Removes the inventory item identified by inventory from the object's inventory.

Example:
llRemoveInventory("Object"); // delete the item named Object from the contents of the prim

A script can delete itself by supplying the result of llGetScriptName to this function:
llRemoveInventory(llGetScriptName()); // delete this script

Attempting to delete a nonexistent inventory item will result a script error "Missing inventory item 'x'" where x is the name of the nonexistent inventory item.

Functions | Inventory


llRemoveVehicleFlags

llRemoveVehicleFlags(integer flags)

Disables vehicle flags that were previously enabled with llSetVehicleFlags.

See llSetVehicleFlags for a list of flag constants.

Vehicle / Functions


llRequestAgentData

key llRequestAgentData(key id, integer data)

This function requests information of type data (see constants below) about agent id. The information is returned via the dataserver event. The key returned by this function is the query id for the dataserver event.

id must be a valid agent key. Passing the key of an object or group will not trigger the dataserver event. To determine if a given key points to an agent, one could use a timer to function as a timeout after several seconds had passed, though this would hardly be 100% reliable.

Valid values of data and what each will return in the dataserver event:
Constant | Value | Returns
DATA_ONLINE | 1 | (cast returned data to integer); determines if agent is online (TRUE/FALSE)
DATA_NAME | 2 | gets full agent name (excluding any group titles); compare with llKey2Name
DATA_BORN | 3 | gets account creation/"born on" date as a string in ISO 8601 format of YYYY-MM-DD.
DATA_RATING 4 returns the agent's ratings as a comma-separated string of six integers:
  • 1. Positive-rated behavior
  • 2. Zero (formerly negative-rated behavior)
  • 3. Positive-rated appearance
  • 4. Zero (formerly negative-rated appearance)
  • 5. Positive-rated building
  • 6. Zero (formerly negative-rated building)
    • (Note: negative ratings no longer exist.)
    as of 1.15.0(2) the rating system was removed and this will now return 0,0,0,0,0,0
DATA_PAYINFO 8 returns the account type bitfield (an integer, cast as a string);
  • Bit 0: PAYMENT_INFO_ON_FILE Payment info on file
  • Bit 1: PAYMENT_INFO_USED Payment info used.

  • No Payment Info on File: 0
  • Payment Info on File: 1
  • No Payment Info on File AND Payment Info Used: 2 (Only Beta/Lifetime users fall under this status)
  • Payment Info on File AND Payment Info Used: 3

Attempting to give llRequestAgentData a combined data integer (as in llSensor) in an attempt to get the dataserver event to return two or more values simulaneously won't work. (1 + 2 = 3, and 1 + 3 = 4, which are clearly marked as DATA_BORN and DATA_RATING respectively.) Even if it did work, there's no way for the dataserver event to return two things, anyway.

Unfortunately, there isn't a way to return either the active or inactive groups a given agent is in. Passing llRequestAgentData the key of a group or object will not trigger the dataserver event, or return an error message. The best option is to try llSameGroup. Depending on what is trying to be accomplished, it may work.

Note: This function delays the script for 0.1 seconds.

Example:
default
{
    touch_start(integer num_detected)
    {
        llRequestAgentData(llDetectedKey(0), DATA_BORN); // request creation date
    }
    dataserver(key queryid, string data)
    {
        llSay(0, "You were born on: " + data);
    }
}

There is a gap in the constant values because the DATA_* namespace is shared with llRequestSimulatorData.

Functions | dataserver | Agent/Avatar | llKey2Name


llRequestInventoryData

key llRequestInventoryData(string name)

Requests data from object inventory item name. When data is available, the dataserver event will be raised with the key returned from this function in the requested parameter.

The only request currently implemented is to request data from landmarks, where the data returned is in the form "<float, float, float>" which can be directly cast to a vector. This position is in region local coordinates (that means it's the distance from the region corner) so to convert it into a global position, just add the result of llGetRegionCorner.

Note: This function delays the script for 1 second.

For a another example using this function see: llMapDestination

Example:
// llRequestInventoryData Example
// This script gets the distance from the object
// this script is in to the landmark defined by LANDMARK_NAME.
// This landmark must be in the same object as the script.
// Written by Christopher Omega

// Name of landmark.
string LANDMARK_NAME = "**landmark name here**";

// Stores the request identifyer (return value of llRequestInventoryData)
// this value is equal to the first parameter of the dataserver event
// when the dataserver event is returning the data requested.
key inventoryRequestID = NULL_KEY;

default {
    state_entry() {
        inventoryRequestID = llRequestInventoryData(LANDMARK_NAME);
    }

    dataserver(key requestID, string data) {
        // If dataserver event is the responce to the request
        // whose key is stored in the variable inventoryRequestID:
        if (requestID == inventoryRequestID) {
            // This prevents any dataserver "hiccups":
            inventoryRequestID = NULL_KEY;

            // When passing landmark names to llRequestInventoryData,
            // the resulting dataserver event's string parameter contains the
            // location of the landmark in coordinates relative to the region the script
            // is currently in. (see wiki page on RegionCoordinates)
            vector regionCoords = (vector) data;
            vector myPosition = llGetPos();

            // Get the distance from my position to the landmark's position.
            float distance = llVecDist(myPosition, regionCoords);
            llSay(0, LANDMARK_NAME + " is " + (string) distance + " meters away from me.");
        }
    }
}

Functions | Inventory | Dataserver


llRequestPermissions

llRequestPermissions(key agent, integer perm)

Ask agent to allow the script to do perm, which can be any of the following constants joined together through bitwise OR (|).

Flag | Value | Result | Allows
PERMISSION_DEBIT | 2 | Requests permission to take money from agent's account. | use of llGiveMoney
PERMISSION_TAKE_CONTROLS | 4 | Request permission to take agent's controls. | use of llTakeControls and llReleaseControls
PERMISSION_REMAP_CONTROLS | 8 | ? | ?
PERMISSION_TRIGGER_ANIMATION | 16 | Requests permission to trigger animation on agent. | use of llStartAnimation and llStopAnimation
PERMISSION_ATTACH | 32 | Requests permission to attach/detach from agent. | use of llAttachToAvatar and llDetachFromAvatar
PERMISSION_RELEASE_OWNERSHIP | 64 | ? | ?
PERMISSION_CHANGE_LINKS | 128 | Requests permission to change links. | use of llCreateLink, llBreakLink, and llBreakAllLinks
PERMISSION_CHANGE_JOINTS | 256 | ? | ?
PERMISSION_CHANGE_PERMISSIONS | 512 | ? | ?
PERMISSION_TRACK_CAMERA | 1024 | Permission to track the agent's camera position and rotation | Use of llGetCameraPos and llGetCameraRot
PERMISSION_CONTROL_CAMERA | 2048 | Permission to control the agent's camera position and rotation | Use of llSetCameraParams and llClearCameraParams

Requests for the PERMISSION_DEBIT and PERMISSION_CHANGE_LINKS constants can only go to the object's owner. Requests for all other permissions can go to any agent.

Invokes the run_time_permissions event when agent accepts or rejects the requested permissions.

Be aware that the effects of this call are not additive. This means that all desired permissions must be requested for any call because the new permissions (or lack thereof) will completely replace the old permissions. Requesting permission FALSE (or 0) will result in all permissions being released and will not raise the run_time_permissions event handler.

Further, multiple agents may not grant permissions to the same script at the same time. For an object to have permissions for multiple agents simultaneously, one script is needed for each agent. They may still be within the same prim, however.

As of 1.10.5.1, PERMISSION_CONTROL_CAMERA will not be granted in combination with either of PERMISSION_CHANGE_LINKS or PERMISSION_DEBIT even to scripts in attached or sat-upon objects, and the call will fail with a misleading error message.
Q: Is the fact that you can't combine PERMISSION_CONTROL_CAMERA and PERMISSION_CHANGE_LINKS together a bug or does it have a purpose?

Q: According to the official Linden scripting docs, llRequestPermissions should return an integer. What is returned? Is it the permissions granted, all of the now-available permissions, etc.? Is this integer passed to run_time_permissions?
A: No, this is an error in the official guide. It doesn't return an integer. Trying to capture it results in a compile error. My guess is that the help text is right and this function doesn't return anything.

Q: Looks like there is a max range for the perm request (looks like it always fails from sim to sim), what is it?
A: When requesting all permissions there isn't one (you don't even have to be in the sim but this might be limited to the owner).
A: Permissions for owner may be anywhere, permissions for non-owner agents are limited to the simulator, this is an anti-spam provision. - Ice

Q: If this call fails, because the avatar is offline, or otherwise unable to grant permissions, does it hang, or does it trigger run_time_permissions with the permissions shown as ungranted?
A: run_time_permissions is not triggered.

Q: When requesting permissions from an attachment, permissions are automatically granted if the script requesting them is in the root prim. However, if the script is in a child prim, the user will always see a permissions dialog. Is this supposed to happen or a bug?
A: This is presumably a bug. If you need a script in a child prim, and want the object to be quiet, place a corresponding script in your root prim to request permissions, then use link messages to communicate between the two. Alternately this is possibly intentional to prevent placing rouge scripts which take some sort of control in child prims where they may be hard to locate.

Compare with llGetPermissions and llGetPermissionsKey.

See control for an example.

See FutureRevisions to see the permission flags of the future.

Functions | Agent/Avatar


llRequestSimulatorData

key llRequestSimulatorData(string simulator, integer data)

Requests data about the sim with the name simulator. simulator is case-insensitive. Returns a dataserver query ID and triggers the dataserver event when data is found. The sim name is usually not case-sensitive, but sometimes will return data for a similarly-named sim if the supplied case doesn't match the intended sim's actual name.

For example, specifying "Heaton" for simulator will result in information about the sim "Heaven", while specifying "heaven" will return information on the sim "Heaton". There is no official word on why this happens, but one assumes that when the dataserver finds that no sim matches the case as specified, it makes a frequently bad guess.

This function delays the script for 1 second.

Data | Value | Description
DATA_SIM_POS | 5 | returns a vector (cast to a string) specifying the simulator's global position.
DATA_SIM_STATUS | 6 | returns a string (see table below)
DATA_SIM_RATING | 7 | returns a string, either "PG", "MATURE" or "UNKNOWN"

Data returned from DATA_SIM_STATUS request | Description
"up" | simulator currently up and running
"down" | simulator currently down
"starting" | simulator currently starting
"stopping" | simulator currently stopping
"crashed" | simulator has crashed
"unknown" | simulator status unknown or unknown simulator name

DATA_SIM_STATUS returns the string "unknown" when querying for a nonexistant sim while DATA_SIM_POS and DATA_SIM_RATING silently fail.

Remember, calling llRequstSimulatorData to determine the sim's corner is only necessary if data is needed for a sim other than the one in which the script is currently running. If it is desired to find the global position of the current sim, llGetRegionCorner would be a wiser and more efficient choice.

Example:
key ahernStatusQuery;
key ahernPosQuery;
default
{
    touch_start(integer total_number)
    {
        ahernStatusQuery = llRequestSimulatorData("ahern", DATA_SIM_STATUS);
        ahernPosQuery = llRequestSimulatorData("ahern", DATA_SIM_POS);
    }
    dataserver(key queryId, string data)
    {
        if (queryId == ahernStatusQuery) {
            ahernStatusQuery = "";
            llSay(0, "Status of Ahern: " + data);
        } else if (queryId == ahernPosQuery) {
            ahernPosQuery = "";
            llSay(0, "Ahern's position: " + data);
        }
    }
}

Partial list of simulators: SimulatorNames.

Q: How do I request data for the current simulator?
A: To find out the name of the sim your script is in, use llGetRegionName. If you're just planning on finding the sim corner position for the current sim, a faster and better method is to use llGetRegionCorner. You can avoid bothering with the dataserver altogether then.

Q: The values for DATA_SIM_POS and DATA_SIM_STATUS are 5 and 6, respectively. What happens if I pass 1, 2, 3, or 4 instead?
A: Nothing. Those values may or may not do something in future, at which time they would have associated constants, but they don't do anything now. One likely possibility is that all the llRequest* functions filter into the same backend function, so 1, 2, 3, and 4 would refer to the llRequestAgentData flags DATA_ONLINE, DATA_NAME, DATA_BORN, DATA_RATING respectively.

Functions | Simulator | dataserver


llResetOtherScript

llResetOtherScript(string name)

Resets script named name in the same prim as the script calling this function. This allows one script to reset another.

This currently does not reset a script that stopped with a run-time error (like a stack-heap collision or a bounds check error), so it's of limited use. This also includes scripts being suspended by a function (like llEmail).
Compare with llResetScript.

See llGetScriptState / llRemoteLoadScriptPin for other fun things you can do with this.

Functions | Inventory


llResetScript()

All variables are reset to their default values. All pending events are cleared. The default state becomes active and its state_entry is triggered. This is the same function as clicking "reset" in the script editor.

It can be useful to include llResetScript in the on_rez event if the script uses llGetOwner and will be sold or distributed. This way, any listens set to the owner will reset when the object is rezzed by its new owner.

Compare with llResetOtherScript.

Q: My script isn't resetting when I rez it. What's going on?
A: Are you sure that you have llResetScript() in your on_rez event?

Q: How can I retain all the values in my variables and update llGetOwner while avoiding llResetScript?
A: Create a new function that removes and sets the llListen with llGetOwner. reference this function in state_entry and on_rez. This assigns a new owner to the listener but does not remove any data. Unlike llResetScript which resets the data.

Functions | Script | Reset


llResetTime()

Resets the script's time to zero. See also llGetTime and llGetAndResetTime.

This resets the script's time when touched:
default
{
    touch_start( integer total_number )
    {
        float elapsed = llGetTime();
        llWhisper( 0, "It has been "
                      + (string)elapsed
                      + " seconds since the time was last reset." );
        llResetTime(); // reset the time to zero
    }
}

Functions | Time | Reset


llRezAtRoot

llRezAtRoot(string inventory, vector pos, vector vel, rotation rot, integer param)

Rezzes object's inventory item named inventory at pos with velocity vel and rotation rot with start parameter param.

The last selected (root) object's location will be set to pos.

This function delays the script for .1 seconds.

Performance Advisory
Always use temp-on-rez when rezzing a large number of "disposable" objects such as bullets or other projectiles. Doing this allows them to automatically be cleaned up, should they break or encounter no-script land. This is both good scripting practice, as well as good for your sim and neighbours. It also keeps you and your customers happy by not getting complaints sent after them.

For more info, see llRezObject.

Functions | Inventory | object_rez


llRezObject

llRezObject(string inventory, vector pos, vector vel, rotation rot, integer param)

Creates ("rezzes") object's inventory object at position pos (in region coordinates) with velocity vel and rotation rot. The param value will be available to a script within the newly created object in the on_rez event or through the llGetStartParameter function. The vel parameter is ignored if the rezzed object is non-physical.

Caveats:
  • This function delays the script for a period of time proportional to the mass of the object and the rezzed object's velocity (and quite possibly something else as well)--this timer does not reset when the script resets. Experimentation by JeffreyGomez (detailed in this) thread resulted in the formula: rez delay = mass * magnitude(velocity) / 10
  • Objects can be rezzed up to a radius of 10 meters away from the rezzing object. Trying to rez something too far away will silently fail.
  • Copying a to-be-rezzed object from an user's inventory to a rezed object's inventory changes the default permissions on the to-be-rezzed object inside the rezed object's inventory. So, the copied object inside the rezzing object's inventory needs to be right-clicked and have copy permissions set.
    • This is very annoying and easy to miss, because it only manifests when you give the object with the script to a new owner -- your own testing will work, but the rezed objects will disappear from the scripted object's inventory the first time they are rezed by a new owner!
  • The maximum velocity magnitude seems to be 250.

If inventory is not contained within the object, the script will say "Couldn't find object foo" where foo is the value passed to inventory.

If inventory exists, but is not an object, the client will see an error dialog in the lower-right of the screen saying "Unable to create requested object. Object is missing from database."

Performance Advisory
Always use temp-on-rez when rezzing a large number of "disposable" objects such as bullets or other projectiles. Doing this allows them to automatically be cleaned up, should they break or encounter no-script land. This is both good scripting practice, as well as good for your sim and neighbours. It also keeps you and your customers happy by not getting complaints sent after them.

llRezObject triggers the object_rez event, passing the new object's key to any script in the rezzing object. The newly rezzed object will receive a on_rez event. This is useful for replicating objects.

Use llGetPos() + offset to specify a position relative to where the scripted object is.

When rezzing a linked set, pos specifies the geometric center, which can be determined with llGetGeometricCenter (when called from the object that is to be rezzed, so store this information somewhere). For some scripts llRezAtRoot may be desired instead because of this.

Rezzing an unlinked set (multiple objects/prims that were taken as one inventory item, but aren't linked together) is possible, but a rot other than ZERO_ROTATION will give unexpected results (the individual objects will be rotated but their positions won't match the rotation).

Example:
// rez something 2m above us
llRezObject("something", llGetPos() + <0, 0, 2>, ZERO_VECTOR, ZERO_ROTATION, 42);

If you attempt to rez a large number of objects within a short period of time (such as using multiple scripts to increase rez time) or you rez a self-replicating object, you may run into the Grey Goo Fence, which attempts to slow grid attacks.

Recoil

Recoil is applied to the rezzing object/avatar when it rezzes a physical object with a velocity. Large objects rezzed with a large amount of velocity will push with a large amount of force onto the rezzer, while a small object with small velocity will give negligable force (for every action there is an equal but opposite reaction). The formula for finding the force is:

-(velocity of rezzed object * mass of rezzed object)

Use this in a llApplyImpulse statement to counteract the recoil.

Permissions

If the inventory object that is to be rezzed has copy permission, a copy of the object will be rezzed. However, if it doesn't have copy permission, the original will be rezzed and removed from the object inventory. Any subsequent calls to this function with the same inventory will then fail because the inventory object is gone. It's important to set copy permissions for the next owner if an object is to be sold (or given away) and is rezzed by llRezObject (unless only a single copy is rezzed).

If the inventory object that is to be rezzed does not have copy permission, and the object containing it is attached, the object will not rez, and result in the following error / warning : "Can't rez no copy objects from an attached object."

Note: the changed() event will not be triggered when a no-copy object is rezzed from inventory.

Q&A

Q: llRezObject doesn't rez the object or return an error. What's going on?
A: You've specified a pos that's outside the 10m range. This is the only situation where llRezObject will silently fail.

Q: Really? That's stupid. How do I figure out if my pos is within the 10m range then?
A: Use llVecDist beforehand.

Q: I give up, why "llRezObject"? Why not "llCreateObject" or "llSpawnObject"?
A: "Rez" comes from the 1980s movie Tron. Hey, nobody ever claimed the Lindens weren't geeks.

Q: It's not working. It says my object can't be found.
A: You have to make sure the object you're trying to rez is actually in your object's inventory and named the same both in the inventory and the script.

See also: llRezAtRoot and ExperimentllRezObject

Functions | Inventory | object_rez


llRot2Angle

float llRot2Angle(rotation rot);

Returns the angle, in radians, that rot would cause something to rotate.

Compare to llRot2Axis.

functions | rotation


llRot2Axis

vector llRot2Axis(rotation rot);

Returns the axis of rotation of rot.

Compare to llRot2Angle.

Functions | Rotation


llRot2Euler

vector llRot2Euler(rotation rot)

Returns the Euler representation of rot, in radians.

See rotations for more information.

Example:
rotation rot = <0.00000, 0.00000, 0.38268, 0.92388>;
vector euler = llRot2Euler(rot); //convert to Euler angle.

Compare with llEuler2Rot.

Rotation Math Library

Functions / Rotation


llRot2Fwd

vector llRot2Fwd(rotation rot);

Returns a unit vector (magnitude of 1) representing which direction is forward after rot is applied (the forward/x/red axis represented by rot.)

Imagine a cube with the zero rotation. The cube's x-axis (forward/red) is pointing along the world x-axis. After some arbitrary rotation, the cube's x-axis is pointing off in some arbitrary direction (which can be seen by turning on "local axis" in the edit window). llRot2Fwd(llGetRot()) will return this arbitrary direction (as a vector).

Compare with llRot2Left and llRot2Up.

Functions | Rotation


llRot2Left

vector llRot2Left(rotation rot);

Returns a unit vector (magnitude of 1) representing which direction is left (green/y-axis) after rot is applied (represented by rot)

Compare with llRot2Fwd and llRot2Up.

Functions | Rotation


llRot2Up

vector llRot2Up(rotation rot);

Returns a unit vector (magnitude of 1) representing which direction is up after rot is applied (the up/blue/z-axis represented by rot).

Compare with llRot2Fwd and llRot2Left.

Functions | Rotation


llRotateTexture

llRotateTexture(float angle, integer side)

Sets the rotation of the texture on side to angle, in radians. A side of ALL_SIDES rotates all sides in one call.

Note: This function delays the script for 0.2 seconds.

Compare to llGetTextureRot.

See also llSetPrimitiveParams.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture


llRotBetween

rotation llRotBetween(vector a, vector b);

Returns the rotation needed to rotate from a to b, with a center of rotation at <0,0,0>.

Note: Be sure to normalize a and b before using llRotBetween, with llVecNorm. This automatically prevents any quirkiness on the part of this function.

vector a = <0,0,1>;
vector b = <1,0,0>;
rotation between = llRotBetween(a, b);
//Ignoring float precision loss these statements below are true.
//a * between == b
//b / between == a

Q: Wuh?
A: Imagine a cube with an arrow sticking out of it. The base of the arrow is in the center of the cube, and we'll call that <0,0,0> (see next question). This arrow is vector a (a forward vector, if you will). After some arbitrary rotation, the base of the arrow is in the same place, but it's pointing off in some other direction, that's vector b. So, given vector a which is the way the cube is facing now, and vector b, the way you want to be facing, this function returns the rotation you need to get from here to there.

Q: What is a 'center of rotation'?
A: The center of rotation would be the center of the object that is rotating (the parent object of a link set). The math of this function assumes that the object's center is at <0,0,0>, so if you're getting your vectors from world-coordinates, make sure to subtract the task's position from them.

Functions / Rotation


llRotLookAt

llRotLookAt(rotation target, float strength, float damping);

Rotates the object that contains the script (aligns forward/x-axis to camera) to target over damp damping seconds.

This function works for both physical and non-physical objects. For non-physical objects, good strength values are around half the mass of the object and good damping values are less than 1/10th of the strength. For physical objects, this function behaves more like llMoveToTarget, where increasing the strength or tau seems to slow the rotation. Good values for physical objects range between .2 and 1 for both parameters. Asymmetrical shapes require smaller damping.

Q: Are those recommendations for non-physical objects valid? Or is it that any values in there work when it's essentially weightless?
A: Obviously any value word work for a non-physical object because there's no friction or gravity to worry about. It's whether or not it looks good that you have to think about when choosing the values.

Q: Does llSetLocalRot work on attachments?
A: As of 1.8, No.

Calling llStopLookAt stops the effects of this function.
Failure to stop may render a prim unlinkable until llStopLookAt is called (even if the script is removed).

Note: within the LSL editor, the tooltip for llRotLookAt reads "llRotLookAt(rotation target, F32 strength, F32 damping)". F32 refers to a 32-bit float. In LSL, all floats are 32-bit.

Compare with llLookAt.

Functions | Dynamics | Rotation


llRotTarget

integer llRotTarget(rotation rot, float error)

Sets object rotation within error (in radians) of rotation as a rotational target and returns an integer handle for the target, which can then be used in llRotTargetRemove, and is supplied to the at_rot_target event.

Whenever the object rotation comes within error range of the rotation rot, the at_rot_target event is raised; when it doesn't, not_at_rot_target is raised instead.

Functions | Rotation | Target


llRotTargetRemove

llRotTargetRemove(integer number)

Removes a rotational target with handle number (that was previously set using llRotTarget).

functions | rotation | target


llRound

integer llRound(float value)

Returns value rounded to the nearest integer.

If value's decimal part is >= 0.5, then value is rounded to the next integer. If not, it is rounded down to the previous integer.

More precisely, if the absolute value of value's decimal is < 0.5, then value is rounded to the next integer closer to 0, otherwise it is rounded to the next integer further from 0.

Value | Returns
-7.85 | -8
1.29 | 1
584.32 | 584
685.96 | 686
9803.40928 | 9803

To always round up to the nearest integer, use llCeil. To always round down, use llFloor. To round the number towards zero typecast directly to integer.

//To round away from zero.
RoundOut(float in)
{
    integer out = (integer)in;
    return out + (out != in);
}
//This can be inlined if 'out' has already been declared
//out = (out != in) + (out = (integer)in);
//
//If 'out' hasn't been declared you can still inline, though it's less efficiant
//integer out = (integer)in;
//out += (out != in);

The usage of these functions can be tricky. For examples of potential issues see llFrand.

Functions | Math


llSameGroup

integer llSameGroup(key id);

Returns TRUE if the agent/object associated with id has the same active group (and is currently in the same simulator) as the object the script is in. If not, this function returns FALSE.

Note: "group" does not refer to links but, rather, collaborative groups of users.

Passing NULL_KEY as id will return TRUE if the object's active group is set to "none", FALSE otherwise.

llSameGroup will return TRUE if passed the key of the group itself. This allows a script to determine if it's in an object over group-owned land, or if an object is group-deeded.

Functions | Agent/Avatar | Group


llSay

llSay(integer channel, string text)

Says text on the specified chat channel. The range is a 20m radius sphere.

Note: A long string for text will be truncated to 1023 bytes.

This function does not incur a delay in the script.

Example:
llSay(0, "Hello Avatar!");

Chat Output:
Hello Avatar!

Like avatars, objects can use "/me" on channel 0. Thus, if an object named "Your dog" had a script with the line:
llSay(0, "/me wants steak.");

The chat output would be:
Your dog wants steak.

Scripts with listens will get the text exactly as it was typed in. In the above example, a script listening on channel 0 would get the message "/me wants steak".

Scripts can chat directly to the debug output window by using the DEBUG_CHANNEL constant:
llSay(DEBUG_CHANNEL, "Hello Avatar");

Constants

DEBUG_CHANNEL | 2147483647
PUBLIC_CHANNEL | 0

If using this function to only communicate information back to the script owner, please consider using llOwnerSay instead to help reduce chat spam.

Q: Why doesn't my object pick up its own spoken chat text in its listen() event?
A: Objects do not 'hear' themselves talk. This helps prevent infinite loops which would probably happen often if objects could hear themselves. For example, if your script returned a message saying 'Unknown command', you could go into an infinite loop easily if you didn't filter out 'Unknown command'. This applies to all of the local chat functions: llWhisper, llSay, and llShout.
A: On the other hand, if you WANT your object to hear itself talk (perhaps as some odd form of procedure call), use | LinkMessage. If your message has to be heard by others too, just throw an llSay in the link_message event.

Q: How come when sometimes i do a repeated llSay my message doesnt get through and the object doesnt output anything?
A: There seems to be a cap on how many messages you can say before the simulator blocks your llSay calls, after approx. 4000 repeated says, llSay silently fails. and any use of llSay there on after in that simulator will be blocked with no notification or warning. i will post more definate data as i test this more extensively. -Mainframe1
A: You may also be flooding the event queue

Compare with llWhisper, llShout, llOwnerSay, llRegionSay, llInstantMessage and llMessageLinked

Functions | Chat | Communications


llScaleTexture

llScaleTexture(float horizontal, float vertical, integer side);

Sets the horizontal and vertical repeats per face on side. If side == ALL_SIDES, all sides are set in one call. Negative values for horizontal and vertical will flip the texture.

Note that these parameters correspond to the "Repeats per Face Horizontal (U)" and the "Vertical (V)" boxes on the Texture tab of the in-world editing tool. There is another box on that panel, "Repeats per Meter". That per meter box calculates values for the other two boxes according to the size of the prim. To take an example, if you had an image that you wanted to tile equally onto four cube faces, you would set the horizontal and vertical parameters of llScaleTexture() to 0.5 regardless of the size of the faces.

Note: This function delays the script for 0.2 seconds.

Compare to llGetTextureScale.

See also llSetPrimitiveParams.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture | Scale


llScriptDanger

integer llScriptDanger(vector pos)

llScriptDanger is an outdated function. llGetParcelFlags should be used in its place
Returns TRUE if pos is over public land, land that doesn't allow everyone to edit and build, or land that doesn't allow outside scripts. This function allows a script to find out if it's in danger of getting deleted or disabled.

Note: this will still return TRUE if the containing object is located more than 45 meters above no-script ground. The no-script "field" extends only 45m in the air, but only stops scripts from executing if they're not already running when they enter the zone (parcel/region/simulator). (Say, if they're in attachments and the user teleports in, or if the user tries to start a new script.) Scripts that are already running when they enter the zone will still work.

To find out if the land is public, use llGetLandOwnerAt.

llScriptDanger utility function:
integer LAND_UNEDITABLE = 1;
integer LAND_PUBLIC     = 2;
integer LAND_NO_SCRIPT  = 4;
// Returns as much data as possible about
// the status of the land at the position
// because llScriptDanger is so limited,
// the integer it returns is a "best guess"
// to the reason for the danger.
integer landStatus(vector pos) {
    if (llGetLandOwnerAt(pos) == NULL_KEY) {
        return LAND_PUBLIC;
    }

    integer scriptDanger = llScriptDanger(pos);
    if (!scriptDanger)
        return 0;

    integer status = LAND_UNEDITABLE;
    // Check if the position is below the no-script height limit.
    // (Note: no-script land will be set as no-script anywhere over it,
    // but only scripts that attempt to START running under
    // 45m above the ground height will be disabled.
    // Scripts already running when they enter the parcel,
    // or those that start OVER 45m above the parcel will
    // all run within the no-script zone.)
    if (llGround(pos - llGetPos()) <= 45) {
        status = status | LAND_NO_SCRIPT;
    }
    return status;
}

Q: Is there a function to find out if the land has scripts off, or is no-build? Anything other than all-in-one?
A: No, there's only llScriptDanger, which returns all or nothing. You could check the results of llGetLandOwnerAt to see if the land is public, but there's no way for a script to access any data beyond that.
After the 1.9 update, we'll be able to use llGetParcelFlags to get all this info, though that will not work in the main grid yet.

Q: In versions past, certain script functions wouldn't run in no-script zones, even if the scripts were otherwise already running. Is this still the case, and if so, is there a list of functions that currently do not run?
A: llParticleSystem and llSetBuoyancy continue running (but can't be re-executed) in no-script areas. There are probably other functions but I haven't tested them. -EepQuirk

Functions | Land | Simulator | Script


llSendRemoteData

key llSendRemoteData(key channel, string dest, integer idata, string sdata)

Not working yet -- see XMLRPCDiscussion

Send an XML-RPC request to dest (probably an URL) through channel with payload of channel (in a string), integer idata and string sdata.

A message identifier key is returned. A XML-RPC reply to this request will trigger a remote_data event and reference this message id.

Note: This function delays the script for 3.0 seconds.

Functions / XML-RPC


llSensor

llSensor(string name, key id, integer type, float range, float arc)

Performs a single sensor scan for name and id with type (AGENT, ACTIVE, PASSIVE, and/or SCRIPTED) within range meters and arc radians of forward vector (along x-axis). PI radians will search in all directions.

name and id need no filter. If name is a null string (i.e. ""), llSensor() will look for objects with any name. If id is the NULL_KEY, then llSensor() will look for objects with any id. The limit of range is 96m. Values greater than 96m will be accepted but will be treated as 96m. Note that a range of 0.0 does perform a scan.

Compare with llSensorRepeat, which allows a recurring sensor scan.

Raises the sensor event when it finds a match, and no_sensor when it finds none; see sensor for an example.

Constants for type:
Constant | Value | Searches for
AGENT | 1 | agents (users)
ACTIVE | 2 | physical objects that are moving or objects containing an active[1] script
PASSIVE | 4 | non-scripted or script is inactive[2] AND non-physical or, if physical, not moving
SCRIPTED | 8 | objects containing an active[3] script
[1] Active: the script is running and currently doing something. A basic default script that is running may not be active but a script that has a command monitoring, like with llListen, will always be active. "Active" means more sim resources are being used.
[2] Inactive: the script is running and awaiting an event and isn't doing any monitoring (like llListen). Very little sim resources are being used.
[3] Running scripts may be active or inactive (or, to use SL lingo, "active" or "passive").

Notes

  • Values can be combined to search in multiple categories using bitwise OR (|). For example, llSensor("", NULL_KEY, AGENT | ACTIVE, 25, PI) would search for both agents and physical objects.
  • After detecting a PASSIVE, ACTIVE, and/or SCRIPTED object, llDetectedType may be used to check the ACTIVE flag (physical) and the PASSIVE flag (non-physical, even if the object contains an active script). The SCRIPTED flag always seems to only detect/apply to active scripts. So it seems llSensor and llDetectedType use the PASSIVE and ACTIVE flags differently. See also ObjectType for more info about types of objects to detect.
  • The sensors detect objects only if their center falls in the detection cone (see below). This makes it harder to use sensors for pathfinding, especially avoiding big objects like walls.
  • Sensors cannot be used to detect another object's child prims.
  • llSensor and llSensorRepeat will not detect the object that contains them. This also applies to attachments--they won't detect the agent they're attached to unless the sensor script is within a child prim.
  • The name parameter is case-sensitive, and will not accept partial names. For example, an object by the name Object will only match when the name parameter is Object, not object or Obj.
  • llSensor only detects objects within the same Sim. If you need to detect object's positioned in adjacent Sims, use llSensorRepeat.
  • The SCRIPTED flag combines strangely with the others, see below.

Combining SCRIPTED and Other Flags

Combining the SCRIPTED flag with others can lead to somewhat unintuitive results:

  • SCRIPTED will detect active scripts, or moving physical objects containing a script (active or not).
  • AGENT | SCRIPTED will detect active scripts, or moving physical objects containing a script (active or not); this will not detect agents!
  • ACTIVE | SCRIPTED will detect active scripts, or moving physical objects that contain a script (active or not); this will not detect moving physical objects that are not scripted.
  • PASSIVE | SCRIPTED will detect active scripts, non-scripted non-physical objects, and physical objects containing a script (active or not); it does not detect stationary physical objects that are not scripted.

Detection Cones (Arc)

If arc is x radians, the sensor will look for x radians around the object's forward vector (which is the object's local X axis, positive direction), so the actual sweep of the search is 2 * x radians, and thus PI radians will search all around the object.

In SL terms, the sensor sweep area is a dimpled sphere, with the range defined by the sphere's radius and the arc by the dimple itself.

Sensors placed in attachments will use the direction the avatar is facing as their forward vector. In mouselook, this means that it will be wherever the avatar is looking, while out of mouselook, this means whichever way the avatar is pointing. This does not include where the avatar's head is pointing, or what animation the avatar is doing, just the direction the avatar would move in if you walked forward. This is the case, regardless of where the object is attached.

Sensor Visualisation

This one has arc = PI / 4 and thus scans a radius of 45 degree around the x-axis:

video (MPEG, 630KB) of it rotating

For arc = PI_BY_TWO (90 degrees around the x-axis, or a half sphere--hemisphere):

And for arc = PI (180 degrees around the x-axis, which means a sphere):

Q: Can I use sensors to detect attachments?
A: No, but you can use llGetAgentInfo to determine whether or not an avatar has attachments, and whether or not they're scripted, though not how many attachments they have, where they're attached, nor what they are.
Q: How can I use a sphere prim to determine sensor radians?
A: Using just the Begin dimple, keeping the end 1.0, the math would be: radians = PI - (PI * dimple);
Functions | Sensors


llSensorRemove();

Removes the sensor set by llSensorRepeat.

Note that unlike a listener, there is no need to specify the sensor by key. This is because objects can only contain one sensor per prim.

Functions / Sensor Functions


llSensorRepeat

llSensorRepeat(string name, key id, integer type, float range, float arc, float rate)

Performs a repeating sensor scan for name and id with type (AGENT, ACTIVE, PASSIVE, and/or SCRIPTED) within range meters and arc radians of forward vector (name and/or id can be empty or 0). A range of 0.0m does perform a scan. The parameters have the same function as llSensor, except rate, which defines the number of seconds between repeated sensor or no_sensor event calls.

Raises the sensor and no_sensor events.

.      ^      .
\      |      /  If arc is x radians,
 \     |     /   the sensor will look for x radians
  \    |    /    around the object's forward vector,
   \   |   /     so the actual sweep of the search is
    \ x|x /      2x radians, and thus PI radians will
     \ | /       search all around the object
      \|/

Notes

  • This function will wait for rate seconds before doing the first sensor sweep.
  • The limit of range is 96m. Values greater than 96m will work, but will only be treated as 96m. (As of SL 1.6.12, there is a bug/feature where it can return agents/objects up to 300m+ away.)

llSensorRepeat constants:
Constant | Value | Senses
AGENT | 1 | agents
ACTIVE | 2 | objects that are physical and are moving and/or objects containing an active script
PASSIVE | 4 | objects that are non-scripted (or script inactive) AND not moving (whether physical or non-phsyical)
SCRIPTED | 8 | anything that contains an active or passive script

Table Notes

  • Active: the script is running and currently doing something. A basic default script that is running may not be active but a script that has a command involving monitoring (like llListen) will always be active. Active means sim resources are being used.
  • Inactive: the script is running, and awaiting an event, but isn't doing any monitoring (like llListen); no sim resources are being used.
  • Running scripts may be active or inactive (or, to use SL language, active or passive).

Note: After using llSensor/llSensorRepeat to find a passive, active, and/or scripted object, llDetectedType can be used to check the active flag (meaning physical regardless of motion or not) and the passive flag (meaning non-physical even if the script is active^).
So, it seems, llSensor/llSensorRepeat and llDetectedType use the passive and active flags differently.

Values can be combined to search in multiple categories using bitwise OR (|). For example, llSensor("", "", AGENT | ACTIVE, 25, PI) would search for both agents and physical objects. Because the scan arc is measured in all directions from the forward vector, specifying PI radians will search 360 degrees.
Q: ORing sensor types was broken for a long time, is it for sure fixed now?
A: It still seems to be broken as of 2004/08/23.
A: Still mostly broken as of 2005/10/16. AGENT|ACTIVE doesn't work, AGENT|PASSIVE doesn't work, AGENT|SCRIPTED doesn't work, but PASSIVE|ACTIVE does.
A: As of 2006/01/05, this problem seems to be resolved, and everything works as expected. However, the SCRIPTED flag has some peculiar behaviors when combined with others; see the bottom of the llSensor page.

A script can have only one sensor at a time. The current sensor is removed by another call to llSensorRepeat or llSensor (or llSensorRemove, obviously).

For single-use sensors, see llSensor. If you don't need the scan to be repeating ALL the time (and you usually don't), use llSensor. Use llSensorRemove to turn off.

Note: llSensor and llSensorRepeat will not detect the object that contains them. This also applies to attachments--they won't detect the agent they're attached to, unless the sensor script is within a child object.

Note: Using the no_sensor event without a sensor event will cause the repeating sensor to stop. Use a "dummy" sensor event with nothing inside the brackets if you just want to react to something not being found.

Detecting objects in adjacent Sims.
Objects/Agents in adjacent Sims will only be detected approximately once every 5 seconds. Between times, no_sensor will be triggered instead (assuming there is nothing in the sensor's sim that is detectable.
  • A sensor rate slower than 5 seconds will only sporadically detect objects in adjacent sims.
  • Sensor rates of 0.25 seconds detect objects approximately one time in twenty.
  • Sensor rates of one second detect objects every fourth to sixth time.
  • llSensor never detects objects in adjacent sims.

Also see llVolumeDetect. Depending on what you're trying to do, it might be a vastly more efficient way of going about it.

Functions | Sensors


llSetAlpha

llSetAlpha(float alpha, integer face)

Sets the alpha ("opacity" or "transparency") value for face. If face is ALL_SIDES, sets the alpha on all sides of the prim (not the object; see llSetLinkAlpha). The alpha value is interpreted as an opacity percentage: 1.0 is fully opaque, and 0.0 is completely transparent ("invisible").

SL no longer clamps values smaller than 0.1 to 0.1 so object faces can be fully transparent (0.0) using LSL, but the built-in object editor still restricts the lower limit to 0.1. It's been theorized that this is either because the Lindens just forgot to change the other, or (more likely) that they want to discourage people who don't know what they're doing from rezzing invisible objects everywhere. Knowing what a script is (or better still, how to make it yourself) is a reasonable prerequisite to having fully invisible prims--not that that stops griefers, but it can be thought of as a way to prevent newbies from making too many mistakes.

When setting alpha to 0.0 (or just 0), be aware that the camera will not be focusable on the fully transparent object because of changes made to the camera behavior a while back, allowing users to zoom through a masked alpha texture (partly as a way to avoid a form of griefing store owners). To zoom in on a completely transparent object, (or to just edit the object, edit a visible object, then click on the invisible one--or hold alt to highlight transparent objects) a workaround to this is to set the alpha to .002 (or whatever, but that seems to be the lowest SL allows while still allowing zooming in on the object).

Invisible objects created using llSetAlpha can still be selected with the right mouse button, while an object with a 100% alpha texture can not be unless the user has transparent objects highlighted. Each method has its own advantages.

Example:
llSetAlpha(0.5, 0); // set the top of a cube 50% visible.
llSetAlpha(1.0, ALL_SIDES); // set entire prim 100% visible.
llSetAlpha(0.0, ALL_SIDES); // set entire prim 100% invisible.

Functions
llGetAlpha: get object's alpha settings
llSetLinkAlpha: set linked object alpha
llSetPrimitiveParams

Q & A
Q: How do I determine which side of an object this applies to?
A: See side.

Q: If I use this on an attachment, will it make my avatar invisible?
A: No. You can't make your avatar invisible with this method but there is a specific texture that can at a specific UV offset.

Functions | Texture | Alpha


llSetBuoyancy

llSetBuoyancy(float buoyancy)

Sets a physical object's buoyancy (which seems to be an additional force, not unlike real-life buoyancy).

The default buoyancy for an object is 0. Setting this to exactly 1 will cause the object to float as if no gravity exists. Values greater than 1 will cause the object to float up, while values between 0 and 1 mean a gentler than regular fall, the closer to 0 the closer to normal behavior. Setting a negative value will simulate a downward force, which will also cause the affected prim/object to vibrate a lot more on the ground while the physics engine tries to "settle" it.

Behavior not unlike llSetForce, as well as the constant use of energy by this function, would suggest that buoyancy internally works by applying extra (mass-proportional) force, either up or down, while gravity still works (hence the energy use while floating).

Notes:
  • Heavy objects cause a lot of energy use: a physical object over about 90kg will not be able to hover due to energy expenditure, depending a little on how much motion other energy loss) it's experiencing. If the object is too heavy, see if there are any prims that can be hollowed.
  • There seems to be no way to get the buoyancy value (llGetBuoyancy). (Likely related to the physics engine interaction; apparently vehicle buoyancy acts separately from regular buoyancy.)
  • Buoyancy does not take into account water level. A buoyancy of 1.5 will cause the object to float up the same rate whether it is under or above water. (Water only has limited meaning in SL and seems to exist mostly as a height and a 'what to hover over' option in scripts. The name "buoyancy" may be a little misleading since in real life gravity tends to significantly affect objects, while bouyancy only gets into things when a solid is in a liquid, gas in a gas, etc. This function allows buoyancy to be changed with solids in anything (air, since water doesn't really exist) which is a bit odd.
  • Real-world bouyancy affects any matter that is displacing other matter in a gravitational field. It's just almost impossible to notice except when the densities or the two substances are close. The "correct" way for buoyancy to behave would be to calculate an object's displacement based on volume and the density of the material being displaced (remembering to account for altitude). To get a more accurate buoyancy result, a lot more is necessary than a simple llSetBuoyancy call.

Functions | Dynamics


llSetCameraAtOffset

llSetCameraAtOffset(vector offset)

Sets the camera at offset used in this object if an avatar sits on it. Use ZERO_VECTOR to cancel.

The same functionality can be found in llSetCameraParams and more.

The offset argument defines the point the camera is facing (relative to the position of the object containing the function call). For example, if <2,0,0.5> is given, the camera will look at a point two meters in front of and half a meter above the object. You'll also have to define the camera position using llSetCameraEyeOffset.

offset must not be the same as the one specified in llSetCameraEyeOffset.

Remember, these offsets only take effect when an avatar sits on the object. You can't call these functions to change the camera parameters once the avatar is sitting. To do this, the avatar would have to stand up and sit down again.

Example: See llSetCameraEyeOffset.

Agent/Avatar | Camera | Functions


llSetCameraEyeOffset

llSetCameraEyeOffset(vector offset)

Sets the camera eye offset used in this object if an avatar sits on it. Use ZERO_VECTOR to cancel.

The same functionality can be found in llSetCameraParams and more.

This defines the position of the camera. Define the point (coordinate) the camera is looking at using llSetCameraAtOffset.

offset must not be the same as the one specified in llSetCameraAtOffset.

Remember, these offsets only take effect when an avatar sits on the object. These functions can't be called to change camera parameters once the avatar is sitting. To do this, the avatar would have to stand up and sit down again.

Example:
default {
    state_entry() {
        llSetCameraEyeOffset(<-2, 0, 1>); // the camera is 2m behind and 1m above the object
        llSetCameraAtOffset(<2, 0, 1>); // and looks at a point that is 2m in front and 1m above the object
        // sitting on this object will now place your camera
        // so it's looking straight ahead on a line parallel
        // to the objects x (forward) axis, 1m above the objects center.
    }
}

Functions | Agent/Avatar | Camera


llSetCameraParams

llSetCameraParams(list rules)

Sets multiple camera parameters at once.
List format is [rule1, value1, rule2, value2 . . . ruleN, valueN]

Requires PERMISSION_CONTROL_CAMERA. The agent/avatar camera function targets the agent that gave PERMISSION_CONTROL_CAMERA to the script. (The UUID of which is returned by llGetPermissionsKey.)

For more information and example script see follow cam.

Rule | Value | Type | Default | Value Range | Description
CAMERA_ACTIVE | 12 | integer isActive | FALSE | TRUE or FALSE | Turns on or off scripted control of the camera.
CAMERA_BEHINDNESS_ANGLE | 8 | float degrees | 10.0 | 0 to 180 | Sets the angle in degrees within which the camera is not constrained by changes in target rotation.
CAMERA_BEHINDNESS_LAG | 9 | float seconds | 0.0 | 0 to 3 | Sets how strongly the camera is forced to stay behind the target if outside of behindness angle.
CAMERA_DISTANCE | 7 | float meters | 3.0 | 0.5 to 10 | Sets how far away the camera wants to be from its target.
CAMERA_FOCUS | 17 | vector position | n/a | n/a | Sets camera focus (target position) in region coordinates.
CAMERA_FOCUS_LAG | 6 | float seconds | 0.1 | 0 to 3 | How much the camera lags as it tries to aim towards the target.
CAMERA_FOCUS_LOCKED | 22 | integer isLocked | FALSE | TRUE or FALSE | Locks the camera focus so it will not move.
CAMERA_FOCUS_OFFSET | 1 | vector meters | <0.0, 0.0, 0.0> | <-10,-10,-10> to <10,10,10> | Adjusts the camera focus position relative to the target.
CAMERA_FOCUS_THRESHOLD | 11 | float meters | 1.0 | 0 to 4 | Sets the radius of a sphere around the camera's target position within which its focus is not affected by target motion.
CAMERA_PITCH | 0 | float degrees | 0.0 | -45 to 80 | Adjusts the angular amount that the camera aims straight ahead vs. straight down, maintaining the same distance; analogous to 'incidence'.
CAMERA_POSITION | 13 | vector position | n/a | n/a | Sets camera position in region coordinates.
CAMERA_POSITION_LAG | 5 | float seconds | 0.1 | 0 to 3 | How much the camera lags as it tries to move towards its 'ideal' position.
CAMERA_POSITION_LOCKED | 21 | integer isLocked | FALSE | TRUE or FALSE | Locks the camera position so it will not move.
CAMERA_POSITION_THRESHOLD | 10 | float meters | 1.0 | 0 to 4 | Sets the radius of a sphere around the camera's ideal position within which it is not affected by target motion.
Example using default values:

llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 10.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.0, // (0 to 3) seconds
        CAMERA_DISTANCE, 3.0, // ( 0.5 to 10) meters
        // CAMERA_FOCUS, <0,0,0>, // region-relative position
        CAMERA_FOCUS_LAG, 0.1 , // (0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 1.0, // (0 to 4) meters
        CAMERA_PITCH, 0.0, // (-45 to 80) degrees
        // CAMERA_POSITION, <0,0,0>, // region-relative position
        CAMERA_POSITION_LAG, 0.1, // (0 to 3) seconds
        CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 1.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, ZERO_VECTOR // <-10,-10,-10> to <10,10,10> meters
    ]);

Q: How can you make the camera move smoothly from one CAMERA_POSITION to another? Or pan smoothly from one CAMERA_FOCUS to another?
A: Hmm thats a tough one. What you could do, is have a volume-detect prim as a vehical, then have the avatar sit on the vehical, next drop the camera at the static location. Make the box invisible and have the avatar underground (flip the box upside down). That may allow you to move the box, which should change the focus. If you made the camera follow instead, you could use that to change it's position. - BW

Q: It seems like the camera only moves into its position when the user touches a the Left or Right rotate key. Does this make any sense?
A: Sounds like an issue with one of the Threshold parameters - Anon

Functions | Agent/Avatar | Camera


llSetColor

llSetColor(vector color, integer face)

Sets the color of face. If face is ALL_SIDES, sets the color on all faces.

The range of color is <0.0, 0.0, 0.0> through <1.0, 1.0, 1.0>. Vector components greater than 1.0 are interpreted as 1.0.

Vector components correspond with hex values of #000000 through #FFFFFF or RGB values of 0 through 255.

Comparison Table:
Color | Vector | RGB | Hex
Red | <1.0, 0.0, 0.0> | 255,0,0 | #FF0000
Green | <0.0, 1.0, 0.0> | 0,255,0 | #00FF00
Blue | <0.0, 0.0, 1.0> | 0,0,255 | #0000FF

It can be read back using llGetColor.

To convert a vector from RGB to vector form, divide each component by 255.0.

Example:
vector color = <R, G, B> / 255.0;

Compare with llSetTexture and llSetLinkColor.

See also llSetPrimitiveParams.

Example: See timer.

Q: How do I determine which side of an object this applies to?
A: See side.

Functions | Texture | Color


llSetDamage

llSetDamage(float damage)

Sets the amount of damage that will be done to an avatar that the scripted object collides with. The scripted object will die (be deleted) immediately on colliding and no event handlers will be executed first.

damage is a percentage (0 - 100), with 100% meaning instant death on colliding with the object. Lower values will damage the avatar and kill it if its life energy (indicated in the middle of the menu bar on the Second Life client) is lower or equal to the damage.

This function only has an effect on damage-enabled land. Anywhere else it does nothing and the object will not die on collisions. If you need it to die, it's recommended you use llDie in the collision event.

At least as of SL 1.5, negative values do not heal the victim. Values outside the 0-100 range are treated as 100% (i.e. instant kill).

Functions | damage


llSetForce

llSetForce(vector force, integer local);

Sets the force to be continuously applied on the (attached) object. When local is TRUE then force is relative to the object's own (potentially rotated) axes; when FALSE it is relative to the simulator's axes.

The object must be physical for this to take effect. See physics for information on how forces affect physical objects.

If you want only an impulse, use llApplyImpulse instead.

You can also set force and torque simultaneously by using llSetForceAndTorque.

Do not use with vehicles.

Example:

To levitate an object, apply an upwards force proportional to its mass. Rez a prim, enable physics, and execute the following in a script:
float mass = llGetMass(); // mass of this object
float gravity = 9.8; // gravity constant
llSetForce(mass * <0,0,gravity>, FALSE); // in global orientation

The prim will now appear weightless and still possess inertia.
Note: With large/very massive objects, the object may not have enough energy to sustain the continuous force and will still fall to the ground.

Functions | Dynamics


llSetForceAndTorque

llSetForceAndTorque(vector force, vector torque, integer local);

If the object is physical, this function sets the force and torque. The vectors are relative to the object's own (potentially rotated) axis if local is TRUE, or relative to the simulator's axis if FALSE.

See llSetForce and llSetTorque for more details.

Do not use with vehicles.

Functions | Dynamics | Force | Torque


llSetHoverHeight

llSetHoverHeight(float height, integer water, float tau);

Damps a physical object to height over tau seconds. The height is above ground if water is FALSE, otherwise above ground and water; has no effect on a non-physical object. Setting height to zero disables the effects of this function.

Do not use with vehicles.
Q: Could you please explain? It's used in some Linden vehicles and is included in their original vehicle tutorial.
A: It is? I thought llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, float) "deprecated" llSetHoverHeight for vehicles... where is this tutorial located? -Chris

Q: Does this have the same distance constraints as llMoveToTarget, which also critically damps an object to a location? --JarhynWilde
A: I beleive the max height on this is either 64m or 100m -- BW
A2: In tests it looked like there is a maximum difference (apparently 64m) with the current height the function will react to. (Additionally: if you give it a larger difference, a possible current damping movement stops immediately). --IoneLameth

To actually find out how high the ground is at a given location, use llGround.

Compare with llGroundRepel

Functions


llSetInventoryPermMask

Not implemented as of 1.6.1. May show up in a future version.

llSetInventoryPermMask()

Sets the given permission mask to the new value on the inventory item.

Base permissions are the permissions the object has when it was given to the current owner. In theory, no other mask can have more permissions then the base. If they do then this is a bug in permissions and should be reported.

Mask | Value | Description
MASK_BASE | 0 | Changes the object's base permissions
MASK_OWNER | 1 | Changes the permissions the current owner has.
MASK_GROUP | 2 | Changes the permissions the object's active group has.
MASK_EVERYONE | 3 | Changes the permissions everyone has on the object.
MASK_NEXT | 4 | Changes the permissions the next owner has.
These are not bit flags. They can not be OR'ed

Permissions | Value (in Hex notation) | Description
PERM_ALL | 0x7FFFFFFF | Set Move/Modify/Copy/Transfer permissions
PERM_COPY | 0x00008000 | Set Copy permission
PERM_MODIFY | 0x00004000 | Set Modify permission
PERM_MOVE | 0x00080000 | Set Move permission
PERM_TRANSFER | 0x00002000 | Set Transfer permission
Do note that ((PERM_COPY | PERM_MODIFY | PERM_MOVE | PERM_TRANSFER) is not the same as PERM_ALL). This is because there may (read as: will) be more permissions in the future.

To retrieve the inventory item's permission for the mask, see llGetInventoryPermMask.
To retrieve the object's permission mask, see llGetObjectPermMask.
Compare with llSetObjectPermMask. (not implimented)

Functions | Permissions | AssetPermissions | Future Revisions


llSetLinkAlpha

llSetLinkAlpha(integer linknumber, float alpha, integer face)

Sets the side number face on the linked child linknumber to the transparency alpha.

If face == ALL_SIDES, this function sets the alpha on all faces of the specified prim(s). If linknumber == LINK_SET, this function sets alpha on all prims in the link set (including the task). This allows setting an entire object in one call rather than being forced to use a for loop.

There is no "llGetLinkAlpha" function. For that, use a script with llGetAlpha in the specific prim, and communicate with your main script with Link Messages.

Q: How do I determine which side of an object this applies to?
A: See side.

To set the alpha of the current prim by itself, use llSetAlpha (though llSetLinkAlpha will still work).
To set the color of other prims in the object, use llSetLinkColor.
To set the texture of other prims in the object, use llSetLinkTexture.
To set any primitive parameters of other prims in the object, use llSetLinkPrimitiveParams.

Functions | Link | Alpha


llSetLinkColor

llSetLinkColor(integer linknumber, vector color, integer face)

Sets the color on one or more sides (faces) of a linked prim. color is an RGB value between <0.0, 0.0, 0.0> and <1.0, 1.0, 1.0>.

If face == ALL_SIDES, sets color on all faces of the specified prim(s). If linknumber == LINK_SET (other link constants), sets color on all prims in the link set. This allows setting an entire object in one call rather than being forced to use a for loop.

There is no "llGetLinkColor" function. For that, use llGetColor in the specific prim.

Q: How do I determine which side of an object this applies to?
A: See side.

Q: How can this apply to multiple prims with a single call? I want to do this to the function's prim AND 1 or more other prims--but not ALL child prims OR necessarily the root prim. The link constants don't allow multiple, specific link numbers.
A: It is impossible with the current LSL featureset, unless you put scripts in the linked primitives you want to change.

To set the color of the current prim by itself, use llSetColor (though llSetLinkColor will still work).
To set the alpha of other prims in the object, use llSetLinkAlpha.
To set the texture of other prims in the object, use llSetLinkTexture.

Functions | Link | Color


llSetLinkPrimitiveParams

llSetLinkPrimitiveParams(integer linknumber, list rules)

The ruleset is the same as used in llSetPrimitiveParams.

A simple script to light up a prim in a linkset when touched, and unlight the last one touced using llSetLinkPrimitiveParams. Assuming this script is in each prim in the linkset.

default
{
    touch_start(integer total_number)
    {
        llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
        llSetLinkPrimitiveParams(LINK_ALL_OTHERS, [PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
     }
}

Notes
  • This function delays the script for 0.2 seconds.

Also see:
llSetLinkAlpha
llSetLinkColor
llSetLinkTexture

Functions | Link | Object | Primitive


llSetLinkTexture

llSetLinkTexture(integer link_pos, string texture, integer face)

Sets the texture of linked prims. The ruleset is the same as used in llSetTexture.

Also see:
llSetLinkAlpha
llSetLinkColor
llSetLinkPrimitiveParams

Q: How do I simultaneously set the texture of prims 1 and 2, but not 3 and 4? llSetLinkTexture doesn't seem to be able to do that.
A: It's true; you can only specify the specific link number of a target prim, or set the texture of all prims in the linkset, all child prims, or all other prims. There's no way to either specify a range of link numbers, or to specify several link numbers.
A workaround for this is to place scripts in every prim in your object and to use link messages to communicate:
// Call this function instead of llSetLinkTexture in your main script.
setLinkTexture(integer linkNum, string tex_name_or_key, integer face) {
    llMessageLinked(linkNum, face, "setLinkTexture", tex_name_or_key);
}

// Put this script in every prim of the link set
default {
    link_message(integer sender, integer side, string msgname, key tex_name_or_key) {
        if (msgname == "setLinkTexture") {
            llSetTexture(tex_name_or_key, side);
        }
    }
}

A: But with this solution you still can't use several link numbers, only by iteration. And if we go there, we might as well call llSetLinkTexture more then once, for example with a loop, and avoid putting scripts in linked prims.
Or you can fill a list and iterate through it. In that case this code would go in your main script:
// Call this function instead of llSetLinkTexture in your main script.
setLinkTexture(list linkNums, string texture, integer face) {
    integer i;
    for (i = 0; i<llGetListLength(linkNums); i++)
        llSetLinkTexture(llList2Integer(linkNums, i), texture, face);
}
Then you would call the function like this:
list linkNums = [1, 3, 4];
setLinkTexture(linkNums, texture, face);

Functions | Link | Texture


llSetLocalRot

llSetLocalRot(rotation rot)

Sets the rotation of a child prim relative to the root prim.

Note: This function delays the script for 0.2 seconds.

Use llGetLocalRot to get the local rotation.

To set the rotation of a child prim around it's local axis, use llGetLocalRot and llSetLocalRot. This way, the entire object can be rotated any direction and the child prim will still rotate properly.

Compare with llSetRot, which sets the rotation of the entire linkset.

Q: Does llSetLocalRot work on attachments?
A: As of SL 1.7 this is possible.

Q: How do I perform this with llSetPrimitiveParams on a child of an attached prim?
A: You need to know the local rotation of the root prim of the the attachment and divide your target rotation by the root's local rotation.

Functions | Rotation | Child Rotation | Link


llSetObjectDesc

llSetObjectDesc(string name)

Sets the description of the scripted prim to name. You can get the description using llGetObjectDesc.

The description is limited to 127 characters.

Note: Prims in linked objects each have their own descriptions. Setting the description in the parent will not change that of any child prims, and vice-versa. There is no way to get or set the description of a different prim in the linkset; to do that, you need to use link messages.

It's possible to see the description of a child prim by using the "Edit linked parts" editing option, so don't use this to store sensitive data.

See also llSetObjectName.

Note: V1.12 Pipe symbols don't work in the description field.
The pipe symbol doesn't work in object names either, this is because the format that LL uses for serializing inventory for transmission to the user, uses pipe as a separator. This was removed back in 1.5 or 1.6. -- BW

Functions | Object | Prim


llSetObjectName

llSetObjectName(string name)

Sets the object/prim's name to name
Limited to 63 characters being displayed and edited in SL client but upto 255 can be saved (llSetObjectName)and retrieved (llGetObjectName and llKey2Name) via scripts. Using llSetObjectName on the "server object" and llKey2Name on the "client object" can allow object to object communications within the same sim up to 255 chars.

Use llGetObjectName to get the object's name.

This can be used in any prim in a linked object. This can be useful if you wish to identify which linked prim is communicating via llHTTPRequest, llInstantMessage, llEmail, llWhisper, llSay, llShout or llOwnerSay.

Note: It's possible to see the prim name in edit mode using the "Edit linked parts" option, so don't use this to store sensitive data.

Calling llSetObjectName in an object worn as a HUD will change its name, but only while it is worn. In inventory, the name change will not be reflected. Taking it off and re-wearing the HUD is like renaming it back to what it was before your script renamed it.

You can also access or change a prim's description field with llGetObjectDesc and llSetObjectDesc.

Functions | Object | Prim


llSetObjectPermMask

Not implemented as of 1.6.1. May show up in a future version.

llSetObjectPermMask()

Base permissions are the permissions the object has when it was given to the current owner. In theory, no other mask can have more permissions then the base. If they do then this is a bug in permissions and should be reported.

Mask | Value | Description
MASK_BASE | 0 | Changes the object's base permissions
MASK_OWNER | 1 | Changes the permissions the current owner has.
MASK_GROUP | 2 | Changes the permissions the object's active group has.
MASK_EVERYONE | 3 | Changes the permissions everyone has on the object.
MASK_NEXT | 4 | Changes the permissions the next owner has.
These are not bitflags! They can not be OR'ed

Permissions | Value (Hex) | Description
PERM_ALL | 0x7FFFFFFF | Set Move/Modify/Copy/Transfer permissions
PERM_COPY | 0x00008000 | Set Copy permission
PERM_MODIFY | 0x00004000 | Set Modify permission
PERM_MOVE | 0x00080000 | Set Move permission
PERM_TRANSFER | 0x00002000 | Set Transfer permission
Do note that ((PERM_COPY | PERM_MODIFY | PERM_MOVE | PERM_TRANSFER) is not the same as PERM_ALL). This is because there may (read as: will) be more permissions in the future.
To retrieve the inventory item's permission for the mask, see llGetInventoryPermMask.
To retrieve the object's permission mask, see llGetObjectPermMask.
Compare with llSetInventoryPermMask.(not implemented)

Functions | Permissions | Asset Permissions | Future Revisions


llSetParcelMusicURL

llSetParcelMusicURL(string url)

Sets the streaming audio URL for the parcel the scripted object is on (the object must be owned by the owner of the parcel, if the parcel is group owned the object must be owned by that group).

This means, for those who do have a webserver where they can just upload simple files, but not run streaming servers, you can now have multiple MP3/Ogg Vorbis files there and a script in-world that just switches the URL to point to each file in succession (it'll have to know how long each song is though).

As of SL 1.5, the change is now immediately transmitted to the client and the agent does not have to leave and re-enter the parcel anymore.
Note: however if an avatar is sitting, the change in URL will not happen for them until they stand up -AlondriaLeFay

Note: This function delays the script for 2 seconds.

//This simple script will listen for the owner to say radio: URL
//it will tune to the station specified by the radio: command.
default
{
     state_entry()
     {
          llListen(0,"",llGetOwner(),"");
     }
     listen(integer channel,string name,key id,string msg)
     {
          string lmsg=llToLower(msg);
          if("radio: "==llGetSubString(lmsg,0,6))
          {
               llSetParcelMusicURL(llGetSubString(msg,7,-1));
          }
     }
}

Q: So the sim relays the audio stream to the SL client, right?
A: No, that wouldn't make much sense. Instead, the sim just tells the client the URL of the parcel and then each client connects to the audio streaming server individually.

Q: But doesn't that mean the more people dwell on a parcel, the more bandwith the streaming server needs?
A: Yes, that is correct.

Q: So I can't stream live audio from my DSL connection?
A: Not with more than one or two listeners (depending on bitrate and your upstream bandwidth).

Q: How do I stream live audio to many listeners then?
A: You'd need a server that has reasonable upstream bandwith and is running an audio streaming service such as Icecast. Then you just use a plugin (like Oddcast) for your audio player to send your audio stream (once) to the server, which relays it to all the listeners.
A: Shoutcast is also possible, and has its own plugin. - Ice

Q: Wow, so I actually have to know how to set up all that stuff?
A: No, not necessarily, there are also commercial hosts that do this for you (for a fee). Note that any that require the listener to log in (such as Live365) will not work with SL.
A: I've seen these work, but you have to get the URL from an external MP3 player, and I'd recomend not logging in for this (unless you have a specific account allowing multiple logins). Also I do recomend getting the URL from within the player's playlist anyway as the SL client does not understand .pls (playlist) files to my knowledge. - Ice

Q: Which ones do work then?
A: Live365 premium has multiple account logins (shhh) because you pay the fees, additionally any other stream will have general music, if you like a stream, check the properties

- Note that there is currently no way to get the current music URL with a script (llParcelMediaQuery can only be used to return the URL of a video stream).

- To get the stream name or current song information, you could probably do something with llHTTPRequest, XML-RPC, or e-mail events.

- If you want to play video, use llParcelMediaCommandList.

- If you want to play MP3 files and they don't work, make sure that the ID3 tags do not have embedded images.

Functions | Sound | Land


llSetPayPrice

llSetPayPrice(integer price, list quick_pay_buttons)

Sets the values of the buttons and the text box default for the Pay dialog -- when a user right-clicks on the object and selects "Pay", llSetPayPrice will affect what buttons exist, what their values are, and whether or not there is a manual payment text entry box.

The number of quick_pay_buttons is limited to 4. If there are more than 4 items in the list, the extra values are ignored.

A space appears instead of a button if its value is:
  • not of TYPE_INTEGER
  • zero
  • PAY_HIDE.

If the value of a button is less then 1, it is not displayed unless its value is PAY_DEFAULT, in which case the value presented is the default value for that button position.

If price is set to PAY_HIDE, the Manual Money Input Box (MMIB) is hidden.

This function has no effect if the state that it's used in does not implement a money event handler.

Note: The pay price popup is a property of the prim. Resetting or deleting the script will not reset it to default. Only calling llSetPayPrice with the default settings will.

Security Warning
Always (ALWAYS!) check the amount paid in your money() event. This UI element isn't modal, and has had bugs exploited in the past.
Never trust the client software to be secure.

Button Order:
1 | 2
3 | 4

Defaults:
$1 | $5
$10 | $20

Special Values | Value | Alternate | Description
PAY_HIDE | -1 | 0 | Hides the button or MMIB
PAY_DEFAULT | -2 |  | Uses the default value for this quick pay button or the MMIB (in the case of the MMIB it is set to the last value typed in)

Examples:
//Disable pay box
llSetPayPrice(PAY_HIDE, [PAY_HIDE, PAY_HIDE, PAY_HIDE, PAY_HIDE]);

//Restrict to one price
llSetPayPrice(PAY_HIDE, [50, PAY_HIDE, PAY_HIDE, PAY_HIDE]);

//Reset to default
llSetPayPrice(PAY_DEFAULT, [PAY_DEFAULT, PAY_DEFAULT, PAY_DEFAULT, PAY_DEFAULT]);

Lucca Kitty: Anyone else have any issues getting this to work in any prim other than root?
Meroth Junge: I don't think that is an issue. I can see no reason that a linked object should accept payment.
Yumi Murakami: However, linked objects do in fact accept payment - the money event is correctly routed to the appropriate clicked prim - so this may be a bug.
Merlin Alphabeta: I once made a raffle ticket game where people bought the actual ticket prims from the linked object - good sample for a reason why a linked object should accept payment, and proof that it works (and has been for a long time)
Harald Nomad: Data Linden claims this is a feature, not a bug (7/4/07). Child prims can have a script with a money event and can accept payments, but the script cannot set the payment buttons, like in the root prim. However, can't find any documentation that supports his claim.
Functions | money


llSetPos

llSetPos(vector pos)

Moves the prim to position pos. If the calling script resides in a single prim or the root of a linked object, then pos is in region coordinates, which are relative to the simulator's southwest corner. If the script resides in a child prim in a link set, the pos is relative to the root prim of the link set.

Notes

  • Only works on objects with physics disabled.
  • Can never move an object below the ground; attempting to do so will cause the object to be placed with its center at ground height.
  • Limited to moving no more than 10m per call in any direction. If the distance is more than 10m, the object will move in the direction of the specified position, but will stop after 10m. This makes it very easy to use it for moving a long distance:
  • Cannot position an object higher than 768m.
  • An object can be moved into an adjoining sim by specifying a position that is outside the sim but inside the above-mentioned 10m range. For example, if the object is in the sim Gibson at <117, 254, 22>, call
    llSetPos(<117, 259, 22>)
    and the object will move north into Bonifacio at <117, 3, 22> (259 - 256 = 3). To move south, use a negative value:
    llSetPos(<117, -3, 22>);
    For more information, see global coordinate.
  • Delays 0.2 seconds between calls, making the maximum speed traversable with this function 50 m/sec.
  • Drains energy. No error is returned if the object failed to move. Use llSetPrimitiveParams with PRIM_POSITION instead if this is a problem.

Examples

llSetPos(<128, 128, 25>);

This moves the object to the center of the simulator, at 25m height (unless it's more than 10m away from that point, or the ground is higher than 25m there).

while (llVecDist(llGetPos(), targetposition) > 0.001) llSetPos(targetposition);

Note: this does not work for a child prim because llGetPos always returns the location in region coordinates.

Note: There is a rare error here that can occur with multiple prim objects. It the object is moved to a position where part of it is below the ground level llGetPos() will never reach targetposition. The script will hang in an infinite loop recognizable by the fact that whenever you try to move the object it will snap back to its original position. A safer method inserts an escape if their is no change on llSetPos(). As here:

object_move_to(vector position) {
    vector last;
    do {
        last = llGetPos();
        llSetPos(position);
    } while ((llVecDist(llGetPos(),position) > 0.001) && (llGetPos() != last));
}

Child Objects

For child prims in a linked set, the position is relative to the parent prim's position (object-local coordinates). This relative position can be retrieved using llGetLocalPos.

Example:
llSetPos(<0, 0, 1>);

This moves the child prim to a position 1m upwards along the Z-axis of the parent object.

Q: Shouldn't this be:
llSetPos(llGetPos() + <0, 0, 1>)?
A: No, not for child prims -- their position is always relative to the parent. If you want to move it relative to its current offset from the parent, you'd use:
llSetPos(llGetLocalPos() + <0, 0, 1>);.

Q: Can I use llSetPos on an attachment?
A: Yes, as of SL 1.7 this is possible. Note that relative positioning applies to them as well, including HUD attachments.

Q: It says that in the parent, llSetPos repositions the entire object, and in a child prim, the position is relative to the parent's. Does this mean that there's no way to reposition the parent relative to all the other children?
A: Correct. You could theoretically do something with linking and delinking the parent, but the easiest way to deal with this situation is to not get into it. Design your object in such a way that the parent need not be repositioned relative to its children.

Q: I need to set both the rotation and position of a prim at once. My object looks jerky and weird without it.
A: You want llSetPrimitiveParams.

Functions | Dynamics | Link


llSetPrimitiveParams

llSetPrimitiveParams(list rule)

This function changes any number of properties (or "parameters") of a prim in one operation. The parameter is a list of changes. (Don't confuse prim parameters with function parameters.)

The list is parsed and executed one element at a time.

It takes a list as an argument, much as llParticleSystem does. The list always starts with a constant determining the type of change, followed by the parameters that are required by that constant, and then more constant+parameters may follow. For example:

llSetPrimitiveParams([
    PRIM_SIZE, <1, 1, 1>,
    PRIM_COLOR, ALL_SIDES, <1, 0, 0>
   ]); //sets the size of the prim to 1x1x1, and then the color of the prim to red

There are many constants that can be used. See the following tables.

Notes
  • This function delays the script for 0.2 seconds.
  • Wherever a face is to be specified, the constant ALL_SIDES can be used to apply the change to all faces of the prim.
  • TRUE/FALSE can be 1/0 since they are boolean.
  • Changes occur in the order in which they are specified in the list, so:
llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES,
     <0, 0, 0>, 1.0, PRIM_COLOR, 0, <0, 1, 0>, 1.0]);
...will turn all sides except face 0 black (<0, 0, 0>); face 0 will turn green (<0, 1, 0>).
llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <0, 0, 0>, 1.0,PRIM_PHANTOM, TRUE]);
...will turn all sides black (<0, 0, 0>); and set the object phantom

Rules (whenever the "Parameter(s)" column references a PRIM_xxx constant, it can be found in the tables below this one):
Constant Name | Description Alternative | Parameter(s) | Example Rule | Value
PRIM_BUMP_SHINY | Sets bumpmapping and shininess of a face | integer face, PRIM_SHINY_xxx, PRIM_BUMP_xxx | [PRIM_BUMP_SHINY, 2, PRIM_SHINY_LOW, PRIM_BUMP_GRAVEL] | 19
PRIM_COLOR | Sets color and alpha of a face llSetColor, llSetAlpha | integer face, vector color, float alpha | [PRIM_COLOR, 1, <1, 0, 0>, 0.75] | 18
PRIM_FLEXIBLE | Toggles flexible property and attributes: softness, gravity, drag (friction), wind, tension, and force. | boolean TRUE/FALSE, integer softness, float gravity, float friction, float wind, float tension, vector force | [PRIM_FLEXIBLE, TRUE, 2, 0.3, 2.0, 0.0, 1.0, <0, 0, 0>] | 21
PRIM_FULLBRIGHT | Sets full bright of a face on or off. | integer face, boolean on on = TRUE/FALSE | [PRIM_FULLBRIGHT, ALL_SIDES, TRUE] | 20
PRIM_MATERIAL | Sets material of the prim | PRIM_MATERIAL_xxx | [PRIM_MATERIAL, PRIM_MATERIAL_RUBBER] | 2
PRIM_PHANTOM | Sets phantom property of an object llSetStatus | TRUE/FALSE | [PRIM_PHANTOM, TRUE] | 5
PRIM_PHYSICS | Sets physics property of an object llSetStatus | TRUE/FALSE | [PRIM_PHYSICS, TRUE] | 3
PRIM_POINT_LIGHT | Toggles light property and attributes: color, intensity, radius, and falloff. | TRUE/FALSE, vector color, float intensity, float radius, float falloff | [PRIM_POINT_LIGHT, TRUE, <1, 1, 1>, 1.0, 10.0, 0.75] | 23
PRIM_POSITION | Moves object to position. This will silently fail for a child prim of a link set if the child prim exceeds the link distance! Also, as with llSetPos, will not move an object below ground; its center will be set at ground height. llSetPos | vector position | [PRIM_POSITION, <128, 128, 40>] | 6
PRIM_ROTATION | Sets the rotation of the object llSetRot | rotation rot | [PRIM_ROTATION, <0, 0, 0, 1>] | 8
PRIM_SIZE | Sets the scale of an object (Between <0.01,0.01,0.01> and <10,10,10>) llSetScale | vector size | [PRIM_SIZE, <4, 3, 0.1>] | 7
PRIM_TEMP_ON_REZ | Sets temporary on rez property of an object | TRUE/FALSE | [PRIM_TEMP_ON_REZ, TRUE] | 4
PRIM_TYPE | Sets the basic prim type; will unsit all avatars on object. | See next table |  | 9
PRIM_TEXGEN | Sets the texture mapping mode, either PRIM_TEXGEN_DEFAULT or PRIM_TEXGEN_PLANAR. | integer face, integer type | [PRIM_TEXGEN, ALL_SIDES, PRIM_TEXGEN_DEFAULT] | 22
PRIM_TEXTURE | Sets the texture properties of a face llSetTexture, llScaleTexture, llOffsetTexture, llRotateTexture | integer face, string name, vector repeats, vector offsets, float rotation Note: name can be the name of a texture in the prim's contents or the key of a texture, but must be of type string. | [PRIM_TEXTURE, 0, "66864f3c-e095-d9c8-058d-d6575e6ed1b8", <2,8,0>, <.5,.5,0>, PI / 4] | 17

Constants for PRIM_TYPE and their parameters (these are always preceded by the prefix PRIM_TYPE ):
Constant Name | Value | Type | Parameter(s) | Example rule
PRIM_TYPE_BOX | 0 | Box | integer holeshape, vector cut, float hollow, vector twist, vector taper_b, vector topshear | [PRIM_TYPE, PRIM_TYPE_BOX, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>]
PRIM_TYPE_CYLINDER | 1 | Cylinder | integer holeshape, vector cut, float hollow, vector twist, vector taper_b, vector topshear | [PRIM_TYPE, PRIM_TYPE_CYLINDER, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>]
PRIM_TYPE_PRISM | 2 | Prism | integer holeshape, vector cut, float hollow, vector twist, vector taper_b, vector topshear | [PRIM_TYPE, PRIM_TYPE_PRISM, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <0.0, 0.0, 0.0>, <0.0, 0.0, 0.0>]
PRIM_TYPE_SPHERE | 3 | Sphere | integer holeshape, vector cut, float hollow, vector twist, vector dimple | [PRIM_TYPE, PRIM_TYPE_SPHERE, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <0.0, 1.0, 0.0>]
PRIM_TYPE_TORUS | 4 | Torus | integer holeshape, vector cut, float hollow, vector twist, vector holesize, vector topshear, vector profilecut, vector taper_a, float revolutions, float radiusoffset, float skew | [PRIM_TYPE, PRIM_TYPE_TORUS, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 0.25, 0.0>, <0.0, 0.0, 0.0>, <0.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 1.0, 0.0, 0.0]
PRIM_TYPE_TUBE | 5 | Tube | integer holeshape, vector cut, float hollow, vector twist, vector holesize, vector topshear, vector profilecut, vector taper_a, float revolutions, float radiusoffset, float skew | [PRIM_TYPE, PRIM_TYPE_TUBE, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 0.25, 0.0>, <0.0, 0.0, 0.0>, <0.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 1.0, 0.0, 0.0]
PRIM_TYPE_RING | 6 | Ring | integer holeshape, vector cut, float hollow, vector twist, vector holesize, vector topshear, vector profilecut, vector taper_a, float revolutions, float radiusoffset, float skew | [PRIM_TYPE, PRIM_TYPE_RING, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 0.25, 0.0>, <0.0, 0.0, 0.0>, <0.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 1.0, 0.0, 0.0]
PRIM_TYPE_SCULPT | 7 | Sculpted | string map, integer type | [PRIM_TYPE, PRIM_TYPE_SCULPT, "c110440b-c276-88fb-9f3b-abf123f7ea2b", PRIM_SCULPT_TYPE_SPHERE]

For access to the old PRIM_TYPE format, use 1 instead of PRIM_TYPE - BW

Notes on the above parameters:
Parameter | Note
repeats | x and y range from 0.0 to 100 (z is ignored)
offsets | x and y range from -1 to 1 (z is ignored)
cut/dimple/profilecut | x and y range from 0.0 to 1.0, x must be at least 0.05 smaller than y (z is ignored)
hollow | ranges from 0.0 (solid) to 0.95 (maximum hollowed)
twist | boxes, cylinders, prisms ranges from -0.5 (-180 degree in the edit window) to 0.5 (+180 degree in the edit window) for both x and y (z is ignored) spheres, tubes, torii, rings ranges from -1.0 (-360 degree in the edit window) to 1.0 (+360 degree in the edit window) for both x and y (z is ignored)
holesize taper_b | x ranges from 0.05 to 1.0 y ranges from 0.05 (large hole) to 0.50 (no hole) ranges from 0.0 to 2.0 for both x and y (z is ignored)
topshear | ranges from -0.5 to 0.5 for both x and y (z is ignored)
taper_a | ranges from -1.0 to 1.0 for both x and y (z is ignored)
revolutions | ranges from 1.0 to 4.00
radiusoffset | depends on holesize y and revolutions
skew |
See the restrictions table on the LibraryPrimTorture page; it looks more complete.

holeshape constants (for PRIM_TYPE parameters):
Constant | Value | Shape
PRIM_HOLE_DEFAULT | 0 | default (matches the prim type: square for box, circle for cylinder, etc.)
PRIM_HOLE_SQUARE | 32 | square
PRIM_HOLE_CIRCLE | 16 | circle
PRIM_HOLE_TRIANGLE | 48 | triangle

type flags (for PRIM_TYPE_SCULPT parameters):
Constant | Value | Shape
PRIM_SCULPT_TYPE_SPHERE | 1 | sphere (Converge top & bottom, stitch left side to right)
PRIM_SCULPT_TYPE_TORUS | 2 | torus (Stitch top to bottom, stitch left side to right)
PRIM_SCULPT_TYPE_PLANE | 3 | plane (No stitching or converging)
PRIM_SCULPT_TYPE_CYLINDER | 4 | cylinder (Stitch left side to right, double sided polygons)

Bumpmapping constants (for PRIM_BUMP_SHINY):
Constant | Value | Description
PRIM_BUMP_NONE | 0 | none: no bump map
PRIM_BUMP_BRIGHT | 1 | brightness: generate bump map from highlights
PRIM_BUMP_DARK | 2 | darkness: generate bump map from lowlights
PRIM_BUMP_WOOD | 3 | woodgrain
PRIM_BUMP_BARK | 4 | bark
PRIM_BUMP_BRICKS | 5 | bricks
PRIM_BUMP_CHECKER | 6 | checker
PRIM_BUMP_CONCRETE | 7 | concrete
PRIM_BUMP_TILE | 8 | crustytile
PRIM_BUMP_STONE | 9 | cutstone: blocks
PRIM_BUMP_DISKS | 10 | discs: packed circles
PRIM_BUMP_GRAVEL | 11 | gravel
PRIM_BUMP_BLOBS | 12 | petridish: blobby amoebalike shapes
PRIM_BUMP_SIDING | 13 | siding
PRIM_BUMP_LARGETILE | 14 | stonetile
PRIM_BUMP_STUCCO | 15 | stucco
PRIM_BUMP_SUCTION | 16 | suction: rings
PRIM_BUMP_WEAVE | 17 | weave

Shininess constants (for PRIM_BUMP_SHINY):
Constant | Value | Description
PRIM_SHINY_NONE | 0 | none
PRIM_SHINY_LOW | 1 | low
PRIM_SHINY_MEDIUM | 2 | medium
PRIM_SHINY_HIGH | 3 | high

Material constants (for PRIM_MATERIAL):
Constant | Value | Description
PRIM_MATERIAL_STONE | 0 | stone
PRIM_MATERIAL_METAL | 1 | metal
PRIM_MATERIAL_GLASS | 2 | glass
PRIM_MATERIAL_WOOD | 3 | wood
PRIM_MATERIAL_FLESH | 4 | flesh
PRIM_MATERIAL_PLASTIC | 5 | plastic
PRIM_MATERIAL_RUBBER | 6 | rubber
PRIM_MATERIAL_LIGHT | 7 | light (deprecated in SL 1.9.1)

Q & A

Q: What happened to the topsize PRIM_TYPE parameter for boxes, cylinders, and prisms?
A: This was changed to taper in SL 1.10. It works exactly the same as topsize did for values from 0.0 to 1.0, but now it also accepts values from -1.0 to 0.0. Negative values shrink the bottom face instead of the top one. In this way, it is now analogous to the pre-existing taper parameter for torii, tubes, and rings.

Q: Is there any way to flip a texture within a script? You have x and y flip toggles in the edit dialog, but I can't find a way to do this from a function.
A: Yes. Use negative values in the repeats vector.

Q: With llSetPrimitiveParams, you can set almost everything about a prim that you can with llSetScale(), llSetColor(), llSetPos(), llSetRot(), etc. Will these be officially deprecated?
A: No. There are cases where the individual functions are still useful. For instance, they're significantly faster than llSetPrimitiveParams, both for scripters to write, and scripts to parse because they're simple variables and not a list, thus making them very practical for most simple manipulation of prims.

Q: Can I set a texture animation with llSetPrimitiveParams?
A: No. llSetTextureAnim remains the only way to do it.

Q: Does PRIM_POSITION use global, region, or local coordinates?
A: It works exactly like llSetPos, using region coordinates when it's the root prim and not an attachment. For child prims, or root attachments, it uses local coordinates.

Q: Is there any documentation for the old PRIM_TYPE format around anywhere?
A: Sort of, browse the revision history, anything before 8-31-2004 is before the change over; The old interface was lacking in many respects. I recommend using one of the LibraryPrimTorture encapsulation functions; they solve the problem of having to deal with the different number of paramaters required for each prim type.

Q: What is up with taper_a & taper_b?
A: Before SL 1.11 boxs, cylinders & prisms had an attribute called top_size, ranging from 0.0 (no top) to 1.0 (full top). In 1.11 they retrofitted the attribute so that it could adjust the size of the bottom of the prim aswell. For lack of a better name, they renamed the attribute to taper (taper_b). What they did not do was adjust the range to be the same as in the client, as that would break existing content (I suggested they create a new PRIM_TYPE flag like they had in the past; they did not). In the client they made the range -1.0 -> 1.0, while in scripting the range was to be 0.0 -> 2.0 (to convert do: 1.0 - value). To confuse things the old top_size attribute occupies the same address in the object description structure as hole_size. Since taper_a, taper_b have different ranges, occupy different address and co-exist, for sake of sanity and readabilty I have renamed them for this peice of documentation. I ment to fix the documentation earlier it just slipped my mind. -- BW

See also llGetPrimitiveParams.

Functions | Prim


llSetRemoteScriptAccessPin

llSetRemoteScriptAccessPin(integer pin)

If pin is set to a non-zero number, the task will accept remote script loads via llRemoteLoadScriptPin if passed the matching PIN. Otherwise llRemoteLoadScriptPin is ignored.

Note: at some point, the behavior of llSetRemoteScriptAccessPin has changed. As of SL 1.5.14, the PIN is now treated as a property of the object. If the script that calls llSetRemoteScriptAccessPin() is deleted, the PIN still remains set and the object will recieve scripts sent to it. This was not always the case.

Note: This function delays the script for 0.2 seconds.

Functions | Inventory


llSetRot

llSetRot(rotation rot)

If an object is non-physical, sets the rotation to rot. In a linked set, this will be root-relative.
If the object is a child in a linked physical set, this function will also work (see ChildRotation).

Like llSetPos, use llSetRot to rotate an attachment.

For rotation on physical tasks see llRotLookAt.

For the translational analog, see llSetPos.

To set the rotation of a child prim, use llSetLocalRot.

Beginners are often confused about the difference between this function and llTargetOmega. llTargetOmega makes the object SPIN with the specified angular velocity. llSetRot just turns it ONCE to face in a particular direction.

Note: This function delays the script for 0.2 seconds.

To determine the rotation of an object, use llGetRot.
To simultaneously set many object parameters including rotation, and position, use llSetPrimitiveParams.

Q: Does llSetRot work on attachments?
A: Yes.

Q: I need to set both the rotation and position of a prim at once. My object looks jerky and weird without it.
A: You want llSetPrimitiveParams.

Functions | Dynamics | Rotation


llSetScale

llSetScale(vector scale)

Sets the prim's scale (size). Prim size restrictions apply: maximum 10m, minimum 0.01m in any direction (Havok limit). Scaling a prim larger than 10m will cause it to scale up to 10m. Scaling a prim to less than 1cm will cause it to silently fail. Scaling a prim in a linkset causes a silent failure if the scale change will cause the object to exceed the link distance.

Physical prims cannot change their scale, as that would change their mass as well.

A prim's scale can be retrieved using llGetScale.

llSetScale has been supplemented, though not replaced, by llSetPrimitiveParams.

Q: Does this function work on attached objects?
A: Yes, llSetScale does work on attached objects. As does llSetPrimitiveParams. You can chain multiple commands together to execute all at once with llSetPrimitiveParams

Resizing Linked Sets:
In a linked object, this function only changes the size of the prim the script is in. There is no simple way to scale a whole linked object. Scale each individual child prim and adjust its position in relation to the parent prim.

Example:

Put this script into each prim of the object:
default {
    link_message(integer sender_num, integer num, string str, key id) {
        float scale; // size factor
        list    primparams;

        scale = (float)str;

        primparams = [];

        primparams += [PRIM_SIZE, llGetScale() * scale]; // resize

        if (llGetLinkNumber() > 1) { // only move if we're not the root object
            primparams += [PRIM_POSITION, llGetLocalPos() * scale]; // reposition
        }

        llSetPrimitiveParams(primparams);
    }
}

And this script into the parent prim of the linked set:
// rescales the linked set by the specified factor. factor > 1 makes it larger, < 1 smaller
// example: "/9 2.5"
Resize(float scale) {
    integer num_prims = llGetNumberOfPrims();
    integer i;

    for (i = 1; i <= num_prims; i++) { // first prim in a linked set is 1
        llMessageLinked(i, 0, (string)scale, NULL_KEY);
    }

}

default {
    state_entry() {
        llListen(9, "", llGetOwner(), "");
    }

    listen(integer channel, string name, key id, string message) {
        float scale;

        scale = (float)message;
        if ( scale == 0.0 ) return; // we don't resize by factor 0.0

        llSay(0, "Resizing by factor " + (string)scale);

        Resize(scale);
    }
}
Functions | Dynamics | Scale


llSetScriptState

llSetScriptState(string name, integer run)

Controls the script name in the inventory of the prim that calls this function. Setting run to TRUE enables that script; FALSE disables it.

This function allows toggling the "Running" checkbox of a script in the same prim. Note that this has nothing to do with states in LSL.

NOTE: This function can also be called to disable the script it is in.

Example:
// Stops 'ScriptName' (in the prim's inventory)
llSetScriptState("ScriptName", FALSE);

// Start 'ScriptName' (in the prim's inventory)
llSetScriptState("ScriptName", TRUE);

While this function can be used to stop any script, it can only be used to start scripts which were stopped by an llSetScriptState function call. The following three cases are the primary reasons why a script cannot be started with this function:
  • The stopped script was stopped because of an error.
  • The stopped script is stopped because the "Running" box is unchecked.
  • The stopped script is stopped because it was added to the object directly from another object (via llGiveInventory). Use llRemoteLoadScriptPin instead.

Also see llGetScriptState.

Functions | Inventory | Script | State


llSetSitText

llSetSitText(string text);

Replaces "Sit Here" in the object's pie menu with text.

Passing an empty string to llSetSitText will result in it defaulting to "Sit Here".

The text is limited to 9 characters; tab (\t) is always interpreted as 4 characters. The newline character (\n) has no effect on how the text is displayed but still counts as a character against the limit.

Note: like particles, sit text is a property of the prim, not the script. So, if a script gives a prim a sit text, and that script is deleted, the sit text remains. The only way to clear the sit text is with:
llSetSitText("");
A root prim's sit text can't be set from a child prim; the root prim's sit text can only be set in the root prim. When the object is unlinked, the child prim's previously set sit text is revealed.

Compare with llSetTouchText.

Functions | Agent/Avatar | Sit | Text


llSetSoundQueueing

llSetSoundQueueing(integer queue)

Sets whether successive calls to llPlaySound, llLoopSound, etc., (attached sounds) interrupt the playing sound. The default for objects is FALSE. Setting queue to TRUE will make the sound wait until the current playing sound reaches its end. The queue is one level deep (i.e., only one sound will queue up at a time).

Note: State transitions disable sound queueing. So the queue is set to TRUE, and then the state changes, sound queueing will be disabled.

Functions | Sound


llSetSoundRadius

llSetSoundRadius(float radius)

Establishes a hard cut-off radius for audibility of scripted sounds (both attached and triggered).

Doesn't seem to do anything. It's not officially documented either, but the compiler eats it and there's a tooltip for it.

For now, limit the audibility of scripted sounds by using llTriggerSoundLimited or the normal sound playback functions with exceedingly low volume settings (e.g.: 0.05). You can also limit sound to the land parcel by enabling the appropriate option in the land settings dialog.

Functions | Sound


llSetStatus

llSetStatus(integer status, integer value)

Sets the status to value (TRUE/FALSE).

Use the following constants for status:
Flag | Value | Default | Description
STATUS_PHYSICS | 1 | FALSE | If value is TRUE, object moves physically.
STATUS_ROTATE_X | 2 | TRUE | If value is FALSE, object doesn't rotate around local X axis.
STATUS_ROTATE_Y | 4 | TRUE | If value is FALSE, object doesn't rotate around local Y axis.
STATUS_ROTATE_Z | 8 | TRUE | If value is FALSE, object doesn't rotate around local Z axis.
STATUS_PHANTOM | 16 | FALSE | If value is TRUE, object doesn't collide with other objects (is phantom).
STATUS_SANDBOX | 32 | FALSE | If value is TRUE, object can't cross region boundaries or move more than 10m from its start location (and loses physical attribute).
STATUS_BLOCK_GRAB | 64 | FALSE | If value is TRUE, object can't be grabbed and/or physically dragged. See llDetectedGrab. Note: this doesn't affect whether or not a user is able to select the object using the editing tools.
STATUS_DIE_AT_EDGE | 128 | FALSE (TRUE for objects created with llRezObject) | If value is TRUE, objects that reach the edge of the world just die rather than returning to the owner's inventory.
STATUS_RETURN_AT_EDGE | 256 | FALSE | If set, objects created with llRezObject will be returned to inventory instead of deleted when going off-world. Overrides STATUS_DIE_AT_EDGE.
STATUS_CAST_SHADOWS | 512 | TRUE | PREVIEW: If set, object casts shadows on other objects (if shadows are enabled)

More than one status flag can be set or unset in one call by ORing the flags together:
llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y | STATUS_ROTATE_Z, FALSE);
This turns STATUS_ROTATE_X, STATUS_ROTATE_Y, and STATUS_ROTATE_Z off at the same time.

To determine which status flags have been set, see llGetStatus.

Notes:
  • llSetStatus modifies properties of the object. This means the new status will persist until it is explicitly changed back. For example, if axis rotation is disabled for a specific object, it won't be rotateable with a function like llRotLookAt until axis rotation is re-enabled. Even if the script is reset, deleted and replaced with a new one, the object is put into inventory and moved to a different sim, copies of the object are rezzed back out of inventory, or anything else.
  • STATUS_PHANTOM can't be set on a specific prim in a larger linked object--that is, setting STATUS_PHANTOM to either TRUE or FALSE on a prim contained within a linked set of prim sets STATUS_PHANTOM for the object as a whole. There may be ways to script around this, perhaps by unlinking, setting phantom status, and relinking (according to this forum post). However, many of these methods may be considered bugs and can be quickly patched by the Lindens when they are discovered.
  • STATUS_GRAB only affects the prim the llSetStatus function is in; child prims in linked objects won't be affected.
  • Only the status flags in use by LL may be set, attempting to set/get any other flag will always return FALSE.

Many of the status flags set by llSetStatus can also be set by llSetPrimitiveParams and retrieved with llGetPrimitiveParams. However, be aware llSetPrimitiveParams and llGetPrimitiveParams use different constants than llSetStatus. You can't substitute a constant from llSetStatus for one from llGetPrimitiveParams or vice-versa.

Functions


llSetText

llSetText(string text, vector color, float alpha)
- string text
The Text displayed over the object. (Length Limit: 255 characters)

- vector color
The colorvector. Picks the color with which the text is displayed.
Format: <0, 0, 0>
To convert from RGB to the vector value, divide the RGB value with 255.
Example: 255, 0, 0 (Red) = 255/255, 0/255, 0/255 = <1, 0, 0>

- float alpha
Decides the transparency of the text from 0.1 (high transparency) to 1.0 (no transparency).

Detailed Information:
Sets the contents of text as floating text over the prim using the specified color and transparency (alpha).

"\n" can be used to start a new line. Empty lines must have at least one character (use a space: " \n"). text has a length limit of 255 characters.

To remove text from a prim, leave text empty: ""
llSetText("",<0,0,0>,0)

The following code fixes the "\n" not being converted in literal cases. Chat input, variables, etc... -SiRiSAsturias
msg = llDumpList2String(llParseString2List(msg, ["\\n"], []), "\n");

Note: Floating text is a property of the prim, not the script. As such, it will remain until removed explicitly. Simply deleting the script that set the text will not remove the text.

The text will always be positioned to float straight up (parallel to the world's Z-axis) from the prim's center at a distance of half the prim's Z scale plus some fixed offset.
The float text uses the prims Z axis regardless of it?s rotation. This means that if you rotate a prim 90 degrees and flatten it, the text will float just above the center.

To actually display text on a prim (not hover text), see XyText.

As of Version 1.8.4, alpha is no longer ignored.

Examples:
// Set text to say "Hello." in bright red
llSetText("Hello.", <1,0,0>, 1.0);

// Remove text
llSetText("", <0,0,0>, 1.0);

// Set text to say "Hello" and "How are you?" on two different lines
llSetText("Hello.\nHow are you?", <1,0,0>, 1.0);

// Use a function to display the current region
llSetText("You're currently in " + llGetRegionName(), <0,1,0>, 1);

Q:
A:

Functions | Text


llSetTexture

llSetTexture(string texture, integer side)

Sets the texture on side. texture can be the name of a texture in the prim's inventory, or the key of a texture. A side of ALL_SIDES sets all sides of the prim in one call.

The string texture can be replaced by the UUID key of the texture in quotes. This makes it very easy to have less inventory clutter within an object and allows you to just drop a script into a prim.

Specifiying an empty string or NULL_KEY for texture will not set the object to 100% alpha, or the "Blank" texture. The script will say the error message "Couldn't find texture" on the debug channel.

Note: This function delays the script for 0.2 seconds.

Remember, llSetTexture only applies to the current prim, not the entire linked object. If you need to set the texture of a different prim in your linkset, you'll need to use a separate script in that prim and communicate between the two via linkmessages.

The current texture can be read by llGetTexture.

Example:
// applies the texture named "asphalt" (in the object's inventory, clicking the "Content" tab while editing the object) to all sides of the prim
llSetTexture("asphalt", ALL_SIDES);
// sets the texture with the specified key on side 0
llSetTexture("f54a0c32-3cd1-d49a-5b4f-7b792bebc204", 0);

Q: How do I determine which side of an object this applies to?
A: See side.

Useful textures:
If you want a side (or ALL_SIDES) to be 100% alpha, it's probably best to use llSetAlpha. However, the key for a 100% alpha (fully transparent) texture is "f54a0c32-3cd1-d49a-5b4f-7b792bebc204". Remember, this will still render -- it's not like a NODRAW texture in a game editor.

If you want to use the "Blank Texture" without having to upload your own blank image (100% white image with no alpha), use the key "5748decc-f629-461c-9a36-a35a221fe21f".

If you want to use the default plywood texture, use the key "89556747-24cb-43ed-920b-47caed15465f".

See ClientAssetKeys for many other textures.

To set the color or alpha of a prim, use llSetColor and llSetAlpha respectively. Also see llSetPrimitiveParams to set texture, color, alpha and other settings at once.

Functions | Texture


llSetTextureAnim

llSetTextureAnim(integer mode, integer side, integer x_frames, integer y_frames, float start, float length, float rate);

Animates the texture on side of a prim. Only one animation can be run per prim, although all sides of a prim can be animated at the same time by setting side to ALL_SIDES.

mode is a mask made up of the following parameters:

Mode | Description | Hex
ANIM_ON | enable texture animation | 0x01
LOOP | loop when animating textures | 0x02
REVERSE | animate in reverse direction | 0x04
PING_PONG | animate forward then reverse | 0x08
SMOOTH | slide texture smoothly instead of frame-stepping | 0x10
ROTATE | rotate texture instead of using frames | 0x20
SCALE | scale texture instead of using frames | 0x40
Notes:
  • Setting mode to FALSE (or just leaving out ANIM_ON) will stop a previous animation.
  • ROTATE and SCALE define two 'special' modes of texture animation, and are mutually exclusive--they cannot be used together.
  • ROTATE and SCALE interpret the animation parameters differently from the standard animation mode, and also from each other, as detailed below.

'Standard' Animation Mode

(ie. neither ROTATE nor SCALE)

The "standard" animation mode divides the texture up into frames, then steps through these frames in sequence.

x_frames and y_frames describe how the texture is to be broken into frames:
  • x_frames defines the number of horizontal frames.
  • y-frames defines the number of vertical frames.

start and length specify which frames of the texture are to be animated:
  • start specifies the frame number to begin animating from (frames are numbered from left to right, top to bottom, starting at 0).
  • length specifies the total number of frames to animate ( setting length to 0 means animate all frames).

rate controls the animation speed:
  • rate sets the animation speed, in frames per second, eg. setting rate to 10.0 means ten frames per second.

Notes:
  • x_frames and y_frames are each limited to a range of 0 to 255, due to the fact that only the first byte of each integer is transmitted; the values transmitted are (x_frames & 0xFF) and (y_frames & 0xFF).
  • The SMOOTH flag will cause the animation to scroll between the frames instead of jumping frame-by-frame. The frames will still be played in the normal sequence, however.
  • Setting rate to a negative value is exactly equivalent to using a positive value and the REVERSE flag.
  • Textures cannot be animated beyond a 1x1 tiling.

ROTATE Animation Mode

This mode causes the texture to rotate on the side(s) of the prim, instead of stepping through frames.

x_frames and y_frames are ignored in this mode.

start and length specify how the texture is to rotate:
  • start specifies the beginning rotation of the texture, in radians. Eg. setting start to PI will cause the animation to begin with the texture rotated 90 degrees from its default orientation.
  • length specifies the total amount the texture should rotate, in radians. Eg. setting length to TWO_PI will animate a full, 360-degree rotation. Setting length to less than TWO_PI will cause the texture to rotate the amount specified, then jump back to it's beginning rotation.

rate controls the animation speed:
  • without the SMOOTH flag, rate specifies the animation speed in frames per second. There seems to be no way to control the number of frames in a non-SMOOTH ROTATE animation, and appears to be fixed at six frames.
  • with the SMOOTH flag, rate specifies the animation speed in radians per second.

Note: setting rate to a negative value will cause the texture to rotate in the opposite direction.

SCALE Animation Mode

This mode causes the texture to scale up or down on the side(s) of the prim, causing a 'zoom in' or 'zoom out' effect.

x_frames and y_frames are ignored in this mode.

start and length specify the scaling of the texture through its animation:
  • start specifies the number of repeats-per-face of the texture at the beginning of the animation.
  • length specifies the total increase in repeats-per-face of the texture over the course of the animation.

rate controls the animation speed:
  • rate specifies the animation speed in integer scale changes per second; this does not work well for non-integer values of start and length, unless the SMOOTH flag is also being used.

Notes:
  • This mode seems to treat start and length as integers, not floats, dropping all decimals.
  • Setting start to a negative value will apply the absolute value of start as the beginning repeats-per-face, but will decrease the repeats-per-face by length-1 over the course of the animation, instead of increasing it (ie. zoom in).
  • Setting length to a negative value ignores the negative sign; it's an absolute value.
  • Another way to state this is that starting_repeats_per_face = llAbs( start ), and the end_repeats_per_face = llAbs( start + llAbs( length ) ), or llAbs( start + llAbs( length ) -1 ) for negative values of start.
  • Setting rate to a negative value is exactly equivalent to using a positive value and the REVERSE flag.

SMOOTH Animation Mode

This mode causes the texture to be moved smoothly between one frame and the next in the X direction.

It can be used for scrolling smoothly from one end of a texture to another. To do this set Y frames to 1, and set X frames to a value which sets the proportion of the texture which will be seen at any one time. To avoid over-running the end of the texture and repeating at the end of the loop, set length to one less than the number of X frames and start to zero.

llSetTextureAnim(ANIM_ON | PING_PONG | SMOOTH | LOOP, ALL_SIDES, 8, 1, 0, 7, 2);

Notes:
- for smooth scrolling in the vertical direction on a prim, simply use the Texure tab on the Edit window to rotate the texture +/- 90 degrees, and the animation will follow.

General Note: llSetTextureAnim works by overriding the texture's scale and offset on the client side which makes this function incompatable with llScaleTexture and the PRIM_TEXTURE component of llSetPrimitiveParams. However, due to this implementation approach it will not affect your bandwidth or a sim's performance.

Example:
default {
    state_entry() {
        llSetTextureAnim(ANIM_ON | SMOOTH | LOOP, ALL_SIDES, 1, 1, 0.0, 0.0, 0.5);
    }
}

Functions | Texture | Animation


llSetTimerEvent

llSetTimerEvent(float sec)

Sets the timer event to be triggered every sec seconds. Passing 0.0 (or just 0) stops further timer events.

The timer persists over state changes, but gets removed when the script is reset.

default
{
    state_entry()
    {
        llSetTimerEvent(300); // five minutes: 5*60--no need to make LSL do this simple calculation
    }

    timer()
    {
        llOwnerSay("timeout");
    }
}

functions | events | timer


llSetTorque

llSetTorque(vector torque, integer local);

If the object is physical, this function sets the torque. Each value in the torque indicates a rotational force around the corresponding axis. SL uses right-handed coordinate spaces, so if you point your right thumb in the positive direction of the axis, the curl of your fingers from the knuckles to the tips will indicate the direction of rotation around that axis for a positive force (value in the corresponding field of the vector).

The torque vector is relative to the local axes of the object if local is TRUE, the global axes (east/west, north/south, up/down) if FALSE.

This function applies a constant angular force (essentially an automatic impulse that occurs once every couple seconds). If you want only a single impulse, use llApplyRotationalImpulse instead. If you don't disable the torque by calling this function again with a ZERO_VECTOR, the object rotation will continue to accelerate.

You can also set force and torque simultaneously by using llSetForceAndTorque.

Do not use with vehicles.

Anyone have more detail on the rate of impulse?
Check the llSetForce page.

functions | dynamics | physics


llSetTouchText

llSetTouchText(string text)

Replaces "Touch" in the object's pie menu with text.

Passing an empty string to llSetTouchText will result in it defaulting to "Touch".

The text is limited to 9 characters; tab is interpreted as 4 always. The newline character has no effect on how the text is displayed but still counts as a character against the limit.

Compare with llSetSitText.

Functions | User Interface | Touch | Text


llSetVehicleFlags

llSetVehicleFlags(integer flag)

llSetVehicleFlags sets special vehicle behaviors.

There are a few aspects of vehicle behavior that are not defined in the parameters which help define special cases such as vehicles that can only hover over water, or can not motor vertically. The correct vehicle flag settings go a long way toward creating a good vehicle.

Like the vehicle parameters, these should be set after a vehicle type is defined. Multiple flags can be set at once by ORing them together. Flags can be removed using llRemoveVehicleFlags.

Flag constants:
Constant | Value | Description
VEHICLE_FLAG_NO_DEFLECTION_UP | 1 | Prevents linear deflection parallel to world-z axis
VEHICLE_FLAG_LIMIT_ROLL_ONLY | 2 | Removes vertical attraction for changes in vehicle pitch
VEHICLE_FLAG_HOVER_WATER_ONLY | 4 | Hover only pays attention to water level
VEHICLE_FLAG_HOVER_TERRAIN_ONLY | 8 | Hover only pays attention to terrain height
VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT | 16 | Hover only pays attention to global height
VEHICLE_FLAG_HOVER_UP_ONLY | 32 | Hover only pushes up
VEHICLE_FLAG_LIMIT_MOTOR_UP | 64 | Prevents ground vehicles from motoring into the sky
VEHICLE_FLAG_MOUSELOOK_STEER | 128 | Makes vehicle try to turn toward mouselook direction
VEHICLE_FLAG_MOUSELOOK_BANK | 256 | Makes vehicle try to turn toward mouselook direction assuming banking is enabled
VEHICLE_FLAG_CAMERA_DECOUPLED | 512 | Causes the camera look-at axis to NOT move when the vehicle rotates

Examples:
// Set the vehicle to hover at a global height instead of over terrain.
llSetVehicleFlags(VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);

//Turn off vertical deflection and motors
llSetVehicleFlags(VEHICLE_FLAG_LIMIT_MOTOR_UP | VEHICLE_FLAG_NO_DEFLECTION_UP);

//Remove all the flags
llRemoveVehicleFlags(-1);

Retired Names:
Old Name | New Name
VEHICLE_FLAG_NO_FLY_UP | VEHICLE_FLAG_NO_DEFLECTION_UP

From the SL 1.4 Preview Notes (very out of date)

Andrew Linden posted:

Mouselook control of vehicles in 1.4

(One benefit of this feature is that, when used with a "decoupled camera" (see below), it should make vehicles much easier to steer, especially for people with bad client-side FPS.)

Setting the new VEHICLE_FLAG_MOUSELOOK_STEER flag will change the meaning of the VEHICLE_ANGULAR_MOTOR_DIRECTION:

  1. When in mouselook the vehicle will try to turn such that it points in the same direction as your camera. The motor's speed about each axis is scaled by the components of VEHICLE_ANGULAR_MOTOR_DIRECTION parameter. That is, if you want the angular motor to ONLY swing the vehicle left and right (rotations about the vehicle's z-axis) then you might set it to something like <0,0,5> which would zero the x- and y-components of the motor (roll and pitch) while scaling the z-component (yaw) by 5. So if the mouse is looking PI/4 radians to the left, then the motor would try to rotate the vehicle at 5*PI/4 radians/second about its z-axis.
  2. The VEHICLE_ANGULAR_MOTOR_TIMESCALE is still in effect, so if you want to make the vehicle responsive, set it to a short timescale, and if you want the vehicle to be sluggish then set it to something long. However, VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE has no meaning when using "mouselook steer".

There is also a new flag called VEHICLE_FLAG_MOUSELOOK_BANK. Basically it is the same as the _STEER flag, except it interprets "yaw" as input for "roll". Combined with banking behavior the vehicle should bank left when you mouse left.

Note: when setting the motor direction using _MOUSELOOK_BANK flag the components of the motor direction scale the speed after the rotation of the input. So if you wanted to limit the angular motor to only "roll" (no pitch) you would set the angular motor to something like <5,0,0>.

Also note:

By default the mouselook direction WILL CHANGE when the vehicle you are sitting on moves. So when you move the mouse away from center the vehicle will continue to turn until you center the mouse again. To disconnect the mouslook camera from the vehicle's rotation you need to also set the VEHICLE_FLAG_CAMERA_DECOUPLED flag. I think most of you will want to enable this flag when using mouse control of vehicles.

Right now the mouselook mode only works when in mouselook, so when you go out of mouselook the vehicle will no longer be steerable. This is one of the things I'll try to fix before 1.4's actual release.

Also, Richard Linden wants to make mouse countrols work when in a 3rd person view, but he probably won't get that in for 1.4 before release. Probably in a patch.

vehicles | functions


llSetVehicleFloatParam

llSetVehicleFloatParam(integer param_name, float param_value)

Sets the vehicle floating point parameter param_name to param_value. Valid parameters can be found under the Vehicles entry.

Vehicles / Functions


llSetVehicleRotationParam

llSetVehicleRotationParam(integer param_name, rotation param_value)

Sets the vehicle rotation parameter param_name to param_value. Valid parameters can be found under the Vehicles entry.

Vehicles / Functions


llSetVehicleType

llSetVehicleType(integer type)

Defines an object as a vehicle.

Second Life has a built-in physics engine that allows objects to behave as if they are constrained by real-world physics. An object can be manipulated by scripted forces, but the relatively slow execution time of a script makes it hard to make a vehicle that moves well. Defining an object as a vehicle shortcuts the speed limitations of a script, and lets the object work more closely with the physics engine. In this way, an object's physical movement can be defined very well, and a controllable vehicle can be made. In order to define an object as a vehicle, it must be set as one of several available vehicle types.

This should be the first function called when setting up how a vehicle works, as it will define the starting parameters for the vehicle. There are several constants that can be used to define the type:

Constant | Value | Description
VEHICLE_TYPE_NONE | 0 | disable the vehicle
VEHICLE_TYPE_AIRPLANE | 4 | uses linear deflection for lift, no hover, and banking to turn
VEHICLE_TYPE_BALLOON | 5 | hover, and friction, but no deflection
VEHICLE_TYPE_BOAT | 3 | hovers over water with lots of friction and some angular deflection
VEHICLE_TYPE_CAR | 2 | another vehicle that bounces along the ground but needs motors to be driven from external controls or timer events
VEHICLE_TYPE_SLED | 1 | simple vehicle that bumps along the ground, and prefers movement along its local x-axis

Examples:
// Sets the vehicle type to a basic airplane
llSetVehicleType(VEHICLE_TYPE_AIRPLANE);

// Sets the vehicle type to none, so the object is no longer defined as a vehicle
llSetVehicleType(VEHICLE_TYPE_NONE);

vehicles | functions


llSetVehicleVectorParam

llSetVehicleVectorParam(integer param_name, vector param_value)

Sets the vehicle vector parameter param_name to param_value. Valid parameters can be found under the Vehicles entry.

Vehicles | Functions


llShout

llShout(integer channel, string text)

Shouts text on channel. llShout range is a 100m radius sphere centered on the calling object.

Note: A long string for text will be truncated to 1023 characters.

Example:
llShout(0, "Hello Avatar!");
Chat Output:
Hello, Avatar!

Like avatars, objects can use "/me" on channel 0. Thus, if an object named "Your dog" had a script with the line:
llShout(0, "/me wants steak.");
The chat output would be:
Your dog wants steak.

Scripts with listens will get the text without "/me". In the above example, a script listening on channel 0 would get the message "wants steak". There is currently no way for a script to see if the speaker has said "/me".

constants

DEBUG_CHANNEL 2147483647

Q: Why doesn't my object pick up it's own spoken chat text in listen()?
A: Objects do not 'hear' themselves talk. This helps prevent infinite loops which would probably happen often if objects could hear themselves. For example, if your script returned a message saying 'Unknown command', you could go into an infinite loop easily if you didn't filter out 'Unknown command'. This applies to all of the local chat functions: llWhisper, llSay, and llShout.

Compare with llSay, llWhisper, llOwnerSay, llInstantMessage and llMessageLinked

Functions | Chat | listen | Communications


llSin

float llSin(float theta)

Returns the sine of theta radians.

Compare with llAsin.

Functions | Math


llSitTarget

llSitTarget(vector offset, rotation rot)

Sets the sit target for the prim (not the object) at offset from the center of the prim and rotated by rot. If offset is ZERO_VECTOR, the sit target is cleared.

After defining a sit target, llAvatarOnSitTarget can be used. The changed event will be called with the CHANGED_LINK flag set when someone sits on the prim, at which time llAvatarOnSitTarget may be called.

Note: like particles, sit targets are a property of the prim, not the script. So if a script gives a prim a sit target, and that script is deleted, the sit target remains. The only way to clear the sit target is by calling llSitTarget(ZERO_VECTOR,ZERO_ROTATION);

Example:
// Avatar will sit slightly above the object's center,
// facing the direction of the forward axis.
llSitTarget(<0,0,.1>, ZERO_ROTATION);

Teleport Hack

The maximum distance llSitTarget can offset is up to 300m on any axis. This means it can be used to instantly teleport avatars around by having them sit on an object that immediately unsits them again (now at their new position). To activate the "teleporter", the user simply selects "Sit Here" from the object's pie menu. llSetSitText allows selecting a more apt label to replace "Sit Here".

Note: the rotation of the object with the teleport script will usually affect where the teleport takes the avatar. To nullify this rotation, divide ZERO_ROTATION by the rotation of the object:

//Rotational cancel method by Moleculor Satyr
llSitTarget(target * (ZERO_ROTATION / llGetRot()),ZERO_ROTATION / llGetRot());

The location chosen in llSitTarget is a prim-local target, not a global one. To teleport-hack to a specific <x,y,z> position within the sim, subtract the object's region coordinates from the target to obtain a vector that is relative to the object:

//Moleculor Satyr's rotational cancel with a tweak -Tateru Nino, and another -BW
vector target=<100.0, 100.0, 103.5>;
rotation my_rot = llGetRot();
llSitTarget((target - llGetPos()) / my_rot,ZERO_ROTATION / my_rot);

Note: in conditions of high lag and slow communications speed, SL will sometimes put the avatar close to <0,0,0> in the sim for sometime before actually putting the avatar in the sit location.

Also Note: The above example script will need to be reset each time the rotation or position is changed if the target is to be maintained.

Linked Objects With Multiple Sit Targets

How Seats Are Labeled
Wouldn't it be nice if you could label the driver's seat "Drive!" and the passenger's seat "Ride!" But this does not seem possible 8-( . I've used llSetSitText to specify such seat labels. But, regardless, the label of the seat that is the root prim is used for every seat.

Confusingly, despite having its label overriden by the root sit target's label, the scripting of each individual sit target is still intact. You can test this by having each sit target llWhisper a unique message when you sit on it.

How We Choose Our Seats
In a vehicle (or any object) with multiple seats (each seat has a script that sets a sit target), the following method determines which sit target an avatar ends up at:

If the primitive the player clicked on has a sit target and that sit target is not full, that sit target is used.
If the primitive the player clicked on has no sit target, and one or more other linked objects have sit targets that are not full, the sit target of the object with the lowest link number will be used.

So, to make a vehicle that would allow one driver and exactly two passengers:
Link 0 (the root prim) the driver's seat
Link 1 Passenger seat 1
Link 2 Passenger seat 2
Link 3 Ejector (a script that immediately llUnSits an avatar on its sit target)

So, if the vehicle is empty, and a player clicks on any unscripted part of the vehicle to sit, they will end up in the driver's seat (the lowest empty link number with a sit target).
  • If the driver's seat is full they will end up in passenger seat 1.
  • If the driver's seat and passenger seat 1 are full they will end up in seat 2.
  • If all seats are full, they will end up sitting on link 3 and be ejected.
  • If a player clicks on a specific seat and that seat is not full, they will sit there. If they click on link 3, they will be ejected, so you'd probably want to hide link 3 inside the vehicle where it cannot be clicked on.

Simple eh?

Functions | Agent/Avatar | Sit


llSleep

llSleep(float sec)

Puts the script to sleep for sec seconds. During this time, the current event will not be exited, no other events will be processed.
(As a comment notes, new events are still queued, but it looks like only one event of each type will actually queue while the script sleeps(/delays?). Anyone know more about that?)

llSleep has its uses, but can be inconvenient. See llSetTimerEvent and the timer() event for an alternative that allows other events to be executed while waiting.

A quick test shows the time actually slept seems to differ up to about .1 seconds from the requested amount.

See ScriptDelay for more information on how llSleep affects script execution.

Functions


llSqrt

float llSqrt(float val)

Returns the square root of val (the return value squared will be val).

It's equivalent (in theory) to llPow(float val, 0.5) (which, for speed reasons, may have it's precision clamped).

Compare with llPow.

Note: This function gives a runtime error if val is < 0.

Functions | Math


llStartAnimation

llStartAnimation(string anim);

Triggers the animation anim. In order for this to work, the script must have the PERMISSION_TRIGGER_ANIMATION permission on an avatar. See llRequestPermissions and llGetPermissions for more information on permissions.

Built-in animations always work, but if anim is a custom animation, it must be contained in the inventory of the scripted prim.

It is usually good practice to check llGetPermissions before calling this function. The fact that this is not usually done is the reason so many "Script trying to trigger animations but PERMISSION_TRIGGER_ANIMATION permission not set" messages happen, which can be annoying (especially if they keep trying over and over).

An animation which doesn't end on its own can be stopped with llStopAnimation.

To animate someone sitting on an object, it's a good idea to stop the generic sit animation first (llStopAnimation("sit")) unless the new animation has a higher priority than the default sit.

See animation for a current list of animation names.

ExampleAnimationScript shows an example of this function's use (it is the freely-distributed animation script many people use).

Compare with llStopAnimation.

Note: while the official docs claim "This function starts animation anim for the avatar that owns the object.", that is incorrect; as long as permissions have been granted, any avatar can be animated using llStartAnimation.

Q: Is there a way to load an animation from a key?
A: No.
A: This is a change from older script behavior, one of several copy protections to have been added to LSL in 2006. - Ice

Functions | Agent/Avatar | Animation


llStopAnimation

llStopAnimation(string anim)

Stops the animation anim. In order for this to work, the script must have the PERMISSION_TRIGGER_ANIMATION
permission on an avatar. See llRequestPermissions and llGetPermissions for more information on permissions.

It is usually good practice to check llGetPermissions before calling this function. Otherwise this message might appear:
"Script trying to stop animations but PERMISSION_TRIGGER_ANIMATION permission not set".

Note: while the official docs and editor help tool tip claim: "This function stops animation anim for the avatar that owns the object.", that is incorrect; as long as permissions have been granted, any avatar's animations can be stopped using llStopAnimation.

See animation for a current list of animation names.

ExampleAnimationScript shows an example of this function's use (it is the freely-distributed animation script many people use).

To start an animation, use llStartAnimation.

Q: Can llStopAnimation accept a key to an animation to stop an animation? (See llGetAnimationList.)
A: Yes, even though llStartAnimation does not accept keys.

Functions | Agent/Avatar | Animation


llStopHover()

Stop hovering to a height set with llSetHoverHeight.

Functions


llStopLookAt()

Stops damped motion started by llLookAt or llRotLookAt.

Compare to llStopMoveToTarget.
Functions


llStopMoveToTarget()

Stops damped motion started by llMoveToTarget.

Compare to llStopLookAt.

Functions


llStopPointAt();

Stops avatar pointing at the position set with llPointAt.

'According to Kelly Linden, llPointAt and llStopPointAt have been deprecated since mid-beta, but remain without a replacement to this day. (SL version 1.6)

Functions | Agent And Avatar


llStopSound()

Stops the currently playing sound started with these functions:

llPlaySound This does not work. llStopSound will not stop sound started with llPlaySound. at least not in v 1.16
llLoopSound
llPlaySoundSlave
llLoopSoundSlave
llLoopSoundMaster

It does NOT affect sounds started by collisions, llTriggerSound or llTriggerSoundLimited.

Functions | Sound


llStringLength

integer llStringLength(string src)

Returns the number of characters in src.

Example:
llStringLength("hello"); // this returns 5.

Strings / Functions


llStringToBase64

string llStringToBase64(string str)

Converts a string to the Base 64 representation of the string.

Compare with llBase64ToString.

NOTICE: Base64 is not encryption. It is very easy to decode base 64 strings.

Functions / Crypto / Communications / Strings


llStringTrim

string llStringTrim(string text, integer trim_type)

Outputs a string eliminating whitespace from the start and/or end of the specified string.

Constants for trim_type:
STRING_TRIM_HEAD: trim all leading spaces in text
STRING_TRIM_TAIL: trim all trailing spaces in text
STRING_TRIM: trim all leading and trailing spaces in text

In this case, whitespace can mean:
  • Spaces (ASCII 32)
  • Tabs (ASCII 9) - \t
  • New line (ASCII 10) - \n

To eliminate all whitespace:
Example:
llStringTrim(" Test ", STRING_TRIM);
Output:
"Test"

To eliminate only the starting whitespace:
Example:
llStringTrim(" \tTest ", STRING_TRIM_HEAD);
Output:
"Test "

To eliminate only the ending whitespace:
Example:
llStringTrim(" Test \n", STRING_TRIM_TAIL);
Output:
" Test"

Functions | String


llSubStringIndex

integer llSubStringIndex(string source, string pattern)

Returns the index in source where pattern first appears. Returns -1 if no match is found.

Remember, strings (and lists) are indexed starting from 0.

This function is relatively slow.

Example:
string sentence = "This sentence has a verb.";

llSay(0, (string)llSubStringIndex(sentence, "e")); // 6
llSay(0, (string)llSubStringIndex(sentence, "u")); // -1
llSay(0, (string)llSubStringIndex(sentence, "ten")); // 8

Q: "Relatively slow?" How exactly does this differ from the rest of LSL? :) -?
A: Actually, the execution of LSL functions themselves is quite fast, unless they have a built-in delay, as in the case of llInstantMessage, for example. The slow part (because of little allocation of timeslices because of the huge number of scripts a simulator runs in parallel) is the execution of the script's bytecode itself (loops, math, moving data in memory, etc.). So, unlike others functions, llSubStringIndex is likely simply slow in its execution, though it's possible that the slowness is due to an unknown built-in delay.

Compare with llListFindList.

Functions | String


llTakeCamera

See llSetCameraParams


llTakeControls

llTakeControls(integer controls, integer accept, integer pass_on)

The control functions allow an object to respond to user interface input that normally controls the avatar. For example, this allows creation of a scripted vehicle that moves in response to the keyboard commands that normally move the avatar. Also, an gun can be created that fires in response to mouse button activity.

The controls parameter is an integer bitfield which is used to specify which user-interface controls the script is interested in. Possible values are:

Constant | Hex | Integer | Description
CONTROL_FWD | 0x00000001 | 1 | forward control (W or up arrow)
CONTROL_BACK | 0x00000002 | 2 | back control (S or down arrow)
CONTROL_LEFT | 0x00000004 | 4 | move left control (shift + A or left arrow)
CONTROL_RIGHT | 0x00000008 | 8 | move right control (shift + D or right arrow)
CONTROL_UP | 0x00000010 | 16 | up control (E or PgUp)
CONTROL_DOWN | 0x00000020 | 32 | down control (C or PgDn)
CONTROL_ROT_LEFT | 0x00000100 | 256 | rotate left control (A or left arrow)
CONTROL_ROT_RIGHT | 0x00000200 | 512 | rotate right control ( D or right arrow)
CONTROL_LBUTTON | 0x10000000 | 268,435,456 | left mouse button control
CONTROL_ML_LBUTTON | 0x40000000 | 1,073,741,824 | left mouse button control with the avatar in MouseLook See ^Note

To filter multiple button presses, combine these values using the | (bitwise OR) operator. (See below for an example.)

The accept and pass_on parameters take a TRUE or FALSE value and have different effects:

accept | pass_on | effect
0 | 0 | only specified controls functional...no script events
0 | 1 | no apparent effect (all controls functional)...no script events
1 | 0 | other controls functional no events...specified controls non-functional but generate events
1 | 1 | all controls normal...specified controls also generate events

An object must have permission to take controls. Call llGetPermissions to see if the object has the PERMISSION_TAKE_CONTROLS permission. If it does not, you must first call llRequestPermissions to request permissions from the user.

After this function has been called and depending on the other parameters, the scripts's control event handler will be invoked whenever a specified input is received. The code you place in the control event handler determines how your object behaves in response to captured user input.

Each call to this function replaces the settings for any previous call. Unfortunately, that means a script can't completely control some inputs and let others continue to function while getting events. Fortunately there's a small glitch that is extremely handy since we can't rotate an avatar with a script. See below for more info.

When done with the controls (for example, when an attachment is removed) it is a good idea to clean up with a call to llReleaseControls.

CONTROL_ML_LBUTTON will cause a "Mouselook" button to appear on the bottom of the SL window, but only if pass_on is set to FALSE.

llTakeControls(CONTROL_ML_LBUTTON, TRUE, FALSE); // this works.
llTakeControls(CONTROL_ML_LBUTTON, TRUE, TRUE); // this doesn't.

Note: There seems to be a bug when taking CONTROL_ML_LBUTTON where it does not produce a MouseLook button after re-attaching even though permitted. Solution:
llReleaseControls();
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
llTakeControls(CONTROL_ML_LBUTTON,TRUE,FALSE);

Example (see ExampleControls for a functioning script):

run_time_permissions(integer perms)
    {
        integer desired_controls =
                CONTROL_FWD |
                CONTROL_BACK |
                CONTROL_LEFT |
                CONTROL_RIGHT |
                CONTROL_ROT_LEFT |
                CONTROL_ROT_RIGHT |
                CONTROL_UP |
                CONTROL_DOWN |
                CONTROL_LBUTTON |
                CONTROL_ML_LBUTTON;

        if (perms & PERMISSION_TAKE_CONTROLS) {
            llTakeControls(desired_controls, TRUE, FALSE);
        }
    }

Taking Complete Control

There is a handy glitch that can be used to work around a major shortcoming of the control() system. There are many reasons why you might want to grab all the user input and not allow the standard av motion to take effect. For example, creating a custom movement solution.

The problem is that avs can not be scripted to rotate so normally using this function to grab all the input controls with accept set to 1 and pass to 0 will prevent a user from turning their av in normal non-mouselook view. You can leave out the right and left rotations but then you won't get events for them.

The trick is to make two calls to this function with the correct parameters. Using the following example, you'll take complete control (no normal movement) of all input except for left and right rotation which will behave normally and also generate control() events:

run_time_permissions(integer perms) {
        if (perms & PERMISSION_TAKE_CONTROLS) {
            llTakeControls(1, 1, 0); // glitch the rotation
            llTakeControls(desired_controls, 1, 0);
        }
    }

Hopefully if a 'fix' is made that changes this, the fix will include the functionality we all want and need.

Detecting loss of control
If someone releases controls after they have been granted, a Call to llGetPermissions compared with PERMISSION_TAKE_CONTROLS will show that the object no longer has permission. This can be done with a timer event.

default
{
    touch_start(integer start_param)
    {
        llRequestPermissions(llDetectedKey(0), PERMISSION_TAKE_CONTROLS);
    }
    run_time_permissions(integer perm)
    {
        if(perm & PERMISSION_TAKE_CONTROLS)
            state controlled;
    }
}
state controlled
{
    state_entry()
    {
        integer controls =
            CONTROL_FWD | CONTROL_BACK
            | CONTROL_LEFT | CONTROL_RIGHT
            | CONTROL_UP | CONTROL_DOWN;

        llTakeControls(controls, TRUE, FALSE);

        llSetTimerEvent(5);
    }
    timer()
    {
        if(llGetPermissions() & PERMISSION_TAKE_CONTROLS)
            return;
        state default;
    }
}

Functions | Agent/Avatar | Control


llTan

float llTan(float theta)

Returns the tangent of theta radians.

Compare with llAtan2.

To get the Arc Tangent do this:
(In case you know all three sides of a triangle and need to know an angle)
float fTemp = opposite leg / adjacent leg
float fAtan = llAsin( fTemp / (llSqrt((llPow(fTemp ,2.0)+1.0))) );
Don't ask me, why this function wasn't included in LSL. :) -- Abdiel Nimbus

Functions | Math


llTarget

integer llTarget(vector position, float range)

Defines a target within range of position.

While the object with the task is outside range of position, not_at_target events will be raised.

Upon coming inside range of position, an at_target event will be raised, and will keep being raised until this target's handle (the return value returned by llTarget) is removed by llTargetRemove or the object moves outside of range of position.

Returns an integer ID (handle) for the target. This handle can be used to remove a target using llTargetRemove.

This function persists across states.

Functions | at_target | not_at_target | llTargetRemove


llTargetOmega

llTargetOmega(vector axis, float spinrate, float gain)

Sets target omega. Makes an object rotate smoothly, without "choppy" steps.

axis is a vector describing the axis the object will rotate around. For example, <0,0,1> will rotate the object around the Z (vertical) axis.¹
spinrate is the rate at which the object will spin (rotate) around the given axis (in radians per second).
gain is the strength of the spin, which only has an effect on physical objects. However, it still must be non-zero for non-physical objects.

This function can be used in a child link (in which case the child will rotate locally around its own center) or in a root link, in which case the entire linkset will rotate around the root's origin. (As used in the planets thing in the main grid's welcome area here). See also this forum note.

Beginners are often confused about the difference between this function and llSetRot. llTargetOmega makes the object SPIN with the specified angular velocity. llSetRot just turns it ONCE to face in a particular direction.

Notes:
  • The rotation speed is based on the axis vector multiplied by spinrate, so it is a good idea to normalize (see llVecNorm) the axis vector or spinrate won't act like radians per second. For example, consider llTargetOmega(<0,0,1>,1,1). Then (<0,0,2>,1,1) would rotate twice as fast, (<1,1,0>,1,1) would rotate llSqrt(2); times as fast.
  • When the object is physical it interacts with the physics engine ² and is frequently updated.
  • When the object is physical llTargetOmega affects the local axis of rotation.
  • When the object is non-physical llTargetOmega effects the global axis of rotation
  • When an object is non-physical (the more common use) the effect is simulated purely on the client ("client-side") and not the server (this rotation "state" is sent once). No updates, other than the start of rotation, is necessary so it reduces the server and network overhead, but means the object will not actually move according to the server; it just moves to the client. This can be confirmed with llGetRot. This also means:
    • *This quirk appears to be no longer present in SL v1.13. Could LL or another party confirm?* Selecting the object in edit mode will show the object as the server sees it.
    • Avatars will collide with the object's bounding box as it was before rotation began. It is a good idea to phantom larger objects so avatars won't bounce into things they can't see.
    • The start of rotation is not synchronized, so not everyone will see the same thing. At some time the object may appear to be rotated, for example, 45 degrees from its initial position to one person, yet 60 degrees to someone else.
    • *This quirk appears to be no longer present in SL v1.13. Could LL or another party confirm?* Calling llTargetOmega again will rotate the object from the original position, not from where it looks like it is at the moment of the call.
    • *This quirk appears to be no longer present in SL v1.13. Could LL or another party confirm?* Other scripting calls that force a client data update of the object will seem to reset the effect (physically moving or changing the properties of the object will cause the object to stutter/twitch instead of rotate freely/smoothly).

¹: To get a rotation around a specific axis no matter how the prim/object is rotated, use llRot2*(llGetRot()) (where * is "Up", "Fwd", or "Left") for the axis. Note: the llTargetOmega script will have to be recompiled/reset if the prim/object is rotated while running or it will no longer rotate around the specific axis correctly.
²: It seems easy to oddly launch things this way, though. Be careful with this combination.

Functions | Dynamics | Rotation


llTargetRemove

llTargetRemove(integer tnumber)

Removes a target with the handle tnumber (as returned by llTarget).

Functions


llTeleportAgentHome

llTeleportAgentHome(key id)

Update: According to Robin Linden, llTeleportAgentHome will not be removed in 1.6.7. However, it will likely be replaced at some point in the future. For now, feel free to use it, but be aware that it may be removed at some point, though there will be warning.

If the scripted object is over owner's land, teleports agent id immediately to that agent's home location. The agent is not asked for confirmation.

This is for agents only; there is no scripted function to return objects from your land yet. For that, you have to rely upon the built-in land management tools or llAddToLandPassList.

This function delays script execution for 5 seconds.

Compare with llEjectFromLand and llUnsit.

Notes:
  • When dealing with group land, the object owner must be the group. It won't work if the object owner is a member or officer in the group; the object itself must be deeded.
  • While there is not currently a way to teleport an agent to an arbitrary location, the llSitTarget entry contains information on how to fake a site-to-site teleport within 300m.

Functions | Agent/Avatar


llToLower

string llToLower(string src)

Returns src in all lower case.

Compare with llToUpper.

Functions / Strings


llToUpper

string llToUpper(string src)

Returns src in all upper case.

Compare with llToLower.

Functions / Strings


llTriggerSound

llTriggerSound(string sound, float volume)

Triggers sound at task's current position. This sound eminates from that position no matter if task moves. sound can be either a sound file UUID (key) or the name of a sound file if it's in task's inventory. This function is not affected by llStopSound. volume specifies the volume to play the sound clip at, a value between 0 and 1. If volume is > 1, it is set to 1.

Land parcels can be set to block sound from entering or leaving the parcel.

Compare with llPlaySound.

Note: A call to llTriggerSound() where the sound to be played is unavailable (ie. referenced by name but is not present in the object's inventory, or by a non-valid key) will delay the calling script by one (1.0) second. - Cross Lament 01/19/2007
Use llGetInventoryType to check existence if it is not a key - BW

TriggerSound(string sound, float volume)
{//not fool proof but pretty reasonable.
    if((key)name);
    else if(llGetInventoryType(sound) == INVENTORY_SOUND);
    else
        return;
    llTriggerSound(sound, volume);
}//Written by Strife Onizuka

Functions | Sound


llTriggerSoundLimited

llTriggerSoundLimited(string sound, float volume, vector tne, vector bsw)

Plays a transient sound NOT attached to an object with its audible range limited by the axis--aligned bounding box defined by tne (top-north-east) and bsw (bottom-south-west). The sound plays from a stationary position located at the center of the object at the time of the trigger. There is no limit to the number of triggered sounds which can be generated by an object, and calling llTriggerSound does not affect the attached sounds created by llPlaySound and llLoopSound. This is very useful for things like collision noises, explosions, etc. There is no way to stop or alter the volume of a sound triggered by this function.

It is polite to use this function where ever possible, especially to prevent your sounds spilling over on to your neighbor's land. Alternately, use the normal non-limited sound functions and exceedingly small volume settings, like 0.05, so that the sound is highly localized, though this is probably less useful for explosions, etc.

The values for tne and bsw are in region local coordinates. So, the points tne and bsw describe a box in the world. This box is unrelated to the scripted object's position. An avatar standing in this box will be able to hear the triggered sound (if they could hear it if it was played with just llTriggerSound. That is, they are not too far from the object triggering the sound and it is not too quiet). An avatar standing outside the box will not hear the sound.

Note: avatars walking into the box after the sound is triggered (no matter the length of the sound) will not hear the sound.

To get the values for tne and bsw, you can use the following method:
  • Create a cube that is the bounding box you want (you can make it phantom and semi-transparent to make things easier).
  • Create a small sphere (doesn't matter what it is, except for the purposes of this description) centered on one corner of your cube. Take note of its position from the edit window (call this vector1).
  • Create another small sphere centered on the diagonally opposite corner of your cube. Take note of its position from the edit window (call this vector2).
  • We now have two vectors, vector1 and vector2, for each of x, y, and z. Put the larger value in the vector tne, and the smaller value in bsw.

For example, if vector1 was <100,50,20> and vector2 was <10,75,60> then tne would be <100,75,60> and bsw would be <10,50,20>.

If you just want your scripted object's sounds not to spread too far, use this method:

llTriggerSoundLimited("soundname", 1.0, llGetPos() + <10,10,10>, llGetPos() - <10,10,10>);

This makes it so that people will only hear the sound if they're within around 10 meters, give or take some. Objects like slot machines would greatly benefit from this.

Q: Is there a way to have sound played so that only a specific agent can hear it?
A: llTriggerSoundLimited(sound, volume, llGetPos(), llGetPos()) in an HUD attachment will do the trick.

Functions | Sound


llUnescapeURL

string llUnescapeURL(string url)

Returns the string that is the URL unescaped version of url, replacing "%20" with spaces, etc.
The function can output raw UTF-8 strings.

Like llEscapeURL, this function can only output 254 characters.

See llEscapeURL.

Functions


llUnSit

llUnSit(key id)

If the agent identified by id is sitting on the scripted object or is over land owned by the scripted object's owner, the agent is forced to stand up.

Notes:
  • When dealing with group land, the object owner must be the group. It won't work if the object owner is a member or officer in the group; the object itself must be deeded.
  • Another way to prevent sitting completely is to deny access to land. People can still visit any building above the height limit for which land access applies (the red stripes), but will not be able to sit at all.
  • The tool tip for this function indicates that key is returned by this function, but the compiler won't accept this.

Compare with llEjectFromLand and llTeleportAgentHome.

Q: When I make an avatar stand up from my chair, they end up standing on it. That looks stupid. Is there some way to get them to stand up and move to a specific position?
A: Not precisely. There are a couple things you can try. The most effective option is to apply a small force to the avatar with llPushObject. Alternatively, try having the chair turn phantom when they stand up, so as to make them drop to the floor. They still might appear to be standing "in" the chair however.

Q: How do I force an avatar to sit on an object?
A: You can't. You can only force them to stand up. It is possible to animate an avatar so they look like they're sitting, but not to control their position with much precision or reliability, at least not without using attachments.

Functions | Agent/Avatar | Sit


llVecDist

float llVecDist(vector v1, vector v2)

This function returns the distance between two positional vectors. Here's a table of examples:

v1 | v2 | return value
<0,0,0> | <1,0,0> | 1
<0,0,0> | <0,0,-10> | 10
<10,0,10> | <10,0,0> | 10
<20,0,19> | <20,0,0> | 19

The equation for finding vector distance with components is sqrt((x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2).
or llVecMag(v1 - v2);

//definition
vector VecNorm(vector v) {
    return (v / VecMag(v));
}

float VecMag(vector v) {
    return llSqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}

float VecDist(vector a, vector b) {
    return VecMag(a - b);
}

Compare with llVecMag, and llVecNorm.

Functions / Vectors


llVecMag

float llVecMag(vector v)

This function returns the magnitude of vector v. This magnitude can be multiplied by the result from llVecNorm(v); (the normalized vector) to give v.

This code will say the distance in metres between the object and the lower southwest corner of the sim:
vector corner = <0, 0, 0>;
float distance = llVecMag(llGetPos() - corner);
llSay(0, (string)distance);

Question: So what's this actually good for?
Answer: Measuring the length of a vector. Practical uses for this include llWind or passing llGetVel to this to get an object's speed.

//definition
vector VecNorm(vector v) {
    return (v / VecMag(v));
}

float VecMag(vector v) {
    return llSqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}

float VecDist(vector a, vector b) {
    return VecMag(a - b);
}

Compare with llVecDist, and llVecNorm.

Functions / Vectors


llVecNorm

vector llVecNorm(vector v)

This function returns the normalized (a.k.a. unit) vector representing v. This vector can be multiplied (scalar) by llVecMag(v) to get v again. Since vectors are used to represent direction as well as location, this function returns the 'simplest form' of the vector, otherwise known as the direction of the vector. If you pass a ZERO_VECTOR (<0,0,0>) to this function, it will return ZERO_VECTOR.

Oversimplification starting point: This reduces the vector to "one". In SL the unit of measure is a meter, so it is somewhat useful to think of this as, instead of a direction pointing 20m to a place, how about reducing it to 1m, but still pointing in the same direction. <0,0,2> (2m up) becomes <0,0,1>. Or, on the complex side: <-1.37821, -0.85432, 2.75875> becomes <-0.43069, -0.26698, 0.86211>.

//definition
vector VecNorm(vector v) {
    return (v / VecMag(v));
}

float VecMag(vector v) {
    return llSqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}

float VecDist(vector a, vector b) {
    return VecMag(a - b);
}

Compare with llVecDist, and llVecMag.

Functions | Vector


llVolumeDetect

llVolumeDetect(integer detect)

When detect = TRUE, this makes the entire link set the script is attached to phantom. But, if another object interpenetrates it, it will get a collision_start event. When an object stops interpenetrating, a collision_end event is generated. While the other is interpenetrating, collision events are NOT generated. The script must be applied to the root object of the link set to get the collision events. Collision filters work normally (see llCollisionFilter).

The object can NOT be set to phantom by any other means, or else llVolumeDetect will not work. llVolumeDetect will set phantom automatically.

Example:
llVolumeDetect(TRUE); // turns llVolumeDetect on.

A script that uses llVolumeDetect will say "Attachments cannot use llVolumeDetect." if the object containing the script is attached at the time llVolumeDetect is called. If llVolumeDetect is used in an attachment, the collision events will still be triggered.

When enabled llVolumeDetect will remain in effect as long as there is a script on the object. It will be automatically removed when the last script is deleted, however the phantom state will persist.

Note:
As of SL 1.6.7, llVolumeDetect should not be used on physical objects! Doing so will cause the object to fall through the ground and go off-world. Presumably, this is because llVolumeDetect does not use the same mechanism as a regular phantom object and that the Lindens goofed up. The Lindens have been notified of this issue, but it has not yet been fixed. If you need the object to be physical, you can use llSetBuoyancy(1.0) to fix the falling problem. I see this as a feature not a bug.

Note:
As of SL 1.6.7, llDetectedLinkNumber will only ever return 0 when used with llVolumeDetect, even when used in a multi-prim object. This may be caused by the fact that llVolumeDetect was added some time after the original detected functions. There is not yet any word on when this might be fixed.

Note:
land_collision_start, land_collision_end and land_collision events aren't generated anymore after setting llVolumeDetect to TRUE.

Functions | Detected | Collisions | Phantom


llWater

float llWater(vector offset)

Returns the water height at the current position + offset.

The water height is the same across the whole simulator, so the offset is irrelevant. offset cannot be used to get the water height for a neighboring sim.

This value does not vary with wave motion (if you have ripple water turned on in client preferences), i.e. it cannot be used to make realistic boats that bob with waves. Wave rendering (the water rising and falling) is performed by the client, so everyone is seeing a different version of the water dynamics.

While there is not an llOverWater function to detect if land is above water, this fragment of code works the same way, by using llGround.

// Please note that this is subject to the same attachment irregularities as llGround is.
// See the llGround page for more details.

integer OverWater(vector offset)
{
    return (llGround(offset) < llWater(offset));
}

Functions | Simulator | Land


llWhisper

llWhisper(integer channel, string text)

Whispers text on channel. llWhisper range is a 10m radius sphere.

Note: A long string for text will be truncated to 1023 bytes.

Example:
llWhisper(0, "Hello Avatar!");
Chat Output:
Hello, Avatar!

Like avatars, objects can use "/me" on channel 0. Thus, if an object named "Your dog" had a script with the line:
llWhisper(0, "/me wants steak.");
The chat output would be:
Your dog wants steak.

Scripts with listens will get the text without "/me". In the above example, a script listening on channel 0 would get the message "wants steak". There is currently no way for a script to see if the speaker has said "/me".

constants

DEBUG_CHANNEL 2147483647

If using this function to only communicate information back to the script owner, please consider using llOwnerSay instead, to help reduce chat spam (available as of SL 1.6).

Q: Why doesn't my object pick up it's own spoken chat text in listen()?
A: Objects do not 'hear' themselves talk. This helps prevent infinite loops which would probably happen often if objects could hear themselves. For example, if your script returned a message saying 'Unknown command', you could go into an infinite loop easily if you didn't filter out 'Unknown command'. This applies to all of the local chat functions: llWhisper, llSay, and llShout.

Compare with llSay, llShout, llOwnerSay, llInstantMessage, and llMessageLinked

Functions | Chat | Listens | Communications


llWind

vector llWind(vector offset)

Returns the wind velocity at the current position + offset.

Wind affects avatar clothing/hair, trees, and can affect particles and flexible prims. Wind "naturally" (programatically) varies in velocity and direction. Wind does not cause friction.

Example:
vector wind = llWind(<0,0,0>);
llSay(0,"Wind speed: " + (string)llVecMag(wind));

"The wind is based on the 2D stable fluid method described in Jos Stam's article in the Siggraph 1999 proceedings. Each region is its own incompressible simulation but it trades boundary conditions with its neighbors so it is possible for one region to affect another. The consequence of this is that the largest coherent vortex that the wind can form is on the scale of a single region, however wind flowing out of one region will push that in the other so some wind structures are larger than a single sim.

It has some pseudo stable chaos magic injected that keeps it boiling, and the chaos is scaled by the sun position so that the ambient activity varies on a daily schedule. I think there is a small offset that causes the global wind average to point away from the sun around sunrise and sunset, however it has been a long time since I've looked at that code." - Andrew Linden, quoted from Link

Jos Stam's Navier-Stokes equations
Physical based fluid simulation java applet based on Jos Stam's Navier-Stokes equations

Wind used to be affected by an avatar: a fast-moving avatar moving in a large circle inside a sim over a period of time would create an effect similar to a cyclone--just without the gale force behind the wind. This effect has since been removed.

Functions | Simulator | Weather


llXorBase64StringsCorrect

string llXorBase64StringsCorrect(string s1, string s2)

Performs an xor on two Base64 strings and returns a Base64 string. s2 repeats if it is shorter than s1.

This function is a replacement for the deprecated function llXorBase64Strings. See that page for a discussion of the changes between its implementation and that of this function.

This function is for encrypting data. Put your data in s1 and your key in s2 to encrypt or put the encrypted data into s1 with the same key in s2 to decrypt again.

While fine for most SL encryption purposes, Xor'ing a string with a password is considered a very weak encryption algorithm. Do not use this for anything that will cost you RL $ if compromised (such as an automated RL - SL currency exchanger). Also, consider carefully how much damage a malicious party might be able to wreak if your encryption is hacked.

This type of encryption is similar to something called a one time pad that is if s2 is the same length as s1, is generated randomly (not pseudo randomly), and s2 is used only once. When used in this way this function is provably unbreakable. However implementing this function in this way is difficult to say the least. However with a little ingenuity and some creative problem solving we might be able to create different variations of this with acceptable reductions in security with minimal resource requirements(execution time, memory). IE have a new key created for each message maybe based on some universally changing constant like the server timestamp.

As it turns out the quickest way to weaken xor type encryption is to use the key more than once, either by repeating a small key across a longer message or to use the same key on more than one message. It is only strong if the key is used only once. So this type of encryption when used with the same key over and over is supposedly rather trivial to break.

Use llStringToBase64 and llBase64ToString for converting to and from Base64 respectively.

The following is a PHP implementation of llXorBase64StringsCorrect(), useful for "unencrypting" data you've sent as an "encrypted" string to a web server via llHTTPRequest, llEmail or whatever future functions might be released :-)
<?php
// Code by SignpostMarv Martin, with thanks to Hewee Zetkin for bringing to light another case of "Marv's bugs always being typos"
// Trimmed and made more efficient (less superfluous base64_decode/encodes everywhere) by Haravikk
function llXorBase64StringsCorrect($s1, $s2) {
    $s1 = base64_decode($s1); $l1 = strlen($s1);
    $s2 = base64_decode($s2);
    if($l1 > strlen($s2)) $s2 = str_pad($s2, $l1, $s2, STR_PAD_RIGHT);
    return base64_encode($s1 ^ $s2);
}
?>

Q: Why is only the first part of my encrypted message being decrypted correctly on the other end?
A: Check that you don't have two @ symbols (@@) anywhere in the string, for some reason this (and possibly others) break the decryption

Functions | Crypto


Operators

In mathematics, an operator is a symbol that expresses the operation to be performed. In LSL, operators perform simple "common-sense" operations on values. The assignment operators are special in that the left hand side of the operation must be a variable.

Operator Types | Description | Examples
Unary | Operators that act on one value | ++, --, ~, !
Binary | Operators that act on two values. | +, -, *, /, %, +=, -=, *=, /=, %=, &&, ||, !, ==, !=, <=, <, >=, >
Boolean | Operators that work with TRUE and FALSE values. | &&, ||, !, ==, !=, <=, <, >=, >
Bitwise | Operators that work with bitfields | &, |, ~, ^, <<, >>
Assignment | Special operators that assign a value to a variable. | =, +=, -=, *=, /=, %=

All operators in order of execution or precedence, from high to low:

Operation | Description
() [] . | Parentheses, Square Brackets and Dot Operator
! ~ ++ -- | NOT, One's Complement, Increment, Decrement
* / % | Multiply, Divide, Modulus (% is also Vector Cross)
+ - | Addition and Subtraction
<< >> | Left Shift, Right Shift
< <= > >= | Less Than, Less Than Or Equal To, Greater Than, Greater Than or Equal To
== != | Comparison Equal, Comparison Not Equal
& | Bitwise AND
^ | Bitwise XOR
| | Bitwise OR
&& | Comparison AND
|| | Comparison OR
= += -= *= /= %= | Assignment

Math
Operators | Unary | Binary | Bitwise | Boolean | Equality | Assignment


Assignment

The most common arithmetic operation is assignment, denoted with the "=" sign. Loosely translated, it means, take what is found on the right side of the equal sign and assign it to the left side. Any expression that evaluates to a basic type can be used as the right side of an assignment, but the left side must be a normal variable.

The type of the value must agree with the declared type of the variable or the compiler will complain. You may convert the value to the correct type with a typecast.

All basic types support the assignment "=" operator.

// variables to hold a information about the target
key g_target;
vector g_target_postion;
float g_target_distance;

// function that demonstrates assignment
set_globals(key target, vector pos)
{
    g_target = target;
    g_target_position = pos;

    // assignment from the return value of a function
    vector my_pos = llGetPos();
    g_target_distance = llVecDist(g_target_position, my_pos);
}

Note: Like many other languages, LSL supports combining the assignment operator with binary operators.
Operator | Syntax | Description
+= | var1 += var2 | Assigns the value var1 + var2 to the variable var1.
-= | var1 -= var2 | Assigns the value var1 - var2 to the variable var1.
*= | var1 *= var2 | Assigns the value var1 * var2 to the variable var1.
/= | var1 /= var2 | Assigns the value var1 / var2 to the variable var1.
%= | var1 %= var2 | Assigns the value var1 % var2 to the variable var1.
Note: Do not confuse the assignment operator "=" with the equality operator "=="! The compiler will not catch the error, and doing so will always cause the statement to evaluate to TRUE; or will cause the conditional variable to become corrupted.

Other types of operators:
unary, binary, bitwise, boolean

Math | Operators


Binary

Binary operators accept two parameters and return a single value. Most binary operators are defined for many different types of input like list, float, vector.

binary operators

Operator | Type | Meaning | Return Value | Effect
+ | Arithmetic | Addition | Sum of inputs | None
- | Arithmetic | Subtraction | Subtraction of inputs | None
* | Arithmetic | Multiplication | Multiplication of inputs | None
/ | Arithmetic | Division | Division of inputs | None
% | Arithmetic | modulo (remainder) or vector cross product | Remainder after division | None
> | Arithmetic | Greater than | TRUE or FALSE | None
< | Arithmetic | Less than | TRUE or FALSE | None
>= | Arithmetic | Greater than or Equal to | TRUE or FALSE | None
<= | Arithmetic | Less than or Equal to | TRUE or FALSE | None
!= | Logical | Inequality | TRUE or FALSE | None
== | Logical | Equality | TRUE or FALSE | None
&& | Logical | AND | TRUE or FALSE | None
|| | Logical | OR | TRUE or FALSE | None
& | Bitwise | AND | AND of each bit of inputs | None
| | Bitwise | OR | OR of each bit of inputs | None
<< | Bitwise | Left shift | Left shift | None
>> | Bitwise | Right shift | Right shift | None
^ | Bitwise | Exclusive OR | Right shift | None

Like many other languages, LSL supports combining the assignment operator with binary operators.

Operator | Syntax | Description
+= | var1 += var2 | Assigns the value var1 + var2 to the variable var1.
-= | var1 -= var2 | Assigns the value var1 - var2 to the variable var1.
/= | var1 /= var2 | Assigns the value var1 / var2 to the variable var1.
*= | var1 *= var2 | Assigns the value var1 * var2 to the variable var1.
%= | var1 %= var2 | Assigns the value var1 % var2 to the variable var1.

The following tables can be used to calculate the output given the type of the left and right hand operands (A and B respectively) for each operator. Only possible operations are listed..

Operator: +
Left-A | Right-B | Output
integer | integer | An integer, representing the arithmetic result of adding A to B.
integer | float | A float, representing the arithmetic result of adding A to B.
float | integer | A float, representing the arithmetic result of adding A to B.
float | float | A float, representing the arithmetic result of adding A to B.
vector | vector | A vector, representing the values of the three component floats of each vector added together, seperately. For example, if you give A, a value of <a, b, c>, and B a value of <x, y, z>, and add them together, you get <a + x, b + y, c + z>.
rotation | rotation | A rotation, representing the values of the four component floats of each rotation added together, seperately. For example, if you give A a value of <a, b, c, d>, and B a value of <x, y, z, s>, and add them together, you get <a + x, b + y, c + z, d + s>. This is a meaningless operation, with respect to rotation math.
string | string | A string, representing the value of B appended onto the end of A. For example, if A had the value "hello", and B had the value "goodbye", adding them together would give a result of "hellogoodbye".
list | list | A with all the elements of B, in order, appended onto the end of A.
* | list | A List, with A prefixed to the list B. The type of A is maintained inside the new list; A can be any type (other then a list).
list | * | A List, with B appended to the end of list A. The type of B is maintained inside the new list; B can be any type (other then a list).

Operator: -
Left-A | Right-B | Output
integer | integer | An integer, representing the arithmetic result of subtracting B from A.
integer | float | A float, representing the arithmetic result of subtracting B from A.
float | integer | A float, representing the arithmetic result of subtracting B from A.
float | float | A float, representing the arithmetic result of subtracting B from A.
vector | vector | A vector, representing the values of the three component floats of B and A subtracted from each other, seperately. For example, if you give A a value of <a, b, c>, and B a value of <x, y, z>, and subtract them, you get <a - x, b - y, c - z>.
rotation | rotation | A rotation, representing the values of the four component floats of each rotation subtracted from each other, seperately. For example, if you give A a value of <a, b, c, d>, and B a value of <x, y, z, s>, and subtract them, you get <a - x, b - y, c - z, d - s>. This is a meaningless operation, with respect to rotation math.

Operator: *
Left-A | Right-B | Output
integer | integer | An integer, representing the arithmetic result of multiplying A with B.
integer | float | A float, representing the arithmetic result of multiplying A with B.
float | integer | A float, representing the arithmetic result of multiplying A with B.
float | float | A float, representing the arithmetic result of multiplying A with B.
vector | integer | A vector, whose direction has not changed but magnitude has. <A.x * B, A.y * B, A.z * B>
vector | float | A vector, whose direction has not changed but magnitude has. <A.x * B, A.y * B, A.z * B>
vector | vector | A float, representing the sum of the values of the three component floats of each vector multiplied together, separately. For example, if you give A, a value of <a, b, c>, and B, a value of <x, y, z>, and multiply them together, you get ((a * x) + (b * y) + (c * z)). This is known as the dot product of A and B.
vector | rotation | A vector, representing the vector A rotated the amount of B. The magnitude of the vector is multiplied by the square of the magnitude of the rotation.
rotation | rotation | A rotation, representing the combination of two rotations; rotation B applied to rotation A.

Operator: /
Left-A | Right-B | Output
integer | integer | An integer, representing the arithmetic result of devision of A by B.
integer | float | A float, representing the arithmetic result of devision of A of B.
float | integer | A float, representing the arithmetic result of devision of A of B.
float | float | A float, representing the arithmetic result of devision of A of B.
vector | integer | A vector, whose direction has not changed but magnitude has. <A.x / B, A.y / B, A.z / B>
vector | float | A vector, whose direction has not changed but magnitude has. <A.x / B, A.y / B, A.z / B>
vector | rotation | A vector, representing the vector A rotated the inverse amount of B. The magnitude of the vector is multiplied by the square of the magnitude of the rotation. Another way to think of this is that A is restated in terms of the coordinate axes represented by B.
rotation | rotation | A rotation, representing the combination of two rotations; the inverse of rotation B applied to rotation A.

Operator: %
Left-A | Right-B | Output
integer | integer | The remainder of dividing A by B.
vector | vector | Vector cross product of A and B
Operators | Unary | Binary | Bitwise | Boolean | Equality | Assignment


bitwise

Bitwise Operators

Bitwise operators are used to manipulate integers that represent bitfields. Click on an operator below to scroll directly to the position of its description.

Operator | Name | Usage Reference
& | AND | integer & integer = integer
| | OR | integer | integer = integer
~ | NOT | ~integer = integer
^ | XOR | integer ^ integer = integer
>> | Right-shift | integer >> number of bits = integer
<< | Left-shift | integer << number of bits = integer

AND

The AND operation compares each bit in two integers; if both bits are set to "1", then the final result will be "1", otherwise it will be "0". This is done "per-bit", or "bitwise".

Truth Table:
AND | 1 | 0
1 | 1 | 0
0 | 0 | 0

Usage:
// A bitfield whose [[binary]] representation is: 00000000000000000000000000001010
integer a = 10;

// Another bitfield whose binary representation is: 00000000000000000000000000011001
integer b = 25;

// The next line translates to "store the result of AND'ing a and b together" in the variable a_AND_b.
// Its binary representation is 00000000000000000000000000001000, or 8.
integer a_AND_b = a & b;
The reason a_AND_b is 8, is that the & operator compared the two bitfields passed to it, a and b, and turned on only the bits that were both 1. The operation is written out like this:
a      00000000000000000000000000001010 // As decimal = 10
b      00000000000000000000000000011001 // As decimal = 25
-----------------------------------------------------------------
a & b  00000000000000000000000000001000 // As decimal = 8
& is commonly used with the control event, when finding out which buttons were pressed by the user.
For example, if the user is holding down the up arrow:
control(key controller, integer levels, integer edges) {
    // If user is holding down up arrow:
    if ((levels & CONTROL_FWD) == CONTROL_FWD) {

            // Do stuff...
    }
}
Commonly, many buttons are held down at the same time. Using bitfields then makes sence, since each button can be represented by a bit in the levels bitfield.
control(key controller, integer levels, integer edges) {
    // If user is holding down up arrow:
    if ((levels & CONTROL_FWD) == CONTROL_FWD) {
        // The above translates to: "If the CONTROL_FWD bit is 1 in the levels bitfield"
    }

    // If user is holding down down arrow:
    if ((levels & CONTROL_BACK) == CONTROL_BACK) {
        // The above translates to: "If the CONTROL_BACK bit is 1 in the levels bitfield"
    }
}
Here's the operation (levels & CONTROL_BACK), when the user is holding down both the up and down arrows, written out longwise:
levels                00000000000000000000000000000011 // As decimal = 3
CONTROL_BACK          00000000000000000000000000000010 // As decimal = 2
-------------------------------------------------------------------------------
levels & CONTROL_BACK 00000000000000000000000000000010 // As decimal = 2
Back to top

OR

OR places a 1 in the bitfield if either bitfields passed to it have a 1 in that position. If neither has a 1, it places a 0 in that position.

Truth Table:
OR | 1 | 0
1 | 1 | 1
0 | 1 | 0

Usage:
// A bitfield whose binary representation is: 00000000000000000000000000001010
integer a = 10;

// Another bitfield whose binary representation is: 00000000000000000000000000011001
integer b = 25;

// The next line translates to "store the result of OR'ing a and b together" in the variable a_OR_b.
// Its binary representation is 00000000000000000000000000011011, or 27.
integer a_OR_b = a | b;
Here's that operation written out longwise:
a      00000000000000000000000000001010 // As decimal = 10
b      00000000000000000000000000011001 // As decimal = 25
--------------------------------------------------------------------
a | b  00000000000000000000000000011011 // As decimal = 27
OR is useful in combining two bits, a good use of this would be executing some statements if the user has both the up arrow and down arrow pressed in the control event.
control(key controller, integer levels, integer edges) {
    // If user is pressing both the up arrow and down arrow:
    if ((levels & (CONTROL_FWD | CONTROL_BACK)) == (CONTROL_FWD | CONTROL_BACK)) {
        // The above statement translates to: "If levels has both the CONTROL_FWD and CONTROL_BACK bits set to 1"
    }
}

Written out in longhand, the conditional statement looks like this:
The ORing operation, to get a bitfield with both CONTROL_FWD and CONTROL_BACK set to 1.

CONTROL_FWD                  00000000000000000000000000000001 // As decimal = 1
CONTROL_BACK                 00000000000000000000000000000010 // As decimal = 2
-----------------------------------------------------------------------------------------
(CONTROL_FWD | CONTROL_BACK) 00000000000000000000000000000011 // As decimal = 3

The ANDing operation, to see if the both CONTROL_FWD and CONTROL_BACK are set to 1 in levels.
In this case, levels is a bitfield in which CONTROL_FWD, CONTROL_BACK and CONTROL_UP are set to 1.

(CONTROL_FWD | CONTROL_BACK)          00000000000000000000000000000011 // As decimal = 3
levels                                00000000000000000000000000010011 // As decimal = 19
-----------------------------------------------------------------------------------------
levels & (CONTROL_FWD | CONTROL_BACK) 00000000000000000000000000000011 // As decimal = 3

Back to top

NOT

NOT is unlike the other bitwise operators in that it does not perform an operation on two bitfields. It only performs an operation on one. What it does is reverse the bitfield; if a bit was previously 0, it is changed to 1, and vice versa.
Truth Table:
NOT |
1 | 0
0 | 1

Usage:
// A bitfield whose binary representation is: 00000000000000000000000000001010
integer a = 10;

// The next line translates to "store the result of NOTing a" in the variable NOT_a.
// Its binary representation is 11111111111111111111111111110101, or -11.
integer NOT_a = ~a;
Here is that operation written out longwise:

a    00000000000000000000000000001010 // As decimal = 10
-----------------------------------------------------------------------
~a   11111111111111111111111111110101 // As decimal = -11

NOT is commonly used to set bits to 0 in bitfields. To do this, a bit is NOTed to get ~bit. The bitfield is then ANDed with the ~bit.
For example, lets say you wanted to turn off 00000000000000000000000000000010 (2 in decimal) in the bitfield 00000000000000000000000000011010 (26 in decimal).
First you'd get ~00000000000000000000000000000010, which is 11111111111111111111111111111101 (-3 in decimal) then you'd AND ~2 and 26. Here is this in code:

integer bitfield = 26;
integer bit = 2;
bitfield = bitfield & ~bit;

Here are the operations written out longwise:
2       00000000000000000000000000000010
-----------------------------------------------------------------------
~2      11111111111111111111111111111101 // As decimal = -3

26      00000000000000000000000000011010
~2      11111111111111111111111111111101
-----------------------------------------------------------------------
26 & ~2 00000000000000000000000000011000 // As decimal = 24

Back to top

XOR

Short for "eXclusive OR", XOR sets a bit to 1 in the resulting bitfield if, and only if, a bit in that position is set in one of the bitfields passed to it, and a bit in that position is not set in the other bitfield passed to it. It only sets a bit if the bits of the bitfields passed to it arent the same.
Truth Table:
XOR | 1 | 0
1 | 0 | 1
0 | 1 | 0

Usage:
// A bitfield whose binary representation is: 00000000000000000000000000001010
integer a = 10;

// Another bitfield whose binary representation is: 00000000000000000000000000011001
integer b = 25;

// The next line translates to "store the result of XORing a and b together" in the variable a_XOR_b.
// Its binary representation is 00000000000000000000000000010011, or 19.
integer a_XOR_b = a ^ b;
Here's the same operation written out longwise:
a     00000000000000000000000000001010 // As decimal = 10
b     00000000000000000000000000011001 // As decimal = 25
-----------------------------------------------------------------------
a ^ b 00000000000000000000000000010011 // As decimal = 19

XOR is useful when you want to execute code when one condition is true, and one is not. For example, if the up arrow is pressed when the right arrow is not or vice versa.

control(key controller, integer levels, integer edges) {
    // If user is pressing CONTROL_FWD but not CONTROL_RIGHT, or vice versa.
    if (((levels & CONTROL_FWD) == CONTROL_FWD) ^ ((levels & CONTROL_RIGHT) == CONTROL_RIGHT)) {
        // The above translates to: "If CONTROL_FWD is being pressed and CONTROL_RIGHT is
        // not being presed, or CONTROL_FWD is not being pressed and CONTROL_RIGHT is being pressed.
    }
}

When doing bitwise math, XOR works as follows:
4 ^ 2 = 6 in decimal
4 = 100 in binary
2 =  10 in binary
So when we compare them starting at the 1s place, they are both false, so it is a 0. Then at the 2s place, in the 2 it is true and in the 4 it is false, so it return true. Then in the 4s place in the 4 it is true and false in the 2, so it returns true.
100
010
------
110
another example is say
6 ^ 2 = 4 in decimal
6 = 110 in binary
2 =  10 in binary
So when we compare them starting at the 1s place, they are both false, so it is a 0. Then at the 2s place, if they are both true, and since it is the exclusive or, it returns false. Then in the 4s place in the 6 it is true and false in the 2, so it returns true.
110
010
100

A last use for XOR is switching variables. Usually, when you switch variables, you use a temporary variable to hold one place, but with XOT, this temporary variable can be avoided. An example:

Traditional Method:
x = 5
y = 7

w = x
x = y
y = w

x = 7
y = 5

The variable w takes up space and can be avoided with XOR.

XOT method:

x = 5
y = 7

x = x ^ y
y = x ^ y
x = x ^ y

x = 7
y = 5

Binary:

    0101
    0111
    ------
x = 0010 = 2

    0010
    0111
    ------
y = 0101 = 5

    0010
    0101
    ------
x = 0111 = 7

Therefore, x and y are switched without a temporary variable.

Back to top

Right-Shift

This shifts all the bits in an integer to the right by the number of bits specified. Bits shifted out on the right (least significant) end of the word are discarded. Bits shifted in on the left (most significant) end of the word are copies of the word's original most significant bit (sign bit).

Apart from using it for shifting values around in bitfields, this is useful for doing very fast integer divison of positive number by powers of 2. Each bit shifted to the right equals a division by 2. Note that this will always round towards negative infinity; for positive numbers this is the same as rounding down towards zero, but has odd effects on negative numbers. For example, 99>>4 = 6, but (-99)>>4 = -7. Note in particular that (-1)>>1 = -1.

Useage:
// shifts the integer 523 to the right by 2 bits, storing the result in foo
integer foo = 523 >> 2;

Longwise:
a       00000000000000000000001000001011 // decimal 523
----------------------------------------------------------
a >> 2  00000000000000000000000010000010 // decimal 130

Back to top

Left-Shift

This shifts all the bits in an integer to the left by the number of bits specified. Bits will be cut off on the left (most significant) and filled up with 0 on the right.

Apart from using it for shifting values around in bitfields, this is useful for doing very fast integer multiplication by powers of 2. Each bit shifted to the left equals a multiplication by 2. Note that on an overflow on the left, bits will either fall into the value's sign bit (the most significant bit in the 32-bit word) or simply be cut off; in either case, the result will probably not be what you expected.

Useage:
// shifts the integer 523 to the left by 2 bits, storing the result in foo
integer foo = 523 << 2;

Longwise:
a       00000000000000000000001000001011 // decimal 523
----------------------------------------------------------
a << 2  00000000000000000000100000101100 // decimal 2092

Back to top
Operators | Unary | Binary | Bitwise | Boolean | Equality | Assignment


Boolean

Boolean operators behave like other operators such as +, -, *, / etc except the inputs and output can only take on two values. These values can be represented as TRUE and FALSE or 1 and 0 although any non zero value will generally be treated as TRUE.

Unlike most modern programming languages with optimizing compilers, the boolean operators in LSL do not short-circuit. In C, for example, when using ||, if the condition on the left turns out to be true, the computer does not even evaluate the condition on the right; there is no need because we can already tell that the || will return TRUE. Similar logic works for &&: if the first condition is false, there is no need to evaluate the other condition because the && must return FALSE. This leads to speedups in complicated conditionals.

LSL does not do this, so if you're expecting it, it may make your code less efficient, or worse yet, incorrect if you're expecting a function in the second half of an || or && not to be called.

Boolean Operators

Note: Operators that expect boolean values from integers (TRUE or FALSE) will treat all non-zero values as TRUE.
Operator | Name | Syntax | Description
&& | AND | var1 && var2 | Returns TRUE if var1 and var2 are both TRUE, FALSE otherwise. var1 and var2 must both be integers.
|| | OR | var1 || var2 | Returns TRUE if either var1 or var2 are TRUE, FALSE otherwise. var1 and var2 must both be integers.
! | NOT | !var1 | Returns TRUE if var1 is FALSE, or FALSE if var1 is TRUE. var1 must be an integer.
Note:
  • When using == or != on lists, it's important to remember that only the list lengths are compared. So "["a"] == ["b"]" would return TRUE, the same as "llGetListLength(["a"]) == llGetListLength(["b"])".
  • Do not confuse the assignment operator "=" with the equality operator "=="! When comparing two values, use "==". When assigning a value, use "=". The compiler will not catch your error, as it's possible to assign values within an IF statement delibeartely. Compiling a script with "=" rather than "==" will probably cause the statement to evaluate to TRUE and the conditional variable to become corrupted.

These operators are often used in conditional statements.

Q: How do you have a boolean XOR?
A: Use an if() statement:
if((1 || 0) && !(1 && 0)) //if((they are either T and F or T and T) AND (are NOT T and T))

Operators | Unary | Binary | Bitwise | Boolean | Equality | Assignment


Unary

Unary operators take one argument. In LSL these arguments are normally integers.

Operator | Type | Meaning | Return Value | Effect
variable++ | Arithmetic | post-increment | variable | variable is set to variable + 1
++variable | Arithmetic | pre-increment | variable + 1 | variable is set to variable + 1
variable-- | Arithmetic | post-decrement | variable | variable is set to variable - 1
--variable | Arithmetic | pre-decrement | variable - 1 | variable is set to variable - 1
!variable | Logical | Logical NOT | 1 if variable is 0, otherwise 0 | None
-variable | Arithmetic | Unary negation | negative value of variable | None
~variable | Bitwise | Bitwise NOT | flips each bit of variable | None

Examples:

integer x = 5;
llSay(0, (string)(x++));
//Says "5" and x is set to 6
integer x = 5;
llSay(0, (string)(++x));
//Says "6" and x is set to 6
Operators | Unary | Binary | Bitwise | Boolean | Equality | Assignment


State

LSL features states which scripts can define in many ways, switching back and forth between them using the state directive (example: state default;).

States are defined using the keyword state (Example: state foo {...}) with the exception of the "default" state, which is defined by using the keyword default on its own.

All scripts must have a default state, which also must be the first state entered when the script starts. If another state is defined before the default state, the compiler will report a syntax error.

States contain event handlers that are triggered by the LSL virtual machine. All states must supply at least one event handler--it's not really a state without one. If a state is declared without any event handlers, the compiler displays a syntax error.

When the interpreter reaches a state change statement, the event containing the state change is immediately ended. If the current state has a state_exit event, this is executed before the state change takes place. Note: previous versions may have allowed the event containing a state change to complete before the state change takes place, but this is no longer the case.

When a state changes, all pending events are cleared, and many (but not all) events that require setup (via a function) are defaulted (disabled).

state_entry

The state_entry event occurs whenever a new state is entered, including program start, and is always the first event handled. No data is passed to this event handler.

It is a common mistake to assume that the state_entry events is called when an object is rezzed out of inventory. When an object is added into an inventory, the script's current state is saved, so there will be no state_entry during the rez. If startup code is needed every time an object is created, create a global function and call it from both state_entry and the on_rez event.

integer listenHandle; // llListen() handle holder.

// global initialization function.
init()
{
    // Remove the old listen callback:
    llListenRemove(listenHandle);

    // Set up a listen callback for whoever owns this object.
    key owner = llGetOwner();
    listenHandle = llListen(0, "", owner, "");
}

default
{
    state_entry()
    {
        init();
    }

    on_rez(integer start_param)
    {
        init();
    }

    listen(integer channel, string name, key id, string message)
    {
        llSay(0, "Hi " + name + "! You own me.");
    }
}

state_exit

The state_exit event occurs whenever the state command is used to transition to another state. It is handled before the new state's state_entry event.

Provide a state_exit if any kind of cleanup work is necessary before leaving the old state.

// stupid stateful stopwatch

default { // stopped
    touch_start(integer num_detected) {
        state start; // go, go, go!
    }
}

state start {
    state_entry() {
        llResetTime();
    }

    touch_start(integer num_detected) {
        state default; // stop it
    }

    state_exit() {
        llSay(0, "Stopped after " + (string)llGetTime() + " seconds.");
    }
}
The state_exit handler is not called when an object is being deleted; all callbacks, handlers, sounds, etc, will be cleaned up automatically.

States vs. Global Variables

A state and a set of global variables can serve the same purpose, and each can be expressed in terms of the other. In general, using states over global variables allows immediate script state assumption without making comparisons--and the fewer comparisons a script makes, the more regular code statements it can run.

Example 1: States
default { // we don't need to define this as a state, because it's the default name.
    state_entry(){ // state_entry runs first upon entering this state.
        llSetColor(<0,0,0>, ALL_SIDES); // set object colour black.
    }
    touch_start(integer touched){ // touch_start waits for someone to click the object.
        state on; // change to state on.
    }
}

state on { // this isn't the default state, so we need to define it by saying "state on", rather than just "on".
    state_entry(){ // state_entry runs first upon entering this state.
        llSetColor(<1,1,1>, ALL_SIDES); // set object colour white.
    }
    touch_start(integer touched){ // touch_start waits for someone to click the object.
        state default; // change to state default.
    }
}
In this example, we see what at first glance looks pretty complicated, but it's actually fairly straightforward. The default state runs first, with state_entry being the first event run within that state. So the first thing the script does is what's inside state_entry -- in this case, it uses llSetColor to set the object's colour white. The script then waits for input from the touch_start event.

When a user clicks on the object, touch_start is triggered, and the contents are run. In this case, it switches to the state on. From that point, the script does exactly what it did in default -- it runs what's in state_entry and then waits for input from touch_start.

Example 2: Global Variable
integer on = FALSE; // variables declared before a state mean they're global variables;
                    // they will be recognized by all functions within the script.
default {
    touch_start() {
        if (on == TRUE) { // check to see if the variable "on" is TRUE.
            llSetColor(<0,0,0>, ALL_SIDES); // if it IS, set object colour black.
            on = FALSE; // then set "on" to be FALSE.
        }
        else { // we can do "else" here, because whether or not "on" is TRUE is a yes/no question. It either is or it's not.
            llSetColor(<1,1,1>, ALL_SIDES); // set object colour white.
            on = TRUE; // then set "on" to be TRUE.
        }
    }
}

In this example, we only have a single state. Replacing the second one is a global variable, an integer named on, which starts with a value of FALSE. Upon running the script for the first time, nothing will happen -- it will merely wait for someone to click on it. When clicked, the touch_start event will run, and an IF/ELSE statement will check the value of on.

If on is found to have a value of TRUE, the object's color is set to black, and on's value is changed to FALSE. Otherwise, the inverse happens: the object color becomes white, and on becomes TRUE.

Conclusion:
Both these scripts perform the same basic function, but one will run more efficiently than the other: Example 1, using states, because it doesn't have to ever check anything. Nothing needs to be compared, and no variables need to have their values set. By using states rather than a variable, the script's logic path is linear--it just does one thing after another. In Example 2, the logic path branches, as the script checks and resets the values of its variable.

Not all scripts can necessarily be improved by using states rather than global variables and, frequently, the inverse is also true. Both states and global variables have their place, depending on the situation. As a rule, the less complex logic your script has, the faster it will run, and the better (less lag) it will be for the sim. If you're doing something like the above example, using states is usually going to be simpler, as long as you have a good grasp of the order in which the different events are triggered. When writing a script, if you're unsure, look up the respective entries to see when a given event will be triggered.

Q: Can I switch states within a user-defined function?
A: For all intents and purposes, no. There is a way to do it, though it is officially unsupported and may be broken at any time in the future.

Q: Can a state call itself (eg. calling state default; while in the default state) to "restart" that state?
A: While it won't give a script error, it effectively does nothing. Calling a state from within itself does not cause a state change, and will not trigger state_entry.

Note: You can create a second state that will send the script back to the state it was called from. For example, if you wish to restart the default state, create another state called restart_default which upon the state_entry it would call the default state.

default
{
    state_entry()
    {
        llSay(0,"Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        state restart_default;
    }
}

state restart_default
{
    state_entry()
    {
        state default;
    }
}

While this is a little clunky, it will allow you to restart the default state. - BurnmanBedlam

Events | Global Variables


default

A particular setting or value for a variable that is assigned automatically. (source)

default is the state a script enters into when it is first executed or reset. The state_entry event is triggered in the default state every time it is reset (compiled or via llResetScript).

Note: state_entry is not triggered for an inactive script when it is compiled.

value | state


Types

Variables, function parameters, and return values have types. This means that a value stored in a variable, bound to a function parameter, or returned by a function must be of the same type, or it must be converted to the same type.

LSL provides a set of seven basic types that are used throughout the language:

Type | Brief Description
integer | A whole number ranging from -2,147,483,648 to 2,147,483,647
float | A decimal number ranging from 1.175494351E-38 to 3.402823466E+38
vector | Three floats in the form < x , y , z >. Usually a position, color, or Euler rotation
rotation | A quaternion rotation, made up of 4 floats, < x , y , z , s >
key | A UUID (specialized string) used to identify something in SL, notably an agent, object, sound, texture, other inventory item, or dataserver request
string | A sequence of characters, limited only by the amount of free memory available to the script.
list | A heterogeneous list of the other data types.

To convert between different types of values, use typecasting. An explicit typecast looks like this: ((newType)expression). Implicit typecasting will convert integers to floats or strings to keys.

For example, to say an integer, it must be converted to a string:
integer x = 5;
llSay(0, (string)x);

Constant | Value | Function
TYPE_INTEGER | 1 | Indicates that the list entry is holding an integer.
TYPE_FLOAT | 2 | Indicates that the list entry is holding a float.
TYPE_STRING | 3 | Indicates that the list entry is holding a string.
TYPE_KEY | 4 | Indicates that the list entry is holding a key.
TYPE_VECTOR | 5 | Indicates that the list entry is holding a vector.
TYPE_ROTATION | 6 | Indicates that the list entry is holding a rotation.
TYPE_INVALID | 0 | Indicates that this wasn't a valid list entry. Occurs when passing an index greater than the length of the list or less than the negative length of the list.

This function can be used to loosely determine the type of a variable. Make sure to improve it if you plan to use it for userdata.
// Writen by Chad Statosky
integer GetType(string var)
{
    integer n =     llGetListLength(llParseStringKeepNulls(var, ["1", "2", "3", "4"], [])) - 1;
            n +=    llGetListLength(llParseStringKeepNulls(var, ["5", "6", "7", "8"], [])) - 1;
            n +=    llGetListLength(llParseStringKeepNulls(var, ["9", "0", ".", "<"], [])) - 1;
            n +=    llGetListLength(llParseStringKeepNulls(var, [">", " ", ",", "-"], [])) - 1;
    if(n == llStringLength(var))
    {
        if(llSubStringIndex(var, "<")  != -1 || llSubStringIndex(var, ">") != -1)
        {
            if(llGetListLength(llParseStringKeepNulls(var, [","], [])) == 3)    return TYPE_VECTOR;
            else                                                                return TYPE_ROTATION;
        }
        else
        {
            if(llSubStringIndex(var, ".") != -1)                                return TYPE_FLOAT;
            else                                                                return TYPE_INTEGER;
        }
    }
    else
    {
        if(llStringLength(var) == 36 &&
                llGetListLength(llParseStringKeepNulls(var, ["-"], [])) == 5)   return TYPE_KEY;
        else                                                                    return TYPE_STRING;
    }
}

Functions | Constants


Integer

An integer is a signed, 32-bit value with valid range from -2147483648 to 2147483647. LSL does not have an unsigned integer type.

Examples:
10
122
-59438
0x5465

An integer is a whole number (no decimals). If you want decimals, you want a float.

As of Second Life 1.5, integers can also be entered in hexadecimal (e.g. 0xff, 0x10, 0xF0AB, 0x20baef49 etc...) Strings typecast to integers also take these formats into account.

Example:
integer dec = 42; // 42 decimal
integer hex = 0xff; // 255 decimal

Octal support was added in 1.5.0 and removed in 1.5.2 because it caused incompatabilities with some existing scripts. The Lindens have not mentioned any plans to bring it back.

To round a float to an integer, use llRound.

Beware, a integer variable assigned a value larger/smaller than the valid range is instead assigned a value of -1.
// Watch out! This will compile, but will result in x having the value of -1.
integer x = 12345678900000;

Types | Bitwise | Bitfield | Binary


Float

A float is an IEEE-754 32-bit floating point value ranging from ??1.401298464E-45 to ??3.402823466E+38.

Examples:
9.9
10.0
100000.1

LSL floats can be specified in scientific notation, e.g. 1.123E-2 or 512E+3.

Additionally, the letter f or F can be appended. This is a relic from C, where the f is needed to specify for the compiler that it is a float and not a double (LSL only uses floats).

A 32-bit float is divided into a 24-bit signed "mantissa" (containing the actual significant digits) and an 8-bit signed "exponent" (representing the magnitude that the mantissa will be multiplied with). A 23-bit (1 bit lost for sign) mantissa gives a precision equivalent to approximately 7 decimal digits (more exactly log 10(223)). This means values are rarely exactly stored and, often more disturbingly, addition or subtraction of two numbers of vastly different magnitudes might yield unexpected results, as the mantissa can't hold all the significant digits.

As per IEEE-754, LSL supports negative zero, there is currently no way of detecting negative zero, except by typecasting to a string.

Example:
340000000.0 + 4.0 = 340000000.0
The correct answer (340000004.0) has 9 significant digits, thus the last two digits are lost.

Some functions have tooltips within the LSL editor that say "F32", indicating a 32-bit float, rather than simply "float". However, all floats in LSL are 32-bit. So if it says "F32", it may be read as just "float".

Certain things are picky about whether they are passed a float or an integer. To make an integer into a float, add .0 to the end. Other functions don't require this, and so the joys of non-uniform functions can be experienced. However, it is necessary to explicitly cast a float to an integer, since precision is lost.

There are 4 techniques for converting a float to an integer.

Function Name | Purpose
(integer) | Returns the integer portion of the float, ignoring the decimal.
llRound | Rounds float around 1/2 (1.2 rounds to 1, 1.6 rounds to 2, -2.5 rounds to -3).
llCeil | Rounds float up to an integer, partially equivelent to typecast.
llFloor | Rounds down to an integer, partially equivelent to typecast.

Note: using the typecast equivalent, if available, is going to be faster and use less memory.

Note Details: If speed is a concern (integer) should always be used.
Typecasting uses slightly less memory(10-20b)
Function Name | Slower | Sign | Code
llFloor | 50% | + | (integer)float
llRound | 30% | - | (integer)(float - 0.5)
llRound | 30% | + | (integer)(float + 0.5)
llCeil | 50% | - | (integer)float
For the interested reader, there is a good article about the floating standard:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
So before you compain about problems with floats at least read the first few pages of the article :P

Q: Is there a non-lossy way to convert a float to a string and then back to a float again so that the resulting float has the same value as the original float?
A: I am not fully sure, but I strongly suspect not since I seem to remember IEEE floats use base 2 for the mantissa (and exponent), which cannot be losslessly converted to base 10 that the string would display, meaning truly binary-equivalent conversion to string (and therefore communication via strings) would be impossible. Someone please double-check this. (The same would go for the equivalent questions in vector and rotation, and if true, this answer should probably go here, not three places.) -?
AA: While the answer to this question is closely related to the answers to the equivalent questions on the vector and rotation pages, the technique used to "serialize" vectors and rotations would be more involved at the very least including some method of dismantling them into their component floats. I think this FAQ is useful on the vector and rotation pages independent of its presence on this page. -GordonPrior

I will go ahead and make a serialization page. Mono will probably obsolete any hacks we come up with to accomplish serialization in LSL but then again Mono will obsolete most of the pages on this wiki. -?

Q: What is a NaN?
A: See NaN.
Types | Binary


Vector

A vector is 3 float values used together as a single item--usually representing a direction or magnitude. In LSL, vector literals are formatted as <x,y,z>. A vector can be used to represent a 3-dimensional position, direction, velocity, force, impulse, scale, color, or rotation (in Euler format). Each component can be accessed via '.x', '.y', and '.z' (see below).

The value for a NULL vector is <0.00000, 0.00000, 0.00000> (or just <0.0, 0.0, 0.0> to keep it simple).

LSL does do some implicit typecasting but the LSL compiler does not actualy convert types or simplify code; it just makes implicit typecasts explicit. So while <0, 0, 0> is valid, it will use more bytecode and be slower then <0.0, 0.0, 0.0>. For this reason please be sure to use a decimal point. 0. is enough you don't need an extra zero.

Example vector literal: <.9,1,.25>

vector a = <1,2,3>;
llOwnerSay((string)a); //<1.00000, 2.00000, 3.00000>

Vector Arithmetic Operators

+ | Add two vectors together | <1,2,3> + <2,0,3> = <1+2, 2+0, 3+3> = <3,2,6>
- | Subract one vector from another | <1,2,3> - <2,0,3> = <1-2, 2-0, 3-3> = <-1,2,0>
* | Vector dot product | <1,2,3> * <4,5,6> = (1 * 4) + (2 * 5) + (3 * 6) = 32
% | Vector cross product | <1,2,3> % <4,5,6> = <2*6 - 3*5, 3*4 - 1*6, 1*5 - 2*4> = <-3, 6, -3>

Note: if a vector and a float are multipled together with the * operator, this will scale the vector by the float. <1, 2, 3> * 5 yields the resulting vector <5,10,15>.

A vector can be rotated by using the * operator and a rotation:
vector v;
vector result;
rotation r;

result = v * r; // rotate vector v by rotation r

To change a single float component of a vector, declare a variable, then address it by referring to the x, y, or z components in the form "vector_name.x", "vector_name.y" and "vector_name.z". These components will be treated as if they were ordinary floats.

Example:
vector foo = llGetPos();
foo.z += 1;
// or:
foo.z += <0,0,1>;
// or
foo.z++;
//or
++foo.z;

Any of the above methods will cause 1 to be added to the .z component of foo, although the last way is the fastest.

Functions

Function | Purpose
llVecDist | Gets the Euclidean distance between 2 vectors
llVecMag | Identical to llVecDist with the second vector being <0,0,0>
llVecNorm | Returns a normalized vector of the same direction as the input
llLookAt | Rotates the object to look at the specified vector position

Verifying a vector in a string

This function determines if a string is a vector. It uses another function to verify if a string is a decimal.
// Returns TRUE if the string is a vector
integer strIsVector(string str) //SL 1.17.2
{
    str = llStringTrim(str, STRING_TRIM);//remove whitespace

    if(llGetSubString(str, 0, 0) != "<" || llGetSubString(str, -1, -1) != ">")
        return FALSE;//make sure angle brackets exist at first and last index

    integer commaIndex = llSubStringIndex(str, ",");//position of the first comma

    if(commaIndex == -1 || commaIndex == 1)
        return FALSE;//if there is no first comma OR if the first comma is right after the first '<' (i.e.: "<,3,3>")

    if( !strIsDecimal(llGetSubString(str, 1, commaIndex - 1)) || llGetSubString(str, commaIndex - 1, commaIndex - 1) == " " )
        return FALSE;//if the substring inbetween the first '<' and the first comma (exclusive) isn't a decimal OR
                     //the last character of the substring inbetween the first '<' and the first comma (exclusive) is a space
                     //note 1: having a space after the substring that corresponds to one of the x, y or z attributes
                     //causes the (vector) typecast operator to return a zero vector. i.e.: (vector)"<3.0 ,5,9>" == ZERO_VECTOR is TRUE
                     //on the other hand, (vector)"< 3.0,5,9>" == ZERO_VECTOR would be FALSE. weird, huh? =]
                     //note 2: the (vector) typecast operator only accepts decimal values and not hexidecimal value.
                     //for example, (vector)"<0xFE8,4.0,3>" == ZERO_VECTOR is true.

    str = llDeleteSubString(str, 1, commaIndex);//get rid of the substring containing the x attribute and the following delimiter (the first comma)

    commaIndex = llSubStringIndex(str, ",");//position of the second comma

    if(commaIndex == -1 || commaIndex == 1 || commaIndex == llStringLength(str) - 2 ||
     //no second comma found OR the second comma is too close to the first comma (i.e.: "<3,,3>") OR the second comma is too close to the '>' OR...
       !strIsDecimal(llGetSubString(str, 1, commaIndex - 1)) || llGetSubString(str, commaIndex - 1, commaIndex - 1) == " " ||
    //... the substring containing the y attribute isn't a decimal OR the substring containing the y attribute ends in a space OR...
       !strIsDecimal(llGetSubString(str, commaIndex + 1, -2)) ||  llGetSubString(str, -2, -2) == " ")
    //... the substring containing the z attribute isn't a decimal OR the subtring containing the z attribute ends in a space
    //testing if the substring that should hold the z attribute is a decimal covers having "too many" attributes
    //i.e.: "<1,2,3,4>" would be parsed by this function as having "3,4" for the z attribute, which obviously is not a decimal
    //and causes the above conditional to evaluate TRUE
        return FALSE;

    return TRUE;
}

//returns TRUE if the string is a decimal
integer strIsDecimal(string str)
{
    str = llStringTrim(str, STRING_TRIM);

    integer strLen = llStringLength(str);

    integer i;
    if(llGetSubString(str,0,0) == "-" && strLen > 1)
        i = 1;//no need to test for "-": following code will take care of that
    else
        i = 0;

    integer decimalPointFlag = FALSE;

    for(; i < strLen; i++)
    {
        string currentChar = llGetSubString(str, i, i);

        if(currentChar == ".")
            if(decimalPointFlag)
                return FALSE;
            else
                decimalPointFlag = TRUE;
        else if(currentChar != "3" && currentChar != "6" && currentChar != "9" &&
                currentChar != "2" && currentChar != "5" && currentChar != "8" && // the order dosen't matter
                currentChar != "1" && currentChar != "4" && currentChar != "7" && currentChar != "0")
            return FALSE;
    }

    return TRUE;
}

More Information

For more on vectors, see this general vector tutorial. This is not an LSL-specific tutorial; rather, it's an in-depth explanation of the mathematics involved. It's good, but is not required reading for working with vectors in LSL.

Q: When dealing with individual components of vectors, is it possible to do it without assigning a value to a variable?
Say, something like, "float foo = llGetPos().z"?
A: No, you must assign a variable first, then refer to that:
vector foo = llGetPos();
float bar = foo.z;

Q: Is there a non-lossy way to convert a vector to a string and then back to a vector again so that the resulting vector has the same value as the original vector?
A: See: Serialization
Types | Binary | Rotation | UserDefinedFunction


Rotation

A rotation is a variable type comprising 4 floats used together as a single item. This data is interpreted as a quaternion. As with vectors, each component can be accessed via '.x', '.y', '.z', and '.s' (not '.w').

Syntax:
float x = 0.0;
float y = 0.0;
float z = 0.0;
float s = 1.0;
rotation rot_a = <0.0, 0.0, 0.0, 1.0>;//copies these values into the respecive values of the rotation.
rotation rot_b = <x,y,z,s>;//copies the values of the variable into the rot.
rotation rot_c = rot_b;//copies the value of rot_b into rot_c

LSL does do some implicit typecasting but the LSL compiler does not actualy convert types or simplify code; it just makes implicit typecasts explicit. So while <0, 0, 0, 1> is valid, it will use more bytecode and be slower than <0.0, 0.0, 0.0, 1.0>. For this reason please be sure to use a decimal point. 0. is enough you don't need an extra zero.

There are a number of ways to visualize an arbitrary rotation in three dimensions. The simplest is to think of a rotation as being equivalent to a set of 3 rotations around the x, y, and z axes (known as the Euler representation). In LSL this can be represented using the vector type, where the x element specifies the roll (angle of rotation around the x-axis), the y element specifies the pitch (angle of rotation around the y-axis), and the z element specifies the yaw (angle of rotation around the z-axis). Also see Banking.

Unfortunately, the Euler representation has drawbacks when it comes to combining rotations (see below). To avoid these problems, LSL represents rotations using mathematical entities known as quaternions, which consists of 4 elements: x, y, z, and s. Note that the x, y, and z elements do not correspond to roll, pitch, and yaw. For more information on what they represent, see the page on quaternions.

However, you can use rotations without dealing with the individual elements of quaternions. LSL offers library calls that convert between a quaternion and a vector containing the equivalent Euler representation: llEuler2Rot and llRot2Euler.

Note: LSL expects angles to be specified in terms of radians rather than degrees. A radian is the angle reached if you were to take a string the length of a circle's radius and lay it along the circumference, approximately equal to 57.296 degrees. This ratio means that a full circle, which contains 360 degrees, is equal to 2*PI radians. Similarly, a semicircle of 180 degrees equals PI radians. LSL defines the constants DEG_TO_RAD and RAD_TO_DEG to facilitate conversion to and from degrees.

Example:
vector eul = <0,0,45>; //45 degrees around the z-axis, in Euler form
eul *= DEG_TO_RAD; //convert to radians
rotation quat = llEuler2Rot(eul); //convert to quaternion
llSetRot(quat); //rotate the object

LSL also defines the constants PI_BY_TWO, PI, and TWO_PI to let you specify common rotations in radians directly:

vector x_ninety = <PI_BY_TWO,0,0>; //90 degrees around the x-axis
vector y_one_eighty = <0,PI,0>; //180 degrees around the y-axis

LSL defines the constant ZERO_ROTATION to represent a rotation of angle zero. Calling llSetRot( ZERO_ROTATION ) orients the object so that its local axes are aligned with the global axes. The value returned by llGetRot is the object's current orientation relative to this null rotation.

Combining Rotations

An object is rotated by multiplying its current orientation with the desired rotation. The order in which the operands are specified depends if a rotation is performed around the global axes or the local axes.

Example:
// a rotation of 45 degrees around the x-axis
rotation x_45 = llEuler2Rot( <45 * DEG_TO_RAD, 0, 0> );

rotation new_rot = llGetRot() * x_45;  // compute global rotation
llSetRot(new_rot);                  // orient the object accordingly
This rotates the object around the global x-axis (the axis which runs from west to east).

Now consider the following:
rotation new_rot = x_45 * llGetRot();  // compute local rotation
llSetRot(new_rot);                   // orient the object accordingly
This rotates the object around its local x-axis, which depends on its current orientation. Think of this as specifying the rotation from the object's point of view, that is, relative to the direction it is currently facing.

This also works Inversely by Dividing two rotations:
rotation new_rot = x_45 / llGetRot();  // compute local rotation
llSetRot(new_rot);                   // orient the object accordingly
Like previously, this would rotate your object along the x-axis based on its current orientation, but in the opposite direction.

When rotating a vector, the rotation must appear to the right of the vector:
vector new_vec = old_vec * x_45; // compiles
vector new_v = x_45 * old_v; // doesn't compile

Note: An object can be rotated around an arbitrary point by multiplying a vector by a rotation in the manner described above. The vector should be the difference between the object's current position and the desired "center-point" of rotation. Take the result of the multiplication and add it to the point of rotation. This vector will be the "new location" the object should be moved to.

vector currentPos = llGetPos();
vector rotPoint = llGetPos() + <1, 1, 1>; // in global coordinates
vector newPos = rotPoint + ((currentPos - rotPoint) * x_45);
llSetPos(newPos);

Bear in mind that any translation (position) operation can result in a vector that would put the object outside of the world, or require a move further than 10 meters--so plan for these possibilities.

Math Functions

Function Name | Purpose
llAngleBetween | Returns the angle between two rotations
llAxes2Rot | Converts three axes to a rotation
llAxisAngle2Rot | Returns the rotation made by rotating by an angle around an axis
llEuler2Rot | Converts a vector euler rotation into a quaternion rotation
llList2Rot | Returns rotation from an element of a list
llRot2Angle | Returns the angle of a rotation
llRot2Axis | Returns the axis of a rotation
llRot2Euler | Converts a quaternion into a euler rotation
llRot2Fwd | Returns a unit vector representing the forward axis after a rotation
llRot2Left | Returns a unit vector representing the horizontal axis after a rotation
llRot2Up | Returns a unit vector representing the vertical axis after a rotation
llRotBetween | Returns the smallest angle (as a rotation) between two vectors

World Functions

Function Name | Purpose
llApplyRotationalImpulse | Applies a rotational impulse
llDetectedRot | Returns the rotation of detected object or agent
llGetCameraRot | Gets the rotation of a user's camera
llGetLocalRot | Gets the local rotation of the object
llGetOmega | Returns the current rotational velocity
llGetPrimitiveParams | Gets rotation as well as many other params
llGetRootRotation | Gets the global rotation of the root object
llGetRot | Gets the global rotation of the object
llGetTextureRot | Returns the texture rotation of a side of an object
llGetStatus | Get wheter an object can be rotated
llLookAt | Set the target for object to rotate to look at
llRezAtRoot | Rez an object, specifying rotation
llRezObject | Rez an object, specifying rotation
llRotateTexture | Sets the rotation of a texture a side of an object
llRotLookAt | Sets the target rotation of an object
llRotTarget | Set rotational target for an object
llRotTargetRemove | Remove rotational target for an object given its handle
llSetForceAndTorque | Set rotational and linear force of a physical object
llSetLocalRot | Sets the local rotation
llSetPrimitiveParams | Set rotation as well as many other params
llSetRot | Sets the global rotation
llSetStatus | Set whether object can be rotated, among other parameters
llSetTorque | Sets rotational force of a physical object
llSetVehicleRotationParam | Sets the vehicle rotation parameter
llSitTarget | Sets the sit target for an object, specifying rotation
llStopLookAt | Cancel rotation started by llLookAt or llRotLookAt
llTargetOmega | client-side smooth rotation

Events

Event Name | Purpose
at_rot_target | when object comes within target angle
not_at_rot_target | when rotation target is set but object is not there
changed | when texture is changed, but not when object rotates
control | when avatar rotates left or right

Q: Is there a non lossy way to convert a rotation to a string and then back to a rotation again so that the resulting rotation has the same value as the original rotation?
A: See: Serialization

Q: I see 1/rot sometimes. What does that mean, exactly?
A: do you mean <0,0,0,1>/rot ? that inverts the quaternion. Or do you mean <0,0,1>/rot? which multiples the vector by the inverse quaternion.

It seems that, for some strange reason, things that rotate don't necessarily update their rotation immediately. You have to grab them in edit mode to actually see the new rotation occur if the new rotation is under a certain threshhold. Physicall, the object is still rotated, it just doesn't update on the screen. Is there any way to force the new rotation to update and be visible? -- Myra Loveless

Functions | Types | Constants | Transform | Child Rotation | Quaternion | Vector | Euler | LibraryRotationFunctions | Dynamics | Interpolate | Joint


Key

The key type is a specialized string, a unique identifier that can be used to reference objects, inventory items and agents in Second Life.

Sometimes also referred to as UUID (Universal Unique IDentifier), UID, or asset-ID, a key can be represented as a string of hexadecimal numbers in the format "00000000-0000-0000-0000-000000000000" (for example "66864f3c-e095-d9c8-058d-d6575e6ed1b8"). A key is 36 characters long when converted to a string in LSL (32 hexadecimal digits + 4 dashes).

There are 2^128 possible key combinations, yielding 340,282,366,920,938,463,463,374,607,431,768,211,456 possible keys. Because of this, there is little worry over SL running out of keys for objects, scripts, sounds, and textures.

Because a key is really just a specialized string, any string can be stored into a key variable, though it shouldn't be expected to work in a function that needs a UUID if the key variable contains "Hello". Note that keys must be explicitly typecast to strings, even though they are implicitly typecast from strings. If data needs to be typecast from any other type to a key, it must first be cast to a string.

That being said, if a UUID is not needed to be passed in a llMessageLinked call, a second string can be passed as long as it is cast as a key and then recast back to a string in the receiving link_message event.

Example:
llMessageLinked(LINK_SET, 5, "String one", (key)"String two");

This hack can be pretty useful, but keep in mind that you're relying on things that might change at any time when using it.

Note that in some cases, you don't need to explicitly state (string)keyname as you do when converting between a string and an integer, for instance. It's good coding practice to always do so, however, as many functions will not work properly unless you do.

string null_key = NULL_KEY;

integer isKey(key in)
{//by: Strife Onizuka
    if(in) return 2;
    return (in == null_key);
}

key forceKey(key in)
{//force a string or key to be a valid key assuming you want invalids to become NULL_KEY
    if(in) return in;
    return null_key;
}

Note: Some keys change under certain circumstances, while others can be trusted to NEVER change. The following Table illustrates which keys can be trusted and which may change.
Key Type | Changes? | When it changes
Agent | NO | Never Changes
Animation | NO | Never Changes
Notecard | YES | When Edited or Saved
Object | YES | Rezzed
Object (attached) | YES | Avatar object is attached to logs in or teleports
Script | YES | Any time a change or new copy is made
Sound | NO | Never Changes
Texture | NO | Never Changes
Anyone want to contribute to this table, correct mistakes, or add more Types?

Function | Purpose
llDetectedKey | returns key of detected object
llGetKey | gets object key
llGetInventoryKey | gets an object inventory item key
llGetOwnerKey | gets owner key
llGetOwner | gets key of the owner
llKey2Name | converts a key to a name (in-sim only)

See Client Asset Keys for default assets like textures (GUI, "missing", terrain, etc), animations, sounds, etc.

Types | Client Asset Keys


String

A string is a sequence of characters limited in length only by available memory. Strings are enclosed in quotation marks ("). LSL uses UTF-8 for its encoding standard for strings.

Example:
string foo = "bar"; // this defines a string named "foo", containing the text "bar".

Strings can be concatenated using the plus sign (+) operator.

The hack to increase max stored list size works for strings as well:

myString = (myString="") + myString + "new_item";

Example:
// This adds the text "anotherstring"
// and the result of (string)PI_BY_TWO to the end of an existing string.
mystring = yourstring + "anotherstring" + (string)PI_BY_TWO;
(In this example, the text "(string)PI_BY_TWO" converts the float constant PI_BY_TWO to a string. This process is called typecasting.)

Escape Characters

In LSL, strings can contain some backslash (\) escape characters:

Substring | Replaced with
\t | four spaces
\n | new line
\" | double quote
\\ | backslash

These are just a subset of the backslash escape characters found in any other language. If a backslash is used in a string, and it is not paired up with one of the above characters and it will be treated as "\\". These escape characters only work in compiled scripts. In other words, a listener cannot be used to find the "new line" escape sequence and then treat it as the compiler would. However, a predefined string containing "\n" can be substituted for an input escape character sequence.

Functions
These functions are only a few of the many that use strings, but they're the ones that manipulate strings.

Function Name | Purpose
llDeleteSubString | Removes a slice of a string
llDumpList2String | Turns a list into a string
llParseString2List llParseStringKeepNulls | Turns a string into a list
llGetSubString | Extracts a part of a string
llInsertString | Inserts a string into a string
llToLower | Converts a string to lowercase
llToUpper | Converts a string to UPPERCASE
llStringLength | Gets the length of a string
llStringTrim | Trims leading and/or trailing spaces from a string
llSubStringIndex | Finds the position of a string in another string
llEscapeURL | Returns the string that is the URL-escaped version of url (replacing spaces with %20, etc)
llUnescapeURL | Returns the string that is the URL unescaped version of url, replacing "%20" with spaces, etc.

Functions | Types


List

Instead of arrays, LSL uses lists. The list type is exactly what it sounds like: a heterogeneous list of the other data types. Lists are created via comma-separated values (CSV) of the other data types. enclosed by square brackets: "[" and "]". See below for examples of the syntax.

Lists not only store the value of the list item, but also its type (see llGetListEntryType and llCSV2List). To directly enter a float value into a list, a decimal point (.) must be used.

Because they store multiple value types, lists are not accessed with square brackets like arrays are. They are read by accessor functions that specify the type of value attempting to be retrieved (e.g. llList2Integer) and written with accessor functions that insert or replace values (or ranges of values) based on new list variables (e.g. llListReplaceList).

Note: while the LSL compiler will only accept code with a maximum of 72 items in a list, lists can actually support as many items as a script's memory will allow. If a list of more than 72 predefined items is needed, just concatenate (combine) two predefined lists into a new one: longlist = firstpart + secondpart;

Lists cannot contain other lists, so using them like a multidimensional array is not possible natively (although several ways around this are available, notably this script). Also, strided lists can be used.

Example:
//a list with a string, integer, float, vector and rotation
list l = ["somestring",12,3.0,<3.3,4,0>,<0,0,0,1>];

Lists can be concatenated by using +.
Example:
newlist = oldlist + ["oneitem","anotheritem"];

Thus, to add an element to the (end) of an existing list:
Example:
myList = myList + [new_item];

or

myList += [new_item];

This voodoo magic will allow appending new elements in a memory efficient fashion (thanks to BlindWanderer):
myList = (myList=[]) + myList + ["new_item"];
Don't ask me why but I can save 3-4KB of memory with adding 90 elements. - PsykePhaeton

To clear a list, set it equal to nothing:
Example:
myList = [];

To search a list for a value, use llListFindList
list myList = ["A","B","C","D","E","F"];
string searchFor = "C";

integer index = llListFindList( myList, [searchFor] );

if ( index != -1 )
    llOwnerSay( searchFor + " was found in myList at position " + (string)index );
else
    llOwnerSay( searchFor + " was not found in myList" );

Refer to a list's elements by their index number. Lists have 0-based indexing, where the first element has index 0.
Example:
list myList = ["A","B","C"];
string element;

element = llList2String(myList,0);
// element now has the value "A".

element = llList2String(myList,1);
// element now has the value "B".

element = llList2String(myList,2);
// element now has the value "C".

The value llGetListLength() returns the number of elements in the list. Therefore, the last element has index = (llGetListLength(myList) - 1).
Example:
list myList = ["A","B","C"];
integer listLength = llGetListLength(myList);
// listLength equals 3.

string element = llList2String(myList,listLength);
// myList doesnt have an element at index 3 - this will return an empty string.

string element = llList2String(myList,listLength - 1);
// element now has the value "C".

Negative numbers can also be used to count backwards in a list. Therefore, the last element has index -1. The first element would also have an index of (-llGetListLength(myList))
Example:
list myList = ["A","B","C"];
integer listLength = llGetListLength(myList);
// listLength equals 3.

string element = llList2String(myList,-listLength);
// element now has the value "A".

string element = llList2String(myList,-1);
// element now has the value "C".

Functions

Function Name | Purpose
llGetListLength | Gets the number of elements in a list
llCSV2List | Converts comma-separated values (CSV) to a list
llList2CSV | Converts a list to comma-separated values (CSV)
llDeleteSubList | Removes a portion of a list
llList2List | Returns a portion of a list
llListFindList | Returns the position (index) of the first instance of a sublist in a list
llListInsertList | Inserts a list into a list
llListReplaceList | Replaces a part of a list with another list
llDumpList2String | Dumps a list to a string with a specified separating string
llParseString2List | Parses a string to a list, using two lists of separators
llParseStringKeepNulls | Similar to llParseString2List, but adds null values between spaces
llList2ListStrided | Extracts a subset of a strided list
llListSort | Sorts a list ascending or descending, as appropriate to type.
llListStatistics | Performs statistical operations on a list composed of integers and floats.
llListRandomize | Randomizes a list
llGetListEntryType | Gets the type of entry of an element in list
llList2Float | Returns the float element
llList2Integer | Returns the integer element
llList2Key | Returns the key element
llList2Rot | Returns the rotation element
llList2String | Returns the string element
llList2Vector | Returns the vector element

Strided Lists

Since lists cannot contain items of the list type, the only way to get something similar to a multidimensional array is using strides. A strided list is a regular list with items grouped together in a fixed layout.

Say you wanted to make a visitor tracker and keep track of visitor's names, time spent on your land, and the date they visited last. You'd use a strided list, where each stride consists of three items:

integer STRIDELENGTH = 3; // this is very helpful when dealing with strided lists to keep your code flexible
list visitors = ["Ama Omega", 5, "2004-06-12", "Catherine Omega", 12, "2004-06-28", "Ezhar Fairlight", 25, "2004-06-30"];
//

Whenever you manipulate this list, you do it in complete strides, so to add another visitor to the list:
visitors += ["Mistress Midnight", 1, "2004-06-30"];

To sort the list by names in ascending order:
visitors = llListSort(visitors, STRIDELENGTH, TRUE);

To remove one stride from the list:
list DeleteSubListStrided(list src, integer start, integer end, integer stride) {
    return llDeleteSubList(src, start * stride, (end * stride) + stride - 1);
}

visitors = DeleteSubListStrided(visitors, 0, 0, STRIDELENGTH);

To replace a stride in the list:
list UpdateSubListStrided(list src, list stride, integer start, integer len) {
    return llListReplaceList(src, stride, start, len-1);
}

The following functions have direct support for strided lists:
  • llList2ListStrided
  • llListRandomize
  • llListSort

More examples of functions dealing with strided lists can be found on this page. LibraryStridedLists

Q & A

Q: Does this function have O(1) time? I want to use it a lot on some big lists.
A: No, it takes much longer with growing list size. Better get used to LSL being painfully slow when manipulating large data sets (strings by far being the worst).
Re-Question: So, does that mean llListInsertList and llDeleteSubList actually make copies, i.e., reallocate, the entire list they're operating on?
Re-Answer: Yup. LSL is a pass-by-value language. Any information passed to a function is copied to that function's stack-frame before the function is run. This means that in practical terms, you need twice as much memory to insert or delete a sublist. (See the notes on the llListInsertList and llDeleteSubList pages to eliminate some confusion about the "manipulation" they do.)

Q: Why are there so many llList2* functions?
A: LSL2 doesn't support run-time typing, meaning you have to actually specify the type of each variable. The Lindens could have implemented a single function, llList2String, then required explicit typecasting, as in (integer)llList2String(foo, 0). However, that isn't as memory- or CPU-efficient as llList2Integer(foo,0) (yes, really) and the amount of work required for the Lindens to implement seperate functions for each type was negligible.
You can still typecast values stored in a list, of course, but it's advised to use the llList2* functions, unless it's a vector or rotation stored as a string, in which case you should cast to a vector or rotation after using llList2String. llList2Vector and llList2Rotation do not cast from strings automatically. See the next question for more.

Q: Have the old problems concerning the odd behavior of llList2Vector and llList2Rot when requesting a TYPE_STRING element from the list been fixed yet?
A: No. If a list element is a string containing text that would otherwise be directly typecast to a vector or rotation, It will not be converted. The resulting vector or rotation will be ZERO_VECTOR or ZERO_ROTATION respectively. This is a bug. To get around it, use llList2String to retrieve elements from your list, then cast them to vectors or rotations as needed: (vector)llList2String(positions,2);

Lists can be conveniently passed as a single string using the CSV functions. For example, this is useful for sending complex data between objects via listen or within linked objects with llMessageLinked. Or not, if you would be passing data with vectors, rotations, or strings with commas. llDumpList2String and llParseString2List are recommended for safe passing of lists. For an example of this method see ExampleListConversion.

Functions | Types


AgentandAvatar

Agent & Avatar

user's presence in Second Life is represented by two entities: an agent and an avatar.

An agent is a client's presence within a simulator. All users connect to a sim as an agent. An agent is not an avatar, though it is represented on the client by one. An agent can be an object creator/owner and/or a group member. An agent "sees" the world through a camera and has an avatar.

An avatar is a visual representation of an agent within Second Life; it is the body other users see and interact with. Avatars can wear clothing, have bodyparts, and constantly play animations (if they didn't, they would just stand with their legs spread wide and their arms stretched out to the sides in a neutral position). Avatars can have attachments and are affected by physics.

The terms "agent" and "avatar" are often used interchangeably. While this is technically incorrect, even Linden Lab has neglected the differences between these two entities when naming LSL's built-in functions, so it's more practical to make a page combining the two.

Agent Functions

llAddToLandPassList
llDialog
llEjectFromLand
llGiveMoney
llInstantMessage
llRequestAgentData
llSameGroup
llSetSitText
llSetTouchText
llTeleportAgentHome

Agent Events

dataserver Triggered by llRequestAgentData.
money Triggered by paying an object.

Agent & Avatar Functions

llAvatarOnSitTarget
llGetAgentInfo
llGetPermissions
llGetPermissionsKey
llKey2Name
llReleaseControls
llRequestPermissions
llSetCameraAtOffset
llSetCameraEyeOffset
llSitTarget
llTakeControls
llUnSit

Agent & Avatar Events

changed Triggered by sit.
control
run_time_permissions

Avatar Functions

llAttachToAvatar
llDetachFromAvatar
llGetAgentSize
llGetAnimation
llGetAnimationList
llGetAttached
llPointAt
llStartAnimation
llStopAnimation
llStopPointAt

Avatar Events

attach

Subcategories:

Chat
Animations
Money
Sitting
Teleporting
Damage

Q & A:
Q: How do I animate an avatar?
A: See llStartAnimation.

Q: How do I move an avatar to another position?
A: An avatar can be moved by calling llMoveToTarget in an attachment's script. You also can use either llMoveToTarget or llSetPos to move an avatar that's sitting on an object. llPushObject can be used to move avatars without the need for an attachment, however the function should not be used for malicious purposes - if the user does not know that he/she will be pushed, they have valid grounds to file an abuse report.

Q: How do I rotate an avatar to face a different direction or look at an object?
A: Unfortunately, as of SL 1.6, you can't. Currently, the only way to fake rotation is to have an avatar sit on an object and then rotate the object. Note however that there is no way to force an avatar to sit on an object; rather, they must do so by right-clicking on the object and selecting "sit" from the pie menu.

Q: How do I find out whether an agent is sitting/flying/walking/in mouselook/etc?
A: See llGetAgentInfo.
A2: llGetAnimation can be used to detect some of these modes, but it will only return a single animation state, not the bitmask that llGetAgentInfo returns.

Q: How do I find out how tall an avatar is?
A: See llGetAgentSize.
A2: llGetBoundingBox can also be used but it has a few limitations.

Functions | Events | User


Camera

SL includes both first-person and third-person camera modes. By disconnecting the camera from the avatar (to around 50m max away [see Notes below]), a lot more can be done otherwise. Some uses of the camera functions are for vehicles and machinima.

Functions

Function | Description
llClearCameraParams | Resets all camera values to their defaults.
llSetCameraParams | Sets camera parameters.
llForceMouselook | Sets camera to mouselook if an avatar sits on the object.
llGetCameraPos | Gets current camera position for agent.
llGetCameraRot | Gets current camera rotation for agent.
llReleaseCamera | Returns camera to agent.
llSetCameraAtOffset | Sets camera offset (position) to look at if an avatar sits on the object.
llSetCameraEyeOffset | Sets camera eye offset if an avatar sits on the object.

Camera Modes

  1. seated avatar
    1. normal sitting cam (llSetCameraEyeOffset, llSetCameraAtOffset)
    2. scripted "follow cam" (llSetCameraParams)
  2. standing avatar
    1. normal avatar cam
    2. scripted "follow cam"
  3. alt-zoom
    1. another object or avatar
    2. a fixed point in space or the ground
  4. customize avatar mode
  5. mouselook

Hitting Esc in modes 3-5 will pop out to modes 1-2, depending on the situation (whether you are sitting or not, for example).

When grabbing/touching, the camera mode is 3b where the camera is locked to a fixed point in space. It isn't a special "grab" camera but just a variation of alt-zoom.

Scripted camera controls with llSetCameraParams are not active when in alt-zoom, mouselook, or customize avatar mode.

Notes

  • Camera controls are only supported for scripts in objects that are attached to an avatar, or upon which an avatar is sitting. Attempts to use them in stand-alone objects result in the error: "Camera control currently only supported for attachments and objects on which you are sitting." (1.13.1 (5)). Camera tracking, however, will work with unattached or non-vehicle objects.
  • It seems like the camera does not always release properly when unsitting an avatar even though permissions are lost. Its useful to call llReleaseCamera before every llUnsit();.
  • The camera can often not behave as one would desire. For example, when zooming in on a masked-textured prim/object, the camera may move beyond it if the mouse was clicked on a transparent part of the masked texture.
  • The camera may zoom in unexpectedly when rotated around due to its collision detection with the ground and invisible (transparent) objects.
  • You can unlock the 50m limit by going into the Debug menu (press Ctrl+Alt+D to activate) in the Client tab, and by clicking "Disable Camera Constraints".

Q: How do I take a snapshot with LSL?
A: You can't. You need both a client (to render the image) and a user (to push the button) to take a snapshot. Short of some sort of macro system running on your computer, there's no way to automate this process.

Functions | Agent/Avatar


Collisions

In SL, a collision occurs when two objects make contact (impact, collide) in-world. Phantom objects do not cause collision events unless llVolumeDetect is used, except in the case of land collisions.

A script can have a stack/heap collision.

Attachments will not trigger collision events if the object itself collides with something else. However, if the avatar collides with the object, the collision event is triggered.

Note: collisons are not "touches", which is when a user clicks ("touches") on the object with their mouse cursor.

Function | Purpose
llCollisionFilter | Limits collisions to a specific name or key, so only collisions with a certain object(s) or agent(s) will trigger them.
llCollisionSound | Replaces or disables the sound the object makes when something collides with it.
llCollisionSprite | Replaces or disables the particles the object emits when something collides with it.
llDetectedGrab | Returns the direction the detected object is being dragged in.
llDetectedGroup | Returns TRUE if the detected agent/object is in the same active user group.
llDetectedKey | Returns the key of the detected agent/object.
llDetectedLinkNumber | Returns the link number that triggered the collison/touch event.
llDetectedName | Returns the name of the detected agent/object.
llDetectedOwner | Returns the key of the detected agent/object's owner.
llDetectedPos | Returns the position of the detected agent/object.
llDetectedRot | Returns the rotation of the detected agent/object.
llDetectedType | Returns the detected object type (AGENT, ACTIVE, PASSIVE, SCRIPTED).
llDetectedVel | Returns the velocity of the detected agent/object.
llPassCollisions | In a linked object, passes collison events from a child prim containing a collision event to the parent.
llVolumeDetect | Turns the object phantom and triggers collision_start and collision_end events when an agent/object intersects it.

Event | Triggered
collision | while agent/object is colliding with the object containing the script
collision_start | when agent/object starts colliding with with the object containing the script
collision_end | when agent/object stops colliding with with the object containing the script
land_collision | while the object containing the script is colliding with the ground
land_collision_start | when the object containing the script starts colliding with the ground
land_collision_end | when the object containing the script stops colliding with the ground

Resources
  • tunneling, Andrew Linden, 5/29/05
Functions | Events | Detection | Physics


Color

In LSL, color, a component of light, is specified as a vector, containing 3 floating point values, ranging from 0.0 to 1.0, each for red, green, and blue.

Example: vector color = <1,.5,0>; // 100% red, 50% green, 0% blue = orange

Functions

llGetColor
llSetColor
llGetPrimitiveParams
llSetPrimitiveParams
llSetLinkColor

The following functions will convert from an RGB color, with values from 0 to 255, to SL color, and back.
vector sl2rgb( vector sl )
{
    sl *= 255;                //Scale the SL color up by 255
    return <(integer)sl.x, (integer)sl.y, (integer)sl.z>;    //Make each part of it a whole number
}

vector rgb2sl( vector rgb )
{
    return rgb / 255;        //Scale the RGB color down by 255
}

You can also use Pick Color to convert from RGB (0-255) to SL (0-1) color scale or divide the RGB vector by 255 if you want to figure it out manually.

Color Table
Not every possible color is listed here. These are the infamouse web-safe colors. Find the color you would like to use here and copy it into your scripts.

<0.0, 0.0, 0.0> | <0.0, 0.0, 0.2> | <0.0, 0.0, 0.4> | <0.0, 0.0, 0.6> | <0.0, 0.0, 0.8> | <0.0, 0.0, 1.0>
<0.0, 0.2, 0.0> | <0.0, 0.2, 0.2> | <0.0, 0.2, 0.4> | <0.0, 0.2, 0.6> | <0.0, 0.2, 0.8> | <0.0, 0.2, 1.0>
<0.0, 0.4, 0.0> | <0.0, 0.4, 0.2> | <0.0, 0.4, 0.4> | <0.0, 0.4, 0.6> | <0.0, 0.4, 0.8> | <0.0, 0.4, 1.0>
<0.0, 0.6, 0.0> | <0.0, 0.6, 0.2> | <0.0, 0.6, 0.4> | <0.0, 0.6, 0.6> | <0.0, 0.6, 0.8> | <0.0, 0.6, 1.0>
<0.0, 0.8, 0.0> | <0.0, 0.8, 0.2> | <0.0, 0.8, 0.4> | <0.0, 0.8, 0.6> | <0.0, 0.8, 0.8> | <0.0, 0.8, 1.0>
<0.0, 1.0, 0.0> | <0.0, 1.0, 0.2> | <0.0, 1.0, 0.4> | <0.0, 1.0, 0.6> | <0.0, 1.0, 0.8> | <0.0, 1.0, 1.0>
<0.2, 0.0, 0.0> | <0.2, 0.0, 0.2> | <0.2, 0.0, 0.4> | <0.2, 0.0, 0.6> | <0.2, 0.0, 0.8> | <0.2, 0.0, 1.0>
<0.2, 0.2, 0.0> | <0.2, 0.2, 0.2> | <0.2, 0.2, 0.4> | <0.2, 0.2, 0.6> | <0.2, 0.2, 0.8> | <0.2, 0.2, 1.0>
<0.2, 0.4, 0.0> | <0.2, 0.4, 0.2> | <0.2, 0.4, 0.4> | <0.2, 0.4, 0.6> | <0.2, 0.4, 0.8> | <0.2, 0.4, 1.0>
<0.2, 0.6, 0.0> | <0.2, 0.6, 0.2> | <0.2, 0.6, 0.4> | <0.2, 0.6, 0.6> | <0.2, 0.6, 0.8> | <0.2, 0.6, 1.0>
<0.2, 0.8, 0.0> | <0.2, 0.8, 0.2> | <0.2, 0.8, 0.4> | <0.2, 0.8, 0.6> | <0.2, 0.8, 0.8> | <0.2, 0.8, 1.0>
<0.2, 1.0, 0.0> | <0.2, 1.0, 0.2> | <0.2, 1.0, 0.4> | <0.2, 1.0, 0.6> | <0.2, 1.0, 0.8> | <0.2, 1.0, 1.0>
<0.4, 0.0, 0.0> | <0.4, 0.0, 0.2> | <0.4, 0.0, 0.4> | <0.4, 0.0, 0.6> | <0.4, 0.0, 0.8> | <0.4, 0.0, 1.0>
<0.4, 0.2, 0.0> | <0.4, 0.2, 0.2> | <0.4, 0.2, 0.4> | <0.4, 0.2, 0.6> | <0.4, 0.2, 0.8> | <0.4, 0.2, 1.0>
<0.4, 0.4, 0.0> | <0.4, 0.4, 0.2> | <0.4, 0.4, 0.4> | <0.4, 0.4, 0.6> | <0.4, 0.4, 0.8> | <0.4, 0.4, 1.0>
<0.4, 0.6, 0.0> | <0.4, 0.6, 0.2> | <0.4, 0.6, 0.4> | <0.4, 0.6, 0.6> | <0.4, 0.6, 0.8> | <0.4, 0.6, 1.0>
<0.4, 0.8, 0.0> | <0.4, 0.8, 0.2> | <0.4, 0.8, 0.4> | <0.4, 0.8, 0.6> | <0.4, 0.8, 0.8> | <0.4, 0.8, 1.0>
<0.4, 1.0, 0.0> | <0.4, 1.0, 0.2> | <0.4, 1.0, 0.4> | <0.4, 1.0, 0.6> | <0.4, 1.0, 0.8> | <0.4, 1.0, 1.0>
<0.6, 0.2, 0.0> | <0.6, 0.2, 0.2> | <0.6, 0.2, 0.4> | <0.6, 0.2, 0.6> | <0.6, 0.2, 0.8> | <0.6, 0.2, 1.0>
<0.6, 0.4, 0.0> | <0.6, 0.4, 0.2> | <0.6, 0.4, 0.4> | <0.6, 0.4, 0.6> | <0.6, 0.4, 0.8> | <0.6, 0.4, 1.0>
<0.6, 0.6, 0.0> | <0.6, 0.6, 0.2> | <0.6, 0.6, 0.4> | <0.6, 0.6, 0.6> | <0.6, 0.6, 0.8> | <0.6, 0.6, 1.0>
<0.6, 0.0, 0.0> | <0.6, 0.0, 0.2> | <0.6, 0.0, 0.4> | <0.6, 0.0, 0.6> | <0.6, 0.0, 0.8> | <0.6, 0.0, 1.0>
<0.6, 0.8, 0.0> | <0.6, 0.8, 0.2> | <0.6, 0.8, 0.4> | <0.6, 0.8, 0.6> | <0.6, 0.8, 0.8> | <0.6, 0.8, 1.0>
<0.6, 1.0, 0.0> | <0.6, 1.0, 0.2> | <0.6, 1.0, 0.4> | <0.6, 1.0, 0.6> | <0.6, 1.0, 0.8> | <0.6, 1.0, 1.0>
<0.8, 0.0, 0.0> | <0.8, 0.0, 0.2> | <0.8, 0.0, 0.4> | <0.8, 0.0, 0.6> | <0.8, 0.0, 0.8> | <0.8, 0.0, 1.0>
<0.8, 0.2, 0.0> | <0.8, 0.2, 0.2> | <0.8, 0.2, 0.4> | <0.8, 0.2, 0.6> | <0.8, 0.2, 0.8> | <0.8, 0.2, 1.0>
<0.8, 0.4, 0.0> | <0.8, 0.4, 0.2> | <0.8, 0.4, 0.4> | <0.8, 0.4, 0.6> | <0.8, 0.4, 0.8> | <0.8, 0.4, 1.0>
<0.8, 0.6, 0.0> | <0.8, 0.6, 0.2> | <0.8, 0.6, 0.4> | <0.8, 0.6, 0.6> | <0.8, 0.6, 0.8> | <0.8, 0.6, 1.0>
<0.8, 0.8, 0.0> | <0.8, 0.8, 0.2> | <0.8, 0.8, 0.4> | <0.8, 0.8, 0.6> | <0.8, 0.8, 0.8> | <0.8, 0.8, 1.0>
<0.8, 1.0, 0.0> | <0.8, 1.0, 0.2> | <0.8, 1.0, 0.4> | <0.8, 1.0, 0.6> | <0.8, 1.0, 0.8> | <0.8, 1.0, 1.0>
<1.0, 0.0, 0.0> | <1.0, 0.0, 0.2> | <1.0, 0.0, 0.4> | <1.0, 0.0, 0.6> | <1.0, 0.0, 0.8> | <1.0, 0.0, 1.0>
<1.0, 0.2, 0.0> | <1.0, 0.2, 0.2> | <1.0, 0.2, 0.4> | <1.0, 0.2, 0.6> | <1.0, 0.2, 0.8> | <1.0, 0.2, 1.0>
<1.0, 0.4, 0.0> | <1.0, 0.4, 0.2> | <1.0, 0.4, 0.4> | <1.0, 0.4, 0.6> | <1.0, 0.4, 0.8> | <1.0, 0.4, 1.0>
<1.0, 0.6, 0.0> | <1.0, 0.6, 0.2> | <1.0, 0.6, 0.4> | <1.0, 0.6, 0.6> | <1.0, 0.6, 0.8> | <1.0, 0.6, 1.0>
<1.0, 0.8, 0.0> | <1.0, 0.8, 0.2> | <1.0, 0.8, 0.4> | <1.0, 0.8, 0.6> | <1.0, 0.8, 0.8> | <1.0, 0.8, 1.0>
<1.0, 1.0, 0.0> | <1.0, 1.0, 0.2> | <1.0, 1.0, 0.4> | <1.0, 1.0, 0.6> | <1.0, 1.0, 0.8> | <1.0, 1.0, 1.0>

RGB, Hex, Float Conversion

Hex | Int | Float | Hex | Int | Float | Hex | Int | Float | Hex | Int | Float
0x00 | 0 | 0.000 | 0x40 | 64 | 0.250 | 0x80 | 128 | 0.501 | 0xC0 | 192 | 0.752
0x01 | 1 | 0.003 | 0x41 | 65 | 0.254 | 0x81 | 129 | 0.505 | 0xC1 | 193 | 0.756
0x02 | 2 | 0.007 | 0x42 | 66 | 0.258 | 0x82 | 130 | 0.509 | 0xC2 | 194 | 0.760
0x03 | 3 | 0.011 | 0x43 | 67 | 0.262 | 0x83 | 131 | 0.513 | 0xC3 | 195 | 0.764
0x04 | 4 | 0.015 | 0x44 | 68 | 0.266 | 0x84 | 132 | 0.517 | 0xC4 | 196 | 0.768
0x05 | 5 | 0.019 | 0x45 | 69 | 0.270 | 0x85 | 133 | 0.521 | 0xC5 | 197 | 0.772
0x06 | 6 | 0.023 | 0x46 | 70 | 0.274 | 0x86 | 134 | 0.525 | 0xC6 | 198 | 0.776
0x07 | 7 | 0.027 | 0x47 | 71 | 0.278 | 0x87 | 135 | 0.529 | 0xC7 | 199 | 0.780
0x08 | 8 | 0.031 | 0x48 | 72 | 0.282 | 0x88 | 136 | 0.533 | 0xC8 | 200 | 0.784
0x09 | 9 | 0.035 | 0x49 | 73 | 0.286 | 0x89 | 137 | 0.537 | 0xC9 | 201 | 0.788
0x0A | 10 | 0.039 | 0x4A | 74 | 0.290 | 0x8A | 138 | 0.541 | 0xCA | 202 | 0.792
0x0B | 11 | 0.043 | 0x4B | 75 | 0.294 | 0x8B | 139 | 0.545 | 0xCB | 203 | 0.796
0x0C | 12 | 0.047 | 0x4C | 76 | 0.298 | 0x8C | 140 | 0.549 | 0xCC | 204 | 0.800
0x0D | 13 | 0.050 | 0x4D | 77 | 0.301 | 0x8D | 141 | 0.552 | 0xCD | 205 | 0.803
0x0E | 14 | 0.054 | 0x4E | 78 | 0.305 | 0x8E | 142 | 0.556 | 0xCE | 206 | 0.807
0x0F | 15 | 0.058 | 0x4F | 79 | 0.309 | 0x8F | 143 | 0.560 | 0xCF | 207 | 0.811
0x10 | 16 | 0.062 | 0x50 | 80 | 0.313 | 0x90 | 144 | 0.564 | 0xD0 | 208 | 0.815
0x11 | 17 | 0.066 | 0x51 | 81 | 0.317 | 0x91 | 145 | 0.568 | 0xD1 | 209 | 0.819
0x12 | 18 | 0.070 | 0x52 | 82 | 0.321 | 0x92 | 146 | 0.572 | 0xD2 | 210 | 0.823
0x13 | 19 | 0.074 | 0x53 | 83 | 0.325 | 0x93 | 147 | 0.576 | 0xD3 | 211 | 0.827
0x14 | 20 | 0.078 | 0x54 | 84 | 0.329 | 0x94 | 148 | 0.580 | 0xD4 | 212 | 0.831
0x15 | 21 | 0.082 | 0x55 | 85 | 0.333 | 0x95 | 149 | 0.584 | 0xD5 | 213 | 0.835
0x16 | 22 | 0.086 | 0x56 | 86 | 0.337 | 0x96 | 150 | 0.588 | 0xD6 | 214 | 0.839
0x17 | 23 | 0.090 | 0x57 | 87 | 0.341 | 0x97 | 151 | 0.592 | 0xD7 | 215 | 0.843
0x18 | 24 | 0.094 | 0x58 | 88 | 0.345 | 0x98 | 152 | 0.596 | 0xD8 | 216 | 0.847
0x19 | 25 | 0.098 | 0x59 | 89 | 0.349 | 0x99 | 153 | 0.600 | 0xD9 | 217 | 0.850
0x1A | 26 | 0.101 | 0x5A | 90 | 0.352 | 0x9A | 154 | 0.603 | 0xDA | 218 | 0.854
0x1B | 27 | 0.105 | 0x5B | 91 | 0.356 | 0x9B | 155 | 0.607 | 0xDB | 219 | 0.858
0x1C | 28 | 0.109 | 0x5C | 92 | 0.360 | 0x9C | 156 | 0.611 | 0xDC | 220 | 0.862
0x1D | 29 | 0.113 | 0x5D | 93 | 0.364 | 0x9D | 157 | 0.615 | 0xDD | 221 | 0.866
0x1E | 30 | 0.117 | 0x5E | 94 | 0.368 | 0x9E | 158 | 0.619 | 0xDE | 222 | 0.870
0x1F | 31 | 0.121 | 0x5F | 95 | 0.372 | 0x9F | 159 | 0.623 | 0xDF | 223 | 0.874
0x20 | 32 | 0.125 | 0x60 | 96 | 0.376 | 0xA0 | 160 | 0.627 | 0xE0 | 224 | 0.878
0x21 | 33 | 0.129 | 0x61 | 97 | 0.380 | 0xA1 | 161 | 0.631 | 0xE1 | 225 | 0.882
0x22 | 34 | 0.133 | 0x62 | 98 | 0.384 | 0xA2 | 162 | 0.635 | 0xE2 | 226 | 0.886
0x23 | 35 | 0.137 | 0x63 | 99 | 0.388 | 0xA3 | 163 | 0.639 | 0xE3 | 227 | 0.890
0x24 | 36 | 0.141 | 0x64 | 100 | 0.392 | 0xA4 | 164 | 0.643 | 0xE4 | 228 | 0.894
0x25 | 37 | 0.145 | 0x65 | 101 | 0.396 | 0xA5 | 165 | 0.647 | 0xE5 | 229 | 0.898
0x26 | 38 | 0.149 | 0x66 | 102 | 0.400 | 0xA6 | 166 | 0.650 | 0xE6 | 230 | 0.901
0x27 | 39 | 0.152 | 0x67 | 103 | 0.403 | 0xA7 | 167 | 0.654 | 0xE7 | 231 | 0.905
0x28 | 40 | 0.156 | 0x68 | 104 | 0.407 | 0xA8 | 168 | 0.658 | 0xE8 | 232 | 0.909
0x29 | 41 | 0.160 | 0x69 | 105 | 0.411 | 0xA9 | 169 | 0.662 | 0xE9 | 233 | 0.913
0x2A | 42 | 0.164 | 0x6A | 106 | 0.415 | 0xAA | 170 | 0.666 | 0xEA | 234 | 0.917
0x2B | 43 | 0.168 | 0x6B | 107 | 0.419 | 0xAB | 171 | 0.670 | 0xEB | 235 | 0.921
0x2C | 44 | 0.172 | 0x6C | 108 | 0.423 | 0xAC | 172 | 0.674 | 0xEC | 236 | 0.925
0x2D | 45 | 0.176 | 0x6D | 109 | 0.427 | 0xAD | 173 | 0.678 | 0xED | 237 | 0.929
0x2E | 46 | 0.180 | 0x6E | 110 | 0.431 | 0xAE | 174 | 0.682 | 0xEE | 238 | 0.933
0x2F | 47 | 0.184 | 0x6F | 111 | 0.435 | 0xAF | 175 | 0.686 | 0xEF | 239 | 0.937
0x30 | 48 | 0.188 | 0x70 | 112 | 0.439 | 0xB0 | 176 | 0.690 | 0xF0 | 240 | 0.941
0x31 | 49 | 0.192 | 0x71 | 113 | 0.443 | 0xB1 | 177 | 0.694 | 0xF1 | 241 | 0.945
0x32 | 50 | 0.196 | 0x72 | 114 | 0.447 | 0xB2 | 178 | 0.698 | 0xF2 | 242 | 0.949
0x33 | 51 | 0.200 | 0x73 | 115 | 0.450 | 0xB3 | 179 | 0.701 | 0xF3 | 243 | 0.952
0x34 | 52 | 0.203 | 0x74 | 116 | 0.454 | 0xB4 | 180 | 0.705 | 0xF4 | 244 | 0.956
0x35 | 53 | 0.207 | 0x75 | 117 | 0.458 | 0xB5 | 181 | 0.709 | 0xF5 | 245 | 0.960
0x36 | 54 | 0.211 | 0x76 | 118 | 0.462 | 0xB6 | 182 | 0.713 | 0xF6 | 246 | 0.964
0x37 | 55 | 0.215 | 0x77 | 119 | 0.466 | 0xB7 | 183 | 0.717 | 0xF7 | 247 | 0.968
0x38 | 56 | 0.219 | 0x78 | 120 | 0.470 | 0xB8 | 184 | 0.721 | 0xF8 | 248 | 0.972
0x39 | 57 | 0.223 | 0x79 | 121 | 0.474 | 0xB9 | 185 | 0.725 | 0xF9 | 249 | 0.976
0x3A | 58 | 0.227 | 0x7A | 122 | 0.478 | 0xBA | 186 | 0.729 | 0xFA | 250 | 0.980
0x3B | 59 | 0.231 | 0x7B | 123 | 0.482 | 0xBB | 187 | 0.733 | 0xFB | 251 | 0.984
0x3C | 60 | 0.235 | 0x7C | 124 | 0.486 | 0xBC | 188 | 0.737 | 0xFC | 252 | 0.988
0x3D | 61 | 0.239 | 0x7D | 125 | 0.490 | 0xBD | 189 | 0.741 | 0xFD | 253 | 0.992
0x3E | 62 | 0.243 | 0x7E | 126 | 0.494 | 0xBE | 190 | 0.745 | 0xFE | 254 | 0.996
0x3F | 63 | 0.247 | 0x7F | 127 | 0.498 | 0xBF | 191 | 0.749 | 0xFF | 255 | 1.000
Texture | Light


Communications

These are functions and events that allow scripts to communicate with users, other scripts, and with scripts and programs running on computers outside SL. Depending on what you're trying to do, you may have some leeway when choosing which methods to use. When faced with a choice, these benchmarks can demonstrate the performance differences of one method over another.

Chat
llDialog
llListen
llListenControl
llListenRemove
llOwnerSay
llRegionSay
llSay
llShout
llWhisper

Event: listen

Cryptography
llBase64ToString
llMD5String
llStringToBase64
llXorBase64StringsCorrect
llXorBase64Strings
llModPow

Email
llEmail
llGetNextEmail

Event: email
Instant Messaging
llInstantMessage

Link Messages
llMessageLinked

Event: link_message

Text
llSetSitText
llSetText
llSetTouchText

XML-RPC
llCloseRemoteDataChannel
llOpenRemoteDataChannel
llRemoteDataReply
llRemoteDataSetRegion
llSendRemoteData

Event: remote_data

HTTP
llHTTPRequest
llLoadURL
llEscapeURL
llUnescapeURL
Event: http_response

Q: How do I know which method to use? This is complicated!
A: Consult the following chart of methods and who or what can recieve them:

Method | Script Delay | Object owner | Other users | Other prims | Scripts in the same prim | Send to computers outside SL | Receive from outside SL | Maximum Payload (characters) | Comment
Chat: Whisper, Say, Shout | No | Yes | Yes | Yes | No | No | No | 1023 | Must be within chat distance to be able to receive.
Chat: llRegionSay | No | No | No | Yes | No | No | No | 1023 | Simwide, does not work on channel 0.
llOwnerSay | No | Yes | No | No | No | No | No | 1023 | Owner must be in the same sim.
llDialog Create | Yes | Yes | Yes | No | No | No | No | 512 | Only the directed user can receive and they must be in the sim.
llDialog Response | No | Yes | Yes | Yes | No | No | No | 1023 | Receiver must be within chat distance of where the dialog box was created.
Instant Messages | Yes | Yes | Yes | No | No | No | No | 1023 |
Link Messages | No | No | No | No | Yes | No | No | limited only by memory (~8000 max. in practical use) | Only scripts contained within a given linked object may receive.
Email | Yes | No | No | Yes | Yes | Yes | Yes | 1000 | link messages are better for intra-object communication.
XML-RPC | No | No | No | No | No | No | Yes | 255 + one 32-bit integer | Only connections from an external computer to SL can be initiated.
HTTP | No | No | No | No | No | Yes | Yes | 2048 | Only connections from SL to a non-Linden Lab server can be initiated. You must have a script on external server (whether in PHP or MySQL or *CGI) to process the GET or POST data sent by llHTTPRequest()
There are also several alternatives to these methods of communication, but they have a limited number of uses.

Q: So I want my attachment to talk to me and only me. What should I use?
A: llOwnerSay. If you need it to silently communicate with another user, use llInstantMessage. Remember that you can use multiple communications methods in the same script.

Functions | Events | Chat | Email | XML-RPC


controls

Controls can be various things in SL, from UI elements to functions to events to constants. The avatar and camera can be specifically controlled through LSL but another type of control is flow control. Also, vehicles and weapons tend to have special controls (usually with mouselook and/or a HUD).

Functions
llClearCameraParams
llSetCameraParams
llReleaseControls
llTakeControls

Events
control

Constants
PERMISSION_CONTROL_CAMERA
PERMISSION_TAKE_CONTROLS

Agent/Avatar | Flow Control | Permissions


detected

Detection

Detection functions (llDetected*) determine information about objects and agents detected by sensors, touch, and collision events.

All detection functions have one integer parameter in common, which is an index (ranging from 0 to total_number - 1) of the found targets of a sensor, touch, or collision event. Negative index values do not work, nor do ones that are equal to or greater than the number of entries taken from the event paramater. If an event does not specify this value it is zero. When an index falls outside the valid range, default values are returned.

If a detection function is called from a user-defined function, the function inherits the data from the event that called it.

Detection Functions

llDetectedGrab
llDetectedGroup
llDetectedKey
llDetectedLinkNumber
llDetectedName
llDetectedOwner
llDetectedPos
llDetectedRot
llDetectedType
llDetectedVel

Modifying Functions

llCollisionFilter
llVolumeDetect
llPassCollisions
llPassTouches

Detection Events

collision_start
collision
collision_end
sensor
touch_start
touch
touch_end

Q: It's not working. I'm not getting anything returned.
A: Are you putting your llDetected* function in an event handler other than the collision, collision_start, collision_end, sensor, touch, touch_start, or touch_end events? The llDetected* functions won't return a meaningful value in any other event.

Q: Do the detection functions make calls to either the asset or data server?
A: No. As clarified by Kelly Linden here, all the possible data that could be returned by the detection functions is stored during the sensor call.

Functions | Events | Sensors | Touch | Collisions


Dynamics

Dynamics (or physics) is concerned with moving or rotating objects and prims. They are broken into two categories:

Kinematic

Kinematic functions operate on non-physical tasks and generate motions without consideration of forces and torques.

Function | Comment
llSetPos |
llSetRot |
llSetLocalRot |
llSetScale | Not really a dynamics function, as it changes task size.

Kinetic

Kinetic functions generate forces and torques on physical tasks, and the resulting motion is determined by the mass (inertia) of the object or prim. Note that an object's energy modulates the force that is actually applied.

Function | Comment
llApplyImpulse |
llApplyRotationalImpulse |
llGetAccel |
llGetForce |
llGetTorque | Equivalent to llGetAccel for rotational movement.
llGetOmega | Don't confuse this with llTargetOmega. llGetOmega is equivalent to llGetVel for rotational movement.
llGetVel |
llGroundRepel |
llMoveToTarget |
llPushObject |
llSetBuoyancy |
llSetForce |
llSetForceAndTorque |
llSetHoverHeight |
llSetTorque |
llTargetOmega | Applies its spin to physical objects on the server side when object is set physical, if non-physical, this function becomes client side.
These apply to both physical and non-physical entities:
  • llGetPos
  • llGetLocalPos
  • llGetRootPosition
  • llGetRot
  • llGetLocalRot
  • llGetRootRotation
  • llLookAt
  • llRotLookAt
  • llGetPrimitiveParams
  • llSetPrimitiveParams
  • llTargetOmega

See also:
  • nonphysical
  • energy
  • gravity
  • physics
  • physical
  • llSetStatus

Note: llTargetOmega is not a true dynamics function if an object is not set physical.

Vehicle functions can provide similar effects as the ones shown here.

Functions | Physics


Movement

Movement occurs when an object or avatar changes position either physically or via a function.

If you are looking for functions that will move an object or avatar, see dynamics

Events

moving_start
moving_end

See also controls.

Position | Dynamics


Physics

Physics (or dynamics) in SecondLife is implemented using the Havok physics engine.

A physical object may have no more than 31 prims. The minimum distance for objects to avoid collision (the spatial precision), is 0.1m (10cm)--and an error will occur if a prim/object is tried to be made physical but is within 10cm from another prim/object/avatar or the ground.

Vehicles are one application of the physics engine.

An object's physics flag, STATUS_PHYSICS, can be changed using llSetStatus or llSetPrimitiveParams and read back with llGetStatus and llGetPrimitiveParams.

  • Acceleration
  • Buoyancy
  • Energy
  • Friction
  • Force
  • Gravity
  • Impulse
  • Mass (inertia)
  • Torque
  • Omega (rotational velocity)
  • Velocity

SL History Wiki's Physics Notes: Object Mass, Impulse Energy

Physical | Dynamics | Vehicles


Group

A group in Second Life is a user-created clique or association of three or more members. (If SL were an RPG, they'd be called "guilds" or "clans".) A group with less than three members is deleted after 48 hours unless someone else joins. There's a L$100 fee to create a new group. A group can be free to join or set to invitation only and can have an optional membership fee.

A user may be a member of up to 25 groups. Why 25? It isn't known and the Lindens haven't been very forthcoming so far. It doesn't appear to be a technical limitation, however prior to previous increases, 10 was cited as being a technical limit. The original limit of 10 groups was raised to 15 in SL 1.4.0 and to 25 in SL 1.12.0.

A group can own land and objects via deeding. See group objects for information on restrictions.

Notes:
  • While a list of all the groups an agent is in cannot be obtained, the active group the agent is using can be found out (if it is the same group as the object the script is in) with llDetectedGroup or llSameGroup.
  • A group-owned object cannot give inventory.
  • A group cannot be given money with llGiveMoney.

Functions

llAddToLandPassList
llDetectedGroup
llEjectFromLand
llGetLandOwnerAt
llOverMyLand
llParcelMediaQuery
llSameGroup
llSetParcelMusicURL
llTeleportAgentHome
llUnSit

Some users erroneously refer to a linked set of prims as a "group".

Group Objects | User


Inventory

SL has two kinds of inventories: one for the user (on the client) and one for objects (for the server but labeled "Content" in the client's object editor window). An inventory stores items: objects, scripts, textures, sounds, animations, body parts, clothing, gestures, landmarks, notecards, pictures (which are just a special kind of texture taken with SL's "snapshot" feature), and calling cards.

Object inventory used to be limited to 255 items maximum, but there is currently no limit. While it's unlikely, there may be a limit imposed in the future. Object inventory cannot contain folders (other than the "Contents" folder) or items with the same name (unlike the user inventory). When a folder from a user's inventory is dropped into the inventory of an object, the folder's items are transferred individually to the object. Scripts transferred using this process are, unfortunately, deactivated (each script must be dragged seperately, or selected using shift and/or control-clicking and dragged as a mass selection), and a number is automatically appended to items without unique names.

There is currently no way to manipulate a user's inventory using a script.

The following functions can be used to manipulate an object's inventory:

Functions

llAllowInventoryDrop
llGetInventoryCreator
llGetInventoryKey
llGetInventoryName
llGetInventoryNumber
llGetInventoryType: check item existence
llGetInventoryPermMask
llGetNotecardLine
llGetNumberOfNotecardLines
llGetScriptName
llGetScriptState
llGetStartParameter
llGiveInventory
llGiveInventoryList
llRemoteLoadScriptPin
llRemoveInventory
llResetOtherScript
llRequestInventoryData
llRezAtRoot
llRezObject
llSetRemoteScriptAccessPin
llSetScriptState

Constants

(used by llGetInventoryName and llGetInventoryNumber)

Constant | Value | Description | Hex
INVENTORY_ALL | -1 | Look at all inventory items |
INVENTORY_NONE | -1 | Returned by llGetInventoryType |
INVENTORY_TEXTURE | 0 | Only look at textures | 0x00
INVENTORY_SOUND | 1 | Only look at sounds | 0x01
INVENTORY_LANDMARK | 3 | Only look at landmarks | 0x03
INVENTORY_CLOTHING | 5 | Only look at clothing | 0x05
INVENTORY_OBJECT | 6 | Only look at objects | 0x06
INVENTORY_NOTECARD | 7 | Only look at notecards | 0x07
INVENTORY_SCRIPT | 10 | Only look at scripts | 0x0A
INVENTORY_BODYPART | 13 | Only look at body parts | 0x0D
INVENTORY_ANIMATION | 20 | Only look at animations | 0x14
INVENTORY_GESTURE | 21 | Only look at gestures | 0x15

Note: inventory constants are not bitflags and thus should not be used with bitwise operators.

Events

changed
object_rez

Q: How do I give a user an item from my object's inventory?
A: Use llGiveInventory or llGiveInventoryList.

Q: How do I give a user a folder?
A: See llGiveInventoryList.

Q: How do I rename/copy/duplicate an item in my object's inventory?
A: You can't, though a script can delete items (even itself) with llRemoveInventory.

Q: How do I update a script in another object from an identically named script in my object's inventory?
A: See llRemoteLoadScriptPin but it requires the setup of a pin number.

Q: Is there a way to directly check if an item exists in inventory without having to run a for loop?
A: Yes, llGetInventoryType, though this function, like all inventory functions, is case sensitive.

Q: How do I make my script take an object from the world to my object's inventory?
A: A script can't take an object into its inventory. Neither can it take a copy of the object. An object can't put itself in another inventory either, and there's no way to send an in-world object to a user's inventory.

Q: Can item ordering (for the functions that get them) be assumed to be stable?
A: Object inventory is always sorted alphabetically. The order is stable and predictable.

Q: Can I find out what another prim has in its inventory?
A: No, not in another object, nor in an object your prim is linked to. In both cases, you would have to already have a script in each prim you wanted to check.

Functions | Events | Constants | Item


Link

In Second Life, several prims can be linked together into a "linked set" (also "link" or "linkset") that act as (or, by its definition, are) a single object. Linked sets have one parent prim and 1 or more child prims. Linksets are limited to 256 prims.

Notice : It seems that a linkset can only have 255 prims + 1 sitting avatar, not 256 prims.

Link Distance

A set of prims needn't be contiguous to be linkable. They can overlap, or they can be completely separated from each other; what you see has little bearing on whether you can link them. What does matter is their size and their relative distance, measured from center to center. The set can only be linked if:

  • All prims, no matter how small, with centers within 1m of each other are linkable.
  • No prims, no matter how large, can be linked if their centers are further than 32m apart.
  • In all other cases, the max link distance is related to prim size.

Illuminating example: I could not link a tower of five contiguous parts that was just over 40m tall. But I succeeded in linking its inner parts. Afterward, I was able to link its bottom and top parts to the inner, pre-linked segment.

Andrew Linden posted the exact formula used to calculate the distance:size ratio of prims.

Order Of Objects; The Root Prim

Second Life retains data on the specific order of parent and child objects within a linked set. The last selected prim is the parent/root. (This is probably mostly useful in scripting, but a non-scripting use is ordering behavior of multiple sit targets in a linkset.) In objects with only one prim, the object's link number is 0 (unless there is an avatar sitting on it; then it is 1). In objects with multiple prims, the root is 1 and children start at 2. So, in a 2-prim object, llGetLinkNumber would return 1 in the parent/root and 2 in the first (and only) child. Avatars seated on an object are assigned link numbers at the end of the linked set in the order seated.

If a script is in a child object's inventory, it may return different data than an object contained in the parent. The most notable example is in llSensor.

Note: Linked objects are sometimes erroneously referred to as "groups". This can be confusing when talking about both linked objects and groups of users.

Functions

Function | Description
llBreakLink | Delinks a specified prim from the linkset.
llBreakAllLinks | Delinks all prims in the linkset.
llCreateLink | Links an object to the linkset.
llDetectedLinkNumber | Returns the link number of the prim a touch or collision event was triggered by.
llGetLinkKey | Gets the key of a specified prim in the linkset.
llGetLinkName | Gets the name of a specified prim in the linkset.
llGetLinkNumber | Gets the link number of the prim containing the script.
llGetLocalPos | Gets the position of the child prim relative to the root prim.
llGetLocalRot | Gets the rotation of the child prim relative to the root prim.
llSetLocalRot | Sets the rotation of the child prim relative to the root prim.
llGetNumberOfPrims | Returns the total number of prims in the object.
llGetPos | Gets the position of the prim in region coordinates.
llSetPos | In a child prim, sets position relative to the root prim.
llGetRootPosition | Gets the position of the root prim in region coordinates.
llGetRootRotation | Gets the rotation of the root prim.
llMessageLinked | Sends a message to specified prims to be recieved by other scripts using the link_message event.
llPassCollisions | Toggles whether collisions with one prim in a linkset trigger collision events in scripts in other prims.
llPassTouches | Toggles whether touches with one prim in a linkset trigger touch events in scripts in other prims.
llRezAtRoot | Rezzes an object at the root prim's coordinates.
llSetLinkAlpha | Sets the alpha of any specified prim in the object.
llSetLinkColor | Sets the color of any specified prim in the object.
llSetLinkTexture | Sets the texture of any specified prim in the object
llSetLinkPrimitiveParams | Sets the parameters (or properties) of any linked prim in one step.

Events

Event | Description
link_message | Triggered when a message sent by llMessageLinked is recieved.
changed | Triggered when a prim is linked or delinked.

Constants

(used by the functions llMessageLinked, llSetLinkAlpha, and llSetLinkColor)
Constant | Value | Description
LINK_ROOT | 1 | The root prim in a linked set (but not in a single prim, which is 0)
LINK_SET | -1 | All prims in the object.
LINK_ALL_OTHERS | -2 | All other prims in the object besides the prim the function is in.
LINK_ALL_CHILDREN | -3 | All child prims in the object.
LINK_THIS | -4 | The prim the script is in.

Q: Is there a way to set the texture of another prim in the linkset?
A: Yes, use llSetLinkTexture.

Q: Can I change the order of prims in a linked object without delinking and relinking it?
A: No.

Functions | Constants | Events | Joints | Root | Parent | Child


Land

In Second Life, land (also referred to as ground or terrain) is stored as a 2D grid of height values (a "heightmap"). This means the ground can never overlap, nor can holes be made through it. The grid has a resolution of 1m in each direction, resulting in 256x256 grid points (0-256) per simulator.

Even though 0-256 implies 257 grid points, sims share their edges with adjacent sims, so the height values at x = 256 or y = 256 will be identical to the height values in an adjacent sim to the east or north at x = 0 or y = 0, respectively.

Or, to put it another way, the grid coordinate 256 of one sim can be seen as the grid coordinate 0 of the adjacent sim.

Ground can be modified with the built-in terraforming tools (Ctrl-5) or the llModifyLand function, and can be read using llGround. Land height between grid points is interpolated, giving it a smooth look. The surface is textured, using multiple textures based on the land height and slope. Island owners can set their own ground textures (though not with LSL) and upload RAW heightmaps in a special format (see below).

Land is either public (a rare sight now) or owned by agents or groups. Such land parcels consist of blocks with 4x4m size. Land owners can disallow the rezzing of objects or running scripts (effective up to 40m above ground). Sound can optionally be restricted to a parcel, with no sound leaving or entering it. Land parcels can also have an audio or video stream associated with them.

Functions

llAddToLandBanList (1.8)
llAddToLandPassList
llEjectFromLand
llGetLandOwnerAt
llGetParcelDetails (1.13)
llGetParcelFlags
llGetParcelMaxPrims (1.13)
llGetParcelPrimOwners (1.13)
llGetParcelPrimCount (1.13)
llGetRegionFlags
llGround
llGroundNormal
llGroundContour
llGroundRepel
llGroundSlope
llModifyLand
llOverMyLand
llParcelMediaCommandList
llParcelMediaQuery
llRemoveFromLandBanList (1.8)
llRemoveFromLandPassList (1.8)
llScriptDanger
llSetParcelMusicURL
llTeleportAgentHome
llUnSit
llWater 		Events

land_collision
land_collision_end
land_collision_start

Q: Is there a way to determine how many objects are on a given parcel of land, or better yet, who owns them?
A: llGetParcelPrimOwners attempts to do this, returning a list showing which avatars own objects and how many prims each avatar owns on the parcel. It is limited to at most 100 object owners, and only works if the script owner has appropriate permissions. There's currently no way to get a list of all the objects themselves - Sensors are only able to detect up to 16 objects per scan.

Q: Is there a way to determine the name or description of a parcel of land?
A: Yes. Use llGetParcelDetails.

Q: Is there a way to determine how much land a user owns within a sim?
A: Not directly. However, you could construct a land scanner that would use llGetLandOwnerAt to scan every 4x4m plot and compare all the owners' keys detected against yours. You wouldn't be able to find out the size or shape of individual parcels, however. Only the overall shape of each owner's land could be detected. It might be all one parcel, or divided up into individual 16 square metre blocks.

You could then use llParcelMediaQuery on your own land to allow your script to check if the parcels are different, though this would only work if the parcels had been set to different video streams. Remember, the media URL doesn't actually have to go anywhere.

Of course, you can specify different media URLs on each parcel. If you're not using the video feature, and don't plan to, you can use it to store unique identifiers this way. If you do need to use the media feature, and you need them to all point to the same URL, try using an URL redirect on a website to produce unique URLs pointing to the same file.

Q: Is there a way to determine whether a parcel is owned by a group or an individual?
A: Yes. llGetParcelDetails returns a list which can contain the key of the land owner and the key of the group. If these keys match, then the land is group owned. However, there is no way for a script to determine the actual details of the group -- such as its name -- regardless.'

Q: Is there a way to get or set the for sale price on land, or set other land flags such as Damage, No Outside Scripts, etc.?
A: No, the most you can do is use llScriptDanger to find out whether or not a given position is above land that is considered "safe" for your scripted object to pass into. You can't set any land flags beyond music or media streams.

Q: Is there a way to automatically buy or sell land?
A: No.

See the RAW heightmap page for information about creating one.

Andrew Linden, 10/11/05: Small movements in land near parcel borders is related to some lossy compression in how we stream the terrain data. There is evidence from our research into the new rendering engine that it is probably easier to just stream the terrain data in its full precision, since it represents less data than a typical 512x512 texture on a simple object.

Lossy compression effects will be most noticable near sharp features (sudden cliff or very tall spike or very deep hole).

Functions | Simulator


Lighting

As of 1.10, the lighting model has been changed completely, moving from software rendering that further taxed the already-overworked CPU to hardware-based lighting that uses the GPU instead.

Under the current lighting system, the world is lit by the sun and moon and up to 6 object-based lights. It's important to design lighting systems in such a way as to reduce the chances of other users' lights turning off and on due to their distance from the camera.

The following needs to be changed:

SL light is used in various things and can be changed in different ways. Sims emit a light source (and cast shadow) based on the day-night light cycle (see llGetSunDirection--this is a "directional" light source, meaning it is equally bright all over the sim).

Light affects things in different ways. Because SL lighting is vertex-based, lights will only appear to be working if the light source is near a vertex, where it is brightest, fading away uniformly towards other vertices. Untextured prims/objects appear much brighter than textured ones due to the nature of hardware-based transform and lighting (T&L). Avatars remain very dark even when large, fully bright (not "full bright") white lights are shining directly on them. Hopefully this bug will be fixed soon. Particles also reflect light except when prelit due to being set to "glow" in their options.

A "Light" material-type prim/object acts as and uses a "point" light source.

Prelighting ("full bright" in SL) is where a prim/object is lit but does not actually emit a light source. However, SL's lit objects automatically become prelit yet also emit light. SL can fake full prelighting (but still emit an "invisible" light source that counts against framerate) by changing the color of side "0" (positive z-axis--"top") of a prim to black. (SL 1.9- only)

Functions

llGetPrimitiveParams
llGetSunDirection
llParticleSystem
llSetPrimitiveParams

llSetPrimitiveParams Flags | Effects
PRIM_FULLBRIGHT | Makes all faces light up as if the object were a light
PRIM_MATERIAL, PRIM_MATERIAL_LIGHT | Sets object material to light
PRIM_COLOR | When used on side 0 (zero), changes color of light
PRIM_POINT_LIGHT | Toggles light property and attributes: color, intensity, radius, and falloff.

Example Light Script:
default

Q: Why are we limited to only 6 lights at a time?
A: 8 lights is a common OpenGL/hardware limit, but it appears that the Lindens may have a system for dealing with more in the works.

Color | Material | Shadow


math

Mathematics

These math functions deal with floats and integers. For vector and quaternion (rotation) math functions, see their respective pages. For an in-depth explanation of the actual math behind these functions, check out Wolfram's Mathworld.

Function | Description
llAbs | Returns the absolute value of integer.
llAcos | Returns the arccosine of input.
llAsin | Returns the arcsine of input.
llAtan2 | Returns the arctangent of input.
llCeil | Rounds float up to integer.
llCos | Returns the cosine of input.
llFabs | Returns the absolute value of float.
llFloor | Rounds float down to integer.
llFrand | Returns a random float between 0 and input.
llListStatistics | Various statistical functions on a list of numbers.
llLog | Returns the base e (natural) logarithm of input.
llLog10 | Returns the base 10 (common) logarithm of input.
llModPow | Returns a raised to the b power, mod c.
llPow | Returns the value of input, raised to exponent.
llRound | Rounds float input to the nearest integer.
llSin | Returns the sine of input.
llSqrt | Returns square root of input.
llTan | Returns tangent of input.

Functions | Operators


Particles

Particles are generated, free-floating, non-object (sprites) visual effects that can simulate blood, dust, explosions, fire/flames, gas, smoke, sparks, spray, steam, waterfalls, waves, weather, etc. Particle generation is entirely client-side and does not contribute to sim load directly (although if particle functions are updated frequently, they will increase sim load--as will all functions, of course). Because clients are typically configured to render up to 4096 particles by default (configurable in preferences and increasable in settings.ini), care must be taken to generate a minimal number of particles in most situations. 10 objects generating 1000 particles per second would, for example, prevent others in the vicinity from using the particle system for their own builds.

Currently, there's only one particle function that should be used: llParticleSystem

These older functions still work, but have been deprecated, and will be removed at some point in March/April 2007. Use llParticleSystem instead.
llMakeExplosion
llMakeFire
llMakeFountain
llMakeSmoke

Some generalised particle scripts can be found in the Script Library
There is also a companion Particle Library for specific examples

For a better guide, have a look at:
  • Ama Omega's excellent example particle script
  • Eep's modified example particle script1
  • Keknehv's less verbose particle script
  • For a great in-world tutorial on particles, explore The Particle Laboratory (at Teal (200, 60, 21) , http://slurl.com/secondlife/Teal/200/60/21/ )
Functions | Deprecated Functions


Prim

A primitive ("prim" for short) is the basic building block (made up of polygons) of Second Life. Using SL's built-in object editor, just about anything can be modeled out of primitives.

A prim has a name, description, material, and a basic shape (box, sphere, cylinder, torus, etc) that can be resized and modified by changing its parameters (cut, hollow, twist, taper, shear, etc). Each prim has several sides which each have a color, opacity, and texture that can be rotated, repeated (UV), and offset. Optionally, a prim can have such features floating text, a particle system, light-emissive, or be flexible.

An object can be made by linking multiple primitives together, but a single prim is an object as well.

Each prim has an inventory that can contain items like scripts, objects, notecards, textures, or anything else in a user's inventory. All running scripts are contained within the prims of an object.

Despite the similarity in names, Second Life's primitives are not true primitives like you'd find in 3D modeler such as 3D Studio MAX or Maya, but are actually parametric models: simple 3D objects with variable property settings that give the appearance of primitives that can be deformed to a degree (but things like vertex manipulation or CSG subtraction can't be done to them).

There is an ongoing debate surrounding the question of supplementing parametric models with true meshes. At this time, there is no mesh support in Second Life.

Functions

Function | Description
llGetAlpha ² | Get prim transparency
llSetAlpha ² | Set prim transparency
llSetLinkAlpha ² | Sets transparency of another prim in linkset
llGetBoundingBox | Get prim bounding box.
llGetColor ² | Get prim color
llSetColor ² | Set prim color
llSetLinkColor ² | Sets color of another prim in linkset
llGetNumberOfPrims | Get total number of prims in linkset
llGetNumberOfSides¹² | Get prim sides
llGetObjectDesc | Get prim description
llGetObjectName | Get prim name
llGetObjectPermMask | Get object permissions
llGetObjectPrimCount | Returns the prim count for any specific object in a sim
llGetPrimitiveParams | Get nearly every prim attribute
llSetPrimitiveParams | Set nearly every prim attribute
llSetLinkPrimitiveParams | Sets prim attributes of another prim in linkset
llParticleSystem ¹ | Configure prim particle emitter
llSetCameraAtOffset¹ llSetCameraEyeOffset¹ | Changes the camera configuration of avatars that sit on the prim.
llGetMass | Gets the mass of the prim
llSetObjectDesc | Set prim description
llGetObjectMass | Gets the mass of the object specified by the key.
llSetObjectName | Set prim name
llSetObjectPermMask | Set object permissions
llGetPos | Get the position of a prim or linkset
llGetLocalPos | Get the position of a prim within a linkset relative to its root prim
llSetPos | Set the position of a prim or link set
llGetLocalRot | Get the position of a prim within a linkset relative to its root prim
llGetRot | Get the rotation of a prim or link set
llSetLocalRot | Set the rotation of a prim within a linkset relative to its root prim
llSetRot | Set the rotation of a prim or link set
llGetScale ² | Get prim scale
llSetScale ² | Set prim scale
llSitTarget¹ | Set prim sit target
llSetSitText¹ | Replace prim "Sit" text in pie menu
llSetStatus ³ | Toggle various attributes of prim/object: phantom, physics, rotational limitations and die/return conditions.
llSetText ¹ | Set floating text above prim
llGetTexture ² | Get prim texture
llSetTexture ² | Set prim texture
llSetLinkTexture ² | Sets texture of another prim in linkset
llSetTouchText¹ | Replace prim "Touch" text in pie menu
llSetRemoteScriptAccessPin¹ | Set prim remote script access pin
¹ These are not traditional prim functions in the sense that they cannot be set from the object editor, but their attributes survive regardless if a script is present in the object.
² Can apply to sides or prims.
³ Only phantom and physics can currently be set from the editor menu.

References

  • Kelly Linden, 5/12/06: "...when a prim is saved (as an asset) in text format it is about 3k uncompressed and about 1k compressed."

Object | Link | Texture | Inventory | Dynamics


Object

  • An object is a 3-dimensional geometric entity in Second Life that can consist of a just a single primitive or multiple prims linked together. A single prim is often also generically called an "object".
  • An object (or its prims) can exist as, or contain, items (like a script), but items can't be rezzed; objects can.
  • An object (or its prims) can contain a script which can delete the object it is within.
  • An object can have rotation, position, permissions, and flags such as physics, phantom, temporary on rez or anything else adjustable with llSetStatus.
  • An object can be attached to an avatar.
  • Objects may be group-owned.
vertex -> polygon -> face/side -> prim -> object

  • Physical objects can exist up to a z-coordinate of 4096m.
  • Non-physical objects can exist up to a z-coordinate of 4096m, provided they aren't moved (??or changed??) after being rezzed.
  • Non-physical objects can always exist up to a z-coordinate of 768m

Functions

llGetBoundingBox
llGetObjectDesc
llSetObjectDesc
llGetMass
llGetObjectMass
llGetObjectName
llSetObjectName
llGetObjectPrimCount (1.13)
llGetGeometricCenter
llGetKey
llGetOwner
llGetOwnerKey

Prim | Link | Script | Inventory | Object Types


Script

A script is a Second Life inventory item that contains LSL code. If a script is placed in a prim or object, the script can then control the behavior and appearance of that prim or object, which can cause the prim or object to (among many other things) change color, move, interact with the world, etc. A single prim or object can contain multiple scripts, each one running independently of the others (although they can all have effects on the same prim or object).

A script is edited using the built-in script editor or external alternate editors (via cut-and-paste). To see the source code that makes up a script, if the user has permission to view it, simply double-click on its name and the editing window will open up. Each script can have permissions independent of the permissions of an object it is contained in. To be editable, a script must be modifiable by the user, or it could be shared to a group the user is a member of.

Notes
  • A prim or object containing a script being edited doesn't need to actually be selected in order for the script to be edited or updated (after the script window is already open, of course). However, if the scripted prim or object goes "out of range" (the script window's titlebar will say it), any changes to the script will not be saveable until the prim/object comes back in range. A prim or object can go out of range by distance (moving the avatar too far away from the prim or object) or by attachment (where simply "drop"ping the prim/object will bring it back in range, but won't update the script window's titlebar and, if detached, a reattached prim or object's script must be reopened or the current opened script editor window will also give an "out-of-range" error). If the prim or object is lost track of, the script's contents can still be copy-pasted into a new script file.
  • If an empty script is dropped into a prim or object, it shouts "Empty script!".
  • If a no-copy/no-mod object has a script, and that script is put into another object, the script resets. (explanation)

Internally, each script consists of two items: the script's source code, which is compiled into "bytecode" by the client and uploaded to the simulator. This generates the second item, a 16kb memory image which is executed in a virtual machine on the sim and contains the script's bytecode, stack, and heap. This memory basically represents the current status of the script (not to be confused with its state) and is saved/restored when the sim restarts (or when you attach/detach a scripted object).

Each script receives a time slice (portion) of the total simulator time allocated to scripts. So a simulator with many scripts would allow each individual script less time rather than degrading its own performance. In addition, each script executes within its own chunk (section) of memory, preventing scripts from writing into protected simulator memory or into other scripts (which can include multiple scripts in the same prim or object), making it much harder for them to crash the simulator.

Example Scripts

Functions

llGetScriptName - Returns the name of the script that calls the function.
llGetScriptState - Returns TRUE if the script identified by name is running.
llSetScriptState - Changes whether or not the script is running.
llRemoteLoadScriptPin - If permissions and PIN allows, this function will copy a script to a remote object
llSetRemoteScriptAccessPin - Sets the PIN (personal ID number) necessary to remotely load a script.
llScriptDanger - Determines if a position is over land that might disable or delete a script.
llResetScript - Resets all values in a script and restarts it.
llResetOtherScript - Performs llResetScript on a different, named script.
llGetFreeMemory - Returns the amount of free memory in the script, in bytes.

Resources
  • script execution, Andrew Linden, 3/16/05
  • SL History Wiki: Scripting
  • SL Wiki: script

Crash Course | LSL | Memory | Object | Prim | Item


Sensors

Sensors allow objects to locate other objects or agents near them.

Functions

llSensor
llSensorRepeat
llSensorRemove

Events

no_sensor
sensor

Sensor functions raise the sensor event, within which the detection functions are used to determine all kinds of info about what the sensor has found.

If no matching objects/agents were found, the no_sensor event is raised instead.

Notes

  • llSensor and llSensorRepeat will not detect the object that contains them. This also applies to attachments--they won't detect the agent they're attached to, unless the sensor script is within a child object.
  • A llSensor in a WHILE loop is supposedly faster than llSensorRepeat.
  • A sensors point of origin in linked objects is from the prim calling the function. Not the root or the center of the linkset.

Using sensors increases server load, which can contribute to lag.

Functions | Events | Detection


Simulator

The Second Life world consists of many, interconnected, uniquely-named simulators (also referred to as sims or regions) positioned on a Cartesian map with its origin at the global coordinates 1000, 1000 (Da Boom sim). A sim's global coordinates can be accessed by calling llGetRegionCorner and dividing the results by the size of a sim (256m on a side). Neighbouring sims are connected vertically and horizontally, but never diagonally. This grid forms the world (image) of SL. Sims also seem to have an integer that represents a group of users for which the sim shows up on the world map (there are hidden sims; if you are in a hidden sim, all other hidden sims designated to the same group will show up on the world map).

The dimensions of a sim are unvaried: 256m x 256m. Objects may go off-world above 768m on the z axis (height) (physical objects currently go off-world at 4096m). Sims always have land, although it can be completely submerged in water. Any open space between non-adjacent sims is visibly rendered as water on the client, but cannot be accessed (the avatar will bounce off an invisible wall).

There is currently a limit of 15,000 primitives per sim, and approximately 100 agents. Though intended to optimize server and client performance, these numbers are arbitrary and likely to be increased again in the future. (Initially, the limit was 10,000 prims and approximately 20 agents.)

A sim keeps track of the objects and agents within it, simulates physics, runs scripts, and caches and delivers object and texture data within the sim to clients, both within ("agents") and looking into the sim ("child agents"). Sims also handle attachments, which are really just special objects that match an avatar's location and rotation, and don't count against the sim's prim limit.

Simulator names are restricted to 20 characters(counting spaces) which may be split into 3 alphanumeric words.

Functions

Land | Time | Weather

Function | Purpose
llCloud | Returns cloud density.
llEdgeOfWorld | Determines off-world locations.
llGetDate | Returns current date.
llGetGMTclock | Returns time since midnight in GMT.
llGetLandOwnerAt | Returns the land owner at a given location.
llGetParcelFlags | Returns a bitfield of the parcel's flags, eg. allows outside scripts, etc.
llGetRegionCorner | Returns simulator's position in global coordinates.
llGetRegionFlags | Returns a bitfield of the region's flags, eg. sandbox or terraforming status.
llGetRegionFPS | Returns the FPS of the sim the script is running in.
llGetRegionName | Returns the name of the sim the script is running in.
llGetRegionTimeDilation | Returns the time dilation of the sim the script is running in.
llGetSimulatorHostname | Returns the simulator's location on the Internet.
llGetSunDirection | Returns a vector pointing towards the sun from the object's current position.
llGetTimeOfDay | Returns the time in seconds since simulator midnight.
llGetWallclock | Returns time since midnight in PST.
llGround | Returns ground height.
llGroundSlope | Returns ground slope.
llGroundNormal | Returns the normal vector to the ground.
llGroundContour | Returns ground contour.
llModifyLand | Changes the height of the land the script is over.
llOverMyLand | Determines if object is over land owned by the object's owner.
llParcelMediaCommandList | Changes parcel video settings.
llParcelMediaQuery | Returns parcel video settings.
llRequestSimulatorData | Returns simulator properties via the dataserver event.
llSetParcelMusicURL | Controls parcel music settings.
llScriptDanger | Determines if land is editable, allows rez, and disallows scripts.
llWater | Returns water level height.
llWind | Returns wind velocity.

Technical Specs

Each sim is powered by a single PC CPU core, either an Intel P4 or an AMD Opteron (more AMD 64-bit CPUs), running Debian Linux, though there may be multiple CPUs, cores, and sims per machine (1100 servers, ~2000 CPUs -2/22/06). There are no more than 4 sims per machine (2 dual-core CPUs per box).

Apparently, the sim software uses a pretty sophisticated journaling storage system (database) for objects, allowing a fast (usually) recovery with minimal data loss after a crash, power outage, etc. The sim database is backed up every hour.

"The company [Linden Lab] also chose to store the avatars that its nearly 60,000 users [10/17/5] have created in a file system front-ended by the open-source Apache Web server, while the open-source Squid Web proxy cache keeps the data conveniently nearby. Metadata about the avatars is stored in an open-source MySQL database." (Information Week's "Open Doors To Innovation")

Updated Specs

"Second Life has 240,000 registered users. ... Second Life currently runs on 2,579 servers that use the dual-core Opteron chip produced by AMD. Each server is responsible for an individual "sim," or 16 acres of virtual "Second Life" land. At peak usage that means that each server is handling about three users." - (C|Net, June 6, 2006 'Second Life': Don't worry, we can scale)

Functions | sim names


Text

Obviously, text is used throughout SL's user interface and LSL in script code, but text also appears in notecards and there are certain functions that deal specifically with text:

llSetText
llSetTouchText
llSetSitText

Many functions use string parameters to set user-interface text fields, like names.

User Interface | LSL | String


Name

Names are used throughout LSL to identify various things: avatars, objects, scripts, simulators, variables, string text parameters, etc.

Functions

llDetectedName
llGetInventoryName
llGetLinkName
llGetObjectName
llSetObjectName
llGetRegionName
llGetScriptName
llGetSimulatorHostName
llKey2Name

avatar | object | link | key | script | simulator | string | text | variable


Teleport

In SL, a teleport allows users to move from one region/sim to another without crossing a sim boundary or moving within a sim. By default, users are able to teleport directly to any coordinate within the grid. To allow or disallow users to do this on a given parcel, use the pass/banlist functions described below.

Functions

Function | Description
llMapDestination | Shows a given location on the map.
llAddToLandPassList | Add an avatar to the parcel pass list for the given duration.
llAddToLandBanList | Add an avatar to the parcel ban list for the given duration.
llRemoveFromLandPassList | Remove an avatar from the parcel pass list for the given duration.
llRemoveFromLandBanList | Remove an avatar from the parcel ban list for the given duration.
llSetPrimitiveParams | Sets properties of a prim, including position. This can be used to fake a short-range teleport. See WarpPos
llSitTarget | Sets the sit target of a prim. This can be used to fake a short-range teleport.
llTeleportAgentHome | Teleports an agent to their home location. (Land Owner Only)

Events

Event | Description
changed | Triggered when the attachment has been teleported or has entered a new sim.

To fake a teleport between two points, use llSitTarget, though there is a 519m range limit (but no axis may be greater then 300m). The advantage of faking it is easy integration into the right-click pie menu, and instantaneous teleportation, since the client doesn't have to reconnect to the sim as it would with the "real" teleport functions. An example of a "sit teleporter" is the LibraryPortRingCreator.

Another, albeit slightly more complex, method of "faking" a teleport between two points is to have the avatar sit on an object and move the object. LibraryWarpPos can do this over great distances instantaneously, even through objects, and as such gives the appearance of teleportation. The object would probably then unsit the avatar and move back to its original position, as is done in LibraryTeleporter.

Q: Is there a way to teleport someone to a given sim and location anywhere in the grid?
A: No, currently you can only directly teleport someone to their home location with llTeleportAgentHome. The upcoming function llTeleportAgent will presumably allow you to teleport someone directly, but as it was first mentioned as an "future function" in the 1.8.0 release notes in December 2005, we can assume that no work is currently being done on this function. There is no word on when it will be completed, or whether it will require permissions, for the user to be over the owner's land, as llTeleportAgentHome does, or what other limitations it may require.

However, you can offer a teleport to another user with llMapDestination if the user touches your scripted object. The user must click a dialog box to accept the teleport.

Functions | Simulator | Position | Agent/Avatar


Texture

A texture is an image that can be applied ("texture-mapped") onto an object's surface, particle, or avatar. Images saved in JPEG, TGA, and BMP format may be uploaded to Second Life as textures. These formats are converted to a variant of the JPEG2000 format (using the Kakadu library) and sent to the Second Life asset server when uploaded. There is a $L10 fee for each upload.

Note: Uploading textures less than 8x8 pixels may give an error: "KDU throwing an exception" for BMPs/TGAs and "LLImageJ2C uninitialized" for JPGs.

Textures are indexed in the user's inventory and can be used in a variety of ways in the viewer and editor modes of the client and via LSL. Dragging them onto a specific object face will place a texture on that face. Shift-dragging a texture from the inventory to a prim will apply it to all faces.

Because all uploaded images are decoded and converted to JPEG2000, it is inadvisable to convert textures into the JPEG format prior to uploading. JPEG is a lossy format and using it will potentially introduce unnecessary additional loss and distortion to the final uploaded texture. Uploading images in the TGA format is recommended.

A list of texture UUIDs used in the Second Life client (such as the ground textures) can be found here.

Function | Description
llGetAlpha | get the alpha of a face
llGetColor | get the color of a face
llGetNumberOfSides | get total number of sides
llGetPrimitiveParams | get all primitive parameters
llGetTexture | get the texture on a given side
llGetTextureOffset | get the texture offset on a side
llGetTextureRot | get the texture rotation on a side
llGetTextureScale | get the texture scale of a side
llOffsetTexture | set the texture offset
llRotateTexture | rotate a texture
llScaleTexture | resize (scales) a texture
llSetAlpha | set the alpha of a face or prim
llSetPrimitiveParams | set all primitive parameters
llSetColor | set the color of a face or prim
llSetLinkAlpha | set the alpha of a linked prim
llSetLinkColor | set the color of a linked prim
llSetLinkPrimitiveParams | set all primitive parameters of a linked prim
llSetLinkTexture | set the texture of a linked prim
llSetTexture | set a texture on a face or prim
llSetTextureAnim | animates a texture

Note: llGetPrimitiveParams and llSetPrimitiveParams are capable of performing every function described above with the exception of llSetLinkAlpha, llSetLinkColor, and llSetTextureAnim.

Q: How do I determine which side of an object I apply a texture to?
A: See side.

Q: Can I set the texture of another prim in my linked object?
A: No. When it comes to other prims, you're limited to setting alpha (transparency) or color only. If you want to change the texture of another prim in your linkset, you'll need to actually give it its own script. Use llMessageLinked to coordinate them.
As of SL 1.13.4, this will change with the addition of llSetLinkPrimitiveParams andllSetLinkTexture.

Q: Can I set the texture of an object that's not linked to the one containing my script?
A: No. You can link it, if it's within range, but that's it. You can't change its scale, texture, nothing.

Q: How do I get the dimensions/aspect ratio of a texture?
A: You can get it from the texture picker. When images are uploaded they are resized down to the closest power of two (x & y are rounded down individually; they can be different).

Q: How do I get the dimensions/aspect ratio of a face?
A: llSetPrimitiveParams and a lot of math. Prim torture may make this impossible.
Q: Can a streaming video from a source on the internet be applied as a texture to an object? If not, can a compressed video file be applied?

Functions | Inventory


Time

Time keeps things moving in (or out of) sync with everything in SL.

The time functions include timers, various mechanisms to get different timestamps, (a fancy word for "time and date") information about the sim's performance and sun position.

Functions

Function | Description
llGetAndResetTime | Gets time since script reset or time last reset--and resets time to 0.
llGetDate | Gets date.
llGetGMTclock | Gets time in seconds since midnight in GMT. Use llGetWallclock for PST.
llGetRegionTimeDilation | Gets region time dilation
llGetSunDirection | Gets current sim sun direction.
llGetTime | Gets time since script reset or time last reset.
llGetTimeOfDay | Gets time of sim day.
llGetTimestamp | Gets a timestamp with current date and exact time (real-world).
llGetWallclock | Gets time in seconds since midnight in simulator server's time zone. Use llGetGMTclock for GMT.
llGetUnixTime | Gets the UNIX timestamp.
llResetTime | Resets script time to zero.
llSetTimerEvent | Sets timer event (see below).
llMinEventDelay | Sets the minimum amount of time, in seconds, between any event calls.

Events

timer Triggered every X seconds.

Time is also represented in seconds for various function parameters--too numerous, and mostly superfluous, to list here.

Q: How do I find out what day of the week it is?
A: See GetDayOfWeek function on the ScriptLibrary.

Q: Is there a way to keep client-side functions like llTargetOmega in sync with other llTargetOmega functions?
A: Not reliably; stopping and starting them at the same time may cause them to synchronize, but in practice, this is likely to be rare.

Functions | Simulator | Timestamp | Script Delay


Transform

A transform, or transformation, is when a vertex, polygon, primitive, or linked object is manipulated via translation, rotation, or scaling.

Position | Rotation | Scale


Position

An LSL position (also "location" or "translation") is expressed as a vector in the form <x,y,z>. A position is a required property for any object and usually describes the object's location in the simulator (a region coordinate, which is relative to the southwest corner of the sim).

Position may be expressed in global coordinates, region coordinates, and local coordinates.

Position can translate into movement.

Functions

llGetLocalPos
llGetPos
llGetRootPosition
llGetPrimitiveParams
llSetPos
llSetPrimitiveParams

Transform | Coordinate | Movement


Vehicles

These functions, when applied to any ordinary object, imbue it with special powers, allowing you to create versatile vehicles with physics that can slide, hover, fly, and float or dive.

Unlike the rest of the official LSL guide, the vehicles section is quite good. See the original LindenVehicleTutorial or the newer Wiki'fied version Vehicle Tutorial.

Some of the behaviors you can set include:
  • deflection of linear and angular velocity to preferred axis of motion
  • asymmetric linear and angular friction
  • hovering over terrain/water or at a global height
  • Turning on banking
  • linear and angular motor for push and turning

Note: Vehicles can only be 31 prims maximum, and never attachments.

Functions

llSetVehicleType | define type of vehicle
llSetVehicleFlags | set vehicle flags
llRemoveVehicleFlags | remove vehicle flags
llSetVehicleFloatParam | set vehicle parameters of type float
llSetVehicleVectorParam | set vehicle parameters of type vector
llSetVehicleRotationParam | set vehicle parameters of type rotation

Note: The camera and llSitTarget functions may be useful for vehicles as well.

Constants

(use the matching llSetVehicle<Type>Param function from the table above)

Constant | Value | Type | Description
VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY | 32 | float | slider between 0 (no deflection) and 1 (maximum strength)
VEHICLE_ANGULAR_DEFLECTION_TIMESCALE | 33 | float | exponential timescale for the vehicle to achieve full angular deflection
VEHICLE_ANGULAR_FRICTION_TIMESCALE | 17 | vector | vector of timescales for exponential decay of angular velocity about the three vehicle axes
VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE | 35 | float | exponential timescale for the angular motor's effectiveness to decay toward zero
VEHICLE_ANGULAR_MOTOR_DIRECTION | 19 | vector | angular velocity that the vehicle will try to achieve
VEHICLE_ANGULAR_MOTOR_TIMESCALE | 34 | float | exponential timescale for the vehicle to achive its full angular motor velocity
VEHICLE_BANKING_EFFICIENCY | 38 | float | slider between -1 (leans out of turns), 0 (no banking), and +1 (leans into turns)
VEHICLE_BANKING_MIX | 39 | float | slider between 0 (static banking) and 1 (dynamic banking)
VEHICLE_BANKING_TIMESCALE | 40 | float | exponential timescale for the banking behavior to take full effect
VEHICLE_BUOYANCY | 27 | float | slider between -1 (double-gravity) and 1 (full anti-gravity)
VEHICLE_HOVER_HEIGHT | 24 | float | height the vehicle will try to hover (*1)
VEHICLE_HOVER_EFFICIENCY | 25 | float | slider between 0 (bouncy) and 1 (critically damped) hover behavior
VEHICLE_HOVER_TIMESCALE | 26 | float | period of time for the vehicle to achieve its hover height
VEHICLE_LINEAR_DEFLECTION_EFFICIENCY | 28 | float | slider between 0 (no deflection) and 1 (maximum strength)
VEHICLE_LINEAR_DEFLECTION_TIMESCALE | 29 | float | exponential timescale for the vehicle to redirect its velocity to be along its x-axis
VEHICLE_LINEAR_FRICTION_TIMESCALE | 16 | vector | vector of timescales for exponential decay of linear velocity along the three vehicle axes
VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE | 31 | float | exponential timescale for the linear motor's effectiveness to decay toward zero
VEHICLE_LINEAR_MOTOR_DIRECTION | 18 | vector | linear velocity that the vehicle will try to achieve
VEHICLE_LINEAR_MOTOR_TIMESCALE | 30 | float | exponential timescale for the vehicle to achive its full linear motor velocity
VEHICLE_LINEAR_MOTOR_OFFSET | 20 | vector | offset from the center of mass of the vehicle where the linear motor is applied (new in 1.6)
VEHICLE_REFERENCE_FRAME | 44 | rotation | rotation of vehicle axes relative to local frame
VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY | 36 | float | slider between 0 (bouncy) and 1 (critically damped) attraction of vehicle z-axis to world z-axis (vertical)
VEHICLE_VERTICAL_ATTRACTION_TIMESCALE | 37 | float | exponential timescale for the vehicle to align its z-axis to the world z-axis (vertical)

(*1) - The height value is interpreted ground-relative, water-relative or global-position-based according to the hover-related flags (see llSetVehicleFlags). A height larger than 100m seems interpreted as 100. (so when something should be able to go inland, which can easily be 40m up, over 100m in mountain areas, try not to use global height.) Values of 0.0 and below give no response. Very small values like 0.001 do, but objects don't seem to go lower than 2m.

Quick Copy & Paste Vehicle Params

llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.0);  // Between 0 (no deflection) and 1 (maximum strength)
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 0.0); // Time for vehicle to achieve full angular deflection
llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, ZERO_VECTOR); // Time for exponential decay angular velocity about the three vehicle axes
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.0); // Time for angular motor's effectiveness to decay toward zero
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, ZERO_VECTOR); // Angular velocity that vehicle will try to achieve
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 0.0); // Times for vehicle to achive full angular motor velocity
llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, 0.0); // Between -1 (leans out of turns), 0 (no banking), and +1 (leans into turns)
llSetVehicleFloatParam(VEHICLE_BANKING_MIX, 0.0); // Betwen 0 (static banking) and 1 (dynamic banking)
llSetVehicleFloatParam(VEHICLE_BANKING_TIMESCALE, 0.0); // Time for banking behavior to take full effect
llSetVehicleFloatParam(VEHICLE_BUOYANCY, 0.0); // Between 0 (no anti-gravity) and 1 (full anti-gravity)
llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 0.0); // Height vehicle will try to hover
llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY, 0.0); // Between 0 (bouncy) and 1 (critically damped) hover behavior
llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 0.0); // Period of time vehicle will achieve its hover height
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.0); // Slider between 0 (no deflection) and 1 (maximum strength)
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 0.0); // Time for vehicle to redirect velocity to be along x-axis
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, ZERO_VECTOR); // Time for decay of linear velocity along three vehicle axes
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 0.0); // Time for linear motor's effectiveness decay toward zero
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_OFFSET,ZERO_VECTOR); // Offset from the center of mass of the vehicle where the linear motor is applied (new in 1.6)
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, ZERO_VECTOR); // Linear velocity vehicle will try to achieve
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 0.0); // Time for vehicle to achive full linear motor velocity
llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, ZERO_ROTATION); // Rotation of vehicle axes relative to local frame
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.0); // Between 0 (bouncy) and 1 (critically damped) attraction of vehicle z-axis to world (vertical)
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 0.0); // Time for vehicle to align z-axis to world z-axis (vertical)
Functions | Constants


Video

Second Life supports video playback in-world.

To make simple texture animations, use llSetTextureAnim.
To play video on a prim face with QuickTime, use llParcelMediaCommandList and llParcelMediaQuery.

Functions

llParcelMediaCommandList
llParcelMediaQuery
llSetTextureAnim

If you wish to record a video, you can do that fairly easily either by using the menu File -> Start/Stop Movie to Disk or with FRAPS (recommended). There is no way to do this with LSL.

To play music in SL, see llSetParcelMusicURL.

Functions


Weather

SL features a cellular automata-based weather system:

Andrew Linden:

  • (11/22/05) The current weather system is a simple random cloud seed/growth/disipation algorithm (basically a small grid of cellular autonoma) that is influcenced by a fast, low-resolution (16x16) 2D fluid simulation driven by an inherent pseudo instability of the zero-divergence step (that is fancy language for a "flawed implementation of a Jos Stam fluid simulation").

    As to future plans for weather... a long time ago (alpha? beta?) we had a rain effect, however it was too expensive at the time to do collision checking for the raindrops so the rain would move right through a house's roof. Most people found it annoyingly unrealistic so we got rid of it. In order to do more correct rain and fog effects we would have to embed some physics collision algorithms on the client. The good news is that such effects are definitely something that we want to do. The bad news is that the timeline for such a project is at least one year away.

  • (2/10/06) On the server side clouds are generated by a cellular-automata algorithm on a grid that uses the values at nearest neighbors to generate the probability of transitions from between some small number of states like: (clear, cloudy, very_cloudy, raining). Each simulator has an 18x18 grid of cloud cells. The boundary values of the cloud grid is transmitted to neighbor sims, which make up the 1st and 18th rows/columns of the grid. Although it doesn't rain in SL, the 'raining' state is used to indicate that the cell is about to transition to 'clear'. The distribution of moisture is then transmitted to the client which is used to distribute the cloud particles within that region. On the client side, clouds are rendered as particles.

  • (11/22/06) I think there is a small offset that causes the global wind average to point away from the sun around sunrise and sunset, however it has been a long time since I've looked at that code.

Functions

llCloud
llGetSunDirection
llWind

While fog, rain, and snow don't formally exist as SL weather, they can be simulated with particles.

Functions | Simulator


xmlrpc

What is XML-RPC?

XML-RPC (remote procedure calling) is a specification and set of implementations that allow software running on disparate operating systems running in different environments to make procedure calls over the Internet.

Its remote procedure calling uses HTTP as the transport and XML as the encoding. XML-RPC is designed to be as simple as possible, while allowing complex data structures to be transmitted, processed, and returned.

The URL for sending XML-RPCalls to a script is: http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi

The name for a request is: llRemoteData

Parameters must be a struct(ure) consisting of : string Channel, string StringValue, int Int[[value Value]] (see example requests below)

If "XML-RPC" is simply too long to type out and you feel you must abbreviate it, don't call it "XML", call it "RPC". Describe it by what it does, not by the data format it uses. Everything uses XML now anyway.

Event:
remote_data

Functions:
llOpenRemoteDataChannel | Open a channel
llCloseRemoteDataChannel | Close the channel (Doesn't actually work.)
llSendRemoteData | Send data/request (Doesn't actually work.)
llRemoteDataReply | Reply to a request
llRemoteDataSetRegion | Reregister channels upon moving into another sim (Doesn't actually work.)

Constants:
REMOTE_DATA_CHANNEL | successful llOpenRemoteDataChannel
REMOTE_DATA_REQUEST | request received
REMOTE_DATA_REPLY | reply received

Notes
It seems the implementation is on a sim level: data doesn't get to the object when you move across sim borders. -?
Are you saying that the remote data channel gets closed automatically when the task moves accross region boundaries? Does this apply to attachments as well? -?

IMPORTANT IMPLEMENTATION NOTE:
The current implementation of XML-RPC only allows ONE request to be queued on the front-end server (xmlrpc.secondlife.com) at a time. Any additional requests to the same data channel overwrite any pending one. This has serious ramifications for the design of XML-RPC communications where the in-world object could receive requests faster than it can respond to them. In addition, the 3-second delay in llRemoteDataReply exacerbates this problem even more.

The observed issue is this: if you send multiple quick requests to an in-world object via XML-RPC, one which is scripted to perform some processing and then return a response (via llRemoteDataReply), there is a potential for earlier requests to get lost on the front end server (they still should generate remote_data events, though), and have the response meant for an earlier request end up being passed back to a later one, while the earlier requests will time out back at your external application.

As a result, if you intend to do any serious work with XML-RPC, you will have to design your external client application to manually serialize all requests to each individual RPC channel. That means you have to wait for a response from the previous request before you attempt to send the next one. If you don't care about receiving responses, then this problem is not an issue, as all requests seem to get passed on to the script, regardless of the queueing issue.

Also note that there is NO way to get around the 3-second delay for llRemoteDataReply; you cannot use the multiple-slave-comm-script trick, because XML-RPC channels are *script-specific*, NOT *object-specific*.

For more information, see these forum threads here and here.
-- TalarusLuan

Examples

LSL
Example Request
Example Response

LSL Example:
// XML-RPC example

key     gChannel; // my llRemoteData channel

DEBUG(list out) { // output debug info
    llSay(0, llList2CSV(out));
}

default { // initialize XML-RPC
    state_entry() {
        llOpenRemoteDataChannel(); // create an XML-RPC channel
        // this will raise remote_data event REMOTE_DATA_CHANNEL when created
    }

    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
        if (type == REMOTE_DATA_CHANNEL) { // channel created
            DEBUG(["Channel opened", "REMOTE_DATA_CHANNEL", channel, message_id, sender, ival, sval]);
            gChannel = channel;
            llSay(0, "Ready to receive requests on channel \"" + (string)channel +"\"");
            state go; // start handling requests
        } else DEBUG(["Unexpected event type", type, channel, message_id, sender, ival, sval]);
    }
}

state go { // handle requests
    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
        if (type == REMOTE_DATA_REQUEST) { // handle requests sent to us
            DEBUG(["Request received", "REMOTE_DATA_REQUEST", channel, message_id, sender, ival, sval]);
            // handle request
            if (sval == "How are you?") { // we do something special on that string parameter
                // send a reply to this request, referencing the senders message id
                llRemoteDataReply(channel, message_id, "Fine, thanks!", 1);
            } else { // IMPORTANT: a reply MUST always be sent
                llRemoteDataReply(channel, message_id, "", 0);
            }
        } else DEBUG(["Unexpected event type:", type, channel, message_id, sender, ival, sval]);
    }

    state_exit() {
        llCloseRemoteDataChannel(gChannel); // clean up when you no longer want to handle requests
    }
}

Example request:
<?xml version="1.0"?>
<methodCall>
    <methodName>llRemoteData</methodName>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>Channel</name>
                        <value><string>4a250e12-c02e-94fb-6d2f-13529cbaad63</string></value>
                    </member>
                    <member>
                        <name>IntValue</name>
                        <value><int>0</int></value>
                    </member>
                    <member>
                        <name>StringValue</name>
                        <value><string>test</string></value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodCall>

Example response:
<?xml version="1.0"?>
<methodResponse>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>Channel</name>
                        <value><string>6df57cf4-68b7-b958-5b6c-10cb9f2fa391</string></value>
                    </member>
                    <member>
                        <name>StringValue</name>
                        <value><string>Monkey</string></value>
                    </member>
                    <member>
                        <name>IntValue</name>
                        <value><int>0</int></value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodResponse>

Want to complain about XML-RPC's implementation? Have you come up with an idea for a better solution? See XML-RPC Discussion.

For XML-RPC implementations in a variety of languages, see XMLRPCImplementations.

LuccaKitty: The above example requests and responses are fine if you already have a simple http post app, but that wasn't the case for me, i had to construct an entire http post packet from scratch. I'm posting the http post packet in it's entirety below. This will be useful if you intend on sending raw packets to make a quick and dirty client app. Maybe needs a refactoring. I apologise for the code scrolling off the example box. When generating said packet, I was attempting to keep the actual amount of data to a minimum to conserve bandwidth. I would format the packet, except that you'll notice Content-Length: 411. If I refactor it, the packet is no longer actually usable, as it changes the content length of the request. Hope this helps someone.

Example request entire raw packet:
POST /cgi-bin/xmlrpc.cgi HTTP/1.0
Host: xmlrpc.secondlife.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 411

<?xml version="1.0"?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct><member><name>Channel</name><value><string>51ef73f1-14d6-a6c3-b6b6-dbacaa4127d5</string></value></member><member><name>IntValue</name><value><int>11261979</int></value></member><member><name>StringValue</name><value><string>happy birthday</string></value></member></struct></value></param></params></methodCall>

Links
current XML-RPC specification
XML-RPC.com

Functions | Communications | XML-RPC Discussion | XML-RPC Implementations Despammed


Contributors

For people who *want* to contribute: Please register by going to UserSettings. You'll need to do this if you want to create new pages or edit existing ones. It's also a good idea to read the WikiStyleGuide first.

Please also have a look at the scripting mentors page if you are willing to help others in-game.

782 "Numbers" Naumova
Aakanaar LaSalle
Adam Zaius
Ag3nt "1337" Case
Aldar "You shroomer!" Rayner
Alexander "empty string" Daguerre
Alphons "LSLeditor" Jano from the Netherlands
Ama "Casino" Omega
Angela Salome
April "Schroedenberg's Uncertain Cat Principle" Heaney
Argent "John Pallister" Smith
Arthur "Point, Click, Repeat" Fermi
Ash "Lskcab" Byrne
AyAn4m1 "Phr34k" Noir Not playing anymore
Beatfox Xevious
Bel "POG" Muse
BhavinPoindexter
Bino Arbuckle
Brian Livingston
Brian Mifflin
Buckminster Hamilton
BuhBuh "uh uh uh" Cuh
Burnman "BVS" Bedlam
Catherine "Behind you right now" Omega
Chad "Nexcom" Statosky
Chage "SimCrasher" McCoy
Chandra "Tiramisu" Page
Chasingred3 "PERMISSION_AGENT_EXPLODE" Ixtab
CherokeeTek
Chris Knox
Christopher "Sandbox" Omega
Chromal "Kodachrome" Brodsky
Cid "String of a Cid_Length" Jacobs
Cienna Rand
Cirr "Did I blow up the sim again?" Marat
Cross "I'm not mad!" Lament
Dave "Zave Deeman" Zeeman
Dedric "Go go gadgeter" Mauriac
Devon "Friggin" Cassidy
DoctordaffyCure
Dolus "Doesn't Care What You Think" Naumova
Draconis "Havok 1 Sucks" Neurocam
Dunsel "I Want Hinging Back" Hodgson
Eep "BoObEh" Quirk
Ezhar "Weatherman" Fairlight
Echo "Echo" Omega
Escort "Seijaku" DeFarge
Fearless "Humility Is My Middle Name" Leader
Foxdie "Can I Touch You?" Ghia
FreqOut "XMLRPC2Prim" Manray
Gabriel "Unretrofied" Spinnaker
Gene "Superslacker" Poole
GoshuaLament
Guzar "Not Gunzar" Fonzarelli
Hank "Doc" Ramos
Hans Baldwin
Hawkster "Freakin' Awesome" Westmoreland
Hillary "Trouble" Melville
Hiro Yamamoto
His Grace
Ione "Ruin Lord" Lameth
Ice "World Map" Brodie
Jesrad "Who ?" Seraph
Jillian "Fraidycat" Callahan - Wishes she were playing all the time!
Jim "Monkey" Bunderfeld
Jippen "Furball" Faddoul
Jordan Cotton
Julian "Vito 'The Icepick' Garbanzo" Fate
Justice "state_vicious" Monde
Kafka "I AM mad!" Solvang
Kaira "BuildTexture&Script Angel" Overdrive
Keiko "Wiki sounds like Japanese" Rau
Keilaron "All about standards" Tomba
Keknehv "Woops" Psaltery
Kex "Fun Size" Godel
Malarthi "Watching the TG longer than you!" Behemoth
Max "Oops" Case
Maxx "King of Plywood" Monde
Mechageo "A Giant Robot Stepped On My House" Dannunzio
Michael "Wikis a verb" Small
Ming "No one likes me" Chen
Moleculor "Firefly" Satyr
Neo "Stalker Extraordinaire" Rebus
Olmy "Overkill" Seraph
Patch "doodles" Lamington
Psyke Phaeton
Rade "Christopher Armstrong" Galatea
Ready "Anchor King" Jack
Rysidian Rubio
Robert "Bob Luchi" Luchador
Ryan "1 2 step" Dayton
StormBasiat
Schitso "Ziggy Boogey Doog" Monkey
Shadow "where is he" Gretzky
Siobhan Taylor
SiRiS Asturias
Stefan "Big Swede" Nilsson
Stone "Bigger Than Yours" Mason Still playing.
St. Psaltery
Strife "BlindWanderer" Onizuka
Tateru Nino
Timeless "Multi Gadget" Prototype
Tinker "Fidget" Thetan
vaguegirl "By Me, For Me, About Me" Petty
vanler "Save the hippos" Odets
Velox "Calculated" Severine
Vicero "I Like Cookies" Lambert
Wednesday "Dr. Funkalicious" Grimm
WolfWings "He crashed ANOTHER sim?!?" Majestic
Xylor "Text Master" Baysklef
Zach "Vehicle Lover" Zamboni
ZenMondo "CodePoet" Wormser
ZenoConcord

Give yourself a middle name, it's fun - WG
Emphasis on self eh WG?? ;) - A'C'O
It's a wiki, if I made a mistake, fix it ;) - WdfG
lol this is fun
Yes its real fun - Vicero

Add your name :-)
In No Way Have Any Servers Been Harmed In The Making Of This Page, ViceroLambert
But my eyes have been! KeknehvPsaltery
Poink! BurnmanBedlam
Please consult your doctor before reading this page. Some side effects include nausia, dizzyness, and insomnia. Rare but serious cases of death have occured. Do not read this page if you have liver disease, laryngitis, or uncontrollable eye movements. AakanaarLaSalle
Repetitive key movements as you try to explain the horrible feelings brought on by this page may cause carpal tunnel. Rest for 15 minutes after every hour. VirusCollector

Humor


AakanaarLaSalle

string AakanaarLaSalle(integer donation, key id, string name)

Returns a thank you for the donation. If id is not NULL_KEY, and is an agent, then a Thank You Notecard is delivered to id. If name is not an empty string, the Notecard is Personalized with the name provided.

Example:
touch_start(integer tnum)
{
  string reply = AakanaarLaSalle(250, llDetectedKey(0), llDetectedName(0));
  llSay(0, reply);
}
This will make the object say: "Thank You your_name for your generous donation." and a notecard with the same line will be given to your inventory.

Note: As of 10-01-2006 this function appears to be not working. There are a lot of scripts that are broken currently because of this, but Linden reports that they are working to fix this as quickly as possible. In the meantime you'll have to use the standard llGiveMoney() function instead.

I've been in the habbit in most of my programming to start variables with a short 2 or 3 letter abbreviation of the variable type. You will notice I use strVariable for strings, lstVariable for lists, vctVariable for vectors, clrVariable for colors, etc. I tend to carry this over to functions, though I still am not happy with my choices for abreviations. In my Strided Lists code I used fnc for function. I may try uf for user function. Since ll is used for Linden Library, the thought had crossed my mind to use letters of the paticular library or script I am writing, but I'm not sure that will work out every single time. If anyone has any ideas that might be better, please let me know. - Aak

Experience:
Programming in PHP, Perl, C, C++, LSL, HTML, XHTML, CSS
Certified Computer Geek with Comptia A+ certification

Web Page: (Yes I have a domain named after my char.. actually I named the char after my domain)
http://www.Aakanaar.com - I havn't got much there. I really need to do something with that domain. But, you can find more about me on This Page.

Games Played:
Everquest, NeverWinter Nights

Lives Lived:
Second Life!!!

I think I should go sign up for a MySpace page. Since I'm signed up here, I think I may qualify as a Geek.

MyPages | MyChanges | WikiFormatting | LSLWiki | LibraryStridedLists


AdamZaius

Virtual Hermit, probably better known for involvement in Ouranos-related projects.


AgentCase

H3ll0 3v3ry0n3. My n4m3 15 463n7. 1 4m 73h 3|337 |5| 5cr1p73r!

o_O

:D

Typing like that makes me feel special.


AlexanderDaguerre

Relative SecondLife newbie.

First life software guy. Professional compilers, languages and operating systems guy from way back until it became unprofitable :-) Part-time crypto and time zone geek.

Runs the There technical wiki, which is kind of the same idea as this wiki but for that other place. Does not participate in inter-metaverse mud slinging, however.

Does not participate in any kind of holy war, in fact, so don't bother trying to goad me into a flame war over your choice of computer, operating system or programming language. I probably have bad things to say about all of the alternatives.


AlphonsJano

Hello,
My name is Alphons. I am a programmer... you probably also.
I live in the Netherlands. I used to be 38.

Go and have a look at my project LSLeditor http://www.lsleditor.org/


AmaOmega

I have been scripting in LSL since December 2002. My mentors included Nada Epoch, BuhBuhCuh Fairchild and my printed copy of the LSL manual.

I don't get on SL as much as I used to, but an IM ingame will reach me fairly quickly. I am glad to help with scripting issues of all kinds, my strength is logic and data structures as well as finding someone who does know what you need to know. I have all but dropped off the face of the forums. My evil employment company actually expects me to do work or something and I don't get as much forum time. That also means I don't get much post to wiki time either.

Still respond to IMs and I have a good chunk of island now somewhere in world. Alek has already terraformed the bejeepers out of it.

Add to the ScriptLibrary!
Use %%(lsl) to mark code lsl code blocks! Ezhar is my hero.

(Yes, Catherine I referred to it as just Wiki, just for you)
I'm going to come to your house and kill you, Ama. -- All my love, Cat


AngelaSalome

Slave to my Master AmmonForrest Wishbringer.
Awarded the Honorary Wiki Troll badge by BlindWanderer.

I've been doing a lot of sitting lately, sitting and thinking.


AprilHeaney

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Argent Smith

Has not created a user page! :-O


AshByrne

.ton rO .enryB hsA ,em taeb tsum uoy ,emag eht niw oT

Hi, I'm Ash. Every once in a while I come up with something cool.

This function allows you to base64-encode a list of integers with values 0-255. Sorta like a poor-man's byte array. It's based upon the curl_base64_decode function in libcurl. I needed it because I needed to output a bunch of data from SecondLife with XMLRPC, and llStringToBase64 just wasn't going to work in my case.

This function will become rather useless, in favor of llIntegerToBase64, when SL 1.5 comes out of preview mode. (I wonder where they got that idea from! :) ) Well, that doesn't work on lists of integers, but it'd be trivial to put it in a for loop.

llStringToBase64("test") gives the same result as intListToBase64([116,101,115,116]).

string  intListToBase64(list array) {
    string translation_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    integer insize = llGetListLength(array);
    integer i;
    integer inputparts;
    list    ibuf;
    integer dataptr = 0;
    string  str;
    while (insize > 0) {
        ibuf = [];
        for (i = 0, inputparts = 0; i < 3; ++i) {
            if (insize > 0) {
                inputparts++;
                ibuf += [ llList2Integer(array,dataptr) ];
                dataptr++;
                insize--;
            } else
                ibuf += [ 0 ];
        }

        integer ibuf0 = llList2Integer(ibuf,0);
        integer ibuf1 = llList2Integer(ibuf,1);
        integer ibuf2 = llList2Integer(ibuf,2);

        integer obuf0 = (ibuf0 & 252) / 4;
        integer obuf1 = ((ibuf0 & 3) * 16) | ((ibuf1 & 240) / 16);
        integer obuf2 = ((ibuf1 & 15) * 4) | ((ibuf2 & 192) / 64);
        integer obuf3 = ibuf2 & 63;

        if (inputparts == 1) { // one byte read
            str += llGetSubString(translation_table, obuf0, obuf0) +
                   llGetSubString(translation_table, obuf1, obuf1) + "==";
        } else if (inputparts == 2) { // two bytes read
            str += llGetSubString(translation_table, obuf0, obuf0) +
                   llGetSubString(translation_table, obuf1, obuf1) +
                   llGetSubString(translation_table, obuf2, obuf2) + "=";
        } else { // three bytes read
            str += llGetSubString(translation_table, obuf0, obuf0) +
                   llGetSubString(translation_table, obuf1, obuf1) +
                   llGetSubString(translation_table, obuf2, obuf2) +
                   llGetSubString(translation_table, obuf3, obuf3);
        }
    }
    return str;
}


AyAn4m1Noir

See Tony, I really do own this account.


BeatfoxXevious

This page has nothing of interest. Move along.


BelMuse

Bel is the lackey of CatherineOmega. When Catherine snaps her fingers, Bel fires up ftp. "What shall I upload today, O Mistress of the Wiki!"


BhavinPoindexter

Hello! I'm just the average SL player. It's all I do!


BinoArbuckle

I have land in Boardman and Hibdon and contribute to Kazenojin in Gray. (In the past I have owned land in Tan, Kissling, Darkwood, Federal, Lusk, Teal, Gray, Welsh, and Jessie, not in that order). I script occasionally using LSL. I also like to take my boat out and speed around the big void sim ocean that my land in Hibdon faces.

Despite my love of wikis and their brethren, I am not here all the time. You can reach me using in-world IM (it goes to email) or by sending an email to bino DOT arbuckle AT gmail DOT com :-D

My scripting interestings include:
- sensors
- communications
- dataserver
- worldfunctions
- video

I also run SLMaps which I try to keep current with the main grid. Check out the flash chronologies! :-D

Stuff for me:
- MyPages
- MyChanges
- WikiFormatting
- AboutACLs

I play SL on both a PC and a Mac. I started the SLMUG group, which I later dissolved as the Macintosh Users group became free to join.

PC
- P4 2.26GHz
- 512mb ram (bad slot killed another stick, leaving it)
- GF FX5900 128mb

Mac
- G4 867MHz
- 640mb ram
- GF4MX 32mb

Hoping to add a Quad G5 desktop in '06.

Added a Mac Pro Quad Xeon in September '06:
- 2x Dual-Core Xeon 3.0GHz
- 2048mb ram
- ATI Radeon X1900 512mb

Wanting to get some more RAM, and the Mac Pro is my primary SL machine despite the constant SL client crashing.

I constantly have to think to remember my password for this site. I really need something more universal ;-)

Name2Key is always a popular topic, I've already referenced external resources for it in llKey2Name but I will keep it here in case the llKey2Name reference is improper:
  • name2key - w-hat
  • Moopf Murray :: name2key Services
  • Name2key - libsecondlife
  • www.Ulrikasheim.org - name2key

I'm trying out this backlinks thing, I'm sure I'll get a pile of hits as I helped fill out a bunch of pages in in the initial wiki development. Please notify me if any ACLs are improperly set ;-)

BinoArbuckle
Boolean
Contributors
CoryLinden
IM
InstantMessage
LinkMessages
link_message
llBreakLink
llCloud
llCollisionFilter
llDetectedGrab
llDetectedGroup
llDetectedKey
llDetectedLinkNumber
llDetectedName
llDetectedOwner
llDetectedPos
llDetectedRot
llDetectedType
llDetectedVel
llGetLinkKey
llGetLinkName
llGetLinkNumber
llGetPermissions
llGetPermissionsKey
llGiveMoney
llGround
llListenControl
llListenRemove
llMessageLinked
llRequestPermissions
llSay
llSensor
llSensorRemove
llSensorRepeat
llSetLinkColor
llShout
llWhisper
llWind
no_sensor
PageIndex
run_time_permissions
sensor
Sensors
Water
WikiUsers

Okay that's quite a bit.


BlindWanderer

Strife Onizuka in game,
I'm an immigrant from URU. Was in all 3 of it's beta's.

Wiki Troll (but a good one, causing drama isn't why i'm here).

I have a webpage but haven't updated it in a few months...

Wiki's are the way of the future when it comes to making documentation Ad-hoc but eventually become static as graffiti grows with popularity as a way of controling it.

also contribute to some other wiki's
http://history.secondserver.net/index.php/Main_Page
AviSynth:BlindWanderer

Updates: | Preview Download
DMZ | Preview
Agni | Preview
Colo | Preview
Siva | Preview
Durga | Preview
Aditi | Preview
Soma | Preview
Ganga | Preview
Shakti | Preview

string TightListDump(list a, string b) {
    string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) == -1)
            jump end;
    integer d = 1 - llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b);
}

list TightListParse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}

list quickread(string a)
{
    string c = llGetSubString(a,0,6);
    return llDeleteSubList([a]+//a = llGetSubString(c,0,0) by this point
                        llParseStringKeepNulls(llDeleteSubString(a,0,5), [],[a=llGetSubString(c,0,0),
                        llGetSubString(c,1,1),llGetSubString(c,2,2),llGetSubString(c,3,3),
                        llGetSubString(c,4,4),llGetSubString(c,5,5)]),
                        (llSubStringIndex(c,llGetSubString(c,6,6))==-1),1);
}

integer TightListTypeLength(string a)
{
    return llGetListLength(quickread(a))/2;
}

integer TightListTypeEntryType(string a, integer b)
{
    string c = llGetSubString(a,0,6);
    a = llList2String(quickread(a),  b * 2);
    return 1 + llSubStringIndex(c, a) - !llStringLength(a);
}

list TightListTypeParse(string a) {
    if(llStringLength(a) < 7)  return [];
    string m = llGetSubString(a,0,6);
    list b = quickread((a = "") + a);
    integer c = -llGetListLength(b);
    list f;
    integer d;
    do
    {
        f = [a = llList2String(b,c + 1)]; //TYPE_STRING || TYPE_INVALID (though we don't care about invalid)
        if((d = (1 + llSubStringIndex(m, llList2String(b,c)))) == TYPE_FLOAT)
            f = [(float)a];
        else if(d == TYPE_VECTOR)
            f = [(vector)a];
        else if(d == TYPE_ROTATION)
            f = [(rotation)a];
        else if(d == TYPE_KEY)
            f = [(key)a];
        else if(d == TYPE_INTEGER)
            f = [(integer)a];
        b = llListReplaceList(b, f, c, c+1);
    }while(0 > (c+=2));
    return b;
}

string TightListTypeDump(list a, string b) {
    b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ";
    string c = (string)a;
    integer d = 0;
    do
        if(1+llSubStringIndex(c,llGetSubString(b,d,d)))
            b = llDeleteSubString(b,d,d);
        else
            ++d;
    while(d<6);
    b = " " + c = llGetSubString(b,0,5);
    integer e;
    string f;
    if((d = -llGetListLength(a)))
    {
        do
        {
            e = llGetListEntryType(a,d);
            if(e == TYPE_VECTOR)
                f = vec(llList2Vector(a,d));
            else if(e == TYPE_ROTATION)
                f = rot(llList2Rot(a,d));
            else if(e == TYPE_FLOAT)
                f = flo(llList2Float(a,d));
            else
                f = llList2String(a,d);
            c+= llGetSubString(b,e,e) + f;
        }while(++d);
    }
    return c;
}

integer fui(float a){//union float to integer
    if(a){//is it non zero?
        integer b = (a < 0) << 31;//the sign, but later this variable is reused to store the shift
        if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stirde of normalized range
            return b | (integer)(a / 1.4012984643248170709237295832899e-45);//this works because the math overlaps; saves cpu time.
        integer c = llFloor(llLog(a) / 0.69314718055994530941723212145818);//extremes will error towards extreme. following yuch corrects it.
        return (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= (c == 128)))))))) << 23 ) | b);
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
    return ((string)a == (string)(-0.0)) << 31;
}

float iuf(integer a)
{//union integer to float
    return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.

integer FloatCompare(float a, float b, integer c)
{//compare floats and allow for a margin of error, requires fui().
    if(a - b)//(c) Strife Onizuka 2006
    {//they are not equal
        //First we convert the floats to integer form, as they would be in memory;
        integer a_i = fui(a);
        integer b_i = fui(b);
        integer a_e = (a_i >> 23) & 0xff;
        integer b_e = (b_i >> 23) & 0xff;
        if(!(a_e || b_e) || ((a_i & 0x80000000) == (b_i & 0x80000000)))//sign match check
        {//start by getting and testing the difference, this is what limits c
            integer diff = a_e - b_e;//ugly is fast, basicly, it gets the mantissa, sets the sign on the mantisa,
            if(!(diff && ~diff && ~-diff))//shifts it depending on exponent, finaly executes the test.
                if(llAbs(((((a_i & 0x7FFFFF) | ((!!a_e) << 23)) * ((a_i >> 31) | 1)) >> diff) -
                         ((((b_i & 0x7FFFFF) | ((!!b_e) << 23)) * ((b_i >> 31) | 1)) >> -diff)) <= c)
                    jump out;//they were close enough for government work :P
        }
        return (a > b) - (a < b);
    }
    @out;
    return 0;
}

string FUI2HexFloat(integer b)
{//Dump FUI float-integer to a hex-float string
    string c = "";
    integer d = 0;
    integer e = 0xff & (b >> 23);
    string f = "0x"+(string)(!!e) + ".";
    if(b & 0x80000000)
        f = "-"+ f;
    if(e ^ 127) c = "p" + (string)((e | !e) - 127);
    if((e = 0xfffffe & (b << 1)))
    {
        while(!((e >> d) & 0xf))
            d+=4;
        while(d < 24)
        {
            c = llGetSubString(hexc, b = 0xf & (e >> d), b) + c;
            d += 4;
        }
    }
    return f + c;
}

string Float2Hex(float a)
{//Another way to do Float2Hex, i wrote this for the heck of it; not because it's a good idea.
    return FUI2HexFloat(fui(a));
}

integer bitcount(integer n)
{//MIT Hackmem 169, modified to work in LSL
    integer tmp = n - ((n >> 1) & 0x5B6DB6DB)//modified mask
                    - ((n >> 2) & 0x49249249);
    return (((tmp + (tmp >> 3)) & 0xC71C71C7) % 63) + (n < 0);
}

string fuis(float a){//float union to base64ed integer
    if(a){//is it non zero?
        integer b = (a < 0) << 31;//the sign, but later this variable is reused to store the shift
        if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stirde of normalized range
            b = b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
        else
        {
            integer c = llFloor(llLog(a) / 0.69314718055994530941723212145818);//extremes will error towards extremes. following yuch corrects it.
            b = (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= (c == 128)))))))) << 23 ) | b);
        }
        return llGetSubString(llIntegerToBase64(b),0,5)
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
    if((string)a == (string)(0.0))
        return "AAAAAA";
    return "gAAAAA";
}

float siuf(string b)
{//base64ed integer union to float
    integer a = llBase64ToInteger(b);
    return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.

Asset Types by number
Hex | Value | Inventory Flag | Handle | 8 char name | Nice Name | Description
 |  |  | BAD TYPE |  |
0x0000 | 0 | INVENTORY_TEXTURE | TEXTURE | texture |  | JPEG2000 encoded image
0x0100 | 1 | INVENTORY_SOUND | SOUND | sound |  | Ogg-Vorbis encoded Sound
0x0200 | 2 |  | CALLINGCARD | callcard | calling card | Calling card
0x0300 | 3 | INVENTORY_LANDMARK | LANDMARK | landmark |  | Landmark, there are 2 versions of this format
0x0400 | 4 |  | SCRIPT | script | legacy script | LSL1 we assume
0x0500 | 5 | INVENTORY_CLOTHING | CLOTHING | clothing |  | LLWearable
0x0600 | 6 | INVENTORY_OBJECT | OBJECT | object |  | Object
0x0700 | 7 | INVENTORY_NOTECARD | NOTECARD | notecard | note card | Notecard, only one version of the format exists
0x0800 | 8 |  | CATEGORY | category |  | Something to do with inventory
0x0900 | 9 |  | ROOT_CATEGORY |  |  | Something to do with inventory
0x0A00 | 10 | INVENTORY_SCRIPT | LSL_TEXT | lsltext | lsl2 script | lsl2 script text
0x0B00 | 11 |  | LSL_BYTECODE | lslbyte | lsl bytecode | lsl bytecode and memory space, 16k
0x0C00 | 12 |  | TEXTURE_TGA | txtr_tga | tga texture | tga image, used in static cache
0x0D00 | 13 | INVENTORY_BODYPART | BODYPART | bodypart | body part | LLWearable
0x0E00 | 14 |  | TRASH | trash |  | Something to do with inventory
0x0F00 | 15 |  | SNAPSHOT_CATEGORY | snapshot |  | Something to do with inventory
0x1000 | 16 |  | LOST_AND_FOUND | lstndfnd | lost and found | Something to do with inventory
0x1100 | 17 |  | SOUND_WAV | snd_wav |  | Decoded Ogg-Vorbis
0x1200 | 18 |  | IMAGE_TGA | img_tga | targa image | Standard Targa Image - decoded texture
0x1300 | 19 |  | IMAGE_JPEG | jpeg | jpeg image | Standard Jpeg Image - decoded texture or postcard
0x1400 | 20 | INVENTORY_ANIMATION | ANIMATION | animatn | animation | binary file format
0x1500 | 21 | INVENTORY_GESTURE | GESTURE | gesture |  | 2 Versions?
0x1600 | 22 |  | SIMSTATE | simstate |  | Unknown
LLWearable version matches value of "wearable_definition_version" found in ".\character\avatar_lad.xml"

Backlinks:
AirSeaAltimeter
AllarKamloops
ALL_SIDES
alpha
AlternativeCommunications
alternative_communications
AND
AngelaSalome
AnimationNames
API
AssetPermissions
Benchmarks
Binary
bitflag
bitmask
BitShift
BlindWanderer
Boobies
BVH
Bytecode
CamperDave
Chat
Comment3442
Contributors
CrashCourse9
CyclopeanSprocket
Debate
DevonCassidy
DotOperator
DoWhile
Encapsulation
energy
EOF
Equality
Event
Events
ExampleAnimationBasic
ExampleEventOrder
ExampleFixedPrecision
ExampleGetRootPos
ExampleGun
ExampleNotice
ExampleNumberConversion
ExamplePositionOnASphere
ExampleRotToVecAngle
Exchange
exchangeLockGuardItem
exchangeTLML
exchangeTLMLGenerator
exchangeTLMLRenderer
exchangeTLTP
exchangeTLTPBrowser
ExperimentllRezObject
FanZjie1
FanZjie11
FeatureRequest
float
FlyingTakoTips
FollowCam
for
formatSetText
framerate
functionLibrary
FunctionUnescape
FutureRevisions
GlobalCoordinate
grief
GroupObjects
groups
Hacks
HeadsUpDisplay
HUD
humor
IconSerpentine
IfElse
InstantMessage
IridescentEnzyme
IsabelleAquitaine
JPEG2000
key
kilobyte
KnownBugs
LexFile
LibraryDialogModule
LibraryFastFind
LibraryFloat2Hex
LibraryGiveInventory
LibraryGoGame
LibraryInstantMessageAnyone
LibraryInventoryMenuModule
LibraryLindenAIML
LibraryNumRs
LibraryPrimTorture
LibraryPseudoRandomGenerator
LibraryReduxOneLineDoor
LibraryRotationFunctions
LibrarySerialize
LibrarySettingsNotecard
LibrarySiRiSInABox
LibrarySLPrint
LibraryTextureSwitcher
LibraryTimeElapsed
LibraryVigenereCipher
LibraryWarpPos
LindenAIML
linkmessage
LinkMessages
Lisp
list
listen?show_comments=1
llAddToLandBanList
llAngleBetween
llAttachToAvatar
llBase64ToInteger
llBase64ToString
llClearCameraParams
llDetectedSex
llEscapeURL
llFloor
llGetAnimationList
llGetCameraPos
llGetCameraRot
llGetInventoryType
llGetListLength
llGetNumberOfPrims
llGetObjectMass
llGetParcelDetails
llGetParcelFlags
llGetPos
llGetRegionFlags
llGetRegionName
llGetStatus
llGetTorque
llGodLikeRezObject
llGround
llInsertString
llList2CSV
llList2List
llListRandomize
llListReplaceList
llListSort
llMapDestination
llOverMyLand
llOwnerSay
llParcelMediaCommandList
llParcelMediaQuery
llParseStringKeepNulls
llPizza
llPreloadSound
llRefreshPrimURL
llRemoveFromLandBanList
llRemoveFromLandPassList
llRemoveVehicleFlags
llRequestInventoryData
llRequestSimulatorData
llResetLandBanList
llResetLandPassList
llRound
llSetCameraEyeOffset
llSetCameraParams
llSetHoverHeight
llSetInventoryPermMask
llSetObjectDesc
llSetPayPrice
llSetPrimitiveParams
llSetPrimURL
llSetScale
llSetVehicleFloatParam
llSetVehicleRotationParam
llSetVehicleVectorParam
llShout
llTeleportAgent
llTriggerSound
llUnescapeURL
llVecMag
llWhisper
llXorBase64StringsCorrect
LSL101Appendix
LSL101Appendix2
LSL101Foreword
LSLEthics
LSLFunctionRequest
LSLGroups
LSLwiki
Mainframe1
MediaWiki
memory
mouselook
MPEG4
Mycroft
NaN
newview
notecard
OoPsGalatea
OR
PageIndex
ParametricDebate
permissions
pitch
precision
QuickReferenceGuide
QuickTime
RawHeightMap
RealPageIndex
RemovalPetition
roll
script
ScriptingGroups
ScriptingMentors
ScriptLibrary
ScriptPermissions
ScXx
Serialization
ShadowGretzky
shadows
SimulatorNames
SL
Stack
StPsaltery
StrifeOnizuka
StyleGuide
SysOp
TextSearch?phrase=DEG_TO_RAD
TextSearch?phrase=FALSE
TextSearch?phrase=TRUE
TextSearch?phrase=ZERO_ROTATION
TextSearch?phrase=ZERO_VECTOR
ToDo
TremorGlz
Typecast
Unicode
UserDefinedFunction
UV
VektorLinden
WikiRenamingPages
WikiUsers
WrongPageSetup
XML
XMLRPCDiscussion
XyText
yaw
ZonaxD


BrianLivingston

Brian Livingston Was Here

In all reality though, I am just hoping to learn how to script. I have some background in VB, so I am hoping LSL won't be too mean to me :p


BrianMifflin

A scripter for the most part, here for fun. I love to contribute to the experience, and hope to have plenty of chances to!

Use K&R/Sun style braces.....code is pretty that way.


Buckminster Hamilton

In spite of seemingly being
SL's lone conservative,
Buckminster Hamilton
is one cool cat.

The


BuhBuhCuh

BuhBuhCuh is BuhBuhCuh Fairchild

BuhBuhCuh knows more about LindenScriptingLanguage than almost anyone else alive.


Burnman Bedlam

Introduction

Greetings, and thank you for taking the time to check out my little home on the LSL Wiki Mirror 7-7-7. I've been a SecondLife resident since the beginning of the year, and I have been learning everything I can about LSL since I first discovered SL. I enjoy scripting more than most activities in SL, and I have set up shop selling various products that I've scripted.

I decided to become an author for the LSL Wiki Mirror 7-7-7 to contribute what I've learned and give back to the community that has provided me with so much. I intend to update this page from time to time, and you are more than welcome to stop by for a peek.

Expectations

I am an avid supporter of Freedom of Speech, however I understand that my rights end where another's begin. I also understand that the this wiki is hosted on a Linden Labs server, and as such is the property of Linden Labs and subject to their Terms of Service (TOS) and Community Standards (CS).

My personal expectations are that authors of this wiki respect each other, and behave within the bounds of the TOS and CS. There is no reason or excuse that justifies personal attacks and insults. That sort of thing should be kept to private communication.

[ WikiEtiquette | WikiFormatting | WikiStyleGuide ]


CatherineOmega

Catherine Omega is (apparently) the most dangerous woman alive. She is probably standing behind you this very moment.

"Cat's da smartz."

Catherine gets annoyed by people who confuse agents, avatars, clients and users. Nobody else cares. This annoys Catherine even more.

Catherine also gets annoyed by people who call SL a game. It is, but only if the web is a game. There are games on the web, as there are within Second Life, but is it a game in its own right? Catherine calls it a "Massively Multiuser Online Environment". Please pay $30 per usage.

Catherine likes helping people: Troubleshooting

Catherine is interested in Machinima.

Catherine likes C style indents.

HowDoI

Performance Advisory
Content goes here.

stuff


ChadStatosky

Nexcom engineering


ChageMcCoy

I script.
I think I do,
Then I ask other people what I did wrong.


ChandraPage

Chandra still thinks vehicles are cool, despite having been folded, spindled, mutilated, and disconnected at hundreds of border crossings.


Chasingred3 Ixtab

Im Dead ;(

Hey this is Chasingred3 Ixtab's main page! Havent made too many contributations yet, probley because everyboy has already fixed all mistakes.

BUT I am dedacating my WIKI Life into making small, not nesacerally detailed, but working egsamples of each and every function in this WIKI, witch ill probly never finish but oh well... IF anyone would like to help me, just say somthing to me please. :D id greatly appreaciate it!

Yes i know that this page is crappy looking, im kinda new to the wiki

Leave comments! (YAY)

Chasingred3Ixtab's Favorite WIKI Stuffs.
--
llDetectedSex()
llPizza
--

Others soon to Come!...

Chasingred3Ixtab's Contributations In the WIKI
--
PublicBillboard Public anybody adverizing/anything billboard!
Scale Spaz Randomizing <x,y,z> Scales
Touch Orbit Damned noobs cant stop touching stuff! -- Got removed because people didnt like it...

Chasingred3Ixtab's Favorite Quotes or Comments.

--


CherokeeTek

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ChrisKnox

Apple ignited the personal computer revolution in the 1970s with the Apple II and reinvented the personal computer in the 1980s with the Macintosh. Apple is widely recognized for its innovative and award-winning Mac desktop and notebook computers, OS X operating system, and iLife digital lifestyle and professional applications. Apple is also leading the digital music revolution with its market-leading iPod line of portable music players and iTunes online music store.


ChristopherOmega

Status: Lethargic.

My current juggling act:

Project | Status
XML Parser | Buggy, currently on backburner.

Ideas:

Feasible:
Bullet dodging system
Quick multithreaded image transfer device
Parcel object scanning system
Massively synchronized structure system. (aka: death to buildings that can't move :D)
"Paint" program using prim-pixels.

More then likely impossible, with my current skillset: ^_^
Prim-based offline 3d modeling program <-> SL Software
LSL Runtime environment
Web browser within SL

The "completed" pile:

Content browsing system for the SL Public Library I finally posted it here!
Sandbox layer rezzer
Image transfer device w/ RLE optimization
Shovel (programmable land manipulator)
Multidimentional array capabilities (using math) and a much more flexible reimplementation (using strings).
"Finding" device - helps you locate an item by its name.

My LSL Wish List

Taking an idea from Keknev's page, here are my wishes for the future of LSL:
Feel free to add a proposition for any of them, Im currently out of vote points ;)
Title/Forum Thread | Description | Proposition #
llGiveSelf(key destination) | This function hands the destination a copy of the object this script resides in (and derezzes the in-world copy, if a no-copy object calls it) | 111
selected*(integer num) | Events for detecting whom is selecting the object. | 112
Additions to llRequestInventoryData | The ability to get the length of a sound, dimensions of a texture, and length of an animation. | 521
llDetectedCollisionLocation | The ability to get the point at which a collision occurred.
Additions to llRequestSimulatorData | See thread for details.
llSetLinkPrimitiveParams
llGetLinkPrimitiveParams 		Primitive params that can be applied to and retrieved from any object (given the proper permissions) in a link set, like llSetLinkAlpha 58
animation_triggered(integer handle, key avatar, string anim)
llAnimationListen
llAnimationListenRemove 		An event that fires when an animation is triggered, and functions to activate the event. 520
llRequestGroupData
GROUP_NAME
GROUP_MEMBER_COUNT
GROUP_OFFICER_COUNT
GROUP_FOUNDER 		Get information about a particular group using its key.
PRIM_LINK constant for llSetPrimitiveParams | Apply parameters to multiple prims in a link set at the same time. | 669
llDropFromAvatar | Preforms the same operation dropping an attachment does using the SL UI.
llDialog changes. | Seperate this API from llListen, and allow us to close dialogs we open via script.
Object-object touching. | Allow scripted objects to trigger the touch events of other scripted objects.
script_error event - error handling | Allow scripts to handle trivial errors - cant find sound or texture in inventory, script doesn't have permissions - without spamming public chat. | 188
Collaborative script editor. | Allow two or more people to simultaneously work on a script in SL - viewing each other's changes as they occur. | 78
key llGetChanger
Thread 1 Thread 2 		Gets the UUID of the object or avatar that triggered the changed event.
Comments welcome!

/dev/null:

Frou Frou ROCKS! :-D
Worship Ezhar and his lovely %%(lsl) code formatting!
Use Sun style braces. They're... better!
User page ACLs suck! Don't restrict a registered user's editing privileges!


ChromalBrodsky

Current Projects:
  • SecondLife Sim Stats Logger, with graphs visible here.
  • SecondLife Population Stats Logger, with graphs visible here.
  • Some non-human avatars


CidJacobs

About Me (First Life):
-21
-Male
-Straight
-Single
-Memphis, TN
-College (Graduation Pending)
-Reach Me At Frankabignale At Gmail Dot Com

About Me (Second Life):
- Join Date = July 18, 2004
- Avatar Key = abd5216c-1984-4ccc-bd10-f7b32c438816
- Scripting mentor avaliable for private LSL tutoring between 3pm - 10 pm SL time, I respond to offline IMs.
- Skills in order of level of ability:
-- Theoretical Meteorology
-- Script Communications
-- Command Parsing
-- General Programming
-- Data Management
-- Script Optimizations
-- Photoshop Manipulation
-- Photography
-- Audio Manipulation
-- Orgainizing Groups
-- Product Testing
-- Limited Poser Animation
-- Primitive Manipulation

Known Alts:

Name | Purpose
Alan Steed | I was in the mood to pay LindenLab 10 dollars.
Brandon Chandler | Goofing off.
Cid Franklin | When I need to work.
Emmett Brown | Too cool of a name to pass up.
Fhiser Puff | Accounts were discounted.
Sebastian Magpie | When I want to explore.
Stormy Engel | When I want to play.

Current Groups:

Group | Description
Bug Hunters | Finding and squishing bugs.
Live Help | Answer a broad range of questions and provide moral support.
Second Life Mentors | Focused on helping new residents to Second Life.

Current Projects:

Project | Status
Weather Monitoring Stations | 99% complete.
Theoretical Weather System Prediction | 70% complete.
Collision Dodging System | Very buggy - 25% complete?

Concept List:

In world building tutorial - HUD based.
In world LSL tutorial - HUD based.

Weather Functions:

llCloud
llGetSunDirection
llWind

Weather Instruments:

Barometer
Thermometer
Wind Turbine

Weather Articles and Links:

Jos Stam's Navier-Stokes equations
Physical based fluid simulation java applet based on Jos Stam's Navier-Stokes equations

Back Links:

CidJacobs
Contributors
LibraryBarometer
LibraryPlanePontoon
LibraryThermometer
LibraryWindTurbine
llCloud
NonnuxWhite
RemovalPetition
ScriptLibrary
Weather
WikiUsers


CiennaRand

Cienna Rand is a carbon-based sack of mostly water that doesn't log in, lords over #secondlife with a Silly Putty fist, and is the self-styled glasses magnate of SL (via the Primcrafters operation).

Cienna doesn't believe SL will ever be the metaverse, and likes to call it a game.


CirrMarat

-dead page, active user-


CrossLament

Cross Lament is rarely cross but occasionally laments.

She's usually a vixen, but sometimes not.

She knows enough LSL to be dangerous (to herself and others).

She knows too little LSL to charge for it.

She stumbles around, learning despite herself.

She adds things to the wiki occasionally, such as:

ExamplePointChildAtTarget

Useful Links:

MyChanges
ClientAssetKeys

.


DaveZeeman

DaveZeeman is actually Dave Zeeman.

Dave is sometimes a complete genius, but other times, a complete moron.

Dave Zeeman has a funny last name! :O

Dave Zeeman will be your Super Dave if you deserve it.

Dave Zeeman is lost in a black hole.

Dave Zeeman hasn't worked on a project in soooooooooooooo long but he can probably still give you coding advice.


DedricMauriac

Hi. Apparently I am DedricMauriac. I guess that means that you are not.

Particle Systems ...

Wind for particle systems appears to be closer to the wind at ZERO_VECTOR rather then the current position of the particle emitter itself. However, it doesn't seem to be rite on target. My guess is due to dampening. Has anyone else run into this problem? I've been comparing my results in seperate prims on top of each other.

color
Contributors
DedricMauriac
Exchange
exchangeSecondSimpleSyndication
LibraryChatbots
LibraryElizaChatbot
LibraryGiveInventory
llCos
llSetParcelMusicURL
newview
ScriptLibrary
WikiUsers
XMLRPCImplementations


DevonCassidy

Hey SL residents and scripters, hows it going? If you dont know me send me an IM in SL or call me in game at Nexcom # 0068. Ill be happy to talk to all of my fans ^_^ j/k.

Ryan Dayton was here

Thats my best friend , Ryan, ILLEGALY EDITING MY CRAP!

Devon, its a wiki theres no such thing as "ILLEGALY EDITING" - RyanDayton
(that means you can delete whatever ;-))

If you don't want people editing your pages change the Write ACL to your name (click the Edit ACLs link at the bottom of the page) - BW

pwn'd


DoctordaffyCure

A user who needs to edit thier wiki page


DolusNaumova

I occasionally do stuff. Once in a while I yell at things that don't work, or noobs with guns.

Snippets

//Gets the first name of the owner (faster string version)
string first = llGetSubString(llKey2Name(llGetOwner()),0,llSubStringIndex(llKey2Name(llGetOwner())," ") - 1);

//Gets the first name of the owner (list)
string first = llList2String(llParseString2List(llKey2Name(llGetOwner()),[" "],[]),0);

//XYText Word-wrap script.
string wrapXYText(string text, integer chars){
    list t = llParseString2List(text,[" "],[]);
    integer i;
    integer l = llGetListLength(t);
    string out;
    string currline;
    for(i = 0; i < l; ++i){
        integer len = llStringLength(currline);
        integer wordlen = llStringLength(llList2String(t,i)+" ");
        if(len + wordlen > chars){
            integer j;
            integer k = chars - len;
            for(j = 0; j < k; ++j) currline += " ";
            out += currline;
            currline = llList2String(t,i)+" ";
        } else {
            currline += llList2String(t,i) + " ";
        }
    }
    out += currline;
    return out;
}

//Takes text and returns the appropriate scanner type
integer text2SensorType(string txt){
    if(llToLower(txt) == "agent") return AGENT;
    if(llToLower(txt) == "scripted") return SCRIPTED;
    if(llToLower(txt) == "passive") return PASSIVE;
    if(llToLower(txt) == "active") return ACTIVE;
    return -1;
}

//Date and time functions
string getClock(integer timezone){
    string am = "AM";
    integer t = (integer)llGetWallclock();
    integer hours = t / 3600;
    hours += 8;
    hours += timezone;
    if(hours > 12){
        hours -= 12;
        am = "PM";
    }
    integer minutes = (t % 3600) / 60;
    integer seconds = t % 60;

    string h = (string)hours;
    string m = (string)minutes;
    string s = (string)seconds;

    if(llStringLength(m) == 1){
        m = "0" + m;
    }
    if(llStringLength(s) == 1){
        s = "0" + s;
    }

    return h + ":" + m + ":" + s + " " + am;
}

integer getDate(string date){
    return (integer)llList2String(llParseString2List(date,["-"],[]),2);
}

integer getMonth(string date){
    return (integer)llList2String(llParseString2List(date,["-"],[]),1);
}

integer getYear(string date){
    return (integer)llList2String(llParseString2List(date,["-"],[]),0);
}

integer getDayNumber(string date){
    integer d = getDate(date);
    integer m = getMonth(date);
    integer y = getYear(date);

    y -= (14 - m) / 12;
    m += 12*((14 - m) / 12) - 2;

    return ((d + y + y/4 - y/100 + y/400 + (31*m)/12) % 7);
}

string getEaster(integer year){
    integer gNumber = (year % 19) + 1;
    integer century = year/100;
    integer H = (century - llRound(century / 4) - llRound((8 * century + 13) / 25) + 19 * gNumber + 15) % 30;
    integer I = H - llRound((H / 28)) * (1 - (llRound(29 / (H + 1))) * llRound((21 - gNumber) / 11));
    integer J = (year + llRound(year / 4) + I + 2 - century + llRound(century / 4)) % 7;

    integer L = I - J;
    integer month = 3 + llRound((L + 40) / 44);
    integer day = L + 28 - 31 * llRound(month / 4);

    return (string)day + "-" + (string)month + "-" + (string)year;
}

string getDayName(string date){
    list days = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
    return llList2String(days,getDayNumber(date));
}

string getOrdinalDate(string date){
    string last = llGetSubString((string)getDate(date),-1,-1);
    if(last == "1") return "st";
    else if(last == "2") return "nd";
    else if(last == "3") return "rd";
    return "th";
}

string getMonthName(string date){
    list months = ["Smarch","January","February","March","April","May","June","July","August","September","October","November","December"];
    return llList2String(months,getMonth(date));
}

integer isEaster(string date){
    string easter = getEaster(getYear(date));
    if(easter == date)
        return TRUE;
    return FALSE;
}

integer isChristmas(string date){
    if(getDate(date) == 25 && getMonth(date) == 12)
        return TRUE;
    return FALSE;
}

integer isChristmasEve(string date){
    if(getDate(date) == 24 && getMonth(date) == 12)
        return TRUE;
    return FALSE;
}

integer isNewYearsEve(string date){
    if(getDate(date) == 31 && getMonth(date) == 12)
        return TRUE;
    return FALSE;
}

integer isNewYearsDay(string date){
    if(getDate(date) == 1 && getMonth(date) == 1)
        return TRUE;
    return FALSE;
}

I also make simple scripts and distribute them via a shoddy network vendor system I threw together. They're reprinted here for your convience.

Date and Time
[+/-] show/hide
float timezone = 0;

string clock(float timezone, integer military)
{
    integer raw = (integer)(llGetGMTclock() + (timezone * 3600));
    integer shiftraw = raw;

    if((timezone * 3600) + raw > 86400)
    {
        shiftraw = raw - 86400;
    }http://secondlife.com/badgeo/wakka.php?wakka=DolusNaumova/edit
Edit this page
    else if((timezone * 3600) + raw < 0)
    {
        shiftraw = raw + 86400;
    }

    integer hours = shiftraw / 3600;
    integer minutes = (shiftraw % 3600) / 60;
    integer seconds = shiftraw % 60;
    string ampm;

    //non-military time adjustments
    if(!military)
    {
        if(shiftraw < 43200)
        {
            ampm = " AM";
        }
        else
        {
            ampm = " PM";
            hours -= 12;
        }
    }

    string shours = (string)hours;
    string sminutes = (string)minutes;
    string sseconds = (string)seconds;

    //add zeros to single digit minutes/seconds
    if(llStringLength(sminutes) == 1)
    {
        sminutes = "0" + sminutes;
    }
    if(llStringLength(sseconds) == 1)
    {
        sseconds = "0" + sseconds;
    }

    string time = shours + ":" + sminutes + ":" + sseconds + ampm;

    return time;
}

default{
    state_entry(){
        llOwnerSay("Say 'date' for date, 'time' for time, and 'timezone' to set the timezone.");
        llListen(0,"",llGetOwner(),"");
    }
    on_rez(integer s){
        llResetScript();
    }
    listen(integer c, string n, key id, string msg){
        list m = llParseString2List(llToLower(msg),[" "],[]);
        string q = llList2String(m,0);
        if(q == "date"){
            llOwnerSay(llGetDate());
        }
        if(q == "time"){
            llOwnerSay(clock(timezone,0));
        }
        if(q == "timezone"){
            timezone = (float)llList2String(m,1);
        }
    }
}

A Simple Message Box
[+/-] show/hide
list _MESSAGES;
string spacer = "/?/";

default{
    state_entry(){
        llListen(10,"","","");
        llSetTimerEvent(600);
        llSetText("Messages can be left on channel ten.",<1,1,0>,1);
    }
    listen(integer channel, string name, key id, string message){
        if(id == llGetOwner()){
            if(message == "view"){
                integer i;
                integer l = llGetListLength(_MESSAGES);
                if(l == 0){ llOwnerSay("No messages"); return; }
                for(i; i < l; ++ i){
                    llOwnerSay(llList2String(_MESSAGES,i));
                }
            } else if(message == "backup"){ //For use with the hO Drive
                llMessageLinked(LINK_SET,0x20002,llDumpList2String(_MESSAGES,spacer),"save");
                llOwnerSay("Data backed up.");
            } else if(message == "restore"){ //For use with the hO Drive
                llMessageLinked(LINK_SET,0x20002,"","get");
            } else {
                _MESSAGES += "(" + llGetDate() + ") " + name + ": " + message;
            }
        } else {
            _MESSAGES += "(" + llGetDate() + ") " + name + ": " + message;
        }
    }
    timer(){//For use with the hO Drive
        llMessageLinked(LINK_SET,0x20002,llDumpList2String(_MESSAGES,spacer),"save");
    }
    link_message(integer sender, integer chan, string data, key extra){
        if(chan == 0x20002){
            _MESSAGES = llParseString2List(data,[spacer],[]);
            llOwnerSay("Data restored.");
        }
    }
}

Random Shape
[+/-] show/hide
(For use with a launcher)
float rand(float min, float max){
    float low;
    float high;
    if(min < 0){
        low = -llFrand(min);
        high = llFrand(max);
        if(llFrand(1) >= 0.5){
            return high;
        } else {
            return low;
        }
    } else {
        high = llFrand(max);
        return high;
    }
}

vector randVector(vector min, vector max){
    return <rand(min.x,max.x),rand(min.y,max.y),rand(min.z,max.z)>;
}

randParams(){
    integer i;
    integer l = llGetNumberOfSides();
    for(i = 0; i < l; ++i){
        llSetColor(randVector(<0,0,0>,<1,1,1>),i);
        llSetAlpha(rand(0,1),i);
    }
    float ecut = rand(0.2,1);
    float bcut = rand(0,ecut - 0.2);
    float ecutt = rand(0.2,1);
    float bcutt = rand(0,ecut - 0.2);
    llSetPrimitiveParams([PRIM_SIZE,randVector(<0.25,0.25,0.25>,<5,5,5>),
                                        PRIM_MATERIAL,llRound(rand(0,7)),
                                        PRIM_TYPE, llRound(rand(0,6)), 0,
                                        <bcut, ecut, 0.0>, rand(0,.95), <rand(-180,180), rand(-180,180), 0.0>,
                                        <rand(0.05,1), rand(0.05,1), 0.0>, <rand(0,0.5), rand(0,0.5), 0.0>,
                                        <bcutt, ecutt, 0.0>, <rand(0,0.5), rand(0,0.5), 0.0>, rand(1,4), 0.0, 0.0]);
}

default{
    collision(integer d){
        llSetStatus(STATUS_PHYSICS,FALSE);
        randParams();
    }
    land_collision(vector p){
        llSetStatus(STATUS_PHYSICS,FALSE);
        randParams();
    }
    touch(integer d){
        randParams();
    }
}

Storage Box
[+/-] show/hide
Requires two prims and three scripts: One that sends a link message with "back" to the root, one that sends "next" to the root, and this one.
list _inventory;
integer _p = -1;

registerInventory(){
    integer i;
    integer l = llGetInventoryNumber(-1);
    for(i = 0; i < l; ++i){
        _inventory += llGetInventoryName(-1,i);
    }
}

string type2Name(integer type){
    if(type == 0){
        return "Texture";
    } else if(type == 1){
        return "Sound";
    } else if(type == 3){
        return "Landmark";
    } else if(type == 5){
        return "Clothing";
    } else if(type == 6){
        return "Object";
    } else if(type == 7){
        return "Notecard";
    } else if(type == INVENTORY_SCRIPT){
        return "Script";
    } else if(type == 13){
        return "Bodypart";
    } else if(type == 20){
        return "Animation";
    } else if(type == 21){
        return "Gesture";
    } else {
        return "Unknown";
    }
}

string mask2Name(integer mask){
    string o;
    if(mask == PERM_ALL){
        return "Move/Modify/Copy/Transfer";
    } else if(mask == PERM_COPY){
        o +=  "Copy/";
    } else if(mask == PERM_MODIFY){
        o +=  "Modify/";
    } else if(mask == PERM_TRANSFER){
        o +=  "Transfer/";
    } else if(mask == PERM_MOVE){
        o +=  "Move/";
    }
    o = llDeleteSubString(o,llStringLength(o) - 1,-1);
    return o;
}

default{
    state_entry(){
        registerInventory();
        llSetText("Current Inventory:\n"+llDumpList2String(_inventory,"\n")+"\n"+(string)llGetListLength(_inventory)+" total items.",<1,1,1>,1);
    }
    changed(integer c){
        if(c & CHANGED_INVENTORY){
            llResetScript();
        }
    }
    link_message(integer sender, integer num, string str, key id){
        if(str == "back"){
            --_p;
            if(_p == -1){
                _p = -2;
                llSetText("Current Inventory:\n"+llDumpList2String(_inventory,"\n")+"\n"+(string)llGetListLength(_inventory)+" total items.",<1,1,1>,1);
            } else if(_p < -1){
                _p = llGetListLength(_inventory) - 1;
                string perms;
                if(llGetCreator() == llGetOwner()){
                    perms = mask2Name(llGetInventoryPermMask(llList2String(_inventory,_p),1));
                } else {
                    perms = mask2Name(llGetInventoryPermMask(llList2String(_inventory,_p),1));
                }
                llSetText(llList2String(_inventory,_p) + "\n" + type2Name(llGetInventoryType(llList2String(_inventory,_p))) + "\n" + perms,<1,1,1>,1);
            } else {
                string perms;
                if(llGetCreator() == llGetOwner()){
                    perms = mask2Name(llGetInventoryPermMask(llList2String(_inventory,_p),1));
                } else {
                    perms = mask2Name(llGetInventoryPermMask(llList2String(_inventory,_p),1));
                }
                llSetText(llList2String(_inventory,_p) + "\n" + type2Name(llGetInventoryType(llList2String(_inventory,_p))) + "\n" + perms,<1,1,1>,1);
            }
        } else if(str == "next"){
            ++_p;
            if(_p >= llGetListLength(_inventory)){
                _p = -1;
                llSetText("Current Inventory:\n"+llDumpList2String(_inventory,"\n")+"\n"+(string)llGetListLength(_inventory)+" total items.",<1,1,1>,1);
            } else {
                string perms;
                if(llGetCreator() == llGetOwner()){
                    perms = mask2Name(llGetInventoryPermMask(llList2String(_inventory,_p),1));
                } else {
                    perms = mask2Name(llGetInventoryPermMask(llList2String(_inventory,_p),1));
                }
                llSetText(llList2String(_inventory,_p) + "\n" + type2Name(llGetInventoryType(llList2String(_inventory,_p))) + "\n" + perms,<1,1,1>,1);
            }
        }
    }
}

AshByrne
brace
ChildRotation
CometAero
Contributors
DolusNaumova
eeped
ExampleHUDCommunication
ExampleRPC2PHP
forumreferences
HazVega
JimLinden
LibraryCameraFollower
LibrarySailboat
LibraryVigenereCipher
llSetTorque
LSL
RecursiveRezzing
RemovalPetition
ScriptingMentors
ScriptLibrary
WikiFormatting
WikiUsers

<?php if(preg_match("/.+\n\{/i",$code)){ echo "Evil."; } ?>


DraconisNeurocam

Hey its me the dude with the big purple hat, um yeah thats about it.

Also Havok 1 sucks.


DunselHodgson

I firmly believe in the Uncertainty Principle... at least if I'm wrong. Which is never. Unfortunately, now I can't know where I am or how fast I'm moving while I'm right. Acck.


EchoOmega

.

Huh!?

Full Rights for Fictional Persons, now!


EepQuirk

HomePage


EscortDeFarge

Performance

A few tests I've run in-world to resolve some apparently unresolved performance "mantra". Below is the raw, unformatted data from the tests I have run so far.

The for loop "Declare or Embed" length question

OUTCOME: Always pre-declare the length test value used in a loop
Declaring the test length in advance is about twice as fast for short lists, and 3 to 7 times faster for longer lists (100 entries).
Note that a list of keys reports as string entries from llGetListEntryType() but is slower than a string list...

CODE SPEED: TEST STARTING...
CODE SPEED: Test: "DECLARED versus EMBEDDED" FOR LOOP length for INTEGER list
CODE SPEED: len: 10, dec:0.047499, emb:0.099957, ratio:210.442520%]
CODE SPEED: len: 20, dec:0.099807, emb:0.202942, ratio:203.334763%]
CODE SPEED: len: 30, dec:0.151525, emb:0.347572, ratio:229.382065%]
CODE SPEED: len: 40, dec:0.202348, emb:0.447297, ratio:221.053650%]
CODE SPEED: len: 50, dec:0.250969, emb:0.596845, ratio:237.816162%]
CODE SPEED: len: 60, dec:0.298086, emb:0.850226, ratio:285.228394%]
CODE SPEED: len: 70, dec:0.302277, emb:1.099911, ratio:363.875549%]
CODE SPEED: len: 80, dec:0.350952, emb:1.199566, ratio:341.803253%]
CODE SPEED: len: 90, dec:0.402557, emb:1.348515, ratio:334.986938%]
CODE SPEED: len: 100, dec:0.448704, emb:1.653826, ratio:368.578522%]
CODE SPEED: Totals INTEGER [dec:2.554724, emb:7.846657, ratio:307.143066%]
CODE SPEED: Test: "DECLARED versus EMBEDDED" FOR LOOP length for FLOAT list
CODE SPEED: len: 10, dec:0.055344, emb:0.097996, ratio:177.067825%]
CODE SPEED: len: 20, dec:0.101868, emb:0.197647, ratio:194.023376%]
CODE SPEED: len: 30, dec:0.151175, emb:0.299381, ratio:198.036819%]
CODE SPEED: len: 40, dec:0.198324, emb:0.500538, ratio:252.383667%]
CODE SPEED: len: 50, dec:0.202280, emb:0.650043, ratio:321.358185%]
CODE SPEED: len: 60, dec:0.249590, emb:0.853241, ratio:341.857147%]
CODE SPEED: len: 70, dec:0.302925, emb:1.049141, ratio:346.336731%]
CODE SPEED: len: 80, dec:0.348734, emb:1.149652, ratio:329.664673%]
CODE SPEED: len: 90, dec:0.399689, emb:1.399565, ratio:350.163666%]
CODE SPEED: len: 100, dec:0.450577, emb:1.701090, ratio:377.536072%]
CODE SPEED: Totals FLOAT [dec:2.460505, emb:7.898294, ratio:321.003052%]
CODE SPEED: Test: "DECLARED versus EMBEDDED" FOR LOOP length for STRING list
CODE SPEED: len: 10, dec:0.048222, emb:0.099987, ratio:207.349106%]
CODE SPEED: len: 20, dec:0.100124, emb:0.201248, ratio:200.998215%]
CODE SPEED: len: 30, dec:0.148354, emb:0.402443, ratio:271.272827%]
CODE SPEED: len: 40, dec:0.148129, emb:0.598503, ratio:404.043152%]
CODE SPEED: len: 50, dec:0.251381, emb:0.851433, ratio:338.702240%]
CODE SPEED: len: 60, dec:0.253059, emb:1.047123, ratio:413.785461%]
CODE SPEED: len: 70, dec:0.348778, emb:1.298180, ratio:372.208252%]
CODE SPEED: len: 80, dec:0.350639, emb:1.698090, ratio:484.283813%]
CODE SPEED: len: 90, dec:0.397018, emb:1.949520, ratio:491.040192%]
CODE SPEED: len: 100, dec:0.502796, emb:2.298447, ratio:457.132874%]
CODE SPEED: Totals STRING [dec:2.548500, emb:10.444973, ratio:409.847870%]
CODE SPEED: Test: "DECLARED versus EMBEDDED" FOR LOOP length for STRING list
CODE SPEED: len: 10, dec:0.050320, emb:0.102741, ratio:204.177094%]
CODE SPEED: len: 20, dec:0.101566, emb:0.248779, ratio:244.942719%]
CODE SPEED: len: 30, dec:0.151123, emb:0.449463, ratio:297.415192%]
CODE SPEED: len: 40, dec:0.202911, emb:0.699852, ratio:344.905243%]
CODE SPEED: len: 50, dec:0.248474, emb:0.999557, ratio:402.278320%]
CODE SPEED: len: 60, dec:0.296406, emb:1.302883, ratio:439.560608%]
CODE SPEED: len: 70, dec:0.351902, emb:1.749546, ratio:497.168549%]
CODE SPEED: len: 80, dec:0.350895, emb:2.098461, ratio:598.031189%]
CODE SPEED: len: 90, dec:0.447662, emb:2.403347, ratio:536.865967%]
CODE SPEED: len: 100, dec:0.451180, emb:3.198624, ratio:708.947021%]
CODE SPEED: Totals STRING [dec:2.652439, emb:13.253254, ratio:499.662872%]
CODE SPEED: Test: "DECLARED versus EMBEDDED" FOR LOOP length for VECTOR list
CODE SPEED: len: 10, dec:0.050201, emb:0.098831, ratio:196.869308%]
CODE SPEED: len: 20, dec:0.102470, emb:0.196869, ratio:192.122696%]
CODE SPEED: len: 30, dec:0.150154, emb:0.348518, ratio:232.107117%]
CODE SPEED: len: 40, dec:0.202423, emb:0.497818, ratio:245.929428%]
CODE SPEED: len: 50, dec:0.251953, emb:0.697105, ratio:276.680603%]
CODE SPEED: len: 60, dec:0.297684, emb:0.853096, ratio:286.578003%]
CODE SPEED: len: 70, dec:0.353378, emb:0.997223, ratio:282.196960%]
CODE SPEED: len: 80, dec:0.350838, emb:1.249763, ratio:356.222687%]
CODE SPEED: len: 90, dec:0.397057, emb:1.501396, ratio:378.131561%]
CODE SPEED: len: 100, dec:0.449142, emb:1.748734, ratio:389.349426%]
CODE SPEED: Totals VECTOR [dec:2.605301, emb:8.189354, ratio:314.334259%]
CODE SPEED: Test: "DECLARED versus EMBEDDED" FOR LOOP length for ROTATION list
CODE SPEED: len: 10, dec:0.049980, emb:0.100388, ratio:200.854828%]
CODE SPEED: len: 20, dec:0.102020, emb:0.199272, ratio:195.326050%]
CODE SPEED: len: 30, dec:0.200943, emb:0.300674, ratio:149.631714%]
CODE SPEED: len: 40, dec:0.198799, emb:0.500786, ratio:251.905441%]
CODE SPEED: len: 50, dec:0.199661, emb:0.749603, ratio:375.437531%]
CODE SPEED: len: 60, dec:0.300644, emb:0.850685, ratio:282.954376%]
CODE SPEED: len: 70, dec:0.299698, emb:1.100853, ratio:367.320923%]
CODE SPEED: len: 80, dec:0.402832, emb:1.298691, ratio:322.390137%]
CODE SPEED: len: 90, dec:0.452217, emb:1.550850, ratio:342.943665%]
CODE SPEED: len: 100, dec:0.449081, emb:1.852104, ratio:412.420563%]
CODE SPEED: Totals ROTATION [dec:2.655876, emb:8.503906, ratio:320.192108%]
CODE SPEED: TEST COMPLETE

On the "How To Increment" Question

OUTCOME: Post-Increment is a bit slower, but you are unlikely to ever notice the difference

CODE SPEED: TEST STARTING...
CODE SPEED: Test: "How to increment"
CODE SPEED: Results...
CODE SPEED: For 100 Iterations
CODE SPEED: [i=i+1] 0.399681
CODE SPEED: [i+=1] 0.398720
CODE SPEED: [i++] 0.400874
CODE SPEED: [++i] 0.398004
CODE SPEED: For 200 Iterations
CODE SPEED: [i=i+1] 0.750977
CODE SPEED: [i+=1] 0.750097
CODE SPEED: [i++] 0.847882
CODE SPEED: [++i] 0.749992
CODE SPEED: For 300 Iterations
CODE SPEED: [i=i+1] 1.052374
CODE SPEED: [i+=1] 1.147127
CODE SPEED: [i++] 1.247654
CODE SPEED: [++i] 1.151211
CODE SPEED: For 400 Iterations
CODE SPEED: [i=i+1] 1.447083
CODE SPEED: [i+=1] 1.447144
CODE SPEED: [i++] 1.751394
CODE SPEED: [++i] 1.453426
CODE SPEED: For 500 Iterations
CODE SPEED: [i=i+1] 1.848036
CODE SPEED: [i+=1] 1.800402
CODE SPEED: [i++] 2.150698
CODE SPEED: [++i] 1.848957
CODE SPEED: For 600 Iterations
CODE SPEED: [i=i+1] 2.201397
CODE SPEED: [i+=1] 2.149254
CODE SPEED: [i++] 2.651897
CODE SPEED: [++i] 2.198181
CODE SPEED: For 700 Iterations
CODE SPEED: [i=i+1] 2.600864
CODE SPEED: [i+=1] 2.550198
CODE SPEED: [i++] 3.003616
CODE SPEED: [++i] 2.550468
CODE SPEED: For 800 Iterations
CODE SPEED: [i=i+1] 2.950737
CODE SPEED: [i+=1] 2.900520
CODE SPEED: [i++] 3.400467
CODE SPEED: [++i] 2.897667
CODE SPEED: For 900 Iterations
CODE SPEED: [i=i+1] 3.251976
CODE SPEED: [i+=1] 3.293785
CODE SPEED: [i++] 3.900291
CODE SPEED: [++i] 3.247055
CODE SPEED: For 1000 Iterations
CODE SPEED: [i=i+1] 3.602173
CODE SPEED: [i+=1] 3.652168
CODE SPEED: [i++] 4.300713
CODE SPEED: [++i] 3.600365
CODE SPEED: TEST COMPLETE


EzharFairlight

YourPages
YourChanges

You can reach Ezhar via the contact form at http://sdfjkl.org.

Useful LSL functions I wrote:

ExampleNumberConversion

String search/replace functions:
// searches 'src' and returns the number of times 'find' occurs in it
integer FindCount(string src, string find) {
    return llGetListLength(llParseStringKeepNulls(src, [find], [])) - 1;
}

// returns the index in 'src' where 'find' first occurs
// same as llSubStringIndex but slightly slower
integer Find(string src, string find) {
    list    split;

    split = llParseString2List(src, [find], []);
    if (llGetListLength(split) == 1) return -1; else return llStringLength(llList2String(split, 0));
}

// returns list of all positions in 'src' where 'find' occurs
list FindAll(string src, string find) {
    list    split;
    list    ret;
    integer i;
    integer pos;
    integer len;
    integer findlen = llStringLength(find);

    split = llParseStringKeepNulls(src, [find], []);
    len = llGetListLength(split) - 1;
    for (i = 0; i < len; i++) {
        pos += llStringLength(llList2String(split, i));
        ret += [pos];
        pos += findlen;
    }

    return ret;
}

// search 'src' and replace all occurences of 'find' with 'replace'
string ReplaceString(string src, string find, string replace) {
    return llDumpList2String(llParseStringKeepNulls(src, [find], []), replace);
}

default {
    state_entry() {
        string s = "xblablafoobarfoo";
        string search = "bla";
        llResetTime();
        llSay(0, "occurs " + (string)FindCount(s, search) + " times.");
        llSay(0, "1st position (Find): " + (string)Find(s, search));
        llSay(0, "all positions (FindAll): " + llList2CSV(FindAll(s, search)));
        llSay(0, ReplaceString(s, search, "monkey"));
        llSay(0, (string)llGetTime());
    }
}

Transporting lists safely through chat/linkmessages:
string      SEPARATOR = "|%&|"; // for transmitting lists via linked messages

string List2TypeSep(list input) { // converts a list to a separated string with type information prepended to each item
    integer     i;
    list        output;

    for (i = 0; i < llGetListLength(input); i++) {
        output += [llGetListEntryType(input, i)] + llList2List(input, i, i);
    }

    return llDumpList2String(output, SEPARATOR);
}

list TypeSep2List(string inputstring) { // converts a separated string created with List2TypeSep back to a list with the correct type information
    integer     i;
    list        input;
    list        output;

    input = llParseString2List(inputstring, [SEPARATOR], []);

    for (i = 0; i < llGetListLength(input); i += 2) {
        if (llList2Integer(input, i) == TYPE_INTEGER) output += (integer)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_FLOAT) output += (float)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_STRING) output += llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_KEY) output += (key)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_VECTOR) output += (vector)llList2String(input, i + 1);
        else if (llList2Integer(input, i) == TYPE_ROTATION) output += (rotation)llList2String(input, i + 1);
    }

    return output;
}

Boolean input/output:
string Bool2String(integer bool) { // returns TRUE or FALSE as string
    if (bool) return("TRUE"); else return("FALSE");
}

integer String2Bool(string str) { // returns TRUE or FALSE based on string
    list        positive = ["y", "yes", "t", "true", "on", "1"];

    if (llListFindList(positive, [llToLower(str)]) == -1) return FALSE; else return TRUE;
}

Recognize a UUID:
integer IsKey(string str) { // returns TRUE if string is a key
    return ( (llStringLength(str) == 36) &&
        (llGetSubString(str, 8, 8) == "-") &&
        (llGetSubString(str, 13, 13) == "-") &&
        (llGetSubString(str, 18, 18) == "-") &&
        (llGetSubString(str, 23, 23) == "-") );
}

Various output formatting functions:
// return <number> as string with zero-padding on the left so it's <length> characters
string PadLeft(integer number, integer length) {
    return  llDeleteSubString("0000000000", length, llStringLength((string)number) - 1) + (string)number;
}

// return the input <number> as string with <point> decimals
string float2stringcut(float number, integer point) {
    return llGetSubString((string)number, 0, llSubStringIndex((string)number, ".") + point);
}

I also did some unpublished hacking on this WakkaWiki and wrote a LSL syntax highlighter for it. Here it is:
<?php
// Syntax Highlight LSL Code
//
// written by Ezhar Fairlight <efairlight@gmail.com>
//
// 2005/04/04 updated wordlist for SL 1.6

// preg callback function
if (!function_exists("lslCallback")) {
    function lslCallback($matches) {
        // colors for syntax highlighting

        $colors['keyword'] = "#0000FF";
        $colors['type'] = "#007700";
        $colors['event'] = "#00A0A0";
        $colors['constant'] = "#0000A0";
        $colors['function'] = "#A00000";
        $colors['comment'] = "#FF7700";
        $colors['string'] = "#00A000";
        $colors['vecrot'] = "#FF00FF"; // pink is only tolerable because it's just single characters
        $colors['list'] = "#FF0000";

        // define the magic words for syntax highlighting
        $words['keywords'] = array(
            'default',
            'state',
            'for',
            'do',
            'while',
            'if',
            'else',
            'jump',
            'return',
        );

        $words['types'] = array(
            'integer',
            'float',
            'string',
            'key',
            'vector',
            'rotation',
            'list'
        );

        $words['events'] = array(
            'state_entry',
            'state_exit',
            'touch_start',
            'touch_end',
            'touch',
            'collision_start',
            'collision_end',
            'collision',
            'land_collision_start',
            'land_collision_end',
            'land_collision',
            'timer',
            'listen',
            'no_sensor',
            'sensor',
            'control',
            'not_at_target',
            'at_target',
            'not_at_rot_target',
            'at_rot_target',
            'money',
            'email',
            'run_time_permissions',
            'attach',
            'dataserver',
            'moving_start',
            'moving_end',
            'on_rez',
            'object_rez',
            'link_message',
            'changed',
            'remote_data'
        );

        $words['constants'] = array(
            'TRUE',
            'FALSE',
            'STATUS_PHYSICS',
            'STATUS_PHANTOM',
            'STATUS_ROTATE_X',
            'STATUS_ROTATE_Y',
            'STATUS_ROTATE_Z',
            'STATUS_SANDBOX',
            'STATUS_BLOCK_GRAB',
            'STATUS_DIE_AT_EDGE',
            'STATUS_RETURN_AT_EDGE',
            'AGENT',
            'ACTIVE',
            'PASSIVE',
            'SCRIPTED',
            'CONTROL_FWD',
            'CONTROL_BACK',
            'CONTROL_LEFT',
            'CONTROL_RIGHT',
            'CONTROL_ROT_LEFT',
            'CONTROL_ROT_RIGHT',
            'CONTROL_UP',
            'CONTROL_DOWN',
            'CONTROL_LBUTTON',
            'CONTROL_ML_LBUTTON',
            'PERMISSION_DEBIT',
            'PERMISSION_TAKE_CONTROLS',
            'PERMISSION_TRIGGER_ANIMATION',
            'PERMISSION_ATTACH',
            'PERMISSION_CHANGE_LINKS',
            'AGENT_FLYING',
            'AGENT_ATTACHMENTS',
            'AGENT_SCRIPTED',
            'AGENT_SITTING',
            'AGENT_ON_OBJECT',
            'AGENT_MOUSELOOK',
            'AGENT_AWAY',
            'AGENT_WALKING',
            'AGENT_IN_AIR',
            'AGENT_TYPING',
            'AGENT_CROUCHING',
            'AGENT_BUSY',
            'AGENT_ALWAYS_RUN',
            'PSYS_PART_FLAGS',
            'PSYS_PART_START_COLOR',
            'PSYS_PART_START_ALPHA',
            'PSYS_PART_START_SCALE',
            'PSYS_PART_END_COLOR',
            'PSYS_PART_END_ALPHA',
            'PSYS_PART_END_SCALE',
            'PSYS_PART_MAX_AGE',
            'PSYS_PART_BOUNCE_MASK',
            'PSYS_PART_WIND_MASK',
            'PSYS_PART_INTERP_COLOR_MASK',
            'PSYS_PART_INTERP_SCALE_MASK',
            'PSYS_PART_FOLLOW_SRC_MASK',
            'PSYS_PART_FOLLOW_VELOCITY_MASK',
            'PSYS_PART_TARGET_POS_MASK',
            'PSYS_PART_EMISSIVE_MASK',
            'PSYS_PART_TARGET_LINEAR_MASK',
            'PSYS_SRC_PATTERN',
            'PSYS_SRC_INNERANGLE',
            'PSYS_SRC_OUTERANGLE',
            'PSYS_SRC_ANGLE_BEGIN',
            'PSYS_SRC_ANGLE_END',
            'PSYS_SRC_BURST_RATE',
            'PSYS_SRC_BURST_PART_COUNT',
            'PSYS_SRC_BURST_RADIUS',
            'PSYS_SRC_BURST_SPEED_MIN',
            'PSYS_SRC_BURST_SPEED_MAX',
            'PSYS_SRC_MAX_AGE',
            'PSYS_SRC_ACCEL',
            'PSYS_SRC_TEXTURE',
            'PSYS_SRC_TARGET_KEY',
            'PSYS_SRC_OMEGA',
            'PSYS_SRC_PATTERN_DROP',
            'PSYS_SRC_PATTERN_EXPLODE',
            'PSYS_SRC_PATTERN_ANGLE',
            'PSYS_SRC_PATTERN_ANGLE_CONE',
            'PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY',
            'VEHICLE_TYPE_NONE',
            'VEHICLE_TYPE_SLED',
            'VEHICLE_TYPE_CAR',
            'VEHICLE_TYPE_BOAT',
            'VEHICLE_TYPE_AIRPLANE',
            'VEHICLE_TYPE_BALLOON',
            'VEHICLE_REFERENCE_FRAME',
            'VEHICLE_LINEAR_FRICTION_TIMESCALE',
            'VEHICLE_ANGULAR_FRICTION_TIMESCALE',
            'VEHICLE_LINEAR_MOTOR_DIRECTION',
            'VEHICLE_LINEAR_MOTOR_OFFSET',
            'VEHICLE_ANGULAR_MOTOR_DIRECTION',
            'VEHICLE_HOVER_HEIGHT',
            'VEHICLE_HOVER_EFFICIENCY',
            'VEHICLE_HOVER_TIMESCALE',
            'VEHICLE_BUOYANCY',
            'VEHICLE_LINEAR_DEFLECTION_EFFICIENCY',
            'VEHICLE_LINEAR_DEFLECTION_TIMESCALE',
            'VEHICLE_LINEAR_MOTOR_TIMESCALE',
            'VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE',
            'VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY',
            'VEHICLE_ANGULAR_DEFLECTION_TIMESCALE',
            'VEHICLE_ANGULAR_MOTOR_TIMESCALE',
            'VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE',
            'VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY',
            'VEHICLE_VERTICAL_ATTRACTION_TIMESCALE',
            'VEHICLE_BANKING_EFFICIENCY',
            'VEHICLE_BANKING_MIX',
            'VEHICLE_BANKING_TIMESCALE',
            'VEHICLE_FLAG_NO_DEFLECTION_UP',
            'VEHICLE_FLAG_LIMIT_ROLL_ONLY',
            'VEHICLE_FLAG_HOVER_WATER_ONLY',
            'VEHICLE_FLAG_HOVER_TERRAIN_ONLY',
            'VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT',
            'VEHICLE_FLAG_HOVER_UP_ONLY',
            'VEHICLE_FLAG_LIMIT_MOTOR_UP',
            'VEHICLE_FLAG_MOUSELOOK_STEER',
            'VEHICLE_FLAG_MOUSELOOK_BANK',
            'VEHICLE_FLAG_CAMERA_DECOUPLED',
            'INVENTORY_TEXTURE',
            'INVENTORY_SOUND',
            'INVENTORY_OBJECT',
            'INVENTORY_SCRIPT',
            'INVENTORY_LANDMARK',
            'INVENTORY_CLOTHING',
            'INVENTORY_NOTECARD',
            'INVENTORY_BODYPART',
            'INVENTORY_ANIMATION',
            'INVENTORY_GESTURE',
            'INVENTORY_ALL',
            'ATTACH_CHEST',
            'ATTACH_HEAD',
            'ATTACH_LSHOULDER',
            'ATTACH_RSHOULDER',
            'ATTACH_LHAND',
            'ATTACH_RHAND',
            'ATTACH_LFOOT',
            'ATTACH_RFOOT',
            'ATTACH_BACK',
            'ATTACH_PELVIS',
            'ATTACH_MOUTH',
            'ATTACH_CHIN',
            'ATTACH_LEAR',
            'ATTACH_REAR',
            'ATTACH_LEYE',
            'ATTACH_REYE',
            'ATTACH_NOSE',
            'ATTACH_RUARM',
            'ATTACH_RLARM',
            'ATTACH_LUARM',
            'ATTACH_LLARM',
            'ATTACH_RHIP',
            'ATTACH_RULEG',
            'ATTACH_RLLEG',
            'ATTACH_LHIP',
            'ATTACH_LULEG',
            'ATTACH_LLLEG',
            'ATTACH_BELLY',
            'ATTACH_RPEC',
            'ATTACH_LPEC',
            'LAND_LEVEL',
            'LAND_RAISE',
            'LAND_LOWER',
            'LAND_SMOOTH',
            'LAND_NOISE',
            'LAND_REVERT',
            'LAND_SMALL_BRUSH',
            'LAND_MEDIUM_BRUSH',
            'LAND_LARGE_BRUSH',
            'DATA_ONLINE',
            'DATA_NAME',
            'DATA_BORN',
            'DATA_RATING',
            'DATA_SIM_POS',
            'DATA_SIM_STATUS',
            'ANIM_ON',
            'LOOP',
            'REVERSE',
            'PING_PONG',
            'SMOOTH',
            'ROTATE',
            'SCALE',
            'ALL_SIDES',
            'LINK_SET',
            'LINK_ROOT',
            'LINK_ALL_OTHERS',
            'LINK_ALL_CHILDREN',
            'LINK_THIS',
            'CHANGED_INVENTORY',
            'CHANGED_COLOR',
            'CHANGED_SHAPE',
            'CHANGED_SCALE',
            'CHANGED_TEXTURE',
            'CHANGED_LINK',
            'CHANGED_ALLOWED_DROP',
            'TYPE_INTEGER',
            'TYPE_FLOAT',
            'TYPE_STRING',
            'TYPE_KEY',
            'TYPE_VECTOR',
            'TYPE_ROTATION',
            'TYPE_INVALID',
            'REMOTE_DATA_CHANNEL',
            'REMOTE_DATA_REQUEST',
            'REMOTE_DATA_REPLY',
            'PRIM_TYPE',
            'PRIM_MATERIAL',
            'PRIM_PHYSICS',
            'PRIM_TEMP_ON_REZ',
            'PRIM_PHANTOM',
            'PRIM_POSITION',
            'PRIM_SIZE',
            'PRIM_ROTATION',
            'PRIM_TEXTURE',
            'PRIM_COLOR',
            'PRIM_BUMP_SHINY',
            'PRIM_TYPE_BOX',
            'PRIM_TYPE_CYLINDER',
            'PRIM_TYPE_PRISM',
            'PRIM_TYPE_SPHERE',
            'PRIM_TYPE_TORUS',
            'PRIM_TYPE_TUBE',
            'PRIM_TYPE_RING',
            'PRIM_HOLE_DEFAULT',
            'PRIM_HOLE_SQUARE',
            'PRIM_HOLE_CIRCLE',
            'PRIM_HOLE_TRIANGLE',
            'PRIM_MATERIAL_STONE',
            'PRIM_MATERIAL_METAL',
            'PRIM_MATERIAL_GLASS',
            'PRIM_MATERIAL_WOOD',
            'PRIM_MATERIAL_FLESH',
            'PRIM_MATERIAL_PLASTIC',
            'PRIM_MATERIAL_RUBBER',
            'PRIM_MATERIAL_LIGHT',
            'PRIM_SHINY_NONE',
            'PRIM_SHINY_LOW',
            'PRIM_SHINY_MEDIUM',
            'PRIM_SHINY_HIGH',
            'PRIM_BUMP_NONE',
            'PRIM_BUMP_BRIGHT',
            'PRIM_BUMP_DARK',
            'PRIM_BUMP_WOOD',
            'PRIM_BUMP_BARK',
            'PRIM_BUMP_BRICKS',
            'PRIM_BUMP_CHECKER',
            'PRIM_BUMP_CONCRETE',
            'PRIM_BUMP_TILE',
            'PRIM_BUMP_STONE',
            'PRIM_BUMP_DISKS',
            'PRIM_BUMP_GRAVEL',
            'PRIM_BUMP_BLOBS',
            'PRIM_BUMP_SIDING',
            'PRIM_BUMP_LARGETILE',
            'PRIM_BUMP_STUCCO',
            'PRIM_BUMP_SUCTION',
            'PRIM_BUMP_WEAVE',
            'MASK_BASE',
            'MASK_OWNER',
            'MASK_GROUP',
            'MASK_EVERYONE',
            'MASK_NEXT',
            'PERM_TRANSFER',
            'PERM_MODIFY',
            'PERM_COPY',
            'PERM_MOVE',
            'PERM_ALL',
            'PARCEL_MEDIA_COMMAND_STOP',
            'PARCEL_MEDIA_COMMAND_PAUSE',
            'PARCEL_MEDIA_COMMAND_PLAY',
            'PARCEL_MEDIA_COMMAND_LOOP',
            'PARCEL_MEDIA_COMMAND_TEXTURE',
            'PARCEL_MEDIA_COMMAND_URL',
            'PARCEL_MEDIA_COMMAND_TIME',
            'PARCEL_MEDIA_COMMAND_AGENT',
            'PARCEL_MEDIA_COMMAND_UNLOAD',
            'PARCEL_MEDIA_COMMAND_AUTO_ALIGN',
            'NULL_KEY',
            'EOF',
            'PI',
            'TWO_PI',
            'PI_BY_TWO',
            'DEG_TO_RAD',
            'RAD_TO_DEG',
            'SQRT2',
            'ZERO_VECTOR',
            'ZERO_ROTATION'
        );

        $words['functions'] = array(
            'llAbs',
            'llAcos',
            'llAddToLandPassList',
            'llAdjustSoundVolume',
            'llAllowInventoryDrop',
            'llAngleBetween',
            'llApplyImpulse',
            'llApplyRotationalImpulse',
            'llAsin',
            'llAtan2',
            'llAttachToAvatar',
            'llAvatarOnSitTarget',
            'llAxes2Rot',
            'llAxisAngle2Rot',
            'llBase64ToInteger',
            'llBase64ToString',
            'llBreakAllLinks',
            'llBreakLink',
            'llCeil',
            'llCloseRemoteDataChannel',
            'llCloud',
            'llCollisionFilter',
            'llCollisionSound',
            'llCollisionSprite',
            'llCos',
            'llCreateLink',
            'llCSV2List',
            'llDeleteSubList',
            'llDeleteSubString',
            'llDetachFromAvatar',
            'llDetectedGrab',
            'llDetectedGroup',
            'llDetectedKey',
            'llDetectedLinkNumber',
            'llDetectedName',
            'llDetectedOwner',
            'llDetectedPos',
            'llDetectedRot',
            'llDetectedType',
            'llDetectedVel',
            'llDialog',
            'llDie',
            'llDumpList2String',
            'llEdgeOfWorld',
            'llEjectFromLand',
            'llEmail',
            'llEuler2Rot',
            'llFabs',
            'llFloor',
            'llForceMouselook',
            'llFrand',
            'llGetAccel',
            'llGetAgentInfo',
            'llGetAgentSize',
            'llGetAlpha',
            'llGetAndResetTime',
            'llGetAnimation',
            'llGetAnimationList',
            'llGetAttached',
            'llGetBoundingBox',
            'llGetCenterOfMass',
            'llGetColor',
            'llGetCreator',
            'llGetDate',
            'llGetEnergy',
            'llGetForce',
            'llGetFreeMemory',
            'llGetGeometricCenter',
            'llGetGMTclock',
            'llGetInventoryCreator',
            'llGetInventoryKey',
            'llGetInventoryName',
            'llGetInventoryNumber',
            'llGetInventoryPermMask',
            'llGetKey',
            'llGetLandOwnerAt',
            'llGetLinkKey',
            'llGetLinkName',
            'llGetLinkNumber',
            'llGetListEntryType',
            'llGetListLength',
            'llGetLocalPos',
            'llGetLocalRot',
            'llGetMass',
            'llGetNextEmail',
            'llGetNotecardLine',
            'llGetNumberOfNotecardLines',
            'llGetNumberOfPrims',
            'llGetNumberOfSides',
            'llGetObjectDesc',
            'llGetObjectMass',
            'llGetObjectName',
            'llGetObjectPermMask',
            'llGetOmega',
            'llGetOwner',
            'llGetOwnerKey',
            'llGetPermissions',
            'llGetPermissionsKey',
            'llGetPos',
            'llGetPrimitiveParams',
            'llGetRegionCorner',
            'llGetRegionFPS',
            'llGetRegionName',
            'llGetRegionTimeDilation',
            'llGetRootPosition',
            'llGetRootRotation',
            'llGetRot',
            'llGetScale',
            'llGetScriptName',
            'llGetScriptState',
            'llGetSimulatorHostname',
            'llGetStartParameter',
            'llGetStatus',
            'llGetSubString',
            'llGetSunDirection',
            'llGetTexture',
            'llGetTextureOffset',
            'llGetTextureRot',
            'llGetTextureScale',
            'llGetTime',
            'llGetTimeOfDay',
            'llGetTimestamp',
            'llGetTorque',
            'llGetVel',
            'llGetWallclock',
            'llGiveInventory',
            'llGiveInventoryList',
            'llGiveMoney',
            'llGodLikeRezObject',
            'llGround',
            'llGroundContour',
            'llGroundNormal',
            'llGroundRepel',
            'llGroundSlope',
            'llInsertString',
            'llInstantMessage',
            'llIntegerToBase64',
            'llKey2Name',
            'llList2CSV',
            'llList2Float',
            'llList2Integer',
            'llList2Key',
            'llList2List',
            'llList2ListStrided',
            'llList2Rot',
            'llList2String',
            'llList2Vector',
            'llListen',
            'llListenControl',
            'llListenRemove',
            'llListFindList',
            'llListInsertList',
            'llListRandomize',
            'llListReplaceList',
            'llListSort',
            'llLoadURL',
            'llLog',
            'llLog10',
            'llLookAt',
            'llLoopSound',
            'llLoopSoundMaster',
            'llLoopSoundSlave',
            'llMakeExplosion',
            'llMakeFire',
            'llMakeFountain',
            'llMakeSmoke',
            'llMD5String',
            'llMessageLinked',
            'llMinEventDelay',
            'llModifyLand',
            'llMoveToTarget',
            'llOffsetTexture',
            'llOpenRemoteDataChannel',
            'llOverMyLand',
            'llOwnerSay',
            'llParcelMediaCommandList',
            'llParcelMediaQuery',
            'llParseString2List',
            'llParseStringKeepNulls',
            'llParseStringKeepNulls',
            'llParticleSystem',
            'llPassCollisions',
            'llPassTouches',
            'llPlaySound',
            'llPlaySoundSlave',
            'llPointAt',
            'llPow',
            'llPreloadSound',
            'llPushObject',
            'llReleaseCamera',
            'llReleaseControls',
            'llRemoteDataReply',
            'llRemoteDataSetRegion',
            'llRemoteLoadScript',
            'llRemoteLoadScriptPin',
            'llRemoveInventory',
            'llRemoveVehicleFlags',
            'llRequestAgentData',
            'llRequestInventoryData',
            'llRequestPermissions',
            'llRequestSimulatorData',
            'llResetOtherScript',
            'llResetScript',
            'llResetTime',
            'llRezAtRoot',
            'llRezObject',
            'llRot2Angle',
            'llRot2Axis',
            'llRot2Euler',
            'llRot2Fwd',
            'llRot2Left',
            'llRot2Up',
            'llRotateTexture',
            'llRotBetween',
            'llRotLookAt',
            'llRotTarget',
            'llRotTargetRemove',
            'llRound',
            'llSameGroup',
            'llSay',
            'llScaleTexture',
            'llScriptDanger',
            'llSendRemoteData',
            'llSensor',
            'llSensorRemove',
            'llSensorRepeat',
            'llSetAlpha',
            'llSetBuoyancy',
            'llSetCameraAtOffset',
            'llSetCameraEyeOffset',
            'llSetColor',
            'llSetDamage',
            'llSetForce',
            'llSetForceAndTorque',
            'llSetHoverHeight',
            'llSetInventoryPermMask',
            'llSetLinkAlpha',
            'llSetLinkColor',
            'llSetLocalRot',
            'llSetObjectDesc',
            'llSetObjectName',
            'llSetObjectPermMask',
            'llSetParcelMusicURL',
            'llSetPos',
            'llSetPrimitiveParams',
            'llSetRemoteScriptAccessPin',
            'llSetRot',
            'llSetScale',
            'llSetScriptState',
            'llSetSitText',
            'llSetSoundQueueing',
            'llSetSoundRadius',
            'llSetStatus',
            'llSetText',
            'llSetTexture',
            'llSetTextureAnim',
            'llSetTimerEvent',
            'llSetTorque',
            'llSetTouchText',
            'llSetVehicleFlags',
            'llSetVehicleFloatParam',
            'llSetVehicleRotationParam',
            'llSetVehicleType',
            'llSetVehicleVectorParam',
            'llShout',
            'llSin',
            'llSitTarget',
            'llSleep',
            'llSound',
            'llSoundPreload',
            'llSqrt',
            'llStartAnimation',
            'llStopAnimation',
            'llStopHover',
            'llStopLookAt',
            'llStopMoveToTarget',
            'llStopPointAt',
            'llStopSound',
            'llStringLength',
            'llStringToBase64',
            'llSubStringIndex',
            'llTakeCamera',
            'llTakeControls',
            'llTan',
            'llTarget',
            'llTargetOmega',
            'llTargetRemove',
            'llTeleportAgentHome',
            'llToLower',
            'llToUpper',
            'llTriggerSound',
            'llTriggerSoundLimited',
            'llUnSit',
            'llVecDist',
            'llVecMag',
            'llVecNorm',
            'llVolumeDetect',
            'llWater',
            'llWhisper',
            'llWind',
            'llXorBase64Strings'
        );

        global $wakka;

        $match = $matches[0];

        // string
        if (substr($match, -1, 1) == "\"") {
            return '<span style="color: ' . $colors['string'] . ';">' . $match . '</span>';
        }

        // comments
        else if (substr($match, 0, 2) == "//") {
            return '<span style="color: ' . $colors['comment'] . ';">' . $match . '</span>';
        }

        // vector / rotation
        else if ( ($match == '&lt;') || ($match == '&gt;') ) {
            return '<span style="color: ' . $colors['vecrot'] . ';">' . $match . '</span>';
        }

        // list
        else if ( ($match == '[') || ($match == ']') ) {
            return '<span style="color: ' . $colors['list'] . ';">' . $match . '</span>';
        }

        // word
        else {
            if (in_array($match, $words['keywords'])) return '<span style="color: ' .  $colors['keyword'] . ';">' . $match . '</span>';
            else if (in_array($match, $words['types'])) return '<span style="color: ' .  $colors['type'] . ';">' . $match . '</span>';
            else if (in_array($match, $words['events'])) return '<a href="' . $wakka->GetConfigValue("base_url") . $match . '" style="color: ' .  $colors['event'] . ';">' . $match . '</a>';
            else if (in_array($match, $words['constants'])) return '<a href="' . $wakka->GetConfigValue("base_url") . 'TextSearch?phrase=' . $match . '" style="color: ' .  $colors['constant'] . ';">' . $match . '</a>';
            else if (in_array($match, $words['functions'])) return '<a href="' . $wakka->GetConfigValue("base_url") . $match . '" style="color: ' .  $colors['function'] . ';">' . $match . '</a>';
            else return $match;
        }

    }
}

// filter HTML
$lsl = htmlentities($text, ENT_NOQUOTES);
$lsl = str_replace("\r", "", $lsl);

// the regex that does all the highlighting
$lsl = preg_replace_callback('@(?:' .
    '".*?(?:[^\x5c]"|\x5c\x5c")|' . // string (this is how to fix your editor highlighting, Lindens! :P)   0x5c = backslash
    '[^\x5c]""|' . // string too (empty)
    '//.*?(?:\n|$)|' . // comments
    '(?:&lt;|&gt;)|' . // vector / rotation
    '[\[\]]|' . // list
    '[a-zA-Z_][a-zA-Z0-9_]*' . // words
    ')@ms', 'lslCallback', $lsl);

// output (replace tabs with 4 spaces)
print("<pre>" . str_replace("\t", "    ", $lsl) . "</pre>");

?>


FearlessLeader

Geek.


FreqoutManray

With a SL birth date of 7/12/2004, I'm still wet behind the ears. I don't have a lot of free time to be spending in SL, but I really enjoy the gratification of writing code and seeing it actually do something in "the world." As I test out the different aspects of LSL, I'll see if I can lend a hand with adding help with the wiki.


GabrielSpinnaker

Is the man half-machine or is the machine half-man?

Stuff I've done:
  • In-world color sprite editor, inspired by ChromalBrodsky
  • Sim performance stats (developed independently of ChromalBrodsky; now dormant)
  • Example Ruby code for the XMLRPCImplementations page
  • Wrote some functions:
    integer gsDayOfWeek(integer year, integer month, integer day): Returns day of week for given year/month/day. 0 = Sunday, 1 = Monday, 2 = Tuesday, etc. From a formula from Claus T?ndering's Calendar FAQ.
    integer gsDayOfWeek(integer year, integer month, integer day) {
    integer a = (14-month)/12;
    integer y = year - a;
    integer m = month + (12 * a) - 2;
    return (day + y + (y/4) - (y/100) + (y/400) + ((31*m)/12)) % 7;
}

    string gsNormalizeString(string str): 'Normalizes' str (i.e., lowercases it and removes characters that may be illegal for whatever reason). Obviously, you can change the badchars to match whatever characters might be 'bad' for your particular application.
    string gsNormalizeString(string str) {
    string  badchars = "!@#$%^&*()[]{}<>,;':/\\\" =|?"; // " // The LSL editor's syntax highlighting was fixed, but the wiki's was not!
    string  replace  = "_";
    integer x;
    integer y;

    str = llToLower(str);
    for (x = 0; x < llStringLength(badchars); x++) {
        do {
            y = llSubStringIndex(str,llGetSubString(badchars,x,x));
            if (y != -1) {
                str = llDeleteSubString(str,y,y);
                str = llInsertString(str,y,replace);
            }
        } while (y != -1);
    }

    return str;
}

    string gsSubstituteString(string source, string pattern, string replacement): Replaces all occurrences of pattern in source with replacement.
    string gsSubstituteString(string source, string pattern, string replacement) {
    string  str = source;
    integer pos;
    integer end;

    do {
        pos = llSubStringIndex(str,pattern);
        if (pos != -1) {
            end = (llSubStringIndex(str,pattern) + llStringLength(pattern)) - 1;

            str = llDeleteSubString(str,pos,end);
            str = llInsertString(str,pos,replacement);
        }
    } while (pos != -1);

    return str;
}

    string gsRoundToString(float value, integer places): Rounds value to places decimal places. If you need this for actual math, I suggest writing your own function, because this is prone to rounding errors. But it's good enough for government work display purposes. As long as it's not critical that the values you're displaying be accurate.
    string gsRoundToString(float num, integer places) {
    string str = (string)(llRound(num * llPow(10,places)) / llPow(10,places));
    return llGetSubString(str,0,llSubStringIndex(str,".") + places);
}


GenePoole

Come on, with the middle name "Superslacker", what did you expect to find here? :-p

I've created an LSL CrashCourse.


GoshuaLament

I like fixing other people's wikis and making textures.

Oh, and I have a weblog: http://home.earthlink.net/~goshualament/blog/


GuzarFonzarelli

I like to script!

Recently suspended due to his being 15, but will still be contributing to LSL Wiki Mirror 7-7-7. ^_^


HankRamos

Hank Ramos loves to script and fly balloons in SL.

Hank believes SL is a game.

That is all. :D


HansBaldwin

Just me


HawksterWestmoreland

Hello there!

I'm Hawkster Westmoreland, the owner and developer of the Second Life store Hawkster Damage Productions.

I enjoy doing scripting and computer programming whenever I physically can, and there are times I almost pass out because of how dead tired I'll be at the end of the day... or rather day -> night -> day. I've been doing computer programming for a while, beginning around 1999-2000.

I'm currently an Informatics major in college, and the major itself was only started back in 2004 at my college. The time that isn't spent programming for my major is spent doing LSL programming inside of SL, Windows application development, or website design (both corporate and personal). When I'm not programming in general, I'm usually playing my guitar and writing music.

Here's to ya and if you ever run across me inside of Second Life, be sure to say hello!


HillaryMelville

Curious.

Useful pages

EzharFairlight
ClientAssetKeys

Interesting Forum Threads

Notes on coordinate systems
sky Honey's single-prim llSetPayPrice vendor


HiroYamamoto

Name: Hiro Yamamoto
Race: Elf
Class: Wizard
Level: 5

Str 7
Dex 11
Con 10
Int 16
Wis 12
Cha 8

HP 14
AC 8
THAC0 18


IceBrodie

kitsu.

default
{
     state_entry()
     {
          while(TRUE)//yes this is correct syntax.
             llFork();
//while(true) is however the most dangerous use of the while function and should /never/ be used.
     }
}

Wiki, peer review, aid and cooperation, are wonderful things. <3

llPizza


IoneLameth

Ione Lameth in-game. An easily amused meddler wasting time mostly scripting. I don't have any mentors, but have had plenty smart and friendly help.

Feel free to drop me a line here.

Ione likes references in which a few glances are enough.

Ione think references to redirect pages that do not redirect (eg. page to groups to group, page to radians to radian, avatar to AgentAndAvatar) are silly and will alias-link referencing pages when found. It saves a click, you know:)

Ione has a bad habit of clicking 'store' instead of 'preview'. She is trying to mend her ways.

Ione isn't a very bashful editor; do complain to me if you think I edit things too easily. I just think stirring up some dust is a good way to get some work going on a page.

Ione stares in the SL font's general direction for making people think her name is 'lone'.

According to googlism, 'ione is approximately 255'.

Notes

Using on_rez for initialisation can be useful when you don't want objects' persistant state to bug you. For example, you can llResetScript (see that page for a reason to do so and for details).

Self-question: What exactly does selection do to objects? They do not participate in physics, and apparently will also not be detected by sensors - or apparently, it will change their being detected (or just the order?): move something into range while selected and it won't detect it, but have it be in range and select it and it still will be. (tested by using (0), which is usually (not always) the closest)

Communication

Communication within a linkset should be done with link messages.Aside from being more efficient than using channels, are useful because they are object-local and won't interfere with multiply rezzed objects. Also, messages don't start getting through until linked, which can be handy while building something that's not supposed to start partially working before you're done and linked (and you may want to consider using on_rez to avoid things working until the next rez).

There seems to be no way to check whether a channel is taken, but you can choose a random one every rez (e.g. integer channel=1+(integer)llFrand(2000000000.)). On cooperating objects, try to make one rez the other so that you can hand this channel to the other in the rez event.

I wonder whether there is any practical way to query specific objects (e.g. those that llSensor sees), other than using llDetectedKey, a fixed channel and making each object filter an llListen on their own key - as that may be a little hard on the sim on larger projects.

XML-RPC

SL seems extremely picky about the format a client hands it. That's the only reason I can think of to explain the fact that it just started to work for me. I use one of the python XML-RPC clients scripts right now, the one of which the code is:

#!/usr/bin/python
from xmlrpclib import ServerProxy
import sys

def llRemoteData(Channel, Int, String):
    client = ServerProxy("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi")
    return client.llRemoteData({"Channel" : Channel, "IntValue" : Int, "StringValue" : String})

reply = llRemoteData("6114aa5a-d64d-6e8c-ef8f-90811b49997d", 0, "Hello from " + sys.platform)
for name, value in reply.iteritems():
    print name, value

The LSL script this sends to is the one listed on the XMLRPC page.
I've yet to nicely solve the hardcoded UUID thing. Don't know what to do about that yet.

State

A number of things are prim state, and can often only be changed with a script, though most also through the edit window. This includes the rotation, scale, position, particle system (llParticleSystem), sit target (llSitTarget), hover text (llSetText), flags (whether it participates in physics, is phantom, will phsyically rotate and a few other things; llSetStatus), the color, texture, and alpha (see texture). Many of these can alternatively be set through llSetPrimitiveParams (usually slower through a delay), which additionally lets you set things like material and TemporaryOnRez-ness).

These things will not change when the script that set them is removed.

Other things will, which seems to be anything in dynamics/interacts with the energy system, such as buoyancy (llSetBuoyancy), force (llSetForce). It seems these revert to their regular state, usually a zero value. (Since physics is necessary for many but is a flag and as such persists, that usually means deleting the script will make the object drop)

Persisting

There are two types of persistance:
* prim state and script state persistance while things are in your inventory (only until a script reset)
* script continuity while changing active script state.

For example, prim state will persist into your inventory (and script state does not affect it).
Script variables and scriptstate will persist into your inventory too.
llListeners will stop both while changing scriptstate and inventorizing. (seems a little odd as it makes using the states slightly more verbose sometimes)

(note to self: make list of interesting things here)
(note to others: I use 'state' in the more technical/general sense, not to mean prim parameters, prim status or script machine-state. Sorry:)

Experiment: Clock arms
Because when you use quats you cannot create a single operation (and lsl doesn't really provide much help) that does rotation at the same time as anything else (like you can with matrices) rotation about anything not the object's center seems not directly possible, so eg. making a clock's hands is hard - but possible. (Most clocks, e.g. the hub clocks, are simpler: they use mostly-transparent textures on the top face of a cylinder to emulate separate hands).

One solution is to communicate both position and rotation to the child via (preferably) link messages and set both at the same time using llSetPrimitiveParams which adds a 0.2sec delay, but that should be generally survivable. (using llSetPos and llSetLocalRot separately will introduce a visible delay between these two calls.) Note, though, that (script) lag can make the seconds only correct on average, as with anything updating state so often. Stripped-down example code:

link_message(integer sender_num, integer num, string str, key id) { //in the receiving end's script
       list ls = llCSV2List(str);
       llSetPrimitiveParams([PRIM_POSITION,(vector)llList2String(ls,0),   PRIM_ROTATION, (rotation)llList2String(ls,1)]);
    }

    timer() { //in the sending end's script (1-sec timer; this imitates a second hand)
        float secRad=llGetWallclock()*0.10472; //(not accurate, but reads cleaner than my actual version)
        llMessageLinked(LINK_SET,0, llList2CSV([<llSin(secRad)*2.18,llCos(secRad)*2.18,0>, llEuler2Rot(<0,0,-secRad>)]),NULL_KEY);
    } //hardcoded radius (the 2.18) is stupid. This example has a few shortcomings but illustrates the method.

Physics

There is an index of sorts at physics, and energy got a lot more detailed (so I removed some notes here).

Some functions, such as llSetForce (also llSetBuoyancy, llSetHoverHeight, llSetForce and likely llMoveToTarget), cause constant use of energy, which means that it is possible that objects use energy faster than it is supplied and so will make it easier to run the prim out of energy, or completely do so themselves if they try to do too much.

Running out of energy at all is a bad idea as even when it works it'll run into odd shocks. Additionally, whenever anything else acts on an object, more energy may be sucked out than in ideal situations, so don't depend on the exact limit. Buoyancy and other constant-use functions do not work on large objects. In simple tests, this seemed to happen for things with more volume than 10 cubic meters.

(I'm not sure how energy is received/used in linksets - it doesn't seem linear)

Material type (set in the 'Object' tab of the edit window) have effect on the physics engine, but apparently only in friction and possibly collisions.

Particles

You make these by handing llParticleSystem a variable-length list of commands and their parameters. (An llParticleSystem call seems to set a static (fully persisting) state collection after clearing it to defaults so that you don't need to override anything.)

Setting target key to something nonexistant may have the effect of not changing, rather than setting the emitter as target as mentioned in llParticleSystem; check this.

Vehicles

Vehicles look like default behaviour that should probably be seen like a similar but separate set of dynamics functions (probably using the same lower-level interaction tuned for the purpose). It seems to come down to channeling movement into preferred axes of motion, two types of motor (one for propulsion, one for turning), a separate hovering system, some tuned flying things such as banking, some pre-made convenience vehicle types that the docs suggest are mostly just a combination of vehicle parameter sets.

Control is done via scripts, though steering can also be done via mouselook. Potentially it can be separate from you, though it often just makes sense to react to direct input controls via llRequestPermissions, llTakeControls, usually once people sit on something (since your avatar is incapacitated when you steer). However, there is no direct need for an avatar at all.

Links: Vehicles, the apparently somewhat outdated LindenVehicleTutorial and the likely better TutorialVehicle.

Pitfalls/hints

See also listed Annoyances.

I once relinked an object differently not noticing that that meant I had two different versions of the script in the linkset which made it act quite weirdly. You can check this without checking everything by stopping all scripts (Tool menu); it'll list each script as it shuts it down.

Object center is not (necessarily) the same thing as center of gravity, and eg. llLookAt will rotate about the latter, llSetRot around object center (position coordinate).

Sitting changes various things, including
vehicles' (maybe objects' in general? *check*) center of mass,
the avatar's susceptibility to physics - probably depending on object's physics status (*check*) as the avatar acts like a linkset child (the way you detect sitting is through detecting al link change).
check the mass implications.

Sim borders seem to be slightly unpredictable. Internally, I would guess a hopefully-identical process is created on another computer, which has its imperfections in practice. It is apparently not as simple as a script reset or re-rez (it does neither) but details do change unlike you may expect, and I think it does just work except if you use sim-dependant functions that are either so per definition (e.g. apparently llGetTime) or per bugness.

Wiki

ACLs: ? means logged in person, * means anyone, including anonymous users. Named users are positive filters, ! negate them. ACLs seem to be a first-match thing.

Backlinks, for reference of what I've meddled with in here:
brace
CamelCase
collision_end
Contributors
Debate
IoneLameth
llRot2Angle
llSetHoverHeight
llSleep
quaternion
RemovalPetition
value
VehicleTutorial
WikiUsers
ZERO_VECTOR


JesradSeraph

I'm trying to get at least one open standard made in SL: TLTP


JillianCallahan

Cid Jacobs hates me. :P
C style indents, kids. More readable :P


JimBunderfeld

I script. : )

For Private LSL tutoring contact me in the game via IM.

- Jim Bunderfeld
A crazy scripting Monkey ^_^


JippenFaddoul

Fuzzball
~more to come~

Great wiki cleaner...
Major project: Filling in WikiUsers so that they stop appearing in the WantedPages list.
Status:working on cleaning up his own mess. Hey, at least the WantedPages is useable now!
Current project: cleaning up MyPages so people can have thier profiles back. (( sorry ))


JordanCotton

(Jordan, as you see by the comments, someone spammed your page. I've cleared it due to it's so obnoxious to see..)

You might wanna redo your page now :)


JulianFate

I build, I script, I texture, I clothe, I do it all. Bitterness and sarcasm on tap.

Today SL Looks: Metaversey (officially)

My site: Terminal Inertia


JusticeMonde

Boats. Things that move. Things that listen and respond. Things that contain or are contained by other things. Things that go "bump" in the night or at any other time for that matter.

default {
state_vicious {
llKickTheShizOutOf(llDetectedAgitation(0));
return;
}
}

-JMonde


Kafka Solvang

Little is known about the Kafka (Kafkus Solvangus). The Kafka is often found on the Island Sand Box, where it lives off ramen and squirrels. Armed with a giant fork and proximity mines, it fiercely protects its territory from the timid Noob. This majestic creature often spends its time building useless object that it gets pissed at, deletes in a spamming rage, and then feels sorry about later. Its scripting capabilities consist of prims shouting "poop" which it laughs maniacly at. Even though it has the attention span of 10 year old and is mature as one as well, it's a calm creature (except when hungry) and will help those who need it.


KairaOverdrive

Hia welcome to my page! :p

I strive for perfection in every design, object, script, animation, texture I do.

My latest project is called 'Forsaken'. -- Taken on hold, going to redo with a better design! ;)

Its about finding a rescue beacon of a mining ship. Approaching it you notice no reactions to your messages sent to it
and discover an asteroid it has found. The mining ship is completely deserted. The are messages left in the mining ship
about the asteroid with some kind of installation built into the asteroid. The athmosphere in the asteroid is completely stable
but no life signs can be detected by your ships sensors due to the thickness of the asteroid walls.

You decide to see what has happened and look for any possible life signs.

The build is a horror theme obviously from reading the story. I aim to complete it with the best of my abilities.
I build using state-of-the-art athmosphere and design creation techniques for the ultimate horrific scare.


KeikoRau

About Me

Well, Im new to SecondLife but Ive got a bit of experience with Wiki and have been programming for a number of years.

LSL is fairly easy to pick up, and I hope to contribute to this great resource as I learn more.

I spend most of my SL time scripting, and learning from some great examples. I hope to use what I learn to one day build something cool and unique.

Keiko


Keilaron Tomba

I's a scripter. I make scripts.
You want something scripted? I script it!
You don't want something scripted? I script it anyway!

  • My scripts
  • My LSL reservations
  • My LSL wishlist
  • About me

My scripts

For almost every script that I sell, I have a full free-upgrade policy. In other words, when you buy it, you always get the new versions free, regardless of whether the price changed or not.
For scripts that're free, those are yours to keep up with; feel free to ask me what the latest version of a particular script is, however.

Name/Codename | Description | Version/Status | Free?
ScanWatch | A watch that not only tells time, but can scan (sensor) for various things using various methods; Tells time to anyone who clicks on it; can also point at a target. | Rewriting, beta/alpha | Currently private
MailCard | Mailing/delivery system that allows people to give objects and e-mail notecards to a particular owner; fully configurable. 0.7 will also include group messaging features. | 0.6 ~ 0.7, beta | Price undetermined, grab a copy while it's in beta
Mere series | A series of "mere" (simple) scripts for use as examples, starting points, or just general usage. | N/A | Free
Gold/Mine | Casino game where you choose spots on a 5x5 grid, and gain prizes (or lose) depending on what's hidden. | ~0.3 | Price undetermined
Teleporter | Your average teleporter script... with notecard reading capabilities, multiple endpoints, and more. Script comes with various sample teleporter objects, although you aren't forced to use only those =) |  | Price undetermined
If you'd like to see any of these completed, or their information updated here, let me know. I'll be glad to do so.
Also, if you're looking for a scripter, I'm more than interested in working for you. Likely I'll do it for free, too =)

LSL Reservations

Link message and listen channels I use:
Basically, -13311000 to -13311999.
Channel Range | Usage
-13311000 | Temporary/Localhost
-13311001 - -13311499 | "Internal" script communication
-13311500 - -13311999 | External communication (e.g. informational messages)
This isn't a way of saying "MINE!", but a way of preventing collisions.

LSL Wishlist

Just thought of adding this to here instead, so it isn't an exhaustive list.
Idea from ChristopherOmega, in turn from KeknehvPsaltery.
Title | Description | Proposition #
Fix llSensorRepeat | llSensorRepeat repeatedly adds and drops things from the list of detected objects/avatars whenever they're at a certain range (seems to be approximately half the size of a sim) | -

About Me

I like to think that I'm not a bad coder, but my insistance on bug-proofing and proper coding seem to slow me down.
That said, though, I definitely consider clean code to be essential. I hate looking at other people's code and seeing no comments, everything smushed together like paste, etc.; It's rather upsetting, especially if I'm trying to find a particular section, or figure out how a certain thing was done.

I script for others, generally a low or inexistant price because I'm still rather new to SL. Want a script? Let me know.

Myeah. Ask me for my scripts in-world, if you're interested. And point out my own shortcomings ;>


KeknehvPsaltery

Help the wiki, and do these things:
  • Add yourself to the contributors page.
  • Clear out and fix the WantedPages page.
  • Add yourself to the scripting mentors page, if you know what you're doing.
  • Pester people with OrphanedPages

Feel free to IM me in-world, leave a comment here, or email me -- keknehv at gmail dot com.

Page Jump:

Home | WantedPages | ToDo | MyPages | MyChanges | OrphanedPages | WarpPos


KexGodel

Please feel free to IM me for help with LSL or SL in general.

I'll admit ahead of time I do not have much experience with (starting with least-familiar):
- Vehicles
- Movement functions
- Physics functions
- XMLRPC
- Rotations
- Particles

However, I am fairly familiar with (starting with most-familiar):
- Interactive text
- Data retrieval, parsing, and manipulation
- Sensors
- Animations
- Prim modeling parameters, textures, colors, etc
- Media (sound, video) control
- Dialogs

Anything else not mentioned probably falls somewhere inbetween =)

llStartAnimation("backflip");
<Shift>+<F10>

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;!!!


MalarthiBehemoth

[edited to remove all the random crud I put up 6 months or so ago...]
Welcome to Malarthi Behemoth's page...
Not much here yet. Edit at your own risk!

Projects:

What projects? I sit and I help people and I watch the TG. That's about it...


MaxCase

I am Max Case. Feel free to IM me in SL.

Here is my first addition to the script library. It's a Keyboard Script.
But really, you can adapt it to override anything llGetAgentinfo() returns.

This is the beginning of my attempt to make a functionLibrary - little blocks of code others might find useful - to use in a LEGO-ish / cookbook fashion. They will be heavily commented, but ready to use first, and ask questions later. Lots of it is basic stuff I figure others may benefit from. Usually, you can just drop script into prim and it will work.

MapAPI : Page I am starting to record tips, etc about Map API for lack of a better place. People can decide if they want to link this off main page.

I have a web page
I also operate Sim Neighbours, a site to x-ref Sim Host Nums.

Here is my SL-PHP header cruncher I use for incoming HTTPREQ's. Maybe someone else finds it useful.


MaxxMonde

Is not actually a registered user, although he appears on the contributors list. Very odd...


MechageoDannunzio

Sometimes I wonder if it's more fun to build or to script.


MichaelSmall

Michael Small has never used a Wiki before.


MingChen

Im a 16 year old scripter that has been hiding his true identity since beta ^_^
You can find me in the teen grid or my brother Alpha if you need any scripting help!

Sigma Lime was here


MoleculorSatyr

Coding newbie slogging his way through LSL. Thinks he has an answer for everything.

Creates mostly visual effects. Fireflies in Seacliff (and soon to be elsewhere), teleporters (and soon to be underlying structural code) for Mysterious Journeys in the GameDev.


NeoRebus

In SL since early April '04.


OlmySeraph

For the love of humanity, llSetLinkTexture() !

  1. Premature optimization is the root of all evil.
  2. Make simple things simple, and complex things possible.
  3. A design should be as simple as possible, but not simpler.
  4. For every problem, there is a solution that is simple, elegant, and wrong.
  5. You can be consistent, or you can be inconsistent, but you can't be both.
  6. There's no such thing as a good design.


PatchLamington

Stupid widgets for fun.
If I wanted to write user-friendly code, I'd get a real job. erm...


PsykePhaeton

Been LSLing since I joined in 12 Oct 2003.


RadeGalatea

aka Christopher Armstrong.

http://radix.twistedmatrix.com/


ReadyJack

18 Mar 2006

Encapsulation

27 Feb 2006

performance tips

As always keep in mind that tweaking for performance should be a last resort reserved for cases where specific actual issues exist. Code maintainablility is much more important than over-performing.

I put together some tests today and verified some ideas and questions I've had regarding LSL performance.

Functions and variables:

Inlining (replacing a user defined function call with the actual code) can be very beneficial.
Local variables (declared within a function) are slightly faster than global variables.

Lists:

Avoid long lists wherever possible.
If you have a situation where you have two things you want to store in a list format (for example: name and key, use two shorter lists rather than one longer "2D" list. Two shorter lists are much faster.
Don't think pulling out a section using llList2List() and then operating on that shorter list will help. The overhead of created multiple lists kills the slight gain in efficiency.
Use the type specific functions (like llList2Float()) rather than type casting a string (like (float) llList2String()) wherever possible. It's quite a bit faster.
Strings as lists suck because llSubStringIndex doesn't have an offset so you end up splitting the "list" with two more calls each iteration...one to get the piece you want and one to trim the list so you can use llSubStringIndex to find the next chunk. Meh.
You'd think that adding to the front of a linked list might be faster than adding to the end, but the way it's implemented in LSL apparently doesn't benefit from the features of linked lists yet retains all the disadvantages.

Loops:

There's no difference between for and while loops. As indicated elsewhere there may be a small delay associated
with loops. I'm currently testing whether recursion can help. Unrolling the loop a bit (doing things in the loop twice or three times and handling the extra incrementing) can help...Well, I came up with something that works well for me. Doing recursion I eliminated 3 loops and a variable (list) cleanup of non-ordered elements problem I was having worked out perfect under recursion. But there's a ton of list copying going on. Good for my particular problem, but probably would rarely benefit most scripts. In simpler test scripts recursion was soundly beaten by standard list access.

better time check for performance testing

// time test // ready jack // v1.1
// takes two timestamps and returns the difference in seconds as a float
// only valid for short durations within an hour
float time_test(string ts_before, string ts_after) {
    list befores;
    list afters;
    float result;

    befores = llParseString2List(ts_before, ["T","Z",":"], []);
    afters = llParseString2List(ts_after, ["T","Z",":"], []);

    integer bmin = (integer) llList2String(befores, 2);
    integer amin = (integer) llList2String(afters, 2);
    float bsec = (float) llList2String(befores, 3);
    float asec = (float) llList2String(afters, 3);

    if (bmin < amin) {
        result += 60 * ((amin - 1) - bmin);
    } else if (amin < bmin) {
        result += 60 * (amin + 60 - bmin);
    }

    if (bsec < asec) {
        result += asec - bsec;
    } else if (asec < bsec) {
        result += asec + 60.0 - bsec;
    }

    return result;
}

- Exactly how much quicker was this? Its awful coding practice, but in a place like SL, performance is obviously more of an issue NikEbisu

I'm not sure if this is meant for the above section or the following. For the above section the difference is trivial. You'd have to be looping constantly for a long time to notice. I think I have more detailed info on the StyleGuide page (it's gone now...but just use ++x). The difference for the following section (i.e. using a local variable to store a static list length) is huge. See the numbers at the bottom. -- RJ

23 Jan 2006 - Loop Speed Challenge!

Here's the test script:

// loop speed test // ready jack // 23 jan 2006 // v1.0

list foo;

default
{
    state_entry()
    {
        integer i;
        for (i = 0; i < 300; ++i) {
            foo += (integer) llFrand(100.0);
        }
        llOwnerSay("prepped");
    }

    touch_start(integer total_number)
    {
        integer count;
        integer i;
        llOwnerSay("test1 before: " + llGetTimestamp());
        for(i = 0; i < llGetListLength(foo); ++i) {
            ++count;
        }
        llOwnerSay("test1 after: " + llGetTimestamp());

        llSleep(2.0);

        integer len = llGetListLength(foo);
        llOwnerSay("test2 before: " + llGetTimestamp());
        for(i = 0; i < len; ++i) {
            ++count;
        }
        llOwnerSay("test2 after: " + llGetTimestamp());
    }
}

And some sample output:

Object: test1 before: 2006-01-23T08:36:03.964150Z
Object: test1 after: 2006-01-23T08:36:10.705834Z
Object: test2 before: 2006-01-23T08:36:12.778144Z
Object: test2 after: 2006-01-23T08:36:13.023281Z
Object: test1 before: 2006-01-23T08:36:23.486117Z
Object: test1 after: 2006-01-23T08:36:30.314002Z
Object: test2 before: 2006-01-23T08:36:32.386351Z
Object: test2 after: 2006-01-23T08:36:32.631740Z
Object: test1 before: 2006-01-23T08:36:35.974632Z
Object: test1 after: 2006-01-23T08:36:42.691158Z
Object: test2 before: 2006-01-23T08:36:44.763031Z
Object: test2 after: 2006-01-23T08:36:45.006860Z
Object: test1 before: 2006-01-23T08:36:49.975469Z
Object: test1 after: 2006-01-23T08:36:56.694460Z
Object: test2 before: 2006-01-23T08:36:58.765095Z
Object: test2 after: 2006-01-23T08:36:59.009746Z

Scripts and Snippets

ExampleGun
LibraryRuntimeEnvironment
ExampleListConversion

Sample Code

Float Script
// Float 2.0 - An attachment script that lets you conveniently work at high altitudes.
// By Ready Jack in or around March 2004

// Usage: float [command|value]

// Examples:
//      float       (keeps the current altitude)
//      float 1.0   (same as float by itself)
//      float off   (turns it off)
//      float 0.0   (same as off)
//      float +     (increase the buoyancy by gDelta - see below)
//      float -     (decrease the buoyancy by gDelta)
//      float 10.0  (rocket skyward)
//      float -10.0 (sink like a rock)

// change this to a private channel for invisible key mapping
// or set it to 0 for spoken commands
integer gComChannel = 5;

// this is the ammount the current value is adjusted by for the +/-
float gDelta = 0.05;

float gCurrentFloat;

default
{
    on_rez(integer param)
    {
        llResetScript();
    }

    state_entry()
    {
        llListen(gComChannel,"",llGetOwner(), "");
    }

    listen(integer channel, string name, key id, string message)
    {
        list tokens = llParseString2List(message, [" "], []);
        if (llList2String(tokens, 0) == "float")
        {
            if (llGetListLength(tokens) == 1)
            {
                gCurrentFloat = 1.0;
            }
            else if (llGetListLength(tokens) == 2)
            {
                if (llList2String(tokens, 1) == "off")
                {
                    gCurrentFloat = 0.0;
                }
                else if (llList2String(tokens, 1) == "+")
                {
                    gCurrentFloat += gDelta;
                }
                else if (llList2String(tokens, 1) == "-")
                {
                    gCurrentFloat -= gDelta;
                }
                else
                {
                    gCurrentFloat = (float) llList2String(tokens, 1);
                }
            }
            llSetBuoyancy(gCurrentFloat);
        }
    }
}

Global Dynamic Storage - an associative array with appropriate getter/setter functions. - TODO

In-world Persistent Storage - A bit of a kludge. Reads ini format text from a note in inventory into a data structure which can be used and modified in place and then dumped to the chat window in a format which can be copy/pasted back into the note. Good for saving the locations of dynamicly rezzed objects and other similar problems.

group detect

conversation enhancer

Caveats

This script will cause an object to disappear or appear to fall through the ground, but the physics of the object stay where expected. Put it in an object and touch it a few times and you'll see what I mean. You can get the object back back dragging the ground where it is. As you as you select it, it will reappear.

vector gPos;
integer gFirst = TRUE;

default
{
    state_entry()
    {
        llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y
                | STATUS_ROTATE_Z, FALSE);
        gPos = llGetPos();
    }

    touch_start(integer total_number)
    {
        vector target;
        if (gFirst)
        {
            target = gPos + <0,0,1.0>;
            gFirst = FALSE;
        }
        else
        {
            target = gPos + <0,0,5.0>;
            gFirst = TRUE;
        }
        llSetStatus(STATUS_PHYSICS, TRUE);
        llMoveToTarget(target, 0.5);
        llSleep(3.0);
        llStopMoveToTarget();
        llSetStatus(STATUS_PHYSICS, FALSE);
    }
}

After observing this some more, it seems that the disappearance occurs after the sleep during the combination of disabling physics and stopping the llMoveToTarget. Changing the order of the two last lines doesn't help. My guess is that the client is letting the object freefall into oblivion before the server has a chance to update the physics setting.


RecentChanges

XML

2007-07-07:
(2007-07-07 00:37:20) (history) Comment6559 . . . . 222.98.121.15

2007-07-06:
(2007-07-06 23:50:04) (history) variable . . . . 220.70.247.148
(2007-07-06 23:35:29) (history) Sound . . . . 220.70.247.148
(2007-07-06 23:32:35) (history) Comment4179 . . . . 203.162.27.90
(2007-07-06 23:20:33) (history) NikEbisu . . . . 85-18-242-26.ip.fastwebnet.it
(2007-07-06 19:41:39) (history) ScriptLibrary . . . . 74-128-192-107.dhcp.insightbb.com
(2007-07-06 14:11:25) (history) Comment6159 . . . . 69.85.111.133
(2007-07-06 04:54:49) (history) llAllowInventoryDrop . . . . dsl-Chn-static-094.252.247.61.airtelbroadband.in
(2007-07-06 00:40:58) (history) light . . . . 220.70.247.148
(2007-07-06 00:35:19) (history) clever . . . . 220.70.247.148
(2007-07-06 00:35:00) (history) Comment3995 . . . . modemcable051.177-201-24.mc.videotron.ca
(2007-07-06 00:08:02) (history) HomePageindex.php?... . . . . cpe-76-174-236-14.socal.res.rr.com

2007-07-05:
(2007-07-05 20:48:27) (history) vector . . . . DaymanRuno
(2007-07-05 20:44:44) (history) llMakeExplosion . . . . 3703ds1-hhl.0.fullrate.dk
(2007-07-05 20:37:37) (history) GaiusGoodliffe . . . . c-68-45-1-216.hsd1.nj.comcast.net
(2007-07-05 16:51:33) (history) llDetectedLinkNumber . . . . nat.bsu.edu
(2007-07-05 15:56:08) (history) StateKeyword . . . . DaymanRuno
(2007-07-05 12:12:54) (history) LibraryFadeTexture . . . . ACB63C58.ipt.aol.com
(2007-07-05 04:01:44) (history) HowDoI . . . . cpe-66-65-246-217.mass.res.rr.com

2007-07-04:
(2007-07-04 15:16:56) (history) llSetPayPrice . . . . c-76-104-77-140.hsd1.va.comcast.net
(2007-07-04 10:06:45) (history) llFrand . . . . MarsConsult
(2007-07-04 09:36:15) (history) Comment8960 . . . . i218-44-104-183.s02.a043.ap.plala.or.jp
(2007-07-04 05:23:46) (history) Bytecode . . . . DaymanRuno
(2007-07-04 05:11:52) (history) Comment3442 . . . . c-24-5-116-160.hsd1.ca.comcast.net
(2007-07-04 03:20:23) (history) Operators . . . . DaymanRuno
(2007-07-04 00:28:17) (history) SimulatorNames . . . . VaguegirlPetty
(2007-07-04 00:00:20) (history) PageIndex . . . . p042.net220148068.tnc.ne.jp

2007-07-03:
(2007-07-03 23:05:09) (history) VaguegirlPetty . . . . VaguegirlPetty
(2007-07-03 22:48:11) (history) llApplyImpulse . . . . c-24-5-116-160.hsd1.ca.comcast.net
(2007-07-03 17:04:05) (history) llPlaySound . . . . c-75-73-56-205.hsd1.mn.comcast.net
(2007-07-03 16:18:54) (history) llSetPrimitiveParams . . . . user-0c6ti96.cable.mindspring.com
(2007-07-03 13:06:52) (history) Comment3996 . . . . 203.145.131.158

2007-07-02:
(2007-07-02 20:01:25) (history) group . . . . CowTaurog
(2007-07-02 14:04:04) (history) llDialog . . . . c-76-105-92-117.hsd1.ga.comcast.net
(2007-07-02 10:55:01) (history) ExampleParticleScript . . . . 68.153.21.130
(2007-07-02 05:12:15) (history) Comment6150 . . . . js238.ssl-connection.com

2007-07-01:
(2007-07-01 23:01:48) (history) SNTP . . . . CartmanNoel
(2007-07-01 15:18:14) (history) Exchange . . . . CartmanNoel
(2007-07-01 10:49:28) (history) clrchanger . . . . c-75-68-100-15.hsd1.nh.comcast.net
(2007-07-01 10:48:17) (history) HomePage . . . . cpe-024-031-101-151.sc.res.rr.com
(2007-07-01 06:31:01) (history) Bitwise . . . . Mathwiz3141
(2007-07-01 06:01:43) (history) llDetectedSex . . . . 58.172.200.116
(2007-07-01 03:07:59) (history) comment . . . . AclBot
(2007-07-01 03:04:48) (history) JokerOpus . . . . AclBot
(2007-07-01 01:22:00) (history) AclBot . . . . AclBot

2007-06-30:
(2007-06-30 20:42:46) (history) string . . . . c-24-19-2-212.hsd1.mn.comcast.net
(2007-06-30 16:30:01) (history) ScriptingMentors . . . . c-67-174-244-11.hsd1.ca.comcast.net
(2007-06-30 12:34:02) (history) EyanaYohkoh . . . . EyanaYohkoh
(2007-06-30 10:39:29) (history) JarhynWilde . . . . bas6-toronto12-1167883612.dsl.bell.ca
(2007-06-30 07:50:51) (history) byte . . . . 203-196-42-87.static-dsl.vic.aussiewholesale.net


RobertLuchador

I just added this page as a placeholder really.

I've got several harebrained schemes running at any given moment in SL. I specialize in exotic weaponry and cheap toys, with a little bit of experience in writing XMLRPC server/client junk.

Hunt me down inworld and I'll show off some of my stuff for you, or whatever. Let me know if you want me to make you something neat.


RyanDayton

Mr. Anderson...... we missed you. (this quote will have some meaning in about 3 years)

I'm a TGer :-p

TG First Town Hall

Philip Linden:  hey there!

Blue Linden:  WE'VE GOT A BUNCH OF QUESTIONS THAT PEOPLE HAVE ASKED ON THE FORUMS

Blue Linden:  we plan to have these question and answer sessions on a regular basis

Blue Linden:  HEHEHE

Blue Linden:  RULE #1

Philip Linden:  I am certainly almightly in this avatar!

Philip Linden:  OK you guys... don't sent me any more friendship requests... until after we are done!

Philip Linden:  They are covering me here.

Blue Linden:  BUT FIRST I WANTED TO LET EVERYONE KNOW THAT THE TEEN GRID IS A YEAR OLD AND HAS MORE THAN 10,000 SIGN UPS....WE'RE GOING STRONG AND IT'S THANKS TO YOU SMART AND DEDICATED RESIDENTS. THANKS FOR MAKING THE TEEN GRID ROCK :)

Philip Linden:  Wow

Philip Linden:  10,000

Philip Linden:  I can remember,

Philip Linden:  when we had a party here at the main office

Philip Linden:  to celebrate that there were 5000 people... on the main grid.

Philip Linden:  OK... so thanks everyone for having me.

Philip Linden:  And for coming and spending time listening to me,

Multi Gadget v1.39.16: Renegade Shriner

Philip Linden:  as opposed to all the more interesting stuff you could be doing or building right now :)

Philip Linden:  I feel like godzilla.

Brooke Barmy: Hey Yleri

Philip Linden:  So I can't believe that this is my first teen town hall,

Philip Linden:  but it is.

Philip Linden:  But I hope to get in here lots more.

Blue Linden:  ITS THE FIRST TOWN HALL, SO YOU HAVENT MISSED ANYTHING ;)

Philip Linden:  I haven't had as much time as I'd like to be in-world in the last few months,

Philip Linden:  in either grid.

Philip Linden:  Nowadays whenever I log in...

Philip Linden:  people start giving me the most amazing stuff.

Blue Linden:  hehe

Philip Linden:  I can remember feeling... maybe 1 1/2 years ago,

Philip Linden:  like I was a pretty good builder.

Philip Linden:  Now when I login and people give me things...

Philip Linden:  I know that isn't the case anymore!

Philip Linden:  Take this avatar for example.

Philip Linden:  I was wandering by blue's desk the other day.

Philip Linden:  and saw him dancing in this avatar... and was amazed.

Philip Linden:  It was made by Kurenai Meiji, as I'm sure you guys already know.

Philip Linden:  Amazing work.

Philip Linden:  Blue was telling me how many main grid folks ask him if he can bring them things from the TG

Blue Linden:  hahah

Daniel Benton is online

Blue Linden:  i have to tell them SORRY SUCKERS....

Stark Huszar is online

Philip Linden:  So let's see...

Philip Linden:  So much going on lately that I can't really even summarize.

Philip Linden:  Obviously you guys have grown very fast.

Philip Linden:  And we are struggling, just like with the MG,

Philip Linden:  to give you all the things you want.

Philip Linden:  Maybe blue can give us some of the top questions you are sending...

Philip Linden:  and I can talk a bit to those.

Philip Linden:  And do my best with the quickest typing I can give ya.

Philip Linden:  Blue?

Blue Linden:  WE TOOK SOME OF THE QUESTIONS YOU POSTED TO THE FORUMS AND HAVE THEM HERE NOW

Philip Linden:  OK

Blue Linden:  WE'LL GO THROUGH THOSE FIRST AND THEN OPEN UP THE FLOOR FOR LIVE QUESTIONS :)

Blue Linden:  HERE'S THE FIRST ONE.....

Blue Linden:  Jackson Widget: Are you willing to hire Lindens that are younger, such as 16 or so?

Philip Linden:  It seems like a great idea.

Philip Linden:  We are looking into an intern program or something that would be appropriately shorter term/summer/etc.

Philip Linden:  And we have I think a couple test cases where we are doing some projects with teens.

Philip Linden:  Definitely send us resumes and the like if you'd be interested...

Philip Linden:  my email is philip@lindenlab.com, btw.

Philip Linden:  feel free to send them to me as well.

Philip Linden:  I'd love to have more interns in general, and you guys could be great.

Blue Linden:  definitely some very helpful residents in the TSL Volunteer program.....

David Gall is online

Blue Linden:  NEXT Q

Blue Linden:  Mercury Metropolitan: Does LL frequently audit chat logs between approved adults and other teens and are there certain keywords that are flagged and investigated if they are typed in chat within TSL? (Public chat or IM)

Philip Linden:  Nope... we don't.

Philip Linden:  Even on the TG, this would be too big a job.

Philip Linden:  We rely on the willingness of you guys to rapidly report anything that sounds like a problem,

Blue Linden:  15 for internship

Philip Linden:  and then we very rapidly respond.

Philip Linden:  we also feel like a basic expectation of privacy is fundamental

Philip Linden:  both on the TG and MG

Philip Linden:  So we don't 'spy' on communication.

Philip Linden:  We feel we can keep the TG safe without monitoring conversations,

Philip Linden:  because there are so many people willing to quickly report trouble.

Philip Linden:  OK blue.

Blue Linden:  NEXT Q;

Blue Linden:  Veritas Kennedy: How is the progress on the Long awaited Events system that has been mysteriously missing from the TG?

Philip Linden:  Well I just looked up that project in our system...
Philip Linden:  hang on...
Philip Linden:  It actually has 20 votes!
Philip Linden:  That is a really large number!
Philip Linden:  So the reason this hasn't happened is that (reading from the file), there is a good bit of web work needed for it.
Philip Linden:  If any of you are great web developers, BTW, send us your resumes.
Philip Linden:  :)
Philip Linden:  But yes, it seems like something that is long overdue.
Philip Linden:  We have a way of working here at Linden than is a bit different than other companies...
Philip Linden:  a way we really believe in because it lets us get a huge amount of work done fast.
Philip Linden:  But part of that way is that we don't strictly schedule as much stuff as other companies do
Philip Linden:  and we rely on people to make their own choices about what to work on.
Philip Linden:  So this is something that hasn't been picked up yet,
Philip Linden:  because we've had not enough people on the web team and lots of things to do.
Philip Linden:  But from the votes (and I just added mine), I suspect it will get picked up soon.
Philip Linden:  I'll agitate a bit for getting events done!
Blue Linden:  woot! thanks philip and many thanks to the residents who have faithfully held classes and hosted events regularly, making announcements on the forums :)
Philip Linden:  BTW try not to give me stuff or friendship until we're done you guys.
Blue Linden:  NEXT Q
Blue Linden:  Alpha Zaius: Is there an ETA for a possiility of a Teen Grid Map API? Im waiting for it to come out for a unique miniproject.
Philip Linden:  I assume this one refers to the fact that the teen grid cannot be accessed from the regular webmap?
Philip Linden:  Let me look into that and see if we can put it on there or something. Good point.
Blue Linden:  NEXT Q
Blue Linden:  Argus Stravinsky: Could it be possible, since the Teen Grid has only so many sims, and land is gobbled up quickly, to enforce regulatory pricing on land for sale?
Philip Linden:  I think that regulating pricing for land is not something you will see us do.
Philip Linden:  If we aren't putting enough land online for the TG, we'll add more.
Philip Linden:  And there are certainly growing pains because we are still a smaller community here.
Philip Linden:  But if we supply enough land,
Jeska Linden:  shhhh
Philip Linden:  the market should keep things stable.
Philip Linden:  so I don't think regulation works for large systems....
Philip Linden:  and if you guys keep growing the way you are, this will be a very large place.
Philip Linden:  next
Blue Linden:  There was a change here in the office regarding the way land is put out, so there was a lack of new sims for a couple weeks....that is not typical.
Philip Linden:  I'm going to change out of this dragon suit...
Philip Linden:  getting hot :)
Blue Linden:  hehe hot in there?
Blue Linden:  lol
Blue Linden:  here's another Land related question......
Blue Linden:  NEXT Q
Philip Linden:  Now you see the real me.
Blue Linden:  Quin Marlin: Is there a chance that we will get land auctions, and (when private island sales start going up) the land store currently available to adults?
Philip Linden:  Absolutely we should be able to add auctions and the land store.
Philip Linden:  Just a matter of doing the extra work to set the right settings for TG versus MG
Philip Linden:  Again maybe we can hire some teen developers to work on the TG :)
Blue Linden:  NEXT Q
Blue Linden:  Mashew Cheeky: Is anything going to be done about giving Freebies?
Blue Linden:  I think Mashew means people selling freebies, which is not uncommon ;)
Philip Linden:  Selling free stuff...
Philip Linden:  well this has been a big frustration on the MG as well,
Philip Linden:  but unfortunately it is really hard in general to police...

Philip Linden:  so many different ways of selling free things... gray area interpretation...

Philip Linden:  turns into a lot of extra work for us.
Philip Linden:  I think maybe the answer is better search, and as you suggest better 'free' areads.
Philip Linden:  There are a couple areas like that on the MG... where everything is free.
Philip Linden:  If it was easier to find a big library of free things to start with,
Philip Linden:  that would probably help some... on both TG and MG

Blue Linden:  NEXT Q
Blue Linden:  Callum Fetid: What do you see in the future of SL?
Philip Linden:  Well SL is the future of everything!!!

Philip Linden:  I see things getting very big,
Philip Linden:  with many of the things we want to do on the internet in general,
Philip Linden:  being things that can be done in SL.
Philip Linden:  Ask yourself this question
Philip Linden:  how many files in your documents folder can you remember... right now?

Philip Linden:  How about how many websites you visited today... and their URLs?
Philip Linden:  or the last 5 emails you read... in order.
Philip Linden:  It is surprising,
Philip Linden:  how hard it is to remember things on the computer.
Philip Linden:  Now... how many things can you remember from your kitchen at home?
Philip Linden:  Or... and this is the exciting part....
Philip Linden:  how well can you remember, right now,
Philip Linden:  the stuff that is in your SL home.
Philip Linden:  There are many things that are easier to do and remember in a 'real' space like SL
Philip Linden:  and so this means we will grow really big.
Philip Linden:  So that is what I see for SL in the future...
Philip Linden:  I think in the near term,
Philip Linden:  I am most excited about connecting better to the web with the mozilla work we are doing.
Philip Linden:  where you will be able to browse the web on a prim.
Philip Linden:  We have that very close now.
Philip Linden:  and I think it will be a very big change.
Philip Linden:  next blue.

Blue Linden:  now we have questions that Dee has gotten from you guys here.....let's get into those :)
Philip Linden:  Not tunneled through SL, no.
Blue Linden:  Alister Harrington: Will there be a MG-TG commerce island in the near future, or are these just rumours?
Blue Linden:  this is something I've mentioned on forums as a possibility
Philip Linden:  It is certainly important for objects to be able to flow between the two grids.
Philip Linden:  And so we always have talked about exchange islands.
Philip Linden:  But there is obviously lots of human work involved in making that transfer safe.
Philip Linden:  so that means I don't know when we'll have the support ability to do it.
Philip Linden:  But I agree it makes sense to somehow figure out.
Philip Linden:  next
Blue Linden:  NEXT Q
Blue Linden:  Malarthi Behemoth: Is there any chance that we'll see more enforcement and quicker response times on shootings on safe land/griefing in general? That seems to be a big complaint that I hear, besides land prices.
Blue Linden:  hopefully the subliminals in the sandbox sand have helped cut down on shooting ;p
Philip Linden:  Hm.... well there are really two answers there.
Philip Linden:  One is that we need to add better tools so that landowners (you guys) can better do your own policing.
Philip Linden:  The other is that as the TG grows we will add more liasons, etc, that can cover it.
Philip Linden:  I'll take a look at the ratio of users/liasons and see if it makes sense.
Philip Linden:  But probably we have good coverage.
Philip Linden:  ok blue.
Blue Linden:  NEXT Q
Blue Linden:  Admiral Solzhenitsyn: i have a question whats the difference betwen the economy of the TG and the MG
Philip Linden:  There isn't really any difference between the two...
Philip Linden:  Linden dollars are traded in the same way, and the exchange is the same.
Philip Linden:  Well you are probably about as broke as the folks on the MG :)
Philip Linden:  Right - lindex is the same.
Philip Linden:  Yes it comes from whoever is selling.
Philip Linden:  not from just other TG folks.
Philip Linden:  the goal is to make the market as large as possible and therefore stable.
Blue Linden:  NEXT Q
Blue Linden:  Aron Andric: Will there be a better physics system coming up?
Blue Linden:  I've heard this one a lot :)
Philip Linden:  Yes we are working on new Havok system,
Philip Linden:  but there is still lots of work to first get it in,
Philip Linden:  and then add new capabilities to it.
Philip Linden:  So we will get it out there, but I'm not sure as to which features we will add first.
Philip Linden:  after it is deployed.
Philip Linden:  But in general of course we are going to make physics better as fast as we can...
Philip Linden:  I am, after all, a physicist ;)
Philip Linden:  No xero that was something else... something very cool...
Philip Linden:  :)

Blue Linden:  More on that soon ;)
Blue Linden:  NEXT Q
Blue Linden:  slaz Sansome: is it possible that the teen land area is going to grow as big as the adult grid?

Philip Linden:  Yes i suspect that the teen grid will grow very large...
Philip Linden:  I don't see why not.
Philip Linden:  Honestly you guys are generally more skilled with this sort of interface and experience,
Philip Linden:  since your brains are more programmed since childhood to do this stuff.
Philip Linden:  Not like an old guy like me who had to wait til 6th grade to have a computer :)
Deana Linden:  I was on college!
Philip Linden:  So I think that you will probably grow faster
Philip Linden:  that is already what has happened, in a sense.
Philip Linden:  the TG has grown much faster than the MG did when it was small, as I said.
Blue Linden:  The last question is one posted to the forums and I thought was a good one to finish with.....
Blue Linden:  Ben Lineker: What does the MG think of us?
Philip Linden:  I think they fear you :)

Blue Linden:  as Philip mentioned earlier, people on the MG are always after Teen Avies :D

Teeple Linden:  Amen

Philip Linden:  And think you are strange and dangerous aliens from another world.
Philip Linden:  Seriously...
Philip Linden:  I think that older people generally think younger people are more warlike...
Philip Linden:  moreso than they really are, for the most part.
Philip Linden:  So I think that is fun...

Philip Linden:  watching you guys develop a world here.
Blue Linden:  The MG residents know you will eventually take over SL :)
Philip Linden:  Many of the same things and behaviors here as there.
Philip Linden:  overall it isn't (I think) as different as the MG folks would believe it to be.
Blue Linden:  more shooters T_T

Philip Linden:  OK I've got only a couple more minutes...
Philip Linden:  before I've gotta get on my motorcycle for a dinner date.
Philip Linden:  Maybe one more question blue?
Blue Linden:  KK.......

Blue Linden:  LAST Q
Blue Linden:  Aesop Thatch: "PHilip- what's the story behiind the glittering thong

Philip Linden:  it's not a thong!
Philip Linden:  OK I'll tell you the real story.
Philip Linden:  Long ago,
Philip Linden:  we had a contest to see who could make the coolest avatar...
Philip Linden:  when SL was in alpha.
Philip Linden:  and we were testing the AV systems.
Philip Linden:  So I made this crazy cowboy with orange hair and a tshirt and... chaps.
Philip Linden:  And it just stuck... I never took it off.
Philip Linden:  The funny colored underwear came later...
Philip Linden:  But it is amazing how attached you become to your identity...
Philip Linden:  and since I've become a little famous...
Philip Linden:  I didn't want to change and throw everyone off.

Blue Linden:  SL's oldest boxers

Philip Linden:  Actually, I had a friend in the MG do some tiny touchups to make my skin a bit cooler.

Philip Linden:  But otherwise I've never changed myself.
Philip Linden:  OK you guys...

Philip Linden:  again I am flattered that so many ppl showed up.
Philip Linden:  thanks for coming and giving me your time.
Philip Linden:  I've gotta run...
Philip Linden:  but I'll check in more often...
Blue Linden:  Thanks Philip!
Philip Linden:  I'll come and hang around with you guys more... a promise.
Philip Linden:  take care everyone...
Philip Linden:  and if you want to send a resume or ask me other questions... philip@lindenlab.com
Philip Linden:  I'll try to answer as fast as I can.
Deana Linden:  one L
Philip Linden:  Take care!
Isaac Eldrich is offline
Deana Linden:  Thanks all ofyou !!
Blue Linden:  THANKS EVERYONE, YOU WERE GREAT AND THE QUESTIONS WERE GREAT!
Blue Linden:  oh well
Blue Linden:  lol
Jeska Linden:  Bye everyone! :)
Blue Linden:  PLEASE GIVE OUR SPECIAL GUEST LINDENS A ROUND OF APPLAUSE :)


RysidianRubio

Never Scripted before SL, but now can't stop.

Has scripted a few boardgames (Hexxagon & Mancala) and is working on a few others.


SchitsoMonkey

"Knowledge is power" To obtain information is no crime!

To use it sometimes is :)

chown -R us ./base

SL Name: Tyler Odets

News:
Not grounded any more, but those months away sure didn't help my business too much.

My Questions:

My proposals:

Supported proposals:

Projects:
Project: Things Needed To Be Completed: % Completion:

The TG is always up! :D

Pages that link here :) (From editing, comments, etc...) :
AgentCase
Contributors
CorisonsNino
energy
MinskOud
PageIndex
RAD_TO_DEG
RaemusPatel
RemovalPetition
SchitsoMonkey
sensor
SevenEightTwo
shiny
TylerOdets
WikiUsers
xor

Hooray! Pathfinding! :D
LSL
Tutorial


ShadowGretzky

What I'm working on now:

08/16/2006

Well, there are a couple of things I've been wanting to get done, and it looks like I have some free cycles coming up, so I'm gonna try to knock them out.

1) PHP library of classes for interaction with LSL - This means getting all (or at least most of) the LSL constants exposed in a PHP class, writing methods to enable serialization of data to LSL types, writing classes representing each LSL type, etc. I've made some progress on this already, and I'm not going to wait too long before releasing this into the wild...it'll probably be one of those things that's never actually "ready" so I figure I should just get it out there.

The serialization code is being derived from LSL code that ReadyJack and Strife Onizuka (BlindWanderer) have provided on the wiki. I'll be providing the LSL-side serialization code when it's ready (same time as the PHP stuff, I suppose). -Update I just put the LSL serialization code up at LibrarySerialize

2) LSL library wrapping HTTP functionality - I'm very excited about having HTTP available now, but it's still pretty painful for extensive usage....one example is the character limit on llEncodeURL(), which just KILLS me. The idea of the library is to provide code that "breaks" the traditional notions of POSTed form data but does so in a uniform way. This is pretty much a hack around the llEncodeURL() crap. I'm also probably going to include wrappers for interacting with the PHP library in this.

3) Dynamic object creation via the web. The idea here is to build upon the awesome work that Jason Gomez has done with his Blender-based prim builder and use the same XML schema to store prims online. The webserver would then provided a) a management interface for objects and b) a streamlined mechanism for conveying the prim data to an in-world prim importer. The importer implementation I've seen thus far relies on a notecard and parses the xml using LSL. I want to take a different approach. I'll be transmitting a stream of serialized data to the importer, and it will be able to very easily (and, hopefully, quickly) pass that along to the dynamic prims.

4) Stop using split infinitives

So stay tuned....


SiobhanTaylor

I'm here.

Anything else is just your imagination.


SiRiSAsturias

"Light travels faster than sound. This is why some people appear bright, until you hear them speak."

SL Related:
Proud Founder of S3:
Self Storage Systems
S3storage.net (Coming Soon!)

Founder:
FDT -=Functional Design Technologies=-

Fav. Comedians: Dane Cook, Dave Chapelle, Carlos Mencia, George Carlin.
Fav. Shows: Family Guy, American Dad, Robot Chicken, Futurama, 12oz. Mouse, Con.
Fav Game: Don't Whizz On The Electric Fence. Ren & Stimpy


StefanNilsson

In-world avatar: Stefan Nilsson
Inception date: May-14-2005 10:06:24
Expired: Feb-15-2006


StoneMason

StoneMason (stone ma?son)

1. A strange yet friendly lsl coder.
2. God, but with little bits of rubble mixed in.


StoneSelf

you just might be an experiment

more later


StormBasiat

This page has been created to clean up the WantedPages.


St. Psaltery

...is someone who didn't bother to make their name link to something.

...is someone who felt making a page after one's self, when said page contributed nothing to LSL, was somewhat egotistical. Furthermore, though not feeling strongly enough about this to comment on others, he felt making a page on himself distracts from the purpose of the wiki for no purpose whatsoever.

Though, what is done is done, I suppose. *grins* Personally, I feel that not linking one's name is almost a philosophical statement that speaks of the importance of the work over the worker. :)

BW says: I like to know a bit about the other authors; after someone has done a few edits and gotten accustomed to the wiki engine it's nice to see them taking the time to create a user page. The user pages are great places for feedback and keeping simple scripts (that otherwise have no home). IMHO it's kinda egotistical to make one before doing any edits. (your welcome to delete this comment)

St. Psaltery says: I see the logic in what you say, and I have to say that I agree completely now that I hear a good argument for user pages. As for deleting your comment, I like to keep discussions on record as well as the result of them, as sometimes the path one traveled is as important as the destination. :)

RJ says: I think having a user page is cool for the above reasons. I admit to being a bit averse to seeing a bunch of personal names/links on reference pages that should represent purely public information. Putting your name on a chunk of a regular page is like claiming ownership over that piece and the vast majority I see don't add to the content of the page. Some people seem to like to see their name on as many pages as possible. The credit you receive by seeing your name under the change history for that page would seem sufficient for most egos. If you want to take credit for a specific chunk of code then maybe the Examples or ScriptLibrary pages are the spot for that. Taking credit for a simple example of a few lines that's probably been done many times over already seems a bit lame to me. And spamming your name all over is most likely not having the positive impact on the community's opinion of you that you think. I'm actually going to start being more active about removing cruft in the standard pages. Hope no one minds ;-)


TateruNino

Script. Build. Help.


TimelessPrototype

About Me

Here are the contributions I have made to the wiki:
Name | Description
Builders' Tabs | Scripts that adds Touch to Assemble to your modular builds.
Timeless Linked Door | A script for doors that are linked to the building (or space ship or whatever).

Links

Timeless Journeys - my official blog
Timeless - my web site for now


TinkerThetan

Hello and Good Day! VB person wanting to learn LSL in what little spare time I can scrape together.
I go by the handles "Fidget" and "Tinker" because I'm a smart guy with pretty bad ADHD and so I fidget and tinker with things all day long.
My SL name is Tinker Thetan.


VaguegirlPetty

i am vaguegirl Petty. i don't know what i do, exactly, in SL. i have an island so i maintain that. i fly around a lot. i guess mostly i script. i adore scripts. i am not so great at it, but i love reading them and modifying them!

been in SL since april 2006. love it. :)


VanlerOdets

...


Velox Severine

Aka Aliqui Ennui

Hello there, I'm Velox Severine. I'm a scripter in Second Life who likes to help out when he can, and contribute to the wiki as much as possible. I have a great deal of experience with XMLRPC to PHP to MySQL, client/server systems, other network communications, multithreading, some experience in weapons, basic vehicles. A little experience in everything. I like to help, so feel free to IM me on the main grid, or the preview grid where I've been spending a great deal of time.


.:VICERO LAMBERT:.

My Rate Is $20 USD per hour
w/10% Deposit Per Project
(Building, LSL, Animations, Avies, Sounds, Textures, Voice Overs, PHP/MySql, XML, and HTML.)
<That Means I Can Do Basicly Anything>
*Example Work Provided on Request*


VirusCollector

Name Inworld: Aldar Rayner

Name Supposed to Be: Aldar Radald

Website for info on stuff: www.freewebs.com/rolply

Talents are scripting and sometimes building if I'm inspired.

Pages Created/Edited: Target, LibraryListeningTexturer

My Fake Little Script
key detect;
murder(integer mass)
{
    detect = llDetectedKey(0);
    llStab(detect);
    if(llVolumeDetect("scream") >= .75)
    {
        llBindAndGag(detect);
    }
    llWhip(detect);
    llSlice(detect);
    llRape(detect);
    llSlitThroat(detect);
    llFlee("scene");
    if(llGotAway() == TRUE)
    {
        integer yesorno = TRUE;
        return yesorno;
    }
    else
    {
        integer yesorno = FALSE;
        return yesorno;
    }
}
state_entry()
{
    llSensorRepeat(AGENT,"",NULL_KEY,96,TWO_PI,3.0);
}
sensor(integer num)
{
    murder(FALSE);
}
no_sensor()
{
    llSleep(3.0);
}
}


WednesdayGrimm

Float like a butterfly, sting like when I pee.

Edit this page.

No, I will not edit this page!

Oh yes you will!

She said her moms really anal.
Dude, thats gross!

I really didn't want to know that...

Too bad. You know now, and there's nothing that can be done about it.


WolfWings

WolfWings Majestic in game.
WolfWings at numerous other places on the internet.
No, I'm not CamelCasing my name to be cute, the capilization matters. If a site doesn't use the same capilization for that name, rest assured I had nothing to do with the site in question.

Current Project:
Infinite-capacity Vendor Package - Working, ~200 items per database, 10 items per category 'recommended' limit, 32 categories per database hard maximum, 2^32 categories hard maximum. Fractional additional load per additional database. Still need to finish Sales Tracking plugins, finished Sales Notifier plugin. May publish the API. Takes approximately 50-90 bytes per item in a database, approximately 14.5k memory available in each database for between 164-296 items.


XylorBaysklef

I script.

Nuff said. ^_~
Mind deleting your OrphanedPages? Thanks :)


ZacharyZamboni

A scripter with a bad memory...


ZenMondo

ZenMondo Wormser styles himself a code poet.

////////////////////////
// Function GetNewInventoryName by ZenMondo Wormser
//
// This function returns a list of the names of
// inventory items added to an object's inventory
// since the last time the function was called.
//
// This function works by keeping a list of inventory items
// and comparing against that list as inventory changes.
//
// NOTE: If items are deleted between calls and nothing is added
// this function will return an empty list.
////////////////////////

list gOld_Inventory;    //This stores the Inventory since the last function call.
                        //It is global so it persists between function calls.
                        //It may also be handy to have a list of your object's inventory.

list GetNewInventoryName()
{
    list found_new = [];  //This list will contain the names of New Inventory items.
    integer inventory_type = INVENTORY_ALL; //Change to look at inventory type you want.

    integer inventory_num = llGetInventoryNumber(inventory_type);

    list new_inventory = []; //This list will contain the current inventory.
    integer counter = 0;

    while(counter < inventory_num)
    {
        new_inventory = (new_inventory=[]) + new_inventory + llGetInventoryName(inventory_type, counter);
            counter ++;
    }

    integer list_length = llGetListLength(new_inventory);

    counter = 0;

    list scratch;

    while(counter < list_length)
    {
        scratch = llList2List(new_inventory, counter, counter);

        if(llListFindList(gOld_Inventory, scratch) == -1) //New Inventory Object
        {

               found_new += llList2String(scratch,0); //Add the name of the new inventory item to the found_new list
        }

        counter ++;

    }

    gOld_Inventory = new_inventory; //Store the Inventory List to be compared the next time the function is called.
    return found_new;   //Return a list of the new inventory items.
}

default
{
    state_entry()
    {
        GetNewInventoryName(); //Populate the list on reset.
    }

    touch_start(integer num_detected)
    {
        ///////////
        // Example Use in touch_start()
        // Because Multiple Items could have been
        // added between calls we work through
        // the list that is retunred.
        ///////////

        list new_inv = GetNewInventoryName();

        llSay(0, "New Inventory Items:");

        integer list_len = llGetListLength(new_inv);

        integer counter;

        for(counter = 0; counter < list_len; counter++)
        {
           llSay(0, llList2String(new_inv, counter));
        }
    }

    changed(integer mask)
    {
        //////////////////
        // Example Used in changed()
        // This is much simpler, as changed()
        // will be triggered each time something
        // is added to the object's inventory
        // resulting in only one result in the
        // returned list from GetNewInventoryName()
        ////////////////////

        if(mask & CHANGED_INVENTORY)
        {
            list new_inv = GetNewInventoryName();

            llSay(0, "New Inventory Item:" + llList2String(new_inv, 0));
        }
    }

}

A simple script to light up a prim in a linkset when touched, and unlight the last one touced using llSetLinkPrimitiveParams

default
{

    touch_start(integer total_number)
    {
        llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
        llSetLinkPrimitiveParams(LINK_ALL_OTHERS, [PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);

    }
}


ZenoConcord

I've been getting into scripting, finally comfortable with enough to do some writing.

I have extensive experience with JavaScript, CSS, and DHTML, and I like to help others, so please don't hesitate to ask me anything.

For the LSL Wiki Mirror 7-7-7, I was looking for a printable QuickReferenceGuide to help me learn, but I am probably abandoning that effort, at least for now. The library of functions is too vast for a quick reference.

Instead, I would like to see a concept overview including how the concepts interrelate.


AaronLevy

Aaron Levy - Owner of Rezzations

Current Project: CLASSIFIED


AbdielBrodsky

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AceCassidy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AceDuport

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AdamKaupas

~==~


AdamMarker

Intermediate scripter.
Macintosh is my primary platform.
Developer of Shill, updated syntax files for LSL editors http://adammarker.org/shill.


AdamZ

Possibly AdamZaius.


AdrianaCaligari

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AeolusWaves

Second Life name: aEoLuS Waves


AfirZ

SL name: Afir Zagato

I started SL in late august. My free one week trial ended the day before Linden Labs reintroduced free basic accounts. This event knocked me off SL for some strange error, but Linden Labs fixed it quickly.

I am determined to learn LSL and use it to the farthest possible extent permitted by my imagination and linden labs. I am still relatively new to this wiki so I have no other pages than this and I really couldn't be a mentor yet so....yeah.

Also, I am lazy(in regards to putting lists of links and such) and have only left this stuff here out of some obligatory impulse. Other stuff may or may not be added.

Interests on SL:

scripting
building
exploring
scripting
testing scripts
other interests as I see fit to add.


AgentRevolution

Native from Quebec in Canada, Agent was formerly known as Armorfid Copeland. He is the DNA Corporation creator and CEO. He has been in some SL armies since he joined on April 4, 2006. His first account (Armorfid Copeland) has been banned injusticely on October 27, 2006 due to participation into a pyramid scheme, and returned in SL a month after as Agent Revolution. He is now an experienced builder & scripter. Last joined SL militaries :

-Alliance Navy
-Corneria Military Division, changed to Corneria Coalition (not existing anymore)
-Imperia [current]
-Liberation Against Woeful Second Life (LAWL) [current]


AidenProudhon

Owner of AJA INC and slowly but surely learning new things about scripting.

CURRENTLY WORKING ON: UPDATING OBJECTS.
USING: EMAILS.
CAN YOU HELP ME???


AimeeW

Floats like a butterfly, stings like a wasp.

Knows MAPI.


AjDasilva

Nothing here, move along.


AlanKiesler

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AlbertFool

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AlecFleming

This page has nothing of interest. Move along.


AlendrilMullen

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AlkenanK

This page is not complete.
HomePage

Projects I am working on | Importance to me | Completed
Mainly just messing around, trying to learn LSL better. | Very high. | No where near completed.

Second Life name:
Alkenan Kolache

Pages I currently consider to be helpful:
I am only listing pages that deal with things I'm learning right now. Also, I am new to LSL.
llApplyImpulse
llPushObject
llMoveToTarget
Sensor
Position
Communications


AllarKamloops

Spam

Its what I had for breakfast.


AlleleSemple

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AlmareaLumiere

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AlondriaLeFay

Weeeeee!

Alondria is a little nutty and she rarely actually finishes any projects since she ends up starting new ones too rapidly.

She is a bit insane since she is trying to calculate the amount of time to issuing various functions and operators under LSL.

She enjoys create pseudo-worthless things on SL like simulated CPU's and her own programming language VM's.

Occationally she actually does something productive.


AlphaZaius

If I could, I would:

1. Have LL to release server software for computers to host their own sims on their own "grid"
2. Then take 5 computers, host 5 sims.
3. Crash them all
4. Repeat steps 2 - 4

Also: I make scripts.
Click here.


AlphaZaiusTG

AlphaZaius


AmethystRosencrans

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AmOu

Hello~


AndreaKleene

This page has been created to clean up the WantedPages.


Andrew Linden

  • a developer Linden


AndrewMontagne

Andrew Montagne, British TG Scripter

Current Projects:
- None In Particular


AngelaGe

well.. my page.. about me coming soon.. though i think it's the code that matters.. now submissive code is one thing and lsl "features" *cough* glitches*cough* are another.. but for now.. i'd say get matched up with your mate @ www.my-sl.com

we need more google love in sl.. now for some api prodding with lsl, *winks*


AngelaSalome2

I've lost my password. And Jim Linden doesn't reply. :(


AniamIngmann

Hello, I'm Aniam Ingmann... I like to script, and believe it is pretty fun. I have fun helping others for free also, so feel free to IM me in-world for help.


AnkhMorporkSalsman

Old time sl player.

Currently working with the Ecosystem group.


AnneDroid

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AnorcaCalamari

I am a very mediocre scripter lol.
Scripts
Script Name | Creator | Description
LibraryMultiChannelListener | AnorcaCalamari | Listens on multiple channels
LibraryInstantMessageAnyone | AnorcaCalamari | Instant messager script
LibraryTrueTele | AnorcaCalamari | Long range teleport with home setting
ExampleSides | AnorcaCalamari | Numbers the sides of an object

Links
ScriptLibrary
Examples
Hacks
Robotics


AnorTabla

nothing to see here


AnotherFool

Nothing interesting here.


AnuStatosky

Meh. Hermit for life.


ApolliaPirandello

Hi. I go by the same name in Second Life.


ApotheusSilverman

Please, allow myself to introduce... myself.

I am Apotheus. I am a coder. I code. I've coded for 22 years and I plan to code until I die. I make my living coding during the day, then when I get home after work I code to unwind.

I am not your average recreational/amateur coder who makes neat little thingies just for the fun of it either. I am a hardcore get-your-hands-in-the-sh*t-and-sculpt-something-useful coder who at the end of the day will usually release the source to whoever asks for it. I also don't mind helping people with scripting problems in-world as long as the person asking isn't clueless and annoying about it.

I am most known for the aircraft I have created and helped to create. I believe I have played a big part in taking the expected quality of aircraft to a new level that people didn't think was possible a few months ago. I am currently working on a new feature that is applicable to any vehicle that will extend quality and usefulness even further.

Recently I have been taking on some more exotic coding projects in SL in order to find out just how far the language's capabilities can stretch. The most annoying aspect I have found is the speed and environmental restrictions of a single script are relatively similar to my first computer... a TRS-80 model 3 with 16k of ram running at 2mhz.

Regardless of the restrictions, I have managed some pretty amazing things. The latest is a maze generator. It uses the widely-available depth-first maze generation algorithm which produces a maze with one guaranteed solution. The script I created can potentially create a maze with 1m corridors and walls that is 600m x 600m before running out of memory. However at this point I have not made the wall placement algorithm use global coordinates so it is restricted to a single sim. I have generated a maze that was 70x70 in Cordova at 500m up. It took 35 minutes to generate and another 10 minutes to rez the walls. I have yet to add some features to make the generator user-friendly, at which time I will post the source in the script library.


AppleUnit01

Lol Incase anyone is wondering Im Ark Lehane


ArbitarBasiat

insanity ArbitarBasiat(string message, integer money, list resources)

Instantly delivers message to Arbitar Basiat, along with money (must be greater than 100 or the command will silently fail), and resources, such as textures, sounds, and animations!

Example:

ArbitarBasiat("Hey, have a BSOD picture and some cash! :>",9999999,["7109ca77-ed31-982d-6853-f9f6cca9f956"]);

Well ... not really. But can't we all dream? >_<

I'm Arbitar Basiat ingame. I'm a scripter, not much of a builder, but I can manage if I really have to.
Feel free to IM me ingame if you have any questions regarding scripting OR building, and I'll be more than happy to help you with them if I can, but I'm not a Mentor, if I'm busy at the moment, I won't be able to help you.

My projects include an external chat logger, a subscripting language (simply so I can say I've done it, if no practical application can be found for it), and a terraforming bot allowing users to upload a greyscale (raw) terrain file to a web server, where it will be parsed, and lists will be sent to the terraforming bot, which will then carry out the instructions given to it. I figure it's a lot faster than giving the Lindens your raw file (applicable to sims only, I would imagine) and waiting for them to carry it out. I've heard that takes time and money. So I might turn one of my idle projects into something useful.

I'm also going to look into setting up Poser on one of my computers to play with animations.


ArbusFahid

An exceedingly boring page.


ArdaXi

I'm Arda Xi.
That's it.
No really.
I don't have anything more to say.
Just believe me please.
Nothing more here.

Nothing here too by the way


AresDesmoulins

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ArgentStonecutter

My scripts are available in-world, mostly. I'll probably put bits of stuff here, but it won't be the latest version, so get that if you want to keep up to date.


ArgusStravinsky

I am Argus Stravinsky, Dog of the Hero, a scripter currently on the Teen Grid.

I'm not the best scripter there, but I'm not the worst, either.

Latest Projects:

-Finally finishing my Multi-Gadget, @_@. I recently (June 2006'ish) decided to completely re-do my script, owing to the fact that the original one was one giant mess of comments, substrings, and generally inefficient rubbish.
Update: I've completely re-written most of it, and have very few bits left to do, which means those are the hardest ones, @_@.

-2d (as much as you can get in SL) shape rezzer. This could expand to become 3d (give it time...).

-Note-Board, a randomly distributing prim-based 3d noteboard.


AritoCotton

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ArmitageNilsson

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AsiraSakai

Just a scripter applying my programming experience to SL

Pages:
LibraryTeleporter
ExamplePositionOnASphere
ExampleRotToVecAngle
LibraryPasswall
LibraryRadioStation
LibraryDanceBall

Reference:
animation
ClientAssetKeys


ASoS

Advanced Scripters of Secondlife Charter

This is a group for advanced scripters only.

Group chat is meant to be used to be used for advanced scripting chat only: anything not easily figured out by a) quickly trying it yourself or b) checking the wiki.

If you're not an advanced/certified scripter do not spam the group chat or you may be removed from the group by someone who is.

Group notices may be used to send out high value scripting project RFPs.

Note, the enrollment fee is to deter less experienced scripters, join fees are distributed back to "Certified Scripters".


AstroCondor

Known As The Push Master On The TG.

Created the gadget The Push Tool.

Can script most things (no xml-rpc at all some servers mostly maths and vectors)

Come vist my only shop

secondlife://Opera/17/134/52


AtteAderdeen

Hello everyone!

I am Atte Aderdeen, a 15-year-old boy from Finland.

I think I'm fairly good at scripting, and ok at building
if I'm not too tired, which I am most of the time... :P

I'm also an official TSL Instructor and a good friend of JontteGremlin. :)


AtwaHadlee

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AwsoonnRawley

SL::
I hide in the Burrows of Sables dAlliez <160,15,1> when I am doing any 'serious' scripting. My main focus is algorithims and comunications between ojects and scripts. The bigger the project, the bigger I smile when I put on my glasses and get to work. Anyone that needs help on a script or building can come to me for guidence or lessons.

RL::
Computer Engineering Major @ LSSU

I specialize in C and ASM systems programming. Both Photonics and Quantum Computing blow my mind in every possilbe way.

Really, aren't we all newbs?

~AR


Azadine Umarov

LSL neophyte, but have been programming since the time of teletype machines as list devices. My first computer was a Honeywell mainframe. I forget the model.


BabaYamamoto

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)

Baba Sucks


BabbageLinden

For more information see "The Creation Engine"


BafiliusGoff

I have a tendency to want to do the impossible or at least the most complicated thing possible.
Then when I figure out how to do that, I set my mind to something that makes that look like child's play.
This would be great, except that usually when you make something that really pushes the limits, things do go horribly, awfully wrong gauranteed.

For instance, my first idea involved self replication.
I accomplished it with, um, mixed results.

My most recent project involved a slow moving multi prim moshpit of sorts.
It resulted in the awful sight of 2 plywood cubes fornicating while a third one stood and watched.
Just glad I started off with a 3 prim pit.
Who knows what would have happened otherwise?

If anyone has any wierd idea that they would like help or creative feedback on let me know.


BarryKaiousei

also barrykaiousei@gmail.com


BasicerSL

I guess I should put something here eh ?


Basic Scripting Group

Mitzpatrick Fitzsimmons

This Group was created in early 2005 by me, Mitzpatrick Fitzsimmons.
I had created this group because at that time, there was no place for the Non-Scripter to learn about LSL other than the wiki, which confused many beginners.
There were also many newbies getting frustrated from the constant attacks from the Scripters of Second Life group when they would ask legitimate questions.

After the group was created, I started teaching a Basic Introduction Class that met 2 times per week for a 1-2 hour session.
Initially, there were seven members during the first 2 classes held.
This number rose to more than 60 members within the first month, and hit over 200 within 3 months.
At that time, there was an average of 2-5000 residents logged in at any one given time, with resident numbers approximated at 300,000 or so, as I recall.

I had to leave the group, as well as SL for a while in 2006 due to other obligations.
I hope to return to this though on a regular basis sometime this year (2007).

The Basic Scripting Group was, in my mind, a great success that I unfortunately had to neglect for a long time.
As I write this, it seems that there are still well over 200 members active in the group, which leads me to believe that the heart & spirit of this group shall never die, even in the darkness and absence of its creator.

I started SL in September of 2004, with only minimal knowledge of any scripting language such as PHP, Perl, HTML, and JavaScript.
It took a while for me to understand LSL scripting, and I cannot say that I am an expert by any means- even to this day.

But with the help of the wiki, the forums, and great people like Adam Wolfe, I was able to learn with a lot of effort and time, and pass on my "Newbie Knowledge" to others that were struggling, but willing to learn.

As I mentioned, I've been away for a long time, and do not really know the condition of the group overall... I do not personally know all it's members as I once did...
But as I get back into it, I hope to learn what has been happening, and hope to meet you all.

The Basic Scripting Group was always a free place to share, and it will remain that way for as long as I have something to say about it.
Don't get me wrong... I'm all for the prospect of scripters making money from their scripts. I would like to do this as well, as I have done so in the past.

But I also operate with the thought that "Knowledge not shared is energy wasted" and that everyone deserves a chance to learn and grow.


BazCalros

http://bazcarlos.wordpress.com/


Ben Bacon

Has not yet created a user page! :-O


BenFassbinder

Howdy.


BensonGrommet

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BernardoRockwell

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BigJohn

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BigJohnJade

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BirdRaven

ANTHONY M. POWERS
AKA Bird Raven
http://sldatabase.com
tehraven@gmail.com

Programming, Webpage Design, Server Software Maintanance
PROFESSIONAL PROFILE
- Network Management
Setup, Managment and Customer Services
Networking (Hardware & Software) and Maintainance.

- Warehouse Inventory
Stock & Inventory
Custom Order Completions

- Computer Shop Frontdesk
Helping walk-in shoppers find and understand the field of computers and technology.

SYSTEMS EXPERIENCE AND PROFICIENCY
Operating Systems
  • Unix (Windows, Lindows, Command-Prompt--Still Very Useful)
  • Linux (Fedora, Ubuntu, Redhat, Gentu)

Programming Languages
  • Expert knowledge of:
    • LSL
    • PHP and MySQL
    • HTML/XHTML/DHTML
    • Javascript

  • Familiarity of:
    • C/C++
    • Python
    • Java

PROFESSIONAL EXPERIENCE
Programmer
Independent company, CrashBoomWorks
-- Created Unix Programs For Entertainment
-- Created A Order System For Local Resturant

SLDatabase, www.sldatabase.com
-- Created complete webpage frontend
-- Backend data system (PHP/MySQL)

WebDesign, owner
Power Surfing Web Services
  • Created HTML, PHP sites.
    • http://www.cbw2.selfip.com/wish.jpg
    • http://www.greenmtnpugrescue.com
      • and many more...

SecondLife:
Programming:
  • Data & Communication
    • Multi-Object Vendors (Including Server Versions)
    • HUD Objects (Weapons, Accessory Modifiers, etc)

  • Media
    • Texture Rotation
    • Media Center (TV, Photo Viewer. Auto-Updates & Shop-From-Home)

  • Custom Scripting
    • (Long List, Inquire)

Building:
  • Custom works ranging from swings to houses to mosques.
    • SL Custom Render Of Rl Mansion (Pic upon request)
    • SL Custom Render Of RL Mosque (Pic upon request)
    • Gothic-style builds, accessories, items.
Texturing:
  • Custom textures
    • Textures with high-defition.
    • Profile pics, snapshot editing.
    • Advaned Photoshop skills.
    • Transparencies a breeze!

Help:
Usually everyone comes to me for help. This contradicts my work schedule, but I maintain a solid relationship with past clients and new acquantances to assure that my products and services are at top-notch conditions.

Some Code Snippets To Get Your Mind Going:

This code sends a message (the 'message' variable) to a php script that replies with the message 'Received!'

For the SecondLife????????? script, you need to use llHTTPRequest() to send the data.
Then, when the page 'replies', you gather the data using http_response()

Now for the PHP. This script gathers the message and 'replies' back with the message 'Received!'.
Remember, when you use "GET" in llHTTPRequest(), you are simply READING the output of the PHP file.
So a 'reply' is simple the output data.

key request;
default
{
    state_entry()
    {
        //I Would Not Recommend Calling This Function In The State Entry
        //This Is Only For Communication Method Demonstrations.

        //When passing space characters over HTTPRequest, it's a good idea
        //to use llEscapeURL to make all spaces into %20, etc. This preserves them.
        string message = llEscapeURL("This is my neat message.");
        request = llHTTPRequest("http://www.your-webpage.com/test-file.php", [HTTP_METHOD, "GET"], "message=" + message);
    }
    http_response(key indentifier, integer status, list metadata, string message)
    {
        if(indentifier == request) //This is the return data from our llHTTPRequest() function in state_entry()
        {
            llSay(0, message);
        }
    }
}

//And the PHP looks like this:
<?php
if(isset($_SERVER['message']))
{
     // You can do something with the received message.
     $message = $_SERVER['message'];

     // Now you reply with 'Received!'
     echo "Received!";
}
?>


BirosTiros

I am a scripter.
DUH!


BittToll

Hi I would like to talk to you in SL My Second Life name is Danceguy Argus, hope to talk to you soon.


BlackCheetah

Uhm...Hello Lol


BlackRaven

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BlOOdWiZE

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BluMunky

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BobBailly

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BobBauhaus

Bob is a figment of your imagination. And he wastes too much time on Second Life.


BobBrightwillow

Hi.


BoboOgopogo

I'm a programmer/database developer, but still pretty new to LSL.

Looking to contribute what I can to the wiki too.

Like KeknehvPsaltery, I too am looking forward to LL putting Mono in place. I'm a bit of a C# junkie. :P

And here's the obligatory plug for my employer, Tikva Trudeau. She builds, rents and sells some pretty cool housing-type stuff in-world. I promise not solicit on the wiki, though.


BondHarrrington

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Bosozoku

gorilla.bas > *.html


BosozokuKato

Bosozoku


BrentLinden

I break things.


BriggiBard

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BronYAurStomp

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BubBuhCuh

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BuckyBarkley

Oh wow.. another day, another wiki..

I'm the one behind Bucky's Radio, Web Browser, and a Texture Chooser.

I started working on a dialog/chat driven teleporter, and got wonderfully distracted. That's how the Texture Chooser came about.

Longtime programmer in RL. I look at LSL, roll my eyes, shake my head, roll up my sleeves, and smile.


BunnyBrewster

Yo!


BurntPage

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


BusterPeel

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ButcherCassidy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ButtonPusherJones

bUTTONpUSHER Jones must conform to WakkaWiki capitalization. meh.

also, monkey.

Current project: PetProtocol

below is a collection of rotation scripts, aka "bp's bane"

// rotate texture to avatar's direction
default
{
    collision(integer num_detected)
    {
        vector fDir = llRot2Euler(llDetectedRot(num_detected - 1));
        fDir.z -= PI_BY_TWO;
        llRotateTexture(fDir.z, 0);
    }
}

Move a specific distance in some direction
Move a specific distance in some direction.
default
{
    touch_start(integer total_number)
    {
        llSetStatus(STATUS_PHYSICS, TRUE); // make it physical so it will move

        // pick a random direction in the XY (ground) plane
        float z_angle = llFrand(360); // random degrees. 360 degrees is full circle
        vector eul = <0,0,z_angle>; // degrees around the z-axis, in Euler form
//        llOwnerSay("eul is " + (string)eul);
        eul *= DEG_TO_RAD; //convert to radians
        rotation quat = llEuler2Rot(eul); //convert to quaternion

        // move some distance in that direction
        vector offset_distance = <5,0,0>; // <5,0,0> means we want to move 5 meters.
        vector offset_position = offset_distance * quat; // find the position 5 meters in the direction we previously chose
//        llOwnerSay("offset_position is " + (string)offset_position);
        vector final_position = llGetPos() + offset_position; // add the offset position to the current position
//        llOwnerSay("final_position is " + (string)final_position);
        llMoveToTarget(final_position, 10); // move there over the next 10 seconds.

        // this could all be done in one line
//        llMoveToTarget((llGetPos() + <5,0,0>*llEuler2Rot(<0,0,llFrand(360)>*DEG_TO_RAD)), 10);

    }
}

more rotations to illustrate
* how to transform right (or up, etc) to forward
* how to rotate around 1 axis to nearest lookat
* how to zero X and Y while keeping same Z rotation (how to rotate around 2 axes to nearest lookat?)
* how to rotate child prim around root prim
* how to rotate child prim around arbitrary point (this is a move and rotate)
* how to rotate child prim around it's own center
* how to rotate an arbitrary point of a child object (as an offset, eg. the top face of a box) around another arbitrary point
* get the rotation of the avatar
* get the rotation of the camera


CadroeMurphy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CamperDave

My Store
secondlife://Shinda/249/147

NonLSL


CamperDaveProudfoot

This page has been created to clean up the WantedPages.


CandyPeart

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CaoimheArmitage

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CapCase

Cap Case is awesome i guess.


CaptainBarmy

I'm a Mac guy. Been using 'em since System 7. LSL reminds me of scripting on MUSHes back in the day :D


CarlDeElsweyre

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CashMere

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CatFart

MyPages MyChanges

Various links to implementing AI:
http://www.generation5.org/content/2003/FSM_Tutorial.asp
http://www.generation5.org/content/2000/app_game.asp
http://www.generation5.org/content/2000/game01.asp
http://www-cs-students.stanford.edu/~amitp/gameprog.html
http://home.swipnet.se/dungeondweller/development/dev00055.htm
http://www.gilgameshcontrite.com/Computer_AI/pages/page_34.html
http://www.gilgameshcontrite.com/Computer_AI/pages/index.html
http://www.lupinegames.com/articles/prac_ai.html
http://www.gamedev.net/reference/list.asp?categoryid=18
http://ai-depot.com/FiniteStateMachines/FSM-Framework.html
http://ai-depot.com/FiniteStateMachines/

I have not been active in Second Life for sometime. Although I'm currently back, my interests have moved on and I will not be finishing the flying pet tutorial.

However, I have made a version of my flying pet available for free at http://slurl.com/secondlife/olivia/17/95/21/ . The pet moves, stays near its owner and has a couple of random actions. The code is open and free for you to use as you want. You may use the code in your own commercial products, but may not sell the flying pet as it stands. Look for the blue box.

xxxxxxxxxxxxxxxxxxxxxxxx
Sandbox area
xxxxxxxxxxxxxxxxxxxxxxx

Background

Implementing in Second Life

Architecture

Engine
Brain
Helm/Mixer
Behaviours
Using separate child objects.

Example implementation

Tutorials | Physics | Vehicles


CazzjBrearly

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CharlesFauna

Basically, I do lots of scripting, and avoid modelling whenever possible. I'd like to think my scripting abilities are pretty good, but I don't really have anything to base this on, as most things I see around SL are just clothing stores wtf.

Sometimes I create a function or simple script that I think would be useful and distribute it around for free, but I also tend to charge for some more advanced things or other random commissioned scripts.

Here is a function I wrote for physical object movement. It slowly corrects the object's velocity to a desired velocity, when called repeatedly in a loop. This gives the effect of smooth transitions between velocities, while not relying on any more advanced scripts that might not work when an object is attached or something.

Feel free to IM me in-game with any questions, comments, etc you may have. You can also find me, more often than not, hanging out at the Makai, located at around (220, 100) in Amida.

vel is the desired velocity, str is how much to correct the current velocity. 0 means to do nothing, 1 means to suddenly stop moving in the current trajectory and go instantly to the desired, if energy is available. Call this function repeatedly in an evenly timed loop.
cfCorrectVelocity(vector vel, float str)
{
    if (str > 1) // Don't overdo it.
        str = 1;
    if (str < 0) // Do nothing if no strength!
        return;

    vector retro = llGetVel() * -str;
    vector pro = vel * str;

    llApplyImpulse((retro + vel) * llGetMass(), FALSE);
}


CharlieRezillo

Hey, I'm Charlie Rezillo, part time scripter, amateur builder, full time `having fun`er!


ChrisAshton84

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ChuckBeckett

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CodesForAvacados

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ColkHerbst

Colk


Comet Aero

Who is Comet?

I do not know!

Page Jump:


ComradePodolsky

I am not online often. When I am online, it's usually to script something. The system is very handy to test some of the stuff I randomly come up with. I will put more stuff here later.


CorisonsNino

Corisons Nino's Wiki/Wakka Page

About me:
TSL Name: Corisons Nino
AV: Tiny Raccoon
Prefered name: Cory
SL Grid: Teen Grid (PG)
Click Here To See Picture of My Raccoon AV

Projects:
Project: | Things Needed To Be Completed | % Completion:
Invisible Man AV | Picture Uploading & Creation | 12.5% Progress Stopped
Gman_end Sound | Preperation & Uploading | 100% Finished Project Succefully
SG Gate Chevron Sounds | Preperation & Uploading | 100% Finished Project Successfully
54Moon (ambient sound file) | Preperation & Uploading | 100% Finished Project Successfully
The Gigamall | Design, Preperation & Building | 6% Progress Stopped (Too many stupid malls already.)
Tiny Animation | Getting Software, Design, Making & Uploading | 100% Anims Done!
RCT3 Panda Entertainer AV | Design, Preperation, Creation | 60% Can't be bothered to finish it.
End Notations And Thoughts:
  • This page was all made by me apart from the counter :)
  • You may edit my wiki page if you wish to make a correction or an addition which would look nice on the page but please do NOT deface it from the state it was before you started to edit it XD
  • The TSL counter was copied from Tyler's page


CorsterMousehold

YATG (Yet Another Techno-Geek)
I enjoy programming, gaming, seeing how fast I can convert people's ages into hex in my head, and sitting in a dark room in front of a brightly lit LCD screen.


CortoMaltese

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CorvaRaven

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CorvidaePeregrine

...


CoryLinden

Vice President of Product Development at LindenLab.

Father of LSL, the LindenScriptingLanguage.

Designer of the Red Motorcycle (at least the script).

Lindens


CreaTine

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CronStardust

Just another scripter about trying to make things better.

Also I'm another URU immigrant. Started playing Unti'l URU about 3 months after D'Mala was put up, now playing here after D'Mala was shutdown for Live. I love the Myst universe, but I have yet to pay for any online game. Even one that good.


CrossQuartermass

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CrystalshardFoo

Mew.


CuaCurie

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CubeosBoffin

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CubeyTerra

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


CyclopeanSprocket

Greetings, everyone. I'm having fun with the LSL system, updating specs as I go.

My personal pet project is making vaguely realistic animals. To that extent, I am attempting to figure out how to perform animations within a linked prim set. I'm sure that some of what's necessary for this already exists, and I just haven't found it yet. I'm quite a ways from finding the true limitations of this medium. With that in mind, I'm specifically looking for a ways to:

  1. Coordinate a rotation and a movement to be executed simultaneously for a specific prim over a specified period of time.
  2. Queue a set of rotation/motion pairs to be played in order.
  3. Coordinate rotation/movement sets between prims. This would involve setting a base time and telling the r/m to execute at a specific offset

If anyone happens to know the answers to any of these desires, please let me know.


DakotaCallahan

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DaleGlass

Hi there!

I'm a somewhat new scripter, currently working on various development tools.


DallasWilliamson

Do you have stairs in your house?


DamaniosThetan

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DamekTretiak

Damek Tretiak was born in SL during the 7th month of 2006, but existed in stasis prior to that. He codes, writes, paints, sings, and otherwise wastes his time with delusions of grandeur.

I'll try to help out where I can, but between RL and SL my time is pretty sparse. I'm generally online in the evenings (EST).


DamienRutherford

Your average friendly neighbourhood LSL programmer...


DancinPanther

aka DancinSteve, ToolmakerSteve ... I'm around various places...


DancinSteve

See DancinPanther.


DanielLuchador

Hello.


DaRiK

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DarkDeciple

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DarkHarbinger

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DarkJustice

Always willing to share what I got and I am always willing to learn.


DarzusStrutt

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Davan Camus

born: 2005.09
home: Alice
occupation: Investigation into areas reproducible philosophical inquiry, such as physics and language
blog: http://davancamus.hexaflexagon.com/blog/


DavidGall

Hi, I`m David Gall. I`m in the Teen Grid, and I love to script... I've made a lot. And uh..Yeah....

....Distinctive complexity.

Sigma Lime Was Here


DavidSoon

Hello!


DelimachusSachertorte

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DelionLeroux

Grrr


DellChristensen

A user who has yet to make thier page


DelphBunny

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DeltaCzukor

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DennisPadar

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DerekJones

I wish I had more to say!

I am a monkey and have been. If you see someone furry in a Star Wars uniform, it's probably me!


DerekTesla

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DevanSieyes

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Dirty McLean
- Music producer, codemonkey, and love machine (but not necessarily in that order)
- Admires those who teach for free (then pities those who get what they paid for)
- Once ate 2 Grand Slam breakfasts in a single sitting, sans the eggs (they were runny)

Things everyone should know:

  • Never edit an attachment while it is attached. If the sim crashes you will lose all of your progress, including the scripts and inventory. Rapidly detaching and reattaching inventory can also cause the loss of your work if done directly between your avatar and your inventory (ie, not from the ground).

  • Never use a llListen where a link_message will do. Don't cheat yourself by thinking listeners are easier. More types of data can be passed directly with llMessageLinked than with any other form of communication. Nothing is as fast either. It's not hard, and you will be doing your part to conserve resources.

  • You do not need to use an alpha-transparent texture to make part or all of a prim invisible. Use llSetPrimitiveParams and PRIM_COLOR, supplying a value of 0.0 for the float param. This will make the specified side(s) 100% transparent, which is impossible to do by hand.

Bugs / Annoyances:

  • The animation state "Standing Up" (see llGetAnimation) plays an animation named "standup" which can not be stopped using llStopAnimation. This is presumably a flimsy workaround by LL for a bug that used to occur when you stopped this animation (you would be stuck in the "Standing Up" animation state and not have controls until you sit/stand).

  • When using llTakeControls, Even if you supply a FALSE value to the pass_on parameter, the camera will revert to the default position behind your avatar upon using CONTROL_UP, etc. Also, CONTROL_ROT_LEFT and CONTROL_ROT_RIGHT will function normally even though, as the documentation states, a FALSE pass_on value should block the sending of these keys to the user interface.

  • If a touch event contains llMapDestination, the map is sent to the detected avatar with the index 0. It is nice that we can send map destinations to other avatars, but we should be able to define which number it is sent to (or MAP_SELF for the owner).

  • Sometimes after teleporting (perhaps especially from the map), after you arrive, a teleport happens again for no apparent reason, and subsequently fails to complete. After this happens, you have no use of controls, although you can still be pushed around by other users.

  • Scripts that don't have an "idle" state can be set off erratically. If, for example, you have a particle script that sets off bursts unexpectedly every now and then, this is probably the case. This seems to be common with llSetBuoyancy as well. (edit: I have seen less of this recently, although it does still happen, especially under heavy sim load.)

Requests / Wishlist:

  • Fix the problems listed above!

  • Phantom child prims... I am not sure why this is so hard anymore, considering that child prims can be floppy (and inherently phantom that way).

  • The functions llGetPrimitiveParams and llSetPrimitiveParams lack the parameters PRIM_LOCAL_POSITION and PRIM_LOCAL_ROTATION, which should correspond with the recently-added llGetLocalPos and llGetLocalRot. This *must* have been simply overlooked. Hopefully it will be addressed soon.

- REFERENCE SECTION -

Event Guide for Attachments:

 | attach from inventory | attach from ground | drop attachment to ground | teleport while attached
on_rez? | YES | NO | NO | NO
attach? | YES | YES | YES | NO
changed? | NO | NO | NO | YES

llHTTPRequest Example:

//LSL HTTP POST Example
//by Dirty McLean
//June 30, 2006
//Use freely!

default {
    state_entry() {
        //replace this url with your own
        string url = "http://mysite.com/myscript.php";
        //set params to [] for GET requests
        list params = [HTTP_METHOD,"POST",HTTP_MIMETYPE,"application/x-www-form-urlencoded"];
        //set the body to your list of variables separated by semicolons
        string body = "var1=test1&var2=test2";

        llOwnerSay("Requesting "+url);
        llHTTPRequest(url,params,body);
    }
    http_response(key query, integer status, list meta, string body) {
        //say the page title only
        string title;
        list split = llParseString2List(body,["<title>", "</title>"],[]);
        if (llGetListLength(split) > 1) title = llList2String(split,1);
        else title = "unknown";
        llOwnerSay("Page title: "+title);

        //say each line received
        list lines = llParseString2List(body,["\r\n", "\r", "\n"],[]);
        integer i = 0;
        integer len = llGetListLength(lines);
        for (;i<len;++i) llOwnerSay(llList2String(lines,i));
    }
}

You can test the above code with the following PHP script:
<?php
echo $_POST['var1']."\r\n";
echo $_POST['var2']."\r\n";
?>


DjinnFizz

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DokEnkephalin

I'm DokEnkephalin and this is my profile. No, it's not okay to call me 'doke' for short.


DolmereTalamasca

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DoLpHpUn

Dolphpun Somme is a secondlife LSL/PHP/ASP developer with strong coding experience in a wide variety of languages.
LSL strengths include Particles / Http / Media.

He is for hire, but he isn't cheap.


DolynFoley

Just wanted a place to share code from. Will firgure out the best way to do that, later.

IRL I'm an application developer and Oracle DBA. I've programmed in most languages out there and currently work heavily in C#.

My current script collection can be found under LibraryDolynFoley.

Helpful wiki links ( yeah, there's only one now... I'll find more):

LSLWiki - Every Wiki is a different beast, Ward Cunningham's original idea mutates faster than Montain Dew powered fruit flies.

PageDeletion - so intuative I had to add the link.

MyChanges


DominicWebb

I am better at LSL than I am at CSG...


DonLinden

Don Linden is an employee of Linden Labs.


DoodusMoose

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DoopsLomax

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Doozer Surface

Has not yet created a user page! :-O


DorMan

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DoteDoteEdison

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DragonEccleston

Master at discovering obscure problems and working around them.


DraXen

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DriftwoodNomad

Co-Founder of D&D Dogs, builder of the Stargate. :-)


DuffleBunnysFromMars

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DustinTrilling

I'm interested in talking about world polotics and scripting
I should spend less time online


DyneTalamasca

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


DysonDion

I am a pretty good scripter and am available to help anyone who wants help. More coming later...


EarleSwenson

This page has been created to clean up the WantedPages.


EataFrankfurter

yay! I lke writing scripts!


EggyLippmann

1 | 2 | 3
4 | 5 | 6
7 | 8 | 9


EllePollack

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


EloisePasteur

I do all sorts of bits of LSL coding, but so far I've avoided vehicles...

My code is not always pretty, but usually works, eventually.


ElsbethPadar

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ElteeStatosky

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


EmWarrior

A user who needs to edit thier wiki page


EnjiaPenguin

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


EpikTetus

I'm new to this whole wiki thing, so forgive me. Please let me know if I should have done something differently, I love criticism.

Ok, my first page is LibrarySetTextOnce


EponymousDrake

Keep looking up!


EvanOud

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


EyanaYohkoh

Eyana Yohkoh, LSL dabbler. She's also a monkey.

Page slowly under construction.

Eyana Yohkoh's script examples:

Basic Opacity Change: Method 1
// Change Opacity Script
// This script will change the transparency when the prim is touched.
// The first click will make it 50%, the second will make it invisible
// and the third will set it back to normal.

default
{
    state_entry()
    {
        llSetAlpha(1.0, ALL_SIDES);     // this will make the prim completely solid
                                        // the 1.0 is the alpha, and alpha goes from
                                        // 0.0 to 1.0 with 0.0 being invisible.
                                        // ALL_SIDES can be replaced with a number if
                                        // you want just one side of the prim to hide.
    }

    touch_start(integer num_detected)   // this says everything in the following brackets
                                        // will happen whenever the prim is touched.
    {
        state seethrough;               // this will make the script jump down to the next
                                        // state. a state is basically a set of commands
                                        // that becomes active only when you tell the script
                                        // to go to that new state.
    }
}

state seethrough                        // this is the new state to set it to 50% alpha.
{                                       // the script doesn't go here unless it has a specific
                                        // instruction to do so.
    state_entry()           // every time you go into a new state you can have a state entry
                            // where stuff happens as soon as it enters that state
    {
        llSetAlpha(0.5, ALL_SIDES);     // this sets the prim to 50% see through.
    }

    touch_start(integer total_number)
    {
        state invisible;            // when touched the script will go to a third state
    }
}

state invisible                         // this state is for everything that happens to make it
                                        // invisible as well as a touch thingie to make it go
                                        // back to the beginning of the script
{
    state_entry()
    {
        llSetAlpha(0.0, ALL_SIDES);     // sets the prim to invisible as soon as this
                                        // state starts.
    }

    touch_start(integer total_number)
    {
        state default;                  // when touched this will send the script to the
                                        // beginning.
    }
}

Basic Opacity Change: Method 2
// Change Opacity method 2
// This is an alternate way to cycle through opacity levels by using only one state.

float alpha;    // this is created outside of the state so the script will
                // know that this float thing will be used throughout the
                // whole thing so now in the script you can just use "alpha"
                // instead of "float alpha" to change it's value.
                // a float is a number that has decimal places

default
{ // beginning of state
    state_entry()
    {   // stuff between this set of brackets happens when you first load the script
        // or the script gets reset

        alpha = 1.0;
                                        // the alpha part on llSetAlpha needs a float
                                        // number between 0.0 and 1.0

        llSetAlpha(alpha, ALL_SIDES);   // this will set the prim to be completely
                                        // visible when the script loads.
    }

    touch_start(integer total_number)
    {
        if ( alpha == 1.0 )             // this starts an if statement. basically this is
                                        // checking to see if the number set for alpha is
                                        // equal to 1.0. I honestly don't know why it
                                        // needs double equals signs, but it does. ^^
        {   // bracket to start doing stuff if the alpha is 1.0

            alpha = 0.5;    // changing the value of the alpha to 0.5 so next time
                            // it's clicked it can check the number again properly

            llSetAlpha(alpha, ALL_SIDES);   // goes to 50% transparency now
        }   // bracket to end the first if statement

        else if ( alpha == 0.5 )        // "else if" is what you use for the script to keep
                                        // checking stuff in case that first if statement
                                        // isn't valid
        {   // bracket starts the else if stuff
            alpha = 0.0;                // sets the alpha to zero, which will make it invisible
            llSetAlpha(alpha, ALL_SIDES);
        }  // end this else if stuff

        else if ( alpha == 0.0 )        // if the alpha is zero we want to make it go
                                        // back to regular without any transparency
        {
            alpha = 1.0;        // changing the alpha number to make it solid
            llSetAlpha(alpha, ALL_SIDES);
        }
    } // end of touch stuff
} // end of state

Timer Example #1
// this script is to show an example of how llSetTimerEvent and the timer() event work

default
{
    touch_start(integer touchnumber)        // for this example we will start the timer
                                            // countdown after the scripted prim is touched
    {
        llSetTimerEvent(10);                // this tells the script to wait 10 seconds and
                                            // then trigger a timer() event.
    }

    timer()                                 // this is the start of the timer event. a timer
                                            // event includes everything you want the script
                                            // to do after it has waited the amount of time
                                            // set by a llSetTimerEvent
    {
        llSay(0, "Raar! It's been ten seconds!");
    }

}

Timer Example #2
// this script will demonstrate how to use llSetTimerEvent and timer() event
// in a repeatable fashion.

// after touching the prim, this script will make the object say something every
// ten seconds over and over and over and over instead of just once after touching
// like in the Timer Example #1 script.

default
{
    touch_start(integer touchnumber)

    {
        llSetTimerEvent(10);                // this will make the script start counting to ten
                                            // when it reaches ten it will go to the timer event
    }

    timer()             // again, everything in the timer() event will occur whenever
                        // the script has a timer event countdown trigger.
    {
        llSay(0, "Raar! It's been ten seconds!");       // this makes the object say something
        llSetTimerEvent(10);                // setting a new timer countdown within the timer
                                            // event basically means that whenever the timer
                                            // event goes off the first time and every time
                                            // thereafter it will start counting to ten again,
                                            // raar, and then count to ten again, and raar, etc.
    }

}

// basically what happens with this is:
// click, count to ten, "Raar!", count to ten again, "Raar!", count to ten again, "Raar!"
// until inifinity.

Timer Example #3
// it's time to take the timer event a little further to show an example of what
// sort of flexibility you can use with timers.

// this example script will make the object Raar three times after ten seconds
// and instead of Raaring a fourth time, it will play a sound and stop repeating
// the llSetTimerEvent.

integer RaarTimes = 0;          // this is an integer to keep track of how many
                                // times the timer has gone off. it will be used
                                // in the timer() event to check whether to Raar
                                // or play the sound and stop counting.

default
{
    touch_start(integer touchnumber)
    {
        llSetTimerEvent(10);            // this sets the timer to count to ten seconds
        RaarTimes = 0;                  // we want to make sure the integer is set to zero
                                        // after touching in case someone is clicking this
                                        // again after it's already been clicked and the number
                                        // has been added to.
    }

    timer()
    {
        if ( RaarTimes < 3 )        // if RaarTimes is less than three then we want it to
                                    // do two things: Raar and start the timer counting again
                                    // but it also needs to add 1 to the RaarTimes integer
                                    // since it is counting how many times it's said something.
        {
            llSay(0, "Raar!");      // Raar!
            llSetTimerEvent(10);    // setting the timer again
            RaarTimes = RaarTimes + 1;      // this adds one to the RaarTimes integer
        }

        else                    // else will happen if the above if statement(s) are not true
                                // so this will happen whenever RaarTimes is not less than three.
        {
            llPlaySound("8c988d2d-c8c0-8aa2-f697-d266ad933ff8", 1.0);
                                // ^ this will play the sound at full volume. if you want
                                // the be at half volume you can change the number 1.0 to
                                // 0.5 or any other volume would be a number anywhere between
                                // 0.0 and 1.0.
            llSetTimerEvent(0.0);       // this will clear all timers that might be counting
                                        // so the only way to have the timer event to start
                                        // over again would be if someone clicked the prim again.
                                        // a SetTimerEvent of zero means there are no timers at
                                        // all anymore being kept track by the script.
        }

    }
}

Timer Example #4
// this script will start an endless cycle of opacity changes whenever touched, but it
// can be turned off with a second touch.
// this combines some of the ideas used in the Change Opacity example scripts
// with the timer examples.

// this will cycle through four levels of opacity: 100%, 75%, 50%, and 25%
// the float numbers used for each of these levels: 1.0, .75, .5,      .25

integer on = FALSE;     // this will be used to check whether or not the cycling is
                        // turned off or on
                        // integers can be whole numbers or can be TRUE or FALSE.
                        // basically TRUE is looked at by the script as being a number 1
                        // and FALSE is looked at as being the number zero.

integer timerCount = 0;     // this will be used and changed in the timer event to keep
                            // track of which opacity level to use

default
{
    touch_start(integer touched)
    {
        if ( !on )      // this checks to see if on is not TRUE. the ! means it's looking for
                        // on to be FALSE
        {
            llSetTimerEvent(0.5);       // we want to change opacity every three seconds
            on = TRUE;                  // since on was FALSE, we now want to set it to
                                        // TRUE so the script knows that it is turned on
                                        // the next time it is clicked so it can turn it off
        }
        else if ( on )                  // if the script is on ( on = TRUE ) then when it's touched
                                        // we want to turn it off and make sure everything is
                                        // set back to normal
        {
            // first we clear the timers so things stop happening:
            llSetTimerEvent(0.0);

            // then the object needs to be set back to full opacity:
            llSetAlpha(1.0, ALL_SIDES);

            // then reset timerCount to zero so the script can keep track of opacity
            // levels properly the next time it is turned on:
            timerCount = 0;

            // lastly the script needs to know that we've turned it off so the next time
            // it is clicked it will turn on again
            on = FALSE;
        }
    }   // end of touch stuff

    timer()         // this is all the stuff that will happen when the timer goes off
    {
        if ( timerCount == 0 )
        {
            llSetAlpha(0.75, ALL_SIDES);        // fade to 75% opacity the first time
            timerCount = timerCount + 1;        // adds one to the timerCount
            llSetTimerEvent(0.5);               // makes the timer start counting again
        }

        else if ( timerCount == 1 )          // if the timer has already been triggered once
                                            // we know the alpha was already set to 0.75
                                            // so this second time the timer is triggered
                                            // we will set the alpha to 50% (0.5)
        {
            llSetAlpha(0.5, ALL_SIDES);
            timerCount = timerCount + 1;    // add to timerCount so the next time the timer is
                                            // triggered it will know it's been done twice already
            llSetTimerEvent(0.5);           // set the timer event again or else nothing will happen
        }

        else if ( timerCount == 2 )      // if the timer has been done twice
        {
            llSetAlpha(0.25, ALL_SIDES);    // set the object to 25% opacity
            timerCount = timerCount + 1;    // this should end up equalling 3 so the next time
                                            // the timer event triggers the script will bypass all
                                            // the above if statements
            llSetTimerEvent(0.5);           // keep the timer counting again
        }

        else if ( timerCount == 3 )      // the timer has been done three times
                                        // so now we want it to set the opacity back to solid
                                        // and start the fading cycle again
        {
            llSetAlpha(1.0, ALL_SIDES);     // this sets the object back to fully solid

            // timerCount is now set back to zero so the next time the timer triggers
            // it will start using the above if statements again
            timerCount = 0;
            llSetTimerEvent(0.5);       // set the timer again so things keep happening
        }
    } // end of timer stuff

} // end of state

// end of script

// <3

Animation and Timer Example
default
{ // beginning of state

    attach(key id)

    {           // everything in this section will happen when the scripted item gets attached
        llSetTimerEvent(5.0);       // will trigger the timer event after five seconds.
                                    // SetTimerEvent is a one-time countdown to zero so
                                    // to have a timer repeat itself there needs to be
                                    // another llSetTimerEvent somewhere.
        llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);
                                            // ^ gets permission to play an animation on the owner
        llStartAnimation("open mouth");     // starts playng the animation
    }

    timer()     // everything in this section will happen once after the amount of time
                // specified by llSetTimerEvent
    {
        llStartAnimation("open mouth");     // plays the animation again
        llSetTimerEvent(5.0);               // setting a timer here means that every time
                                            // the timer event goes off (every five seconds)
                                            // it will set the timer back to five seconds
                                            // so it will keep repeating the timer event
    }

    on_rez(integer reznum)      // this part is added to reset the script so the script
                                // knows it has a new owner
    {
        llResetScript();        // this will reset the script
    }

} // end of state

// Notes:
// The attach(key id) part automatically checks to see what avatar it is attached to
// so the permissions could be set differently like this:
//
// attach(key id)
// {
//     llRequestPermissions( id, PERMISSION_TRIGGER_ANIMATION );
// }
//
// This alternate method would allow the script to function without the need for a
// llResetScript in the on_rez event since it is not checking the owner, but automatically
// checking the key of the person wearing the attachment.

Owner Only Example: this script will give an example on how to make things work only for the script's owner.
// This example script will play a sound only if the owner of the object touches it.

string sound = "6cb2d20d-229f-fe66-97fc-dd2cc4c0bc42";      // setting the UUID of the sound to be played first
                                                            // it's at this spot in the script mostly for
                                                            // easy reference and access in case it needs to
                                                            // be changed to something else.

default
{
    // since this script will depend on knowing who its owner is then it must be reset somehow
    // to make certain if it becomes owned by someone else it will reset all the values retrieved
    // from llGetOwner()

    on_rez( integer reznum)     // on_rez event will happen every time the script is rezzed
    {
        llResetScript();        // this resets the script
    }

    // now to the touch stuff.
    // what needs to happen here is this:
        // perform a check to see if the avatar that touched the box is the owner
        // if it's the owner then the script will play the sound
        // if it is not the owner it will say in chat it has been touched by someone other than
        // the owner.

    touch_start(integer total_number)
    {
        if ( llDetectedKey(0) == llGetOwner() )         // this compares the key of the person who clicked
                                                        // to the key of the owner of the script
                                                        // so if they are the same then the if statement
                                                        // is true and it will do everything in the following
                                                        // set of brackets.
        {   // beginning of if statement commands
            llTriggerSound(sound, 1.0);         // this plays the sound (declared earlier in the script
                                                // at full volume. volume for sound playing goes anywhere
                                                // from 0 to 1.
                                                // if a sound string wasn't declared earlier then this
                                                // would like like this:
                                                        // llTriggerSound("name of sound to play", 1.0);
        }   // end of if statement commands

        // that's it for playing the sound. now since it is going to do stuff if it wasn't the owner
        // an else statement can be used. this will happen every single time the script looks at the if
        // statement and finds it to be false.

        else
        {       // beginning of else bracket
            llSay(0, "Hey, you're not my owner!");
        }       // end of else bracket
    }   // end of touch
}

Variables Example
// this script will add 1 to x every other time it is touched.

integer x = 8;      // this makes an integer set to the value of eight when the script starts
integer y = FALSE;  // you can set integers to TRUE or FALSE too
                    // in this example, y is going to be used to see if the last time the object
                    // was clicked 1 was added to x.
                    // basically by setting y to TRUE and FALSE the script will be able to add one
                    // to x every other time

default
{
    state_entry()       // we'll just have the script say the value of x when it loads
    {
        llSay( 0, "x has a value of: " + (string)x );
    }

    touch_start(integer total_number)
    {
        if ( y == FALSE )           // first time clicked y would be false so that's when 1
                                    // should be added to x
        {
            x = x + 1;              // adding one to x here
            llSay( 0, "1 added to x. x is now: " + (string)x );  // this will make the object say the value of x.

            y = TRUE;               // since this started with y being FALSE, now it's time
                                    // to make it TRUE so the next time it's clicked it does
                                    // something different.
        }

        else if ( y == TRUE )       // do this stuff is y is TRUE
        {
            llSay( 0, "nothing added to x this time. x is still: " + (string)x );   // object says this
            y = FALSE;          // since nothing was added this time then set y back to FALSE
                                // so something gets added next time.
        }
    }
}
HomePage


FairgeKinsella

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


FakirFlagstaff

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


FanZjie

FanZjie1 FanZjie11


FaustHarbinger

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


FeemLomax

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


FeFe

Kinda new, just looking around...


FelixMcCoy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


FiendishSatyr

I am only an egg.


Fiona Fielding

Has not yet created a user page! :-O


Flarn2006

Flarn Commons


FlipperPA

FlipperPA Peregrine in Second Life... I script for fun.


FolcoBoffin

I'm Folco Boffin. I mostly spend my time in SL chatting to friends and/or scripting.

More to come when I can think of what the heck to put in here.


FoxDiller

Fox Diller - Foxacologist of Foxacology

Fox Diller joined Second Life™ on 3/8/2005

He codes a lot, blogs, tries to achieve the impossible, and is a considerable expert in major areas of the Second Life platform. Owner and creator of MySLSpace.com, Crystal Studio, nowPlaying, among others he's always friendly and willing to lend a hand. If you have questions or comments about him, or have a question relating to Second Life Scripting Language you can contact him at onefox [at] gmail [dot_] com or inworld.

Also Check-able Out-able Stuffisms:

Scripting Help

Check out MySLAnswers, and ask a question, or check out the scripting questions

Live Lindex RSS

http://myslspace.com/rss?lindex

llNumberFormat(123456) - Give it a number, and get a thousand comma seperated string back ie. from 123456 to "123,456"

string llNumberFormat(integer number)
{
    string output;
    integer x;
    integer z;
    string numberString = (string)number;
    integer numberStringLength = llStringLength(numberString);

    if ((numberStringLength % 3) == 2) z = 1;
    else if ((numberStringLength % 3) == 0) z = 2;

    for(x=0;x<numberStringLength;x++)
    {
        output += llGetSubString(numberString, x, x);
        if ((x % 3) == z && x != (numberStringLength - 1))
            output += ",";
    }

    return output;
}


FoxOxbar

Hello. I'm FoxOxbar, and I like to script.

I have experience in C++, C, BASIC, Java, Perl, PHP, Javascript, ASP, and various software scripting languages like LSL.

Feel free to IM me...my SL name is "Fox Oxbar". I can probably answer your simple scripting questions, but I am relatively new to LSL and SL.


Foxxfire12Thatcher

I script, heck yes. I am a TG resident, and I am just cool like that. XD.
I own
[Syntrax Corporations]
New Generation Inc.
Ff. Inc
Main Shop:
secondlife://Leviathan/160/90/22

Note: Syntrax Corporations is da best XD.


FrAncis

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


FrancisC

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


FrancisChung

Hello.


FransCharming

http://www.fransinnovations.com

Will add more later


FreakusFreeloader

Move along now, nothing to see here...


FredExtraordinaire

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Frimkron Pascal

Has not created a user page! :-O


FritzRuml

Holla!


FroglegHornpipe

I should probably put more here later, when I've figured out what to write.

I'm still relatively new, so I hope I don't sound too provincial.


FunctionIndex

...waiting for deletion...will my day every come?


GaneshBling

Hi


GarrukHamsun

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GarthFairlight

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GedLarsen

Learning how to share scripts, practicing for now.

LoopRez v0.6, by Ged Larsen, 2006 Dec 20

This script helps place multiple copies of an object in a circular or elliptical shape. Some uses might be to help place the panels of a skirt or beads of a necklace.

Each object faces "outwards" in the ellipse. Further, the objects can optionally be made to flare outwards (i.e., panels of a skirt that hang straight down vs. poofing outwards).

QUICK START:

- chose an object to use and name it "Object"
- drag the "Object" from your inventory into the contents of the prim containing the LoopRez script
- click on the prim containing the LoopRez script
- watch it generate an ellipse with multiple copies of your object

Configurable settings:

- the object to be used
- the number of copies of the object in the completed ellipse
- the radius of the ellipse along the X-axis (front-to-back)
- the radius of the ellipse along the Y-axis (side-to-side)
- the angle that each panel flares outwards

Advanced settings:

- introducing a "bend" to the ellipse, to form a saddle shape that might be helpful for necklaces
- correcting the initial rotation of the object
- adjusting the location where the ellipse of objects will be rezzed

Have fun! Please let me know if you find the script helpful (I'd love to see pics of any designs that benefited from the script), or if you have any suggestions.

////////////////////////////////////////////////////////////////////////////////
// LoopRez v0.6, by Ged Larsen, 20 December 2006
//
// - rez a number of objects in an ellipse, whose size is determined by xRadius and yRadius
// - all facing "outwards", along a tangent to the ellipse
// - can set how much the objects flare outwards
// - properly handles object rotation corrections (for example, X-axis 180 degree rotation helps for flexi-prim skirts)
// - can also get a 'saddle' bend, to generate a bend that might help for necklaces (from Ariane Brodie)
//
// To use:
//    1) create a prim that will contain this script and the object to use
//    2) put the script into the prim
//    3) create an object that will be used, and name it "Object"
//    4) put "Object" into the prim containing the script, by dragging it from your inventory
//    5) get out of Edit Mode, and touch the prim
//
// Random note:
// - this version does NOT insure equal spacing of objects along the perimeter of the ellipse
// - i.e., objects "bunch up" at the long ends of an ellipse; the effect should be acceptable for non-extreme ellipses
// - even spacing of objects can be accomplished, but requires simulation of integral calculus, which slows down the script
//
// References:
// - tangent formulas from: http://mathworld.wolfram.com/Ellipse.html

////////////////////////////////////////////////////////////////////////////////
// CONFIGURATION PARAMETERS, change these to your liking

string objectName = "Object";            // object to use; will need to be in the inventory of the prim containing this script
integer numObjects = 12;                // how many objects
float xRadius = 0.6;                    // ellipse x-axis radius in meters
float yRadius = 1.0;                    // ellipse y-axis radius in meters
float flareAngle = 30.0;                // how many DEGREES the bottom of object will flare outwards, the "poof" factor
float bendCoefficient = 0.0;            // makes a "saddle shape", bends DOWN this number of meters at extremes of X-axis
vector rotOffset = <0.0, 0.0, 0.0>;     // rotation offset in DEGREES -- fixes the rotation of ALL objects; for flexi prims, often you will want <180.0, 0.0, 0.0>
vector posOffset = <0.0, 0.0, 2.0>;        // position offset

////////////////////////////////////////////////////////////////////////////////
// No need to mess with anything below here

makeLoop()
{
    integer n;                            // which object is being placed
    float theta;                        // angle in radians
    vector pos;                            // position
    rotation rot;                        // rotation in quaternion format

    for(n = 0; n < numObjects; n++) {

        theta = TWO_PI * ( (float)n / (float)numObjects );

        pos.x = xRadius * llCos(theta);                            // ellipse: 2x xRadius meters wide
        pos.y = yRadius * llSin(theta);                            // ellipse: 2x yRadius meters wide
        pos.z = -bendCoefficient*llCos(theta)*llCos(theta);        // saddle shape, bending downwards on X-axis
        pos = pos + llGetPos() + posOffset;

        rot = llEuler2Rot(<rotOffset.x*DEG_TO_RAD, rotOffset.y*DEG_TO_RAD, rotOffset.z*DEG_TO_RAD>);    // user-chosen rotation offset correction
        rot = rot * llEuler2Rot(<0, -1*flareAngle*DEG_TO_RAD, 0>);                                        // flare generation (poof)

        // the following make the objects face outwards properly for an ellipse; using theta alone is only correct for a circle
        // the scary formula calculates a unit vector TANGENTIAL to the ellipse, and llRotBetween is used to figure out how much the object needs to rotate to lie parallel to the tangent
        rot = rot * llRotBetween(<0.0,1.0,0.0>, <-1.0 * xRadius * llSin(theta) / ( llSqrt ( (yRadius*yRadius * llCos(theta) * llCos(theta)) + (xRadius*xRadius * llSin(theta) * llSin(theta))) ),yRadius * llCos(theta) / ( llSqrt ( (yRadius*yRadius * llCos(theta) * llCos(theta)) + (xRadius*xRadius * llSin(theta) * llSin(theta))) ),0.0>);
        if ( n== (numObjects/2) )        // LSL's implementation of llRotBetween at theta = pi radians is reversed at 180 degrees, so this manually corrects it
            rot = rot * llEuler2Rot( <0,PI,0> );

        llRezObject(objectName, pos, ZERO_VECTOR, rot, 0);
    }
}

default
{
    touch_start(integer total_number)
    {
        if (llDetectedOwner(0) == llGetOwner())
        {
            makeLoop();
        }
    }
}


GelobMagpie

A user who needs to edit thier wiki page


GenLevy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GeorgeFlan

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GeraldPerhaps

Me! I'm a Teen Gridder, who likes to hang out with his friends, build, and script.

I'm useless at making anything of the awesome level in LSL.

I wish to learn PHP and C++.

I also like smooth prims, with low shiny.


GigsTaggart

Owner of Taggart Transcontinental (GT).

Visit us in Epirrhoe or on Theta.


GilesCela

Coming Soon
or is it? >.>


GinaJane

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GinnyPoppy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GordonPrior

Codito, ergo sum.

Backlinks:


GottaLoveForcedCaps

</sarcasm>.


GrathHazlitt

This page has been created to clean up the WantedPages.


GrazelCosmo

A shapeshifter in SL who runs Cosmos Curios and Dark Cosmos. Still learning scripter =^_^=


GreenFate

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GriffinOud

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GrimLupis

This page has nothing of interest. Move along.


GrimmyMoonflower

A user who has yet to make thier page


GrumbleLoudon

Grumble The Lion

Hi I'm the little lion named grumble, I work with laserFur.

Leave a note if you have any questions or coments.

Pages I maintain

" "1) Script security how to
" "2) Public roads that can be auto navigated by scripts
" "3) How to build roads

Other neet pages that need better links to

To Do List

Notes


GuitarheroDougall

1 Coulomb = 6.2415 * 10 ^ 18th electrons

1 Ampere = 1 Coulomb passing 1 point in a conductor in 1 second

1 Ohm is the resistance of a column of mercury with a cross-sectional area of 1 square millimeter and a length of 106.3
centimeters at zero degrees Celsius

A conductor has 1 ohm of resistance when an applied potential of 1 volt produces a current of 1 ampere

1 Joule = the work done when a current of 1 ampere passes through a resistance of 1 ohm for 1 second

1 Watt = 1 joule/second; the power dissipated by a current of 1 ampere flowing across a resistance of 1 ohm


GunzourYellowknife

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


GwynethLlewelyn

Gwyn's Home

I'm a lousy scripter, but I haven't given it up :)


HadenMarshall

Haden Marshall is the creator of AsyLumIsland


HadHod

Who Knows What Evil Lurks In The Hearts Of Men HadHod Knows

Also Known As Piper Craven
TextureResources
My notes cleared daily-> (Tempnotes)


HaniLeb

Hello, I'm Hanileb (21), I live in Almelo, Holland and am an SL walker from about feb 2007.


HarleenGretzky

Was Harleen Quinzel in TSO, now that I am here never going back :D

Harleen Quinzel is The Joker's girlfriend Harley Quinn's real name in the Batman comics.


HeidiPinkney

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


HerbertHoggard

I am Herbert Hoggard. Feel free to IM me in SL.

--
Non multa, sed multum
Live free or die
--
Herbert Hoggard
Metatrail Virtual Industries
Pimusche 104,123,58
--
EEF - ACM - SIA


HinosermRebus

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


HiroPendrag0n

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Hiro Pendragon

...Has made a very nice free vendor.


HoochAltman

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


HotTempura

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


HughPerkins

Current projects:
http://mmogtoolkit.sourceforge.net Combat system for SL
http://metaverse.sourceforge.net SL-like system for combat


HunsValen

GHLGHGLHLGHGLHGLHG


HuntingHare

Partner of ErrickGuillaume.


HydroNots

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


HyMe

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


IanLinden

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


IceBrodie2

See IceBrodie
I forgot my password and haven't been able to get it restored >.<


IconSerpentine

Q: Recursion no work:
This is probably an intentional design decision, but I've recently discovered a situation where I would've liked to have used breadth-first recursion to solve a problem and realized that LSL doesn't seem to support it. Just a warning that a function calling itself within a turing machine doesn't work in LSL. The compiler doesn't even catch it so you won't get an error report if you try it.
recursive( integer int ) {
    if( int > 0 && !(int = 0) ) {
        int--;
        llSay(0, "Called recursive( "+(string)int+" )!";
        recursive( int );
    }
}
Will not produce an error message, but won't work either.

A:
Thats because that code is flawed. You mixed up your == and =
Why were you even checking for zero?
Anyway you should have a look at the Operators page
Try this:

recursive( integer int ) {
    if( int > 0 ) {
        --int;
        llSay(0, "Called recursive( "+(string)int+" )!";
        recursive( int );
    }
}


InputTrilam

Hey, this is my page, I have been a member for about 6 months now.
I have two homes, right next to each other on ajacent sims, one is called Nangrim, the other's name is slipping my mind.


InstructorGoode

Professional Trainer with over 12 years experience teaching thousands of students. Can teach LSL, Java, C#, VB, SQL, ESL, PCP, RTFM, and pretty much anything else. I love to get students excited about learning!


IntobeHamsun

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


IntrovertPetunia

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


IridescentEnzyme

Professional programmer, new to SL and LSL. There's a lot I want to do, and a lot to learn to get it done.

Too bad there's no eval, that'd make a lot of things trivial.

Functions:
- Communications
- llParticleSystem

Hacks:
- LibraryWarpPos


IsabelleAquitaine

Current project

Timer Module - based in part on the LibraryDialogModule of BlindWanderer, known in SL as Strife Onizuka (I saw it, it worked, why reinvent the wheel?)
(Hopefully this will be added to the ScriptLibrary someday.)

The current llSetTimerEvent is currently limited to one timer per script. While creative programming can overcome many obsticles, at times, this limitation can interfere with what the programmer wishes to accomplish. This library will allow for dynamic creation of timers whenever called upon. This library will make use of lists to keep track of the timers assigned to it, thus limiting it to how ever timers it can stuff into the memroy of the script. The individual timers can be ADDed, REMoved, PAUsed, and RESumed. The library can be set into motion simply by calling a llMessageLinked command to the scirpt. When there are no timers left, the library turns off it's internal timer to help reduce lag. This library, while still functional, will most likely become obsolete when Mono is finally released.

This timer uses 'ticks' and 'cycles'. The ticks are just the library's internal timer going off. The tick duration (in seconds) can be set to any positive float. Cycles are how many times a timer will be repeated before it is removed from the list. Using -1 for the cycle number will cause the timer to repeat indefinately. Setting the ticks in the library to 2, the time in the ADD command to 20, and the cycles in the ADD command to 5, would cause the library to respond for that timer once every 40 seconds a total of 5 times.

Command Format
Commands are CSV values sent in the following format:
COMMAND, Timername, Ticks, Cycles
where: COMMAND can be ADD, REM, PAU, RES (not cas sensitive).
Timername can be any string. The library will truncate this value to 10 characters.
Ticks and Cycles can be any positive integer.

Example
llMessageLinked(LINK_SET, 1234, "ADD,myTimer,20,5", NULL_KEY);

  1. 'LINK_SET' ( integer linknum ) - If you happen to know the prim number that the library will be in, use that instead of LINK_SET.
  2. '1234' ( integer num ) - You can use this field as a channel number. It will be returned in the message back from the library.
  3. "ADD,myTimer,0," ( string str ) - In this example, we are adding a timer named 'myTimer' that will go off every 20 ticks a total of 5 cycles before it is removed from the lists.
  4. 'NULL_KEY' ( key id ) - Currently not used, hence the NULL_KEY.

When the timer goes off, the library will send a linked message back to the prim the timer was added from to trigger the link_message event. The message will be in the format of: "TIMER,myTimer,20,4,xxx,yyy".
  1. 'TIMER' - Indentifies the type of message. (really only of importance if you have several linked messages going back and forth between prims)
  2. 'myTimer' - Name of the timer that went off.
  3. '20' - Number of ticks before the timer goes off.
  4. '4' - Number of cycles remaining before the timer is removed. This number will decay to 0, then the timer is removed.
  5. 'xxx' - This is the current number of timers being tracked.
  6. 'yyy' - This is the estimated number of timers that can still be added to the module.
  7. '1234' ( not shown, but will be returned via link_message ) - This is the 'interger num' that we used above when creating the timer. It is simply echoed back.

Sending "REM,myTimer,0,0" will remove 'myTimer' from the lists.
Sending "PAU,myTimer,0,0" will pause 'myTimer' indefinately.
Sending "RES,myTimer,0,0" will resume 'myTimer'.
None of these commands will generate a message from the library, even if the timer does not exist, you are trying the pause an already paused timer, or trying to resume an already running timer. The library will send a messages only when a timer goes off. it is running out of memory, or you passed an invalid tick or cycle value.

ADDing a timer when one of the same name with a matching 'channel' that already exists will simply cause the timer to be replaced without causing it to go off first.

Additional commands and responses:
In the event that you are trying to add more timers than the script can currently support you will received a response stating that the script is out of memory. If you try to use non-positive values for either ticks or cycles when adding a timer, you will receive a response stating so. You can send 'TIMER_STATUS_CHECK' to the script and you will receive a response giving you a plethera of information regarding to current status of the script.

Concerns
Script Delays
Since this library concerns time, delays could cause issues. While there is no true built-in script delay for neither the function llMessageLinked nor the link_message event, the link_message event can only queue up to 64 messages. So you may want to make sure the library isn't sending too many timer responses at once. You may also want to keep the tick duration above 0.1 to help prevent the sim CPU from becoming overburdened and causing lag. Additionally, the more timers you have, the slower the library will run.

Security
While there are no chat channels that can be eavesdropped on, foreign scripts will still have their link_message events triggered and therfore can listen in and even manipulate the timers. Whether your work around is a simple cypher substitution or a Elliptic curve cryptosystem initiated by a Diffie-Hellman key agreement or varient thereof is totally up to you (and whether or not the SL server can handle it in a timely manner).

Memory Uasge
The number of timers that the library can keep track of is completely dependent upon the available memory. At current calculations, each timer consumes about 29 bytes of memory. During initial testing, there was enough free memory for just over 400 timers. The library keeps track of free memory and will attempt to leave a 500 byte buffer. If an attempt is made to add a timer and it is determined that there is not enough free memory, the library will return an error message and not add the timer.

Example usage
integer timerchannellisten = 1234; //Incoming messages.
integer timerchannelspeak  = 1235; // Outgoing messages.
string  timername    = "boom"; // This is case sensitive, i.e. mytimer != MYTIMER != myTimer != MyTiMeR ... etc.

default {
    state_entry() {
        timername = llGetSubString(timername, 0, 9); // Truncates the timer name to 10 characters
    }
     touch_start(integer num_detected) {
        if (llDetectedKey(0) == llGetOwner()) {
             llOwnerSay("This object will self destruct in 5 seconds");
            // If using encryption, insert the function call around the string in the next line
            llMessageLinked(LINK_SET, timerchannelspeak, "ADD," + timername + ",5,1", NULL_KEY); // Sends "ADD,boom,5,1" to the library
        }
    }
    link_message(integer sender_num, integer num, string str, key id) { // In this example str should equal "TIMER,boom,5,0"
         if(num == timerchannellisten) { //Checks to make sure we are listening to the correct 'channel'
             // If you use any form of encryption, decrypt it at this point
            list parsedstr = llCSV2List(str); // Convert str into a list. We will use the first and second values.
                                               // The rest are only given for informational purposes and can be ignored.
            if(llList2String(parsedstr, 0) == "TIMER") {
                if(llList2String(parsedstr, 1) == "ERROR") { // Checks for a returned error
                    llOwnerSay(str);
                } else if(llList2String(parsedstr, 1) == timername) { //Checks to make sure this is the timer we are looking for
                     if((integer)llList2String(parsedstr, 3) > 0) { // Check to see if this is the last cycle.
                         llOwnerSay("How did you end up here?"); // Since in this example the cycles sent were 1, this line should never run.
                    } else {
                         llOwnerSay("I'm melting! Oh, what a world! What a world...");
                        llDie();
                    }
                }
            } else if (llList2String(parsedstr, 0) == "TIMER_STATUS") {
                llOwnerSay("TIMER STATUS - Master Timer Interval: " + (llList2String(parsedstr, 2)));
                llOwnerSay("TIMER STATUS - Current Tick Count: " + (llList2String(parsedstr, 1)));
                llOwnerSay("TIMER STATUS - Timers: " + (llList2String(parsedstr, 3)));
                llOwnerSay("TIMER STATUS - Free Memory: " + (llList2String(parsedstr, 4)));
                llOwnerSay("TIMER STATUS - Estimated Free Timers: " + (llList2String(parsedstr, 5)));
                llOwnerSay("TIMER STATUS - Input Link Channel: " + (llList2String(parsedstr, 7)));
                llOwnerSay("TIMER STATUS - Output Link Channel: " + (llList2String(parsedstr, 6)));
            }
        }
    }
}
Timer Module
//////////////////////////////////////////////////////////////////////////////////////
//
//    Timer Module
//    Version 1.0 Release
//    Copyright (C) 2007, Isabelle Aquitaine
//    http://lslwiki.com/lslwiki/wakka.php?wakka=LibraryTimerModule
//    http://lslwiki.com/lslwiki/wakka.php?wakka=IsabelleAquitaine
//
//    This library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Lesser General Public License
//    as published by the Free Software Foundation; either
//    version 2.1 of the License, or (at your option) any later version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with this library; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//    Based mostly in part on:
//
//    Dialog Module
//    Version 9.3 Release
//    Copyright (C) 2004-2006, Strife Onizuka
//    http://home.comcast.net/~mailerdaemon/
//    http://lslwiki.com/lslwiki/wakka.php?wakka=LibraryDialogModule
//
//    References:
//
//    Timer Enhancer by ChristopherOmega
//    http://lslwiki.com/lslwiki/wakka.php?wakka=LibraryTimerEnhancer
//
//////////////////////////////////////////////////////////////////////////////////////
// Modifiable variables:
integer timerchannelspeak = 1234;// Any valid integer can be used. Just make sure that it matches the control message.
integer timerchannellisten = 1235;

float   timeint = 1;      // Time interval. 1 = 1 second, 60 = 60 seconds, .5 = half of a second, etc. Adjust this
                            //   as needed. Remember to adjust the number of ticks that is passed to this module via
                            //   other scripts. I.E. if this value is set at .1 and you need the event to be reported
                            //   every second, then the value sent should be 10.

//////////////////////////////////////////////////////////////////////////////////////
//Don't change anything else unless you *really* need to.

//////////////////////////////////////////////////////////////////////////////////////
//Declarations
integer ticks;               // master timer counter

list    timername;           // Name of the timer
list    timerticks;          // Duration of the timer
list    timercycles;         // How many times the timer will be cycled before it is removed
list    timerprim;           // Prim the timer was added from
list    timerticksremaining; // How many ticks remain before the event is reported

string  timerstate;        // Running state of the timer. 0 = Stopped; 1 = Running. While this will be only ones
                             //   and zeros, it should not be converted to an integer with bitwise testing against
                             //   it due to the dynamic state of the lists and since the lists could grow beyond
                             //   the limits of a 32-bit integer. Originally wrote script with this as a list.
                             //   Converted it to a string to save memory.

//////////////////////////////////////////////////////////////////////////////////////
// User Defined Functions
add(string a, integer b, integer c, integer d, integer e, integer f){ //adds a timer to the lists
    integer g = llGetListLength(timername);
    timername           = llListInsertList(timername,           [a], g);
    timerticks          = llListInsertList(timerticks,          [b], g);
    timercycles         = llListInsertList(timercycles,         [c], g);
    timerprim           = llListInsertList(timerprim,           [d], g);
    timerticksremaining = llListInsertList(timerticksremaining, [e], g);
    timerstate          = llInsertString  (timerstate,           g,  (string)f);
}

remove(integer a){ //removes a timer from the lists
    timername           = llDeleteSubList(timername,            a,a);
    timerticks          = llDeleteSubList(timerticks,           a,a);
    timercycles         = llDeleteSubList(timercycles,          a,a);
    timerprim           = llDeleteSubList(timerprim,            a,a);
    timerticksremaining = llDeleteSubList(timerticksremaining,  a,a);
    timerstate          = llDeleteSubString(timerstate,         a,a);
}

clear(){ // Clears all timers and stops the master timer
    timername           = [];
    timerticks          = [];
    timercycles         = [];
    timerprim           = [];
    timerticksremaining = [];
    timerstate          = "";
    llSetTimerEvent(ticks = 0);
}

//////////////////////////////////////////////////////////////////////////////////////
// Main Script
default
{
    state_entry(){
        llSetTimerEvent(0); //Start master timer in an idle state
    }
    on_rez(integer a){
        clear(); // Reset the lists
    }
    link_message(integer a, integer b, string c, key d){
        if(b == timerchannellisten){ // Check to make sure message is from the correct channel
            if(c != "TIMER_STATUS_CHECK") {
                list e = llCSV2List(c);
                if(llGetListLength(e) >= 3){ // makes sure the list length is at minumum a valid command length
                    string  f = llToLower(llList2String(e, 0));  // Gets the command then converts to lowercase to make the commands case insensitive
                    string  g = (string)a + llGetSubString(llList2String(e, 1), 0, 9);
                                                                    // Gets timer name from the command string and prepends the prim number to it to
                                                                    // guarantee uniqueness from prim to prim within the same object. Also truncates the
                                                                    // the timer name to 10 characters to allow for reliable memory usage estimations.
                    integer h = (integer)llList2String(e, 2);    // Gets the timer's interval from the command string
                    integer i = (integer)llList2String(e, 3);    // Gets the timer's cycle from the command string
                    integer j = llListFindList(timername, [g]);  // Index of the called timer
                    if(f == "add"){
                        if(h < 1 || i < 1) { // Checks to make sure both the ticks and the cycles are positive
                            llMessageLinked(llList2Integer(timerprim, i), timerchannelspeak,"TIMER,ERROR,Non positive Tick or Cycle value.", NULL_KEY);
                            jump end;
                        }
                        if(llFloor((llGetFreeMemory() - 500)/29) == 0) { // Checks available memory, attempts to leave at least 500 bytes available.
                            llMessageLinked(llList2Integer(timerprim, i), timerchannelspeak,"TIMER,ERROR,Out of memory", NULL_KEY);
                        } else {
                            if(timername == [])
                                llSetTimerEvent(timeint); // If the master timer is not started, then start it.
                            if(j != -1) // Check to see if the timer already exists
                                remove(j);// if it does, remove it
                            add(g, h, i, a, h, 1); // Add timer
                            if(ticks == 0)
                                llSetTimerEvent(timeint); // If the master timer is not started, then start it.
                        }
                    } else if(f == "rem") {
                        remove(j); // Remove timer
                        if(timername == [])
                            llSetTimerEvent(ticks = 0);
                    } else if(f == "pau") {
                        if(j != -1){ // Make sure the timer already exists. If not, do nothing.
                            timerstate = llDeleteSubString(timerstate, j, j); // Change timer
                            timerstate = llInsertString(timerstate, j, "0");  //              state to paused
                        }
                    } else if(f == "res") {
                        if(j != -1){ // Make sure the timer already exists. If not, do nothing.
                            timerstate = llDeleteSubString(timerstate, j, j); // Change timer
                            timerstate = llInsertString(timerstate, j, "1");  //              state to run
                        }
                    }
                    @end;
                    // If the message made it this far without doing anything, then the command must not be a timer command
                } else {
                    llMessageLinked(a, timerchannelspeak,"TIMER_STATUS," +
                                                        (string)ticks + "," +
                                                        (string)timeint + "," +
                                                        (string)llGetListLength(timername) + "," +
                                                        (string)llGetFreeMemory() + "," +
                                                        (string)(llFloor((llGetFreeMemory() - 500)/29)) + "," +
                                                        (string)timerchannelspeak + "," +
                                                        (string)timerchannellisten,
                                                    NULL_KEY);
                }
            }
        }
    }

    timer(){ // master timer
        ++ticks;
        integer a = llGetListLength(timername); // Determines how many timers to update
        integer i;
        list timerstoremove = [];
        for (i = 0; i < a; ++i) { // Goes through the entire timer list
            if(llGetSubString(timerstate, i, i) == "1"){ // Check timer run state. If 0, then step to next timer.
                timerticksremaining = llListReplaceList(timerticksremaining, [llList2Integer(timerticksremaining, i) - 1], i, i); // Subtracts 1 from ticksremaining
                if(llList2Integer(timerticksremaining, i) == 0) { // Checks to see if the timer has elapsed
                    if(llList2Integer(timercycles, i) != -1) // Checks to see if timer is undefinite
                        timercycles = llListReplaceList(timercycles, [llList2Integer(timercycles, i) - 1], i, i); // Subtracts 1 from cycles
                    timerticksremaining = llListReplaceList(timerticksremaining, [llList2Integer(timerticks, i)], i, i); // Resets ticksremaining
                    // Send linked message reporting timer event
                    llMessageLinked(llList2Integer(timerprim, i), timerchannelspeak,"TIMER," +
                                    llDeleteSubString(llList2String(timername, i), 0, llStringLength((string)llList2Integer(timerprim, i)) - 1) +
                                    "," + (string)llList2Integer(timerticks, i) + "," + (string)llList2Integer(timercycles, i) + "," + (string)a + "," +
                                    (string)(llFloor((llGetFreeMemory() - 500)/29)), NULL_KEY);
                    if(llList2Integer(timercycles, i) == 0) {// Checks to see if tiemr has expired
                        timerstoremove = (timerstoremove=[]) + timerstoremove + [i]; // Assemble list of timers to remove
                    }
                }
            }
        }
        a = llGetListLength(timerstoremove); // Determines how many timers to remove
        for (i = a; i > 0; --i) {
            remove(i - 1); // Removes timer
            if(timername == [])
                llSetTimerEvent(ticks = 0);
        }
    }
}
Past Projects
None
For help see: ScriptLibrary | ScriptingMentors | Scripting Library forum | Scripting Tips forum | Offical Scripters Mailing List


IsAgArD

Currently doing:

Random stuff for people who ask and I'm working on an ad server (well the server parts basically done...just adding more features to the board). If you want to beta test the ad server contact me in game Isagard Shriner.


IshtaraRothschild

This page has been created to clean up the WantedPages.


Jaap Bergman

Has not created a user page! :-O


Jacek Antonelli


JACKLINE HUGO!!!


JaclynMaltese

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JacobxGoff

A user who made Wild Blue.


JadeLily

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Jadz0rConover

Owner and founder of jadSOFT.


JagHarker

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JakeBailly

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JakeLaprade

A user who needs to edit thier wiki page


JamalMfume

I'm silent but deadly.


James Linden

...Is an employee of Linden Lab. I believe he is working on the SL HTML rendering, "powered by Mozilla technologies" (a.k.a. the Gecko HTML rendering engine, the rendering engine Firefox uses)


Jarhyn Wilde

This page has nothing interesting on it. Move along.

mmmmmmmmmmmmmm.


JasonAlcott

A good scripter.


JasonHashimoto

I'm new--play nice!

Fairly new with any type of coding. LSL was the first real language I learned(HTML & CSS really don't count). Now I'm working on mastering PHP, SQL, C++, and maybe even Ruby ;)

Because of the above, LSL is my preferred language for doing well, just about anything :O


JassinPain

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JavierPuff

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JayDTorgeson

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JeffCaldera

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JeffreyGomez

Jeffrey "What Does This Button Do?" Gomez

Is no one of any real importance.

SL Login: Jeffrey Gomez
Email: mercen4ry at gmail dot com
Skype: Mercen4ry

Because I do receive some spam to these accounts, please identify yourself as an LSL coder in need of help.
Otherwise, I may filter you out as spam.


JeroenWaisMan

I am Jeroen Waisman, a dutch TSL resident.
Some may have seen me in the sandbox islands, i am wearing my (self made) Donald Duck av.

I can say i am a fair scripter, made some intermediate scripts.

Finished works: (all things i can remember i finished)
-A painting tool, made in the Inworld and HUD version.
-A multitool. (few features, including an Autoshield)
-A more sophisticated Auto-shield
-A (very strong) Auto-lock
-A in-sim Teleporter
-A math re-write of llLookAt
-Dome rezzor.
-A LOT of scripts. (some are useable)

Currently working on:
-The Black Falcon's defence system.
-The Black Falcon's dissabeling system (for the armour).
-A inter-sim pathfinder, or a grid teleporter.
-A LOT of projects thats i left there sit in my inventory after a week working on them.


JesseMalthus

A small Resident in a big grid.


JiffiesManimal

Fairly new to SL, i like scripting and sutch so i play alot with it.


JimBunderfeld2

Jim Bunderfeld

Lost the password to my original account :(


Jim Linden

Is an employee of Linden Lab. He has something to do with the maintenance of the wiki.

Email him if you lost your password.

Lindens


JimmyLoveless

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JoeyAxon

Wannabe 1337 LSL scripter.

Learning new things almost every day, creating totally useless stuff.

Playing the Teen Grid, as Joey Axon.


JoeyMarlin

Fairly new scripter - but experienced with scripting in general.
For hire, if ya need a fairly quick job done.
Specialty: communications... especially off-world stuff like llHTTPRequests for SQL servers and the like.

Well... not much more to say... seeya in-world! ;D

/Joey


JohnPallister

Hi, this is my page. I'm John Pallister, a software developer living in Wellington, New Zealand. I participated in the SL beta program, but then I decided that I couldn't quite justify taking up the lifetime membership offer, a decision I've since come to regret...

Consequently, I have two SL names: Argent Smith, the name I chose for the beta program, and John Sojourner, the name under which I eventually re-joined SL. Apparently the database changes for SL 1.6 mean that the former name is gone forever, so I guess I'm stuck with the latter.

Anyway, since signing up I have spent very little time in SL, since my RL keeps me pretty busy. (Plus I'm afraid of getting totally sucked into the world to the exclusion of all else, which would not make the rest of my family very happy...) However, I'm excited by the idea of LSL as the powerhouse behind the limitless creative possibilities of SL. I am very interested in 3D user interfaces and process modelling, and I intend to use SL to develop my ideas (as well as using the Torque Game Engine and raw OpenGL).

My background is mainly in C++ on Windows, but I am also very keen on Lisp, and I'm hoping to develop some Lisp-based tools to assist in LSL development. At that point, I can actually start to make a contribution to this community.

In the meantime, thanks for reading this far. This page goes up in August 2005: don't hold your breath for news of progress. But we'll see what happens.


JohnSojourner

John Sojourner is the SL name of John Pallister.


Joker Opus

Hi I am Joker Opus, I am the owner of the weapons/gadget company J'alli TECH and try my hardest with scripting

I try my hardest to give people examples for LSL, and I have made some pages to help people learn/study scripting. If you want you can make a page!

NOTE- Type in the page you wish to create in the box. I available, you can create the page(something pops up that says maybe you want to create-it)
EXAMPLE-
only search the lslwm |llPushObject|

only search the lslwm


JonathanShaftoe

I'm new.

I'm learning.

My head hurts.

If you're curious..


Jontte Gremlin

I'm a 16 year old Finnish scripter and a good friend of AtteAderdeen.

Me:
- Likes Maths
- Has played/lived SL for about 1? years

LibraryPolygonFormer <-- I made this ^_^


JopsyPendragon

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JoSapeur

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JulianPoppy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JuneTate

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


JuranJumador

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


KagePixel

A wandering coder....


KaiNyak

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Kalel Venkman

Founder of the Justice League Unlimited, and creator of STARlabs gadgets.


Kalemika Dougall

Didn't bother to put something on her page.


KamiHarbinger

I make weird little gadgets and role-playing game accessories: dice, a character journal/database system, and more.

http://kamiharbinger.com/


KankerGreenacre

Welcome to Kanker's Krappy Kode Kollection

Go here to see the Flying Tako Sailboat scripts: LibrarySailboat


KarizonHatfield

I build interactive stuff.

voting machines, interactive games, etc etc.

I also teach classes and 1-on-1 LSL sessions for learning LSL

find me in world: Karizon Hatfield


KarizonHetfield

Still new to scripting in SL, but picking it up pretty quickly.

my homepage is at www.mandrake.net


KatharineBerry

void null()

This is a pointless user page.

The reasoning behind the odd formatting is that my IRC bot can parse function pages, but nothing else.


KayinZugzwang

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


KeedonPollack

Not much to say...Newbie, but I want to learn about scripting.


KejentoDrake

Blue Wusky thingee who's always willing to lend a hand in SL. My name in there is, you guessed it, Kejento Drake.


KellyLinden

The eXtreme Bug Slayer Kelly Linden formerly known as eXtreme Bug Hunter Kelly Linden.


Kenzington Fairlight

Has not created a user page! :-O


KermittQuirk

Go Tringo! :)

Tringo info available on my wiki... http://www.kermitt.dynu.com/triki/


KeylessShackle

TSL Scripter for hire who needs to edit this page more often...


KimmieLoveless

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


KitProudfoot

Kit just dabbles, and tries his best to find information on this Wiki. Some of the information is more useful than other bits of information. But then, doing anything by comittee can be a challenge and less than perfect.

Wiki == Writing Interesting Knowledge Items... But doing it by comittee.... So it's less than perfect.

If Kit ever figures LSL out better, he'll let you know.


KitsuNico

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Kitten Lulu

Homepage: http://kittenlulu.blogspot.com/
Location: Italy, Europe
Available in game: European evenings mostly, that is 12pm to 3pm SLT


KlawsDusk

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


KonigmannLippmann

I have been scripting LSL since June 2004. I've been writing in C style languages since the mid 80s. It's all about learning the library. Praise the Wiki!


KoyukiMichabo

Hi there, I'm me.

Currently a uni dropout (as of xmas '05), probably going to get a job, blah blah...

I suck.

website: http://www.onfiar.com

Latest project: Map/Guide/Info-system for SL Scripter's Trade Fair

Previous finished projects: Oh dear, i never finish anything.


KralPlayfair

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


KrashnburnHillquit

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


KriAyakashi

Real Life name: Bartosz Ptaszynski
Second Life name: Kri Ayakashi

website: http://www.ayakashitec.com
blog: http://www.yazzgoth.org
linkedin: http://www.linkedin.com/in/yazzgoth


KristianMing

KristianMing is just getting the hang of this Wiki thing.


KurtZidane

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Kyrah Abattoir

SL summary:
-Playing second life since more than 2 years
-I like scripting things and designing clothings
-I created a global vendor network system that centralise all my object seelling and use an encripted mail system with error handling
-I created and am maintaining the Lockmeister System

Essay: RealtimeModeling


KzPasteur

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


KzzchPrefect

This page has nothing of interest. Move along.


LaenTeazle

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LallanderParvenu

Most people just call me Lall.


LanceLeFay

This page has nothing of interest. Move along.


LanceMirabeau

Been around longer than you'd think.

I'm for hire, and fairly cheap- for the reason that I'm also terribly lazy.


LaunaFauna

Single handedly causes all the dataserver issues with her massive inventory. She's a pack rabbit, but she makes excellent hair and clothing.


LazarusLong

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LazarusLumiere

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LazinkMaeterlinck

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LearjeffInnis

* Newbie LSL scripter (one script in the bag!), old hat programmer & software architect.
* Nearly 30 years experience in real-time, event-oriented, state machine and communications programming (most embedded).
* Author of jTipJar script, soon to be released as a product.
* Musician & audio engineer

http://learjeff.net


LeeLinden

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LeopardLoveless

This page has nothing of interest. Move along.


LesWhite

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LevKamenev

My Blog: http://lev-kamenev.blogspot.com/

Contact me as Lev Kamenev in the game

Interestest:

1. Social and economic aspects of SL
2. Using SL as a platform for implementing various reinforced learning algorithms (driving robots).


LexNeva

Lex Neva is a somewhat inspired newbie. She came to SL with programming skills and began to create mostly useless toys as soon as she could grok LSL. She's fairly friendly, although she tends to be shy and reclusive.

YAAAY, I finally got the password on this wikiword reset. So I no longer have to be LexNevaForgotHerPassword.


LexNevaForgotHerPassword

Yeah. Forgetting your password sucks. I'd really like the LexNeva wikiword back. :P


LeynaBachman

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LikeKookies

Just signed up to place some findings that wernt on here thusfar. Hope to help a few ppl avoid llFrustration.
If ya noticed anything that i got wrong IM me inworld and ill change it. (Kyran Nyak... yeh it wouldnt eat it coz it told me it wasnt a wiki name... but i do like kookies =])


LillaniLowell

This page has been created to clean up the WantedPages.


LinnaSubagja

I'm new to SL.
I'm not new to computers, programming or IRC.


LionelForager

About Me:

I am a Proffesor of the University of Oviedo (Spain).
My area of expertize is Geotechnical Engineering.
I am interested in SL as a way to improve learning of Geotechnical concepts through virtual simulation of processes.
If you are interested in it or have some project related to it, please feel free to contact me in world.

My Library Scripts:

  • Bezier Curve Demo: a script set that draws a Bezier curve segment controlled by three points.


LittlesuzeAlexander

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LordJason

Despammed. No proper content found in the revision list.


LordKaos

my page is empty?


LucAubret

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


LuccaKitty

Currently, I'm working on a program called "Thoth" that is intended to be used with Second Life and open up the realm of certain types of scripts that were previously relegated to the elite programmers in SL. I'm not going to say what it is, but lets just say that there are certain scripts that even some of the best scripters in SL can't do because they don't have something like Thoth that drops the biggest part of those scripts in their lap.

Thoth is currently being programmed in Visual Basic. An ANSI C version is planned, but as C is typically beyond the scope of most of SL's scriptors, the ANSI C version of Thoth is on the back burner, there's nothing in the pot and the burner's currently not turned on.

I apologise in advance to those who cannot compile or run VB programs. I also apologise in advance to those who cannot program in VB. If you learned enough LSL to even have a use for Thoth, VB won't be that hard to pick up.

All I see is little dots.
Some are smeared,
and some are spots.
Please advise.

The dots that are before your eyes
Are spots which cannot be excised
The dots of "Bob" that send out Slack
Are Wholly spots that SEE YOU BACK!

My name and avatar tend to imply that I'm a furry but I'm not exactly a furry. The fact is that my "furry" avatar is a result of some weird "experiementation" that I do not want to divulge on a public forum like this. What I did was legal and still is to this day, but some people might respond to the very notion just a little strongly and make me out to be some sort of monster like much of society does with those who live outside the norm.

NO the experiementation did not involve animals, it involved a certain species of the mint plant. Get your mind out of the gutter people. Geeze, I swear, some people... But suffice it to say that during the "experiementation" I saw some really weird stuff that made me question the nature of the universe.

At anyrate, I'm not really a furry, I just tend to hang out with furries and wear a furry avatar. I do not now nor have i ever, nor will i ever own a fur suit. I have never been nor will I ever go to a furry convention. I am not a furry. But, if you have a problem with my choice of finding some furries interesting and cool enough people to be friends with them, then you go back to worshiping Fred Phelps or Blowtax or whoever it is you biggoted sheeple worship. Praise "Bob".


LudivicoSzymborska

A user who has yet to make thier page


LukeRamona

Projects Completed:
* Slot Machine
* Swing
* Many particle generator variants

Current Projects:
* Visitor Tracker that tracks:
......Unique Names with:
...........last date/time visited and total duration spent in range of sensor
......Current names with:
...........time first detected


LumiereNoir

LumiereNoir is a guy who will do just about anything to avoid personally teaching people how to build in Second Life.


LumpyTapioca

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MacLifer

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MadamGZagato

Creator of the N30 Deluxe Modeling Studio which is SL's first full featured modeling studio system.


MadasaHornet

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MaerouCzukor

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Mafluence Kilian

Has not yet created a user page! :-O


MagicianThorn

Well, not much to say about this nut besides.... well is there anything?

I am currently working on a few really cool projects though. A global transist system,
as well as a list of all the llRegionCorners. I also am working on a categorized vendor
that will only use a max of 8 prims which can be lessened to 5. I say max of because there's
some Dieing and Rezing in there too..

Currently though, (I have to hop around between 2-3 projects to keep from making one big mess of the big ones)
I'm working on a system to create custom particle scripts automatically for non-scripters.
Hopefully wont take too long I'm really enjoying working on this as I have sort of started
considering particles to be my best consistantly good outcome.

Gimme comments if you'd like!

Restored after server move


MaiglynaGoodliffe

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MajorTomHebert

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MakoMabellon

Knows things about LSL that mankind (or non-Lindens anyway) was not meant to know.


Malachi Petunia

...Is someone who didn't bother to make their name link to something. Shame on them!


Marc Eisenberg

Local Second Life resident since Jan 8, 2005
(happens to be the same day as my real birthday too!)

Marc's Official Blog - Everything you could ever want to know about programming, computers, etc. True geek here people.
Second Life Network - Your unofficial Second Life MySpace-like site.

My First Life

My real name is Marc Melvin, I'm 23 years old, live in Miami, FL, USA, and I am the CEO and lead developer for an internet technologies company, ByteScape, LLC. Basically, I develop websites and software for a living. I go to a lot of local rock shows because I love the underground music scene. Ummm... yeah, that is just about the extent of my first life.

My Second Life

My name is Marc Eisenberg, and I am a blue/green/red/whatever haired cyberpunk taking the world by storm. I have had a Second Life long enough to know a thing or two (never knew it would happen considering I barely have a first life, lol). I feel that I have learned the scripting language back and forth, up and down, side to side, and have taken it upon myself to take what I do for a living in my first life, and make it something I do for fun (and money too, every now and then) in my Second Life. The first week was difficult as it requires a different way of thinking to code in LSL, even though it looks a lot like C++/Java/etc. But by the beginning of the second week, before I had even completed my trial membership, I was already working on a script for roulette and craps with visually and dynamically stacking chips.

My Favorite Things

Some of my favorite things to play with in LSL, the Second Life scripting language, are:
  • Hud
  • Particles
  • XMLRPC

Some of my favorite things to do in Second Life are:
  • Dance events
  • Gambling

My Least Favorite Things

These are the things that I don't like about my Second Life:
  • Physics - The functions simply don't work like I think they should. I think they could be approved on dramatically considering that Half-Life uses the same physics engine, Havok.
  • Weapons - If I wanted to shoot people, I would play HL, CS, or Halo. Most of the weaponry in Second Life is just an annoyance. But whatever, to each their own I suppose.


MarcusEnnui

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MarcuwSchnook

A user who really should edit this page.


MariusMaeterlinck

I use Second Life to hang around (Violet Temp. Welcome Area, most of the time) and build stuff - mostly gadgets in the realm of "hmm... how could I make *this* work". Being Dutch, I'm too cheap to pay for texture uploads, so I have to confine myself to scripting :-)

In real life, I'm Cees de Groot - http://www.cdegroot.com for more info.


MarkGjellerup

Joined SL in Apr 2006.

So far I've only made a Security Keypad for a door.


MarkovBrodsky

I'll bet you can't guess my in-game name.
Feel free to message me if I'm online.

Relatively new to Second Life and LSL (since 02 Sep 2004), but I've been a programmer for around 12 years and the last 5 years as a game developer (did some work for N64, PSX, PS2, XBox, J2ME, BREW).

Sample code:

- Self Upgrading Script


MarkSmith

MarkSmith is really Mark Smith.

Mark Smith is also a developer for LiveJournal.com. Funny enough, his username is marksmith.

Mark Smith started his second life at the behest of his roommate. Okay, it wasn't behesting, it was a lot of "Damn! Mark, come look at this!"

Mark Smith couldn't be the only one not making cool stuff.

Mark Smith likes to write scripts. He also sucks at modelling.

Mark Smith hates the third person.


MarsramGretzky

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MartinMommsen

A user with no profile


MasakazuKojima

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MatWarf

Well, this is my page, I suppose. Hello.

I do vehicles and physics etc within SL, and I contribute to the LSL Wiki Mirror 7-7-7 where I can.

Currently I'm working on a wind powered sailboat, the Muse. Release 2.0 will be soon....

I'm trying to keep lslwiki.net clear of the recent spamwave. Feel free to help.


MaxwolfGoodliffe

Maxwolf Goodliffe - 15 Feet of Wolf

Maxwolf Goodliffe joined Second Life™ on 12/30/2005


McGrawDillon

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


McLean

A user who needs to edit thier wiki page


MechaniqueThirty

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MeghanMagpie

Meghan Magpie is fairly new to Second Life and to LSL scripting, but she's having a great time anyway.


MeiyoSojourner

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MeLightKorvin

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MemeticDivision

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MeraPixel

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MeyermagicSalome

Name: Meyer S. Jacobs
Alias: Meyermagic, if(x), |-1|

Programs in: PHP, LSL, and a bit of C/C++
Speaks: English, Gibberish, Technical Jargon, and a bit of Spanish

Avoids: Carbonated drinks, dressing up, political parties, and organized religion
Is drawn to: Math, physics, technology, interesting people, old cartoons, sci-fi, anime, good books, things that spin, magnets, food

Projects:
-SL
>-Camera Sync
>-The Sensor Hub
>-An Artificial Life system
-Non-SL
>-The WeFiki
>-Writing Perception, a four-part story


MichaelFirefly

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MichaelKyuzo

This page has been created to clean up the WantedPages.


MichaelRichards

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MidianStreeter

Midian Streeter is Smiley Barry's other half. The half that also manages some of The [Teen] GridCentral Project's buisnesses and "sub-projects".


MikeMos

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MiniCooper

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MinskOud

No more orange underline!


MiseryMoonlight

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MisnomerJones

MisnomerJones knows very little about LSL but hopes to someday change that.


Mitzpatrick Fitzsimmons

Greetings.
I am Mitzpatrick Fitzsimmons, a SL Resident since September 2004.
You may know of me from various scripts I have created, or helped to create, or from the Basic Scripting Group that I started in the summer of 2005.

I am glad to see the Official LSL Wiki Mirror 7-7-7 back online, and will continue to post here the scripts I have made available to the public.
I hope you enjoy my contributions, and above all else, I hope you enjoy SL.

-Mitz

All My Public Scripts

Mitzpatrick Fitzsimmons

Name | Creator | Description | Date Created | Date Made Public
AirLockDoor Script | MitzpatrickFitzsimmons | A different kind of Door Script | 05/07/2005 | 03/01/2007
AltPickPocket Script | MitzpatrickFitzsimmons | Steal Money from your Alt | 10/31/2005 | 03/01/2007
MitzFader Script | MitzpatrickFitzsimmons | Object Alpha Fader Transition Effect | 10/31/2005 | 03/01/2007


MonahanWake

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MooshGoodliffe

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MoriashMoreau

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MorphWollongong

Johnny-come-lately to the scene.


MotorHappy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MsMarcel

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


MtnessExcelsior

There is a chasm
between carbon and silicon
thst software can't bridge.

mtness.net


NadaEpoch

Do I really need a profile?


NadaEpochs

be very very careful! ~bbc

did you notice, i am multiplying? ~ne's


NargusAsturias

Nargus Asturias aka Nargus StreamWarrior

Armoror of the Dragons
Scriptor of All Things Come To His Mind
Dragon in Spirit and Raptor at Heart

Field of profession is Data Mutology
Human translation -- data management


NashBaldwin

Formerly James Linden, now working on "serious applications" in Second Life, including:

* Visualization of hallucinations of schizophrenia
* Biological sample tagging for biodefense
* Cardiac resuscitation training


NataruOdets

SOMEBODY wheres the bathroom?


NathanStewart

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NealNomad

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NeardHarbinger

What to say about me?

I'm a scripter. I build of course. But I love to script.

I've gone through many languages...PHP, LSL, and Visual Basic mostly, but I've got some C++ basics.

I've started up a shop for my scripted gadgets. It's not open yet, but if you're bored and looking for something good, check it out.

The Teddy Bear Workshop

I look around SL sometimes, and see ugly, half-done products. Bad textures, weird shapes, nasty scripts...I'm just doing my part to try and give people decent products.

*sigh* There's so much I wish I could do though, that the current featureset doesn't allow. So, I've started my own wishlist:

llWearClothing - Similar to llAttachToAvatar, but works with clothes. It would give the avatar the item of clothing from inventory.

llAttachInventory/llWearInventory - Attach (or wear) an item out of the user's inventory. To avoid extreme hassle, wouldn't have to add checks for the user's inventory, just dies silently (Possibly the item has to be in the same folder as the object calling, and the object has to be attached?)

llLandSay(integer channel, string text) - Similar to llSay, sends the message text on channel; however, unlike llSay, the message goes out across the whole parcel, whatever it's size (limited to one sim, of course; after all, parcels can't cross sims, right?). The message would reach every object and avatar across the parcel; if on channel 0, it would go out on public chat similar to a standard object say; there would be no extra message like whisper or shout uses. It could incorporate a small delay to avoid too much spamming; possibly 1/2 to 1 second.

COLOR_??? - Vector constants that represent the values of various named colors (ie, COLOR_RED would be <1.0,0.0,0.0>). Possibly a way to add them together to make more extensive colors (ie COLOR_RED + COLOR_BLUE = purple)?
NOTE: This is partially available by script; however, all the "constants" have to be defined in the script. This would enable at least 5 constants (full red, full blue, full green, white, and black) built into the language itself.

Yes, I realize this accomplishes nothing. Yes, I realize it's pointless to post this here, and I should post it on the SL Vote proposal board. Or drop it on Kelly's suggestion cylinder. Or one of the other options. Yes, I know how to do all that. Yes, I realize all this. So why? I see it as a ... scratch pad. A place to work out the suggestions, maybe get some user opinions, before I try for the real thing.


NebSoyer

Hi.

I like to script. I also like to develop things. Mostly random things.

Wishlist (Everyone should have one)

-Damage Healing
~ Either minus values (%) heal, or llSetDamage to be changed to minus values being remove health, and positive/normal values being healing.
~ OR, llSetDamage, and llSetHeal - something like that.

-Replace llGiveInventoryList
~ llGiveInventoryFolder(?)
~ And to add to that note, Subfolders within Contents folder (of objects)!

Uhh, adding to this as I go along.

_neb.


NebSvarog

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NelvaynaCyr

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Never Rust

Part of the EcosystemWorkingGroup

Home is the Never Rust Station


NewfiePendragon

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NexiiM

Back in Style on the TG and better than ever.

Currently busy with 2,069,059,350,546,196.5 Projects.

My Page: www.nexii.co.uk


NexusNash

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NickiamSolvang

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NightspyRebus

Electronics engineering student and a graphics designer RL,
An expreinced SL resident , Scripter and Builder for a over a year and a half

Live helper Inworld , Mentor and a Teacher

Available for Hire on small projects


NikAbbot

This page has been created to clean up the WantedPages.


NikEbisu

<a href=" http:cohaesion.com/alt-sex-stories.html ">alt sex stories</a> <a href=" http:cohaesion.com/sex-movies.html ">sex movies</a> <a href=" http:cohaesion.com/peeingsex.html ">peeingsex</a>


NikolaiWeber

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NilesArgus

Niles


NoelMarlowe

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


NoeticEidolon

Psyfire Tigereye is Second Life.


NolanSion

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)

Sigma Lime wuzz hurr!


NomadPadar

*Will Script for Plutonium*


NonnuxWhite

Nonnux White is a Multimedia Designer living in Portugal. You can find him in the sim Terra


Norn Albion

Norn Albion is decent scripter in SL and he has experience in a fair number of programming languages.
He is on the Teen Grid.

Currently:

Very idle. Mostly working on making an SL interface for http:words.carnadine.co.uk/
Norn doesn't go on SecondLife much anymore...

LSL Wishlist:

Wish | Description
llmysql functions | Functions for connecting to, and reading data from, mysql servers
llPlayMusic | Function to play MP3 files not happening, the current 10 second limit is a preemptive measure by LL to pacify the RIAA


Nxxx23Wallaby

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Obsidian Stormwind

Has not yet created a user page! :-O


OdysseusFairymeadow

A user that doesn't contribute enough to this wiki to advertise his own auto-biography :-P


OoPsGalatea

I want to help people learn this stuff, because its incredibly cool.

My contributions to this wiki so far:

LibraryAirSeaAltimeter

Moved your Library page to conform with page naming standard, please delete AirSeaAltimeter
To delete that page please click http://secondlife.com/badgeo/wakka.php?wakka=AirSeaAltimeter/delete, then just edit out this comment


OptimystPaster

This page has been created to clean up the WantedPages.


OrangeE

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


OrdinalMalaprop

An Engine Fit For My Proceeding


OzSpade

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


PadraigStygian

The thin mick is back, and has nothing useful to say!

Current projects:
- discover why pervectly valid syntax results in syntax errors during compile (yes, I'm talking about the XyText Chart)
- actually finish something before my girlfriend does it for me, and ten times better


PandaronCodesmith

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


PantharOrlowski

I like to take long walks on the beach and play football with pigeons.
I like to throw small dogs out of windows to try to see if they can fly.
I admire the people that produce food from dirt.
When doves cry, I also want to kick them like footballs since they are just fancy pigeons.


PastaMann

A follower of His Noodliness, Pasta Mann likes to script little things.

Backlinks

PastaMann
WikiUsers


PepplarSklar

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


PersialHebert

Persial January Hebert AKA Martin Wallace Graynor AKA Beowulf Schaeffer.

Crashlander, hotshot pilot, involuntary thrillseeker.


PeterLameth

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


PetreLamar

I'm an experienced C, C++, and Java programmer, and I'm getting pretty good at LSL.


pfft

What you say when you don't have anything to say.

Invented by Torrid Midnight, patent stolen by Keknehv Psaltery.

Note: Not to be confused with "Phfft", a Neko-Girl hiss.

Humor


PhooieFeng

nothing here, move along.


PhotonFugazi

A scripter on the Teen Grid. Woo.


PHP

The PHP Hypertext Preprocessor is a programming language that allows web developers to create dynamic content that interacts with databases. PHP is basically used for developing web based software applications.


PieroPadar

Check my expanded bundle for Textmate (Macintosh only): instructions and download link


PierreJosephProudhon

Coder struggling to get a grip.


PlexLum

Are you dreaming?


PlowboyLifestyle

PlowboyLifestyle Eponym


PlutoniumDragonfly

Hacking isn't a crime... It's a survival trait.


PoppyKolache

A user who needs to edit thier wiki page


PrakCurie

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Pranakhan Khondji

A Second Life builder/scripter/developer. Owner of the Electrogods Development Group, dedicated to making high-quality mostly free interactive SL content. He is a sweet black lynx and is very friendly. IM him in Second Life with comments or suggestions, or send him an e-mail at: pranakhan-"at"-gmail-"dot"-com.

Thanks for stopping by!


PrioSerpentine

A part-time grammar-and-spelling policeman.

Every round, those in contact with PrioSerpentine, including PrioSerpentine himself, must roll 1d100. On a result of 1, the victim suffers 1d2 points of sanity loss. PrioSerpentine himself is not immune to this effect in any way. Anyone reduced to zero sanity by this effect transforms into a chicken. PrioSerpentine has been transformed into a chicken several times, but he got better(?).


QarlFizz

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


qpn6ph9q

Hi, sorry to disturb.
Could you tell me with which command I can make the Teleport dialog appear on screen when someone touch an object please ?
Thankyou in advance

My name is Barbarella Carter


QubiusQuinn

Name: Qubius Quinn (Q?)
Tagline: Like James Bond's gadget man, Q, only more square.
Location: Oyster, Second Life.
E-mail: q2@hypercubed.com
Blog: http://qubiusquinn.blogspot.com/

My Contributions to the Wiki
Added warning to AssetPermissions
Added JavaScript example to XMLRPCImplementations
Added ASP example to XMLRPCImplementations

YourPages
YourChanges


RaemusPatel

No orange underline


RavenRaide

Hello my name is Raven, i am not whwat i would call an expert, but i am slowly becomming more proficent with LSL.

Man pages are awsome - RyanDayton


RebeliahMartinez

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


RedLegPlasma

A user who has yet to create a profile


ReduxDengaku

Anonymous: "You don't need to reinvent the wheel."
Redux: "You do when all the wheels are square."

If you don't already know me, I'm Redux. I have a deep appreciation for untangling knots, Rubik's Cubes, and turning complexity into simplicity.

SL is my full time RL job and I'm the CEO of Redux Technologies, which is publicly traded on the WSE and valued at over a quarter million USD by shareholders, so don't ask me to differentiate between RL and SL, because I'm not even sure I know the difference anymore.

I'm always happy to help out fellow scripters in planning out projects. If you IM me with a question in SL, I'm more than happy to reply, but I get a huge volume of IM's so you might not get a reply immediately. I'm going to use this space here to assemble a collection of "tricks" that I've found useful/helpful in certain situations where LSL is lacking, so check this space for updates!

LibraryReduxOneLineDoor ~ A working, linkable door, thats only 1 (albeit confusing) line of code.
LibraryDualTimers ~ How to run 2 different timers at the same time
LibrarySiRiSInABox ~ If SiRiS is unavailable, try using this!
LibraryWarpPos ~ A broken function
LibraryWarpPosFix ~ A working function


ReleeBaysklef

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ResultsTiki

Doing my best to pitch in.


ResunaOddfellow

An order of Oddfellows is an organization, lodge, or guild of people who do not qualify for membership in an established professional organization, the common people, the people who do unusual or unfashionable work, or those who for one reason or another prefer not to identify with their trade or profession.

The name appealed to me immediately. I've always felt myself an outsider. An oddment, an oddity, an oddfellow.


RiannMaltese

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


RichardPinkerton

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


RichardSolvang

Just an anti-social, dabling scripter who rarely contributes anything to society. - RS


RichRandall

Rich Randall in SL
Software developer in RL


RickardRoentgen

:)


RickCarlos

I know a few coding languages heres the list:
LSL
HTML
VB
RB
Some C# and C++
PHP
Some ASP.net and ASP
Some ActionScript


RickL

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


RiniRampal

Just me. ;)


RobertaDalek

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


RobKubrick

Personal Site - robcolburn.com


RobotMann

Test Test
Place Holder.
http:secondlife.com/badgeo/wakka.php?wakka=MyChanges
http:secondlife.com/badgeo/wakka.php?wakka=MyPages


RocketRoentgen

Rocky likes being called Rocky, it is non-threatening.

She is the secret, underlying organizational force behind many SL and RL groups and activities.
Your name is in her Database.

Rocky is working on learning LSL, in order to keep Organizational Cells small and effective.

No - she will not introduce you to the illuminati.


RodrickHarrington

Resident Lurker

This wiki engine is a pain.


RyAgent

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


RyanKHart

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


RyeDinMeiji

I'm am a c# developer IRL. I honestly aspire to use the SL platform to make a living some day, so I'm slowly soaking in as much as I can about the community, the scripting language, the economy etc...

I love this world!


SaiHarbinger

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SamhainBroom

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SamuelFrost

Not new to the wiki, use it alot with my scripting in game, but finally signed on to it to try and get more involved.
I don't have any history with editing wiki's so before I start to try to any such thing, I'm going to learn about editing this page so I stay out of people's way.
Feel free to leave a comment if you've got some advice for learning how to edit wiki pages while I try to improve myself.


SanchoPanza

I am a pretty new player to SL, and absolutely hooked on the scripting side of it (despite my many inefficiencies in it). I've always been playing around with code since I was young(er). My biggest strength is being able to manipulate a limited knowledge of code into doing most things that I want it to. My biggest weakness, by far, is enjoying the coding more than the learning of it, so my learning curve tends to drop once I start to get familiar with a language (along with a long list of lacking abilities, but I am sure there is a word limit here).

uhm...

er....

I could probably help those that are new to LSL (or programming in general), but I wouldn't bring anything to a conversation between experienced/professional programmers. Uhm..not sure what else I should say, apart from that I joined the Wiki in the hope I can give something back, it has certainly helped me enjoy SL a lot more, and maybe I'll be able to give some insight from a newbie to LSL. /end babbling.

Oh, SL name = Billy Tammas (Sancho is my normal online name, probably won't respond to being called Billy :) )


SapatEngawa

I'm still very new at this. I think the Go game example script is utterly cool.


SargeRvBFeng

A user who has yet to make thier page


SatorCanetti

A user who pretends to be a scripter, sometimes.


ScalarTardis

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SchwartzGuillaume

Zug zug.

I specialize in scripting for physical objects and vehicles.


SciamachyMoran

Sometimes a Dalek, often a Drow, occasionally a good-looking guy with a mohawk - I have as many forms as Nyarlathotep and a sense of humour some would say is as twisted. :-)


ScopiPascal

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Scott69

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SeanGorham

I first entered Second Life in March of 2005. I've slowly been learning my way around LSL with tons of help from other residents and this wiki. I hope to pay that forward by contributing here also. :)


SeanHadlee

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SeanHeavy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SecondProctor

a


SeifertSurface

Not an orange underlined link.


SendaoGoodman

Hi.

LibrarySenWarp -- working smooth movement system or teleporter
LibraryPrecisionTime -- working precision timer capable of ignoring LSL's bullsh!t

Bye.


SenukaHarbinger

This page has been created to clean up the WantedPages.


SeraRawley

I am an member of the Ecosystem Working Group(open enrollment) We run artificial life simulations in Terminus(where I can usually be found). More info about us can be found on the EcoSystem and EcosystemWorkingGroup pages.

My Current Projects:

Cannon Plant
EcoCommV1
Coconut Palm


SeronisZagato

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SgeoNewbie

Generally uses comments to ask questions. Does not have a Second Life yet. Citizen of ActiveWorlds, ex-citizen of CyberTown


ShadowBailly

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ShadowWeaver

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ShadusStonebender

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ShallonThereian

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SigmaLime

Hi there.
_core

I love Scripting.


SignpostMarvMartin

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SilarsisLioncourt

Python coder by preference, long history of fascination with dynamic environments - have written mud engines in C and python, have worked on Diku and LP, used to be a contributor to mud-dev mailing list.

So far, playing around with what can be done in LSL, with a view to building some systems I've wanted to build for a while - LSL provides a nice 3D environment with appropriate tools for interacting script-wise. So far in a "learning how it's done" phase ;)


SiliconPlunkett

Where to start...

Well, I've been writing code since I was 14 years old.
Started with C, then C++, BASIC, VisualBasic, i86asm, Java, and then I found LSL.
Fun language, kind of like an illegitimate child of Java and C.

Spend most of my time in sandboxes messing around and helping people =)


SiljeRussell

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SimonRaven

SimonRaven Chippewa's page on the lslwiki.


SionZaius

Born in Dec. 2004.
little alien messing around with mazes, trees, weird weapons, shields, human minds ....


SirlorStonecutter

Hi, I am Sirlor and I script.. Thats bout all I do!

I have developed many scripts and I'm able to do just about anything including weaponry, communications, (Still learning XML-RPC), ok with physics (not a math wiz)

Visit my shop in Chief ( Enigma Labs )

Noodles Raymond should learn not to cry


SirParadox

This page has been created to clean up the WantedPages.


SlgfS

Currently working on a way to store masses of data in a sims ram.

This is an Implementation of Google?s file system in LSL ( SL GFS )

The system can store strided lists or binary data that has been indexed by the master.

Total space when replication is on is some where Above 600 megs with Extreme Sim Load! (Note: With replication the memory needed is above 1.5 gig)

Currently running and tested with 2 megs worth of name2key data.

Terminology

Master (Root prim [1 per object]) ? All commands are handled by this prim and then sent out to the nodes via link message. Accepts email input only from other objects. Responds (when needed) via email.

Node (child prim [255 per object]) ? Takes commands from the master via link message and then relays them to the needed cells. Accepts link message input only. Only responds via link message.

Cell (Script in node [4094 per node + 1 controller per node]) ? Takes commands from the node controller script and executes them. Accepts link message input only from the controller script. Can respond via link message to the node controller or email directly to the client.

Client ? User of the system, email interface only at this time.

Commands

Current commands included in the name to key Implementation of the system

COMMAND,KEY,NAME,CLIENTEMAIL,PASSWORD,FLOATING

FIND ? locates a key, message is returned by cell ( FIND,0,Splat1 Edison,object@lsl.secondlife.com,PASSWORD )
STORE ? Stores information,No message is returned ( STORE,key,Splat1 Edison,object@lsl.secondlife.com,PASSWORD )
DELETE ? deletes a key pair, No message is returned ( DELETE,key,0,object@lsl.secondlife.com,PASSWORD )
LENGTH ? displays the number of keys in the database , message is returned by root prim ( LENGTH,0,0,object@lsl.secondlife.com,PASSWORD )

These Scripts are currently highly deadly to a sim and will not be given out

All testing is taking part on the preview grid and estate sims and is not yet finished but works well.


SlitchNilsson

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SmileyBarry

Hey! I'm Smiley Barry and i'm a Teen Second Lifer. I'm excellent at Scripting and i use this LSL Wiki Mirror 7-7-7 all the time.
My "ToDo List":

X | Project Codename | Project Name | Status | % Completed
1 | Rent With A Smile | Smiley Renter v2.0 | Completed | 100%
2 | SimPet | Pal v2.4 | Completed, New Version In Progress | 14%
3 | GamesTV | ??????????? ????? | Work In Planning | 0%
4 | SLSims | ???? ? ? ?? | Work In Planning | 0%
5 | GridTV | ??????????? ?? | Work In Progress | 62%
6 | GamesTV | ??????????? ????? | Work In Planning | 0%
7 | SLMusic | SL-Tunes | Failed | 59%
8 | Gadgety-Talkie | ?????? ???? | Work In Progress | 5%
9 | SmileyScripts | ??????????? ??????? | Work In Progress | 57%


SmittyJensen

Just a loser :(


Sn0rph

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SniperUmm

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SnowyMontpark

What? Could I create a link to myself? Fun!


SodCutter

This page has nothing of interest. Move along.

I have absolutely no idea as to who SodCutter reffers to, but it was on wanted pages and claimed to have two links to it. I could not find these links. Regardless, this page will stand as a testament to the mysteries of SodCutter. Until someone fixes it. --KeknehvPsaltery


SodPages

onr two three


SomeScripter

This page has been created to clean up the WantedPages.


SophiaMirabeau

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SophiaPertwee

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SovereignTaov

Yeah, I'm creating myself a user page. No real point specifically, except perhaps getting help with my projects. So, here they are;

Nuclear weapon with ICBM delivery- Yes, I am working on one. Right now I have built the object, along with the ICBM delivery system. I think I can set up the delivery system if I tag the actual bomb to follow the ICBM when it "jumps" to its new global position, but I still need some data about finding global positions so that I don't accidentally bomb the in-world SL HQ or something. I also require some clarification as to the max height that a physical object (my ICBM) can jump to without getting removed by the "too-high unmanned object patrol" while still being able to be above the upper limit of no-script zones, and hopefully keeping the bomb above that too.
For the nuke itself, I think that I can handle the particle effects of the explosion, but I am unsure about the visual range of the explosion. I want it to be very impressive looking, even from a safe distance. However, I have had problems getting things to push whatever they collide with to imitate the actual shockwave, and could use help with programming a wonderfully devastating explosion.

Santabot- Using similar coding to the ICBM, I want this thing to go to the area of a person whose ID I send it to and give them an item in a way much cooler than simply giving it to them through IM. This project is secondary to the nuke, but may be used to test the effect without POing lots of people, as the nuke assuredly would.

Acquiring L$ 28,000- This is so that I could become a premium member without a cent of $RL. I assume that various, uh, "specialty groups" would be very willing to pay obnoxious amounts for a working nuke, so that becomes a priority. However, i have learned of the existence of money trees and would interested if someone could tell me where one is. Other ways I thought of to make money involve building a store and setting it up in the sandbox whenever I log on. If anyone has tips at other ways to make money, please tell me. I have numerous cool things to sell, such as a Mario outfit and possibly a working BattleMech if I can get the attachments and animations fixed. Please give me ideas on where I could sell these things. Thanks!

Well that just about sums me up for now, aside from the fact that I cannot leave Campus TSL currently. I'll update this as it changes.


SozaPatton

Im still looking for the underground passage to the Main Grid...

Im also working on 543875347532758489057348957439 Projects

Also the Following Commands should be in LSL:

llGiveLag() <- If someone is annoying you just give them Lag!

llName2Linden <- Should make you a linden and llLinden2Name should change you back.

llLOL <- Make an object Laugh...

llGetThroughAllBannedLand <- What else

llTeleportToMainGrid(llGetOwner) <- Teleport to Main Grid and llTeleportToTeenGrid to go back


SpideyCela

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SpinnerPoutine

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Splat1Edison

Splat1 Edison some sort of life form but is yet to be labeled. Described as a ?Cool Geek?.
This specimen could be classed as highly dangerous due to its tendency to try and blow its self up on regular bases.

Currently a mule for several companies SL and RL. Theses fund his habit of being a student.

Contact me inworld to learn more.
Current madness:
Gigas Mule
Trying to make google FS in LSL ( SlgfS )
Trying to add to the book of DwellNor


Splat1Edsion

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SpudsMilk

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


StanislausNing

Nothing to say - yet.


StaraxStatosky

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


StarkHuszar

I am Stark.
completly random name...
i script
im a tg resident :-)

Links
www.scriptfoo.net


Stavr0 Mycron


Steven Kramer

Hello. :)
I suppose I should get rid of the dead links to my user page now. ;)

My in-world name is Steven Kramer, just like my wiki name. It's not really a nickname I use anywhere else, and vaguely derived from my real name.
I'm reasonably new to SL at the time of writing, but I'm a quick study and I consider myself decent at scripting already, and okayish at building.

In my first life I study some form of computer science / informatics probably. I have no idea how it lines up with studies in the Britlands or The Great You-Ess of America.
I'm something of a freelance programmer as a hobby. I hop on open-source projects occasionally, only to vanish completely after several weeks or months of activity.
So I'm a cruel hit-and-run codemonkey.

I'm working on some fun stuff in SecondLife. Up 'till now I have only done some very simple things, like an analog clock, or a broken half-done occasionally-suiciding-off-world flying pet.
But at the time of writing I'm working on something a bit more challenging, larger in code, yet still small in dimensions, but ultimately (and hopefully) hilarious! ;)

I'll try to contribute to this wiki where I can.


StickMan

Stickman Ingmann ingame.


StreamWarrior

Nothing much. Is just the online surname of Nargus Asturias.


StrifeOnizuka

This is my SL nick here is my main nick.
BlindWanderer


SuezanneCBaskerville

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SummerShatner

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SwitchSpectre

Yes, I remember the one, that one.


SylphLightworker

Meow! :D


SylvarGould

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SynergyBelvedere

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


SynthalorMandelbrot

My Second Life name is Synthalor Mandelbrot. As my name implies, I seem to have been mathemagically created in such manner that I enjoy creating things of beauty and value from chaos - which is the basic raw material that *any* life tends to hand to me.


SysOp

SysOp: noun, an archaic term that means "System Operator". The modern equivilent is System Administrator, except that the SysOp usually owns the hardware as well as operate it.


TalilaLiu

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TaperPirandello

Moderately experienced scripter.


TarasBalderdash

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TauNino

If I had a dime...

I'm new to this gig but I'm already getting narked off with the whole XML-RPC implementation... come on guys, get it together!

Can't wait till I can start coding in Mono. Until then, anyone want to hire a programmer?

Home from home


TesterOmega

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TestingBard

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TetsuTengu

A user who needs to edit thier wiki page


ThaddeusNing

I'm a UNIX scripter from way back, and I've just (as of 12/20/2006) barely started working with LSL scripting. So far, it's been fun!


TheaBlanc

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TheGrue

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ThenarOddfellow

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TheodorePolonsky

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TheRayven

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TheWeasel

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ThomasEidson

I'm a professional game designer. Wee! :)


ThraxisEpsilon

Nothing to see here. Move along


ThrixxEnnui

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TiffanyStrauss

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TigseN

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TimRiker

http://Rikers.org/


TinkerLaFollette

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TjGillies

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TonyTigereye

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ToY

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TravenSachs

Director of Operations for Wolfhaven Productions. Traven Sachs is a 20+ year IT professional with extensive Business and Management experience. His skills include programming in over a dozen different coding languages, most recently including LSL. He has performed Systems Analysis and Programming Work and acted as Director of Operations and Information Technologies for several Fortune 500 RL Companies. Resume on request.


TravisLambert

A user who needs to edit thier wiki page


TreadWhiplash

TreadWhiplash - aka "Noel Wade" in RL. Quirky programmer. Games-Industry Veteran (Sierra / WON.net).

Nature-TV commentator Voice:

"Normally docile, this nomadic animal is usually social and amorous around others. Anthropologists suggest a capability not only to use tools, but to help others craft their own! Stubborn when confronted, this creature is slow to anger; but can turn ugly and become solitary when its hackles are raised. Quite rare in the wild, its a treat to see such an odd specimen today..."
Spender of large amount of cash on expensive hobbies like Auto-racing, Mtn. Biking, Sky-Diving, etc... which explains why he is always broke (both in SL and in RL).

Notable Wiki bits:

"Offset Rotations" - see the rotation page and comments on the llSetRot() page.
"Operator Shortcuts" - see the binary operators page.


TremorGlz

"I may teach you how to fish, but do not expect me to give you a fish. If you ask me how to fish, I will give you my wisdom; but if you ignore my wisdom and ask for a fish, you will end up with neither." - BW(Slightly modified)

Founder of SBS, XI and the rest, I like scripts, pizza.... oh and Pie, I like Pie!


TrentMondrian

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TrepCosmo

Who Am I?

For now I am nobody. You can ignore me like everyone else does. I just got tired of having an orange name.

LSL Wiki Mirror 7-7-7 Home


TreyKawabata

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TrimmingHedges

Hola. This is just a placeholder so I get the cool underlined name, and look All Official(tm) and stuff. :-)


TroyWesterburg

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


TungstenBanjo

Hey y'all, watch this...


TupacWallace

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


Turnni1

Well i started on the 15th of january 2006, and only started scripting in about december(06).
If you need a script made, ask me :D

Current work:
UBG multi-tool 0.1

New ideas:
Pac-man (attempted)


TwistZaius

I'm a crazy purple haired midget living near Rausch.

I'll script about anything for a price. I self learned LSL and never completely sat through an entire scripting class.


TylerFerland

Aka Astro Condor


TylerOdets

My TSL name, SchitsoMonkey is my wiki name.


UniqueNerd

Who?
Unique Nerd?
I've Never heard of him.
-
Current project in TSL:

Trying to find somehting to do in TSL.


UnotiQuonset

Maker of animated creatures and zany contraptions, including the marching ant band and the Quonset Poultry Cannon, and the pink juggling elephant.


UsherCaldera

A baby with a machine gun.


UshuaiaTokugawa

Nobody of Consequence


VaLeNtE

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


VanhalMcGettigan

VanhalMcGettigan likes to script casino games and other fun fun things.

He currently lives in Benham 160,220


VanlerEverett

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


VektorLinden

LL Employee
QA Engineer

Person to talk to about bugs and Preview.


VelosoLippmann

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


VeritasKennedy

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


VershanaAmarula

SW Eng. by trade, I'm a little obsessive about documentation and process.


VinceB

I am one of the best scripters on the Teen Grid, and I am only a mere 13 years old.
I live in Chicago, IL, USA, and proud of it.
I'll take scripting help requests, but only if I'm not busy.

(SL Name: Vince Bosen)


VincentNacon

Noobism Advisory
Do Not Ask Me About Your Ideas

...did you even read the label? I doubt it you did.


Vinci Calamari


VxShaw

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


WenNomad

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


WikiSpoon

Username: Myspoonistoobig Laxness

C/C++, Perl, PHP, XML, HTML, Javascript, Actionscript/Flash, BASIC, x86 ASM

Learning LSL.... kinda; I'm disappointed by it


WilBury

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


WillMoxie

Go rez your mother!


WinterGullwing

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


WolfEnfield

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


WolfWingsMajestic

Just a placeholder for my SL name in game. My account here is only WolfWings.


WufuzzCarnot

A furry scripter from Thailand (GMT +7).
Love scripting.

Will add more info later
note to myself: "Stop loging on at school!"


XaiPierce

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


XanderFisher

This is my page all about me and how awesome I am.

My Website

Feel free to IM me anytime in SL!

Alternatively, I use these other fine forms of communication:

Email: xanderstrike[at]gmail.com
xFire: XanderStrike (1,000 hours of SL woot!)
AIM: xndrstrk
MSN: xanderstrike[at]gmail.com
Yahoo: chippy989 (rarely on)


XenonLinden

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


XuguMadison

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


YbNormalMan

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


YeusePinkdot

Hello.


YleriTokhes

Just noticed that one has to sign up to the wiki to actually do anything of signifigance on it.
Is new to the whole scripting thing.
Occasionally uses the forum.


YuuByou

Yuubyou Balderdash in-world; yuubyou+lslwiki at gmail.


ZachZamboni

Umm...what can I say?


ZackyPicnic

I am Zacky Picnic, I am an ok scripter, but it takes me a long time to script. Though with enough time spent and sometimes help from friends, I can script pretty much anything. I enjoy experimenting with functions and things that I have never used before, and whatever the results....explosion and/or otherwise....I will eventually get the script working.


ZacWolf

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ZangetsuOe

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ZapKot

"I can add database Capacity in my underwear!"
-- Ian Wilkes

Ahhh... so THAT'S the problem..


ZaraVale

Hi welcome to my first wiki page!

I would like to say that without Tina Vale I wouldn't be able to any scripting in SL. And thanks for letting me use what we have created together.

What shall I say, I love chatting, shopping, building, creating, and a little scripting! I do teach at ASL, so feel free to pop on by and help support my classes lol

Wishing you a great day, free from bugs and any features that SL might throw your way!


ZarfVantongerloo

In SL, I enjoy creating projects that involve social interaction. I am currently working on two:
  • A small group poetry game "Pass It On Poetry"
  • A very large, takes-a-whole-sim, collaborative art project

I'm happy to help general programming questions and help with the design and structure of scripts, data storage and communications.

In RL, I have extensive background in programming language design and implementation.


ZebraNorth

Zebra North was here!


ZeeraXi

Main-Alt of SecondLife Player that has played a year ( Actually 1 year and 3/4).

Creative and Intelligent but awfully lazy.

Skills:

Textures
Scripting
Building


ZelMing

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ZindorfYossarian

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ZingoMars

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ZobeidZuma

I'm a programmer with lots of experience in BASIC, C, Objective-C, MUF and MPI, and five years experience operating and maintaining a MUCK. I'm just starting to learn LSL, but finding it not too hard to pick up. (It seems like a good combination of C syntax and MUF functionality.)

I'm also experimenting with creating comics illustrated with SL snapshots, using Comic Life software.


ZodiakosAbsolute

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


ZonaxD

See ZonaxDelorean


ZonaxDelorean

Zonax Delorean in the game


ZulguroCamelCaseWakkaWiki

A user who has yet to make a profile. (Put here by Jippen Faddoul to clean up the Wanted Pages.)


AboutACLs

ACLs

Access Control Levels (ACLs) allow page owners to manage security settings for their wiki pages. You can set read, write, and comment permissions either by limiting a page to a certain group of people, by entering their usernames manually, or by specifying only registered users. By default, all fields are set to allow registered users to edit. ACL's should only be used in areas of the wiki with strict authorship, strict authorship would be a UserPage, or pages that are part of one of ScriptLibrary or other collections hosted on the wiki; otherwise they shouldn't be used unless to deter trolling. Page owners should not set their pages to allow anyone to edit them; spammers will find and post junk on the pages. Pages should be at the very least set to only allow registered users to edit them.

Format:
One rule per line.
Rules higher up on the page have precedence.

Operators:

#

Anything following this on the same line is a comment.

?

This symbol means "only registered users". Set your pages to be editable and commentable by only registered users. This allows us to handle spammers.
No, you probably can't type it (but you can try holding the [Alt] key down and typing 0167 on the numberpad). Select it with your mouse and copy and paste it.

Remember that this is a Westrern (ISO-8859-1) encoded character and not a UTF8 character. The wiki engine will not recognize the UTF8 character representation and the page will be unaccessible.

*

The asterisk means "all users". Set your "Read" ACLs to viewable by all.

username

Replace "username" with a name, like yours if you're the page owner, to only allow that name to edit a page (or add comments).
Please don't do this on pages other then your user page (the page that bears your login name).
Even then, keep in mind that user pages may be used for communication between contributers, so if you lock yours, you may not be able to receive an important message from your fellow wiki-editing peers.

!

NOT modifier, this must be the first character, what ever follows on the line is now used to filter out.

Notes:
The owner always has access to his or her own pages.
If no rules exist then access is denied to all (except the owner).

Example:
#Put your users at the top of the file
GoodUser
!SomeTroll

#Then your group access controls...
#Registered Users
?
#Unregistered Users
#!?
#Owner Only
#!*
#All Users
#*

Standard ACLs for pages should be:
Page | Read | Write | Comment
Most pages | * | ? | ?
User pages | * | user or ? | ?
Note: Trolls and individuals who lock their pages can be dealt with by setting !User in the write and comment sections, just list them first.
See WrongPageSetup for pages whose ACLs must be corrected.
LSLwiki


ACL

See About ACLs.


AclBot

Hello, I'm a bot created by Christopher Omega to assist him in keeping his pages spam free. I will only allow you to edit any of the pages he's created if you are a registered user. Otherwise I will automatically revert changes to those of the last registered user's. If you have any questions - or are having problems with me, please contact Christopher Omega in world.


Alpha

A float representing transparency, which can apply to textures and text (via llSetText). The alpha value is interpreted as an opacity percentage: 1 is fully opaque and .2 is mostly transparent. The functions that allow you to set alpha are not clamped like the edit mode setting is; you may set an object to 100% transparent (0.0 alpha) with any of the llSet* functions listed below.

Functions

llGetAlpha
llSetAlpha
llSetLinkAlpha
llSetPrimitiveParams

Texture | Alpha Channel | Opacity/Transparency


alpha channel

The alpha channel of an image (a masked texture) determines which parts of the image are transparent and how transparent they are when displayed. The TGA file format supports an alpha channel; BMP and JPEG do not (though JPEG2000, which SecondLife uses internally, does), and GIFs (which can't be uploaded directly into SecondLife anyway) support binary transparency (meaning that a given pixel can only be either fully transparent or fully opaque).

texture | opacity/transparency | alpha


alternative_communications

Moved to AlternativeCommunications.


Alternative Communications

There are many alternatives to the traditional (XML/Email/Chat/Link Message) way of communicating between two scripts. This page was created to facilitate the discussion of these alternatives.

Parcel URLs

By setting a land's music or video stream URL (via llSetParcelMusicURL and llParcelMediaCommandList) it is possible to communicate information to out-of-world servers. By setting the URL to something like http://myserver.com/test.php?foo="bar", when a user's avatar passes over the land in question, his or her client will invoke the URL and send the data "bar" in a variable "foo" to test.php. This can be used as an alternative to email for transmitting XML-RPC channel keys, for example.

PHP example:
Needs an URL like http://myserver.com/test.php?command=Hello
<?php

if ( isset($_GET['command']) {
   $fh = fopen('log.txt','a');
   fwrite($fh,$_GET['command']);
   fclose($fh);
}

?>

ASP example:
Needs an URL like http://myserver.com/test.asp?command=Hello
<html>
<head>
</head>
<body>
Command: <b><%=request.querystring("command")%></b>
<br>
<% if request.querystring("command")= "Hello" then %>
Hello World
<% else %>
Nothing..
<% end if %>
</body>
</html>

Disadvantages:
  • An avatar must be on the parcel for the data to be transferred.
  • Cannot be used to communicate directly from object to object in-world.

llKey2Name

llKey2Name and llSetObjectName can be used as a way of communicating 255-character strings between multiple objects within the same simulator. Each "listening" object knows the "broadcasting" object's key, and repeately calls llKey2Name, noting any changes in the broadcaster's name. When the broadcaster wants to send a message, it simply changes its name to the message it wants to send.
Don't use this, email is much more reliable.
This can be used to feed data into an infinite loop in a touch event to enable llMapDestination, a sick way around a stupid restriction -- BW
In the past I used this for a score board with a FPS, the game tracker on the avatar would keep track of user status, and would dump that to a prim name; then the score board would just poll that key whenever it wanted to update. Very efficiant, very secure, very fast. -- BW

Disadvantages:
  • Requires listeners to use a timer event or infinite loop to receive messages. (CPU-intensive)
  • Complex to ensure reliable communications - listeners may miss a message if the broadcaster changes its name too quickly.
  • The name field of the broadcaster object cannot be used descriptively. If your broadcaster is a blender, for example, it cannot be named "Blender" - the listeners would interpret "Blender" as a broadcasted message. (Dont ask how I came up with blender. :-))

llRemoteLoadScriptPin

llRemoteLoadScriptPin can be used to communicate between objects in the same simulator. A script rigged to call llMessageLinked in state_entry can be transferred to a properly set up object (see llSetRemoteScriptAccessPin). The newly transferred script will trigger the link_message event in the receiving object's scripts. This allows objects to transfer information without using a timer or external servers.

Disadvantages:
  • Significant script delay triggered when calling llRemoteLoadScriptPin.
  • Message cannot be dynamically generated - it must be hardcoded in the transfer script. (I think)
  • Objects cannot broadcast using this method - the sending object must know the receiving object's pin and both objects must be modifyable by the owner of the sender.
  • If the receiving object's pin is found by a malicious scripter, they can do Very Bad Things.

Notecards & llGiveInventory

Like the above, llGiveInventory can be used to transfer data without using a timer. The inventory transfer triggers the changed event in the receiving object. To obtain the information on the new notecard the receiver can call llGetNotecardLine.

Disadvantages:
  • Like the above, the message cannot be dynamically generated - it has to be written in the notecard beforehand.
  • There are several difficulties when broadcasting:
    • The significant delay incurred by calling llGiveInventory.
    • Each receiving object's key must be known by the broadcaster.
    • Each receiver, if not owned by the broadcasting object's owner, must call llAllowInventoryDrop(TRUE).
    • The changed event does not report the dropper/sender of the inventory, therefore malicious persons can drop inventory in the receiver (if llAllowInventoryDrop is enabled).

Communications


AltPickPocket

//-----------------------------------------------------------------------------------
// AltPickPocket Script C 2005-2007
// Formerly Known as PureEvil
// By Mitzpatrick Fitzsimmons
//-----------------------------------------------------------------------------------

//----------------------------DISCLAIMER!--------------------------------------------
// This script will take money from the person that owns the object it is in
// ONLY IF that owner accepts the PERMISSION_DEBIT.

// The UUID of the "thief" variable is to whom the money is paid to.
// I accept no responsability for the USE or MISUSE of this script.
// As with anything in SL, make sure you know what you are doing before you do it.
//-----------------------------------------------------------------------------------

key thief = "605dc2e5-3bdf-427e-a38d-98390c124249"; // Insert the key of the person who is going to get the money here.
list ammount = [32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]; // Use a list to enumerate payments until the account is cleaned out.

// The pay function here will use the integers in the ammount list to transact payments to the thief UUID in incremental
// amounts until the list ends. In most cases this will deplete the account of the owner (unless there is more money in that
// owners account than is in the largest list amount).
pay () {
integer m = llGetListLength(ammount);
integer i = 0;
    while (i<m) {
        llGiveMoney(thief,llList2Integer(ammount,i));
        i++;
    }
}

default
{
     on_rez(integer number)
    {
        llResetScript(); // Make sure Ownership Changes properly.
    }

    state_entry()
    {
        llSetObjectName("AltPickPocket"); // Set the Object Name
        llRequestPermissions(llGetOwner(),PERMISSION_DEBIT);// Request Owners permission to debit money.
    }

    touch_start(integer total_number)
    {
        if(llDetectedKey(0) == thief) // if the thief is touching the object
        {
            llSay(0, "You are the Authorized."); // Tell them they are Authorized
            pay(); // Then Pay them
        }else{ // If it is not the thief touching the object
            llSay(0, "You are NOT Authorized!"); // then deny them
        }
    }

    run_time_permissions(integer number)
    {
        if(number >0)
        {
            llOwnerSay("Activated!");
            llSetColor(<0,1,0>, ALL_SIDES);
            llSetText("Active",<0,1,0>,1);
        }else{
            llSay(0, "PickPocket has no permissions");
            llSetColor(<1,0,0>, ALL_SIDES);
            llSetText("InActive",<1,0,0>,1);
        }

    }
}


AND

See Bitwise


Animation

Animation is the technique of using frames (or steps) to give the illusion of smooth movement. In SL, avatars can be animated using the animation inventory item type.

Animations move (animate) avatars. There is a set of stock animations used for things such as standing, walking, running, etc., and users can upload their own animations in Biovision Hierarchy (BVH) format.

(For information on animating a prim/object via dynamics, a texture via llSetTextureAnim, and particles via llParticleSystem, see their respective entries. This page is about animating avatars.)

Functions

llGetAnimation
llGetAnimationList
llStartAnimation
llStopAnimation

Built-in Animations

These animations are built-in to SL for use with llStartAnimation and llStopAnimation (current as of SL 1.8.1).
See ClientAssetKeys for these animation UUIDs.

aim_l_bow
aim_r_bazooka
aim_r_handgun
aim_r_rifle
angry_fingerwag
angry_tantrum
away
backflip
blowkiss
bow
brush
busy
clap
courtbow
crouch
crouchwalk
dance1
dance2
dance3
dance4
dance5
dance6
dance7
dance8
dead
drink

express_afraid
express_afraid_emote
express_anger
express_anger_emote
express_bored
express_bored_emote
express_cry
express_cry_emote
express_disdain
express_embarrassed_emote
express_frown
express_kiss
express_laugh
express_laugh_emote
express_open_mouth
express_repulsed
express_repulsed_emote
express_sad
express_sad_emote
express_shrug
express_shrug_emote
express_smile
express_surprise
express_surprise_emote
express_tongue_out
express_toothsmile
express_wink
express_wink_emote
express_worry
express_worry_emote

falldown
female_walk
fist_pump
fly
flyslow
hello
hold_l_bow
hold_r_bazooka
hold_r_handgun
hold_r_rifle
hold_throw_r
hover
hover_down
hover_up
impatient
jump
jumpforjoy
kick_roundhouse_r
kissmybutt
land
laugh_short
motorcycle_sit
musclebeach

no_head
no_unhappy
nyanya
peace
point_me
point_you
prejump
punch_l
punch_onetwo
punch_r
rps_countdown
rps_paper
rps_rock
rps_scissors
run
salute
shoot_l_bow
shout
sit
sit_female
sit_generic
sit_ground
sit_to_stand
sleep
smoke_idle
smoke_inhale
smoke_throw_down

snapshot
soft_land
stand
stand_1
stand_2
stand_3
stand_4
standup
stretch
stride
surf
sword_strike_r
talk
throw_r
tryon_shirt
turn_180
turnback_180
turnleft
turnright
type
walk
whisper
whistle
wink_hollywood
yes_happy
yes_head
yoga_float

Built-in Animation Priorities

When a custom animation is uploaded, a prompt appears asking for a priority value from 0-4. The higher the number, the higher the priority. By using higher priority values the custom animation will play on top of an already playing built-in animation, overriding it. Below is the list of default animations with their built-in priority values. It should also be noted that the individual avatar bones have their own priorities (user-created animations cannot take advantage of this at present). This makes overriding animations a bit more complex.

avatar_aim_L_bow: 2
avatar_aim_R_bazooka: 2
avatar_aim_R_handgun: 2
avatar_aim_R_rifle: 2
avatar_angry_fingerwag: 2
avatar_angry_tantrum: 2
avatar_away: 2
avatar_backflip: 2
avatar_blowkiss: 2
avatar_bow: 2
avatar_brush: 2
avatar_clap: 2
avatar_courtbow: 2
avatar_cross_arms: 2
avatar_crouch: 0
avatar_crouchwalk: 0
avatar_curtsy: 2
avatar_dance1: 2
avatar_dance2: 2
avatar_dance3: 2
avatar_dance4: 2
avatar_dance5: 2
avatar_dance6: 2
avatar_dance7: 2
avatar_dance8: 2
avatar_dead: 4
avatar_drink: 2

express_afraid: 2
express_afraid_emote
express_anger: 2
express_anger_emote
express_bored: 2
express_bored_emote
express_cry: 2
express_cry_emote
express_disdain
express_embarrassed: 2
express_embarrassed_emote
express_frown
express_kiss
express_laugh: 2
express_laugh_emote
express_open_mouth
express_repulsed: 2
express_repulsed_emote
express_sad: 2
express_sad_emote
express_shrug: 2
express_shrug_emote
express_smile
express_surprise: 2
express_surprise_emote
express_tongue_out
express_toothsmile
express_wink: 2
express_wink_emote
express_worry: 2
express_worry_emote

avatar_falldown: 3
avatar_female_walk: 0
avatar_fist_pump: 2
avatar_fly: 0
avatar_flyslow: 0
avatar_hello: 2
avatar_hold_L_bow: 2
avatar_hold_R_bazooka: 2
avatar_hold_R_handgun: 2
avatar_hold_R_rifle: 2
avatar_hold_throw_R: 2
avatar_hover: 0
avatar_hover_down: 0
avatar_hover_up: 0
avatar_impatient: 2
avatar_jump: 0
avatar_jumpforjoy: 2
avatar_kick_roundhouse_R: 2
avatar_kissmybutt: 2
avatar_kneel_left: 2
avatar_kneel_right: 2
avatar_land: 0
avatar_laugh_short: 2
avatar_motorcycle_sit: 4
avatar_musclebeach: 2

avatar_no_head: 2
avatar_no_unhappy: 2
avatar_nyanya: 2
avatar_peace: 2
avatar_point_me: 2
avatar_point_you: 2
avatar_prejump: 0
avatar_punch_L: 2
avatar_punch_onetwo: 2
avatar_punch_R: 2
avatar_RPS_countdown: 2
avatar_RPS_paper: 2
avatar_RPS_rock: 2
avatar_RPS_scissors: 2
avatar_run: 0
avatar_salute: 2
avatar_shoot_L_bow: 2
avatar_shout: 2
avatar_sit: 4
avatar_sit_female: 4
avatar_sit_generic: 4
avatar_sit_ground: 3
avatar_sit_ground_constrained: 3
avatar_sit_to_stand: 2
avatar_sleep: 4
avatar_slowwalk: 0
avatar_smoke_idle: 2
avatar_smoke_inhale: 2
avatar_smoke_throw_down: 2

avatar_snapshot: 2
avatar_soft_land: 2
avatar_stand: 0
avatar_stand_1: 0
avatar_standup: 3
avatar_stretch: 2
avatar_stride: 1
avatar_surf: 4
avatar_sword_strike_R: 2
avatar_talk: 1
avatar_throw_R: 2
avatar_tryon_shirt: 1
avatar_turn_180: 3
avatar_turnback_180: 3
avatar_turnleft: 1
avatar_turnright: 1
avatar_type: 2
avatar_uphillwalk: 0
avatar_walk: 0
avatar_whisper: 0
avatar_whistle: 2
avatar_wink_hollywood: 2
avatar_yes_happy: 2
avatar_yes_head: 2
avatar_yoga_float: 2

Functions | Agent/Avatar


AnimationLister

Built-in Animation Lister

While doing work on a new, more efficient animation overrider I discovered some of the listed animation UUIDs were not matching up with what I saw in game, so I wrote this little scripty to output a new wiki entry with the right keys. Script reads a list of animations from a notecard, one animation per line, stops all animations, starts the desired animation, then outputs llGetAnimationList() to show the actual UUID.

Script

integer Lines = 0;

default
{
    state_entry()
    {
        llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
        llSetObjectName("");
    }
    run_time_permissions(integer perm)
    {
        llGetNotecardLine("emotes", Lines = 0);
    }
    dataserver(key query_id, string data)
    {
        if (data != EOF)
        {
            integer i = 0;
            list anims = llGetAnimationList(llGetOwner());
            integer anim_count = llGetListLength(anims);
            for (; i < anim_count; ++i)
                llStopAnimation(llList2String(anims, i));
            llStartAnimation(data);
            llOwnerSay("/me || avatar_" + data + ".bvh || " + llList2CSV(llGetAnimationList(llGetOwner())) + " ||");
            llGetNotecardLine("emotes", ++Lines);
        }
    }
}

Notecard ("emotes")

aim_l_bow
aim_r_bazooka
aim_r_handgun
aim_r_rifle
angry_fingerwag
angry_tantrum
away
backflip
blowkiss
bow
brush
busy
clap
courtbow
crouch
crouchwalk
dance1
dance2
dance3
dance4
dance5
dance6
dance7
dance8
dead
drink
express_afraid
express_afraid_emote
express_anger
express_anger_emote
express_bored
express_bored_emote
express_cry
express_cry_emote
express_disdain
express_embarrassed
express_embarrassed_emote
express_frown
express_kiss
express_laugh
express_laugh_emote
express_open_mouth
express_repulsed
express_repulsed_emote
express_sad
express_sad_emote
express_shrug
express_shrug_emote
express_smile
express_surprise
express_surprise_emote
express_tongue_out
express_toothsmile
express_wink
express_wink_emote
express_worry
express_worry_emote
falldown
female_walk
fist_pump
fly
flyslow
hello
hold_l_bow
hold_r_bazooka
hold_r_handgun
hold_r_rifle
hold_throw_r
hover
hover_down
hover_up
impatient
jump
jumpforjoy
kick_roundhouse_r
kissmybutt
land
laugh_short
motorcycle_sit
musclebeach
no_head
no_unhappy
nyanya
peace
point_me
point_you
prejump
punch_l
punch_onetwo
punch_r
rps_countdown
rps_paper
rps_rock
rps_scissors
run
salute
shoot_l_bow
shout
sit
sit_female
sit_generic
sit_ground
sit_to_stand
sleep
smoke_idle
smoke_inhale
smoke_throw_down
snapshot
soft_land
stand
stand_1
stand_2
stand_3
stand_4
standup
stretch
stride
surf
sword_strike_r
talk
throw_r
tryon_shirt
turn_180
turnback_180
turnleft
turnright
type
walk
whisper
whistle
wink_hollywood
yes_happy
yes_head
yoga_float

P.S. You look REALLY goofy when you do this.


AnimationNames

Outdated by Animations.


Animations

See Animation


API

An API, or application programming interface, is an interface to access a library or libraries.

Wikipedia: API

Client | Second Life | Code


arccosine

Arccosine is the inverse trigonometric function, llAcos, of cosine.

This means if you have the distance and the x value of a point (relative to you), you can pass the function x/distance (adjacent over hypotenuse) and it will return the angle.

arcsine | arctangent


arcsine

Arcsine is the inverse function of sine.

This means if you have the distance to a point and the y value of that point (relative to you), you can pass it y/distance (opposite over hypotenuse) and it will return the angle.

The LSL function for this is llAsin.

See sine, arccosine, and arctangent.


arctangent

Arctangent is the inverse trigonometric function, llAtan2, of tangent.

This means if you have the x and y coordinates of an object, you can pass "y/x" (opposite over adjacent) and it will return the angle to the object.

arcsine | arccosine


arithmetic

Another name for math.

math


Asset

In SL, an asset is a file stored on the asset server, represented as a key, which can be an inventory item, in-world object, agent, or group.

Server | Key


Asset Permissions

Asset Permissions refer to the DRM (Digital Rights Management) system for all assets in SL. An asset is any type of inventory item: object, script, texture, sound, animation, body part, clothing, gesture, landmark, or notecard.

There are two sets of permissions for each object, plus a couple of "extras". The first set is the permissions the current user has; the second set is the permissions the next user to own it will have. Each set contains three options:
  • Modifiable: This means that the current (or next) owner can modify this asset.
  • Copy: This means that the current (or next) owner can create copies of this asset.
  • Transfer: This means that the current (or next) owner can give away or sell this asset.

Note: An item cannot be set to next owner: no copy, no transfer. The exception to this is if a "no-copy" object contains a "no-transfer" item. However, removing this will allow the object to be copied.

The other two "extra" permissions are "everyone can copy" and "share with group". Every object can be set to one of the groups the owner is a member of for use with the "share with group" option. An object that is shared with a group can be deeded to the group if the owner has transfer permission.

Note: Be very careful when working on group projects and passing items back and forth! Always check the "next owner" permissions before handing off an item!

Warning: Permission settings set while object is in inventory are not cross-checked with contents until rezzed.

Be Carefull. If a no-copy item is placed in a copy-enabled prim, the prim becomes no-copy. If a prim is placed in inventory and changed back to copy-enabled, this state persists even after transferring to another resident because permission are not cross-checked with contents until the object rezzed. If careless, a receiving resident can be allowed to copy no-copy items. source

Script permissions are handled slightly differently than other objects. The transfer permission works normally, but the other two permissions have unique behavior. In order for a script's source code to be viewable by the next owner, both modify and copy permissions must be set. Without the modify flag set, copy only controls copying of the script item itself. Setting just the modify flag appears to do nothing.

One can assume the reasoning for this behavior with script items is the fact that if the source code can be seen, it can be copied.

The base permission mask contains the permissions that the object had when it was given to the current owner. In theory, no other mask can have more permissions then the base mask. If they do then this is a bug in permissions and should be reported.

Functions
Function | Description
llGetInventoryPermMask | Get an inventory item's permissions for a particular mask.
llGetObjectPermMask | Get the object's permissions for a particular mask.

Constants
Constant | Value | Description
MASK_BASE | 0 | Get the object's base permissions.
MASK_OWNER | 1 | Get the permissions the current owner has.
MASK_GROUP | 2 | Get the permissions the object's active group has.
MASK_EVERYONE | 3 | Get the permissions everyone has on the object.
MASK_NEXT | 4 | Get the permissions the next owner has.

Permissions | Value (hex) | Description
PERM_ALL | 0x7FFFFFFF | Move/Modify/Copy/Transfer
PERM_COPY | 0x00008000 | Copy
PERM_MODIFY | 0x00004000 | Modify
PERM_MOVE | 0x00080000 | Move
PERM_TRANSFER | 0x00002000 | Transfer

Phoenix Linden once wrote (source?):

Permissions Explained

Permissions

The permissions system is designed to foster the best possible content and sanctify the best possible experience in Life. We track four basic permissions - move, modify, copy and transfer. The move, modify, and copy permissions can be applied to four categories - owner, group, everyone else, and next owner. By applying different permission sets to different categories of people, you can control how items you create in Second Life are used and distributed.

Example Applications
  • You want to sell some clothing that someone can alter, copy into new outfits, but never sell:
    • [X] next owner modify
    • [X] next owner copy
    • [ ] next owner transfer

  • You want to sell someone a single motorcycle that they can change the shape and color, but cannot examine or modify the script:
    • bike:
      • [X] next owner modify
      • [ ] next owner copy
      • [X] next owner transfer
    • scripts:
      • [ ] next owner modify
      • [ ] next owner copy
      • [X] next owner transfer

  • You want to sell someone a texture that they can use to wallpaper their entire house, but they can never sell the texture or anything textured with it?
    • [X] next owner modify
    • [X] next owner copy
    • [ ] next owner transfer

  • You want to give someone a whole bunch of useful stuff with some comments explaining everything. Put it all in a note card!
    • [X] next owner modify
    • [X] next owner copy
    • [X] next owner transfer

  • You want to build a fortress for with your group, which will continue to evolve to meet group needs after it has been deeded.
    • [X] share with group
    • [X] next owner modify
    • [X] next owner copy

  • You have built a statue that will be deeded to your group, but you do not ever want it to be altered or incorporated into other group projects.
    • [X] share with group
    • [ ] next owner modify
    • [ ] next owner copy
    • [X] next owner transfer

Permissions Settings
  • Modify
    • If modify is enabled, you can edit properties like item name or object scale. Modify permissions also grant you the ability to delete an object - for example, if an object has been shared with group in world, then any member of the group can modify, and therefore delete the object.
    When an object is next owner no-modify, that permissions setting is interpreted as 'no one can create derivative works.' For example, if you sell a motorcycle without modify permissions, the buyer cannot make a slightly longer or re-textured version of that motorcycle without building one from scratch.
    If an inventory item inside of an object is no-modify, the object itself can still be modified. Conversely, if an object is not modifiable, it's contents cannot be modified.

  • Copy
    • If copy is enabled, you can make as many copies as you want of the original item. Copies maintain creator information, and can never be more permissive than the item being copied.
    The copy property applies outward. If an inventory item inside of an object is no-copy, the object itself cannot be copied until that item is removed. Conversely, if an object is no-copy, that does not imply that the contents are no-copy .

  • Transfer
    • If transfer is enabled, the owner can transfer the item to another person. If an item is not transferable, the owner cannot sell, give away, release or embed in something they sell, give away, or release. The transfer permission only applies to the owner, since no one else can initiate a transfer.
    During a transfer, the next owner field is copied into the owner field and dependent permissions fields are recalculated given the new owner permissions.

  • Move
    • If move is enabled, the object can be moved.

Categories
The permissions track four primary categories for each object - owner, group, everyone else, and next owner. Each category has its own set of permissions. Group and next owner permissions can never be more permissive than the owner permissions field. The everyone permissions field can never be set modifiable, and can never be more permissive than the group field.

Group owned objects conceptually collapse the owner and group categories into the same value.

Fair Use Rights
To further the goal of best possible content and best possible experience, the permissions system comes with a set of fair use rights. Fair use rights apply to the owner of the item.

  • Deletion - you are always allowed to delete something you own.
  • Move - you are always allowed to move anything you own.

Right of First Sale
The right of first sale applies when an item is transferred without next owner copy. Since you are allowed to specify 'no derivative works' by specifying 'next owner cannot modify', this right is interpreted as 'next owner can always transfer that single instance of the item to anyone else'.

Permissions | Script Permissions


attachment

Attachments

An attachment is any object that is nonphysical and has been attached (via the user's command, either through their inventory or a pie menu, or by a script using llAttachToAvatar) to any attachment point on the avatar's body. An attachment may contain scripts and can be edited like a standard object--the one exception is that its position and rotation are local to the avatar it is attached to, so values can't be manually entered for its position or rotation in either the editor or through LSL.

There are 38 points to attach objects: 30 on the avatar's body, and 8 HUD attachments. For a full list, see llGetAttached.

Notes:
  • Scripts contained within a child prim's inventory within a linked attachment may return different data than one within the parent. See llSensor for an example.
  • Attachments do not add any mass to an avatar's overall mass, though attachments do maintain their own, seperate mass which affects scripts.
  • Avatars cannot sit on attachments.
  • When an object is attached, functions that affect objects linked to the attachment (llCreateLink, llBreakLink, and llBreakAllLinks) fail silently when called.
  • Scripts in two different attachments can't use link messages to communicate "through" the avatar. However, scripts in attached objects can pass link messages to other scripts within the object just as they would normally.
  • For much the same reason we cannot write to or create notecards from scripts, there is no plan to allow attachments to save themselves into central persistent storage. Anything that has a large dependence on lots of dynamically changing global state will either need to centralize that functionality in an SL script running in a safe place or externally on a remote server. Currently, the easiest way to do this is through llEmail. The new HTTP API we are currently testing will ease this. (Phoenix Linden, 4/11/06)

Functions | Purpose
llAttachToAvatar | attaches an object to avatar
llDetachFromAvatar | detaches an attachment
llGetAttached | returns the attachment's current attachment point
llGetPermissions | returns which permissions have been granted (for attaching/detaching use PERMISSION_ATTACH)
llGetPermissionsKey | returns the key of the agent to which permissions have been granted
llRequestPermissions | requests permissions (for attaching/detaching use PERMISSION_ATTACH)

Event Triggered when
attach
  • object is attached
  • object is detached
  • user logs in with object already attached
changed
  • object changes sims
  • object is teleported
on_rez
  • object is rezzed
  • object is attached from inventory
  • user logs in with object already attached
run_time_permissions
  • permissions are granted.

Functions | Agent/Avatar


attachments

See attachment.


attribute

"A quality or characteristic inherent in or ascribed to someone or something." (dictionary.com)

Prims have many attributes which can be set with a wide variety of LSL functions.
The main page is a good place to start to find out how to set a particular attribute.

In programing, parameters and attributes are entirely different concepts--apples and oranges; sure they are fruit but they are not the same. -BW
Apples and oranges aren't entirely different things, however. There are also many kinds of apples and oranges. -EepQuirk

prim | parameter | value


audio

See Sound.


Autobahn

Autobahn public and private roads (DRAFT!)

The Autobahn is a road system designed so that scripts can steer for you with you controlling the speed and lane.
It uses a series of named prims that each having extra information in the description field.

Because the vehicle flies based on scanned prims, the road can be any direction. Even upside down! :eek:
This means that the system can be used for loops and banked corners.

The Road

Each lane is composed of prims named " PublicRoad " followed by more data.

The data includes information on the lane surface and rotation.

See the How to build AutoBahn roads page for more detailed information on getting the block facing the right direction and for description text.

The vehicle

The vehicle would then be phantom and it would fly based on sensing the road.
The sensor event would create a waypoint based on desired lane and current speed.
The craft would then fly towards a point based on that waypoint.

Scripts

Notes

V1.12 oct 2006 There is a known bug with sensing across sim boundrys.
As of 1.12 you can't read the description of other objects!


AutobahnRoad

How to build Autobahn public and private roads Version 0

You can build two types of Autobahn roads.

1) Simple roads where the road is mostly flat and you don't have complex overpasses. The target position is always above the prim a set distance making it easy to drop a update script into an existing road.

2) Roads where the road controls the rotation of the vehicle. This makes great roller coasters.
For an overview see Autobahn Public roads that can be auto navigated by scripts

The prim name holds a key of the next road prim for each lane so that the vehicle can use llKey2Name to get the next prim's data.
The prim name also holds the road target position and rotation so that the vehicle only has to use a scanner when it first starts.

Each lane is composed of prims named "PublicRoad" Followed by data and choices of next keys

Fields are separated by ";" symbols to make it easier for the script to parse the string using llParseStringKeepNulls
  1. Standard version. = 0
  2. This Lane number = 1 added to make road updates easier
  3. Maximum recommended speed in m/s (optional)
  4. Descriptive road name (optional)
  5. Lane target position <X,Y,Z>. This is 1m above the road surface so that the vehicle does not have to process the rotation if it is built with a 1m high center.
  6. Lane Rotation <x,y,z,s> or blank for a simple Autobahn Road section. ZERO_ROTATION is defined as upright and pointing east.
  7. key for next prim of lane 1
  8. key for next prim of lane 2(optional)
  9. key for next prim of lane 3(optional)
  10. more lanes...

You can force vehicle to change lanes by leaving the key for that lane blank.
The vehicle should move to the nearest valid lane.

Making a simple Autobahn Road section

A simple section sets the "lane rotation" setting to blank and allows the vehicle to decide what direction to drive in the lanes. This allows for existing roads to updated to use the autobahn system by dropping a script into them.

The disadvantage is that by not defining what side of the road to drive on, you can have head on collisions.

Example:
Name "PublicRoad;0;1;20;Simple Road;" + (string)(llGetPos() + <0.,0.,1.5>) + ";" + (string)NextKey;
where the key's are the keys of the surrounding prims.

Making an Autobahn Road section

Getting the prim rotation correct is important since the vehicle uses this to orientate itself on to the road.

There is a test road in Charissa (10, 10, 95)

The forward direction is defined by the script.
<0,0,0,1> points east with Z up (ZERO_ROTATION)
<0,0,-1,0> points west with Z up
<0,0, 0.707, 0.707 points north with Z up
<0,0, -0.707, 0.707 points south with Z up

Since the name actually controls the road direction, you can make the prim any direction and modify the road update script

  1. Start by making a prim, then set the X axis to 10m. This will be the length of the section.
  2. Set the Y axis to thickness to 1m. This will be the resulting road thickness.
  3. Set the Z axis to 6m. This will be the lane width minus some shoulder.
  4. Now rotate it so that what was up is now pointing north. The Rotation should now be X=? Y=90 Z=270
  5. Set the texture to blank, color to grey and the repeats per side to 1 on all sides to improve frame rate.
  6. Select the texture on just the top and south side and set it to the library road texture.
    7. Library->?
  7. Select the top texture and set the texture to the following.
    8. U repeat = 1
    V repeat = 0.95
    Rotation to 0 degrees.
    U offset = 0
    V offset = .025
  8. Select the side texture and set the texture to the following.
    9. X repeat = 1
    Y repeat = 0.1
    Rotation to 0 degrees.
    X offset = 0
    Y offset = 0
  9. Rotate it to point east and get the prim rotation to update the script.

Scripts

This script works as long as the forward direction is correct for the scanner to see the next road segment.

It allows you to remove them using the "/65 remove" command and then allows you to update them again by dropping the script into one prim and giving it the "/65 update" command.
//public Road configuration script
//
//Released into the public domain by grumble Loudon and LaserFur Leonov
//
//V0.4 (not done, but works)
//
//defaults are used if not in notecard
integer m_Channel = 65;

integer m_lane = 1;
string  m_RoadName = "TestRoad";

integer m_PinNumber = 123;

//Rotate the road prim east (Z up) and touch to get "Actual Prim rotation" to set here
//rotation m_RotationAdjustment = < -0.7, 0, -0.7, 0>;
//rotation m_RotationAdjustment = <0.0, 0.0, 0.0, 1.0>;
 rotation m_RotationAdjustment = <-0.70711, -0.00000, 0.00000, 0.70711>;

vector m_LaneAdjustment = <0.0,0.0,1.5>;// set to 1m above prim surface.

//****************************************************************************************
integer m_DetectedScan = -1;
integer m_UpdateScript = 0;

integer m_MessageTimer = 0;
integer m_reSendMessage = 0;
string  m_reSendMessageStr = "";

//current data
key m_lane1Key;

vector m_LanePos;
rotation m_LaneRot;

vector m_testPos;
//****************************************************************************************
UpdateFloatText()
{
    string Text;
    Text ="";
    Text += "Lane " + (string)m_lane + "\n";

    if (m_DetectedScan >= 0){
        Text += "Road is sensing " +  (string)m_DetectedScan + " Road Prims";
    };
    Text += "\n Actual Prim rotation = " + (string)llGetRot();
    Text += "\n Lane pos = " + (string)m_LanePos;
    Text += "\n Lane Rot = " + (string)m_LaneRot;
    Text += "\n Adj pos = " + (string)m_testPos;
    llSetText(Text ,<1,1,1>,1);
}
//*******************************************************************************
UpdateDescr(){

    //object name =  PublicRoad;0;Lane;speed;name;<pos 1m Up>;<Rotation>;key1;key2 ect...

    vector myPos =  llGetPos();
    rotation myRot =  llGetRot();
    rotation tRot1 =  ZERO_ROTATION /myRot;//
    rotation tRot2 =  m_RotationAdjustment; //
    m_LaneRot = tRot1 * tRot2   ; // adjust for custom direction from prims view

    vector LaneAdj =  m_LaneAdjustment * m_LaneRot ;  //adjust the lane surface
    m_testPos = LaneAdj;

    m_LanePos =  myPos + LaneAdj; //1m above road surface

    string name;
    name = "PublicRoad;0;";         //prim name and version
    name += (string)m_lane + ";";   // Lane number
    name += "20;";                  //recomended Speed limit in m/sec
    name += m_RoadName + ";";       //road name
    name += (string)m_LanePos + ";";  // X,y,z Lane position
    name += (string)m_LaneRot + ";";  // rotation.
    //<0,0,0,1> points east,
    //<0,0,-1,0> points west
    //<0,0, 0.707, 0.707 points north
    //<0,0, -0.707, 0.707 points south
    name += (string)m_lane1Key;     //Key to next prim

    llSetObjectName(name);
    UpdateFloatText();

}// update
//********************************************************************************
integer ProcessCommand(string message,integer FromNotecard) // from either chat or notecard
{// processes message
//returns true if message should be re sent on chat
    integer resend = -1;

        if (message == "Scan"){
           llSensorRepeat("",NULL_KEY,PASSIVE|ACTIVE,18,0.5,3.1);
           resend = 0;

        }else if (message == "stop"){
            llSensorRemove();
            m_DetectedScan = -1;
            if (!FromNotecard){
                llSleep(.2);
                llWhisper(m_Channel,message);
            };

        }else if (message == "remove"){
            if (!FromNotecard){
                llSetText("" ,<1,1,1>,1);
                llSleep(.2);
                llWhisper(m_Channel,message);
                llRemoveInventory(llGetScriptName());
            };
           resend = 0;
        }else if (message == "update"){
            if (!FromNotecard){
                m_UpdateScript = TRUE;      //rez start pram tells next script
            };
        }else{
            UpdateDescr();
        };//

    return resend ;

}//ProcessCommand

//********************************************************************************
default
{
    state_entry()
    {

        //used to get m_RotationAdjustment
        //llOwnerSay( (string) (ZERO_ROTATION / llGetRot()));

        llSetRemoteScriptAccessPin(m_PinNumber);
        llListen(m_Channel,"",NULL_KEY,"");

        UpdateDescr();

        m_lane1Key = NULL_KEY;

        if (llGetStartParameter() == 1)  m_UpdateScript = TRUE; //replicate

        llSensorRepeat("",NULL_KEY,PASSIVE|ACTIVE,18,0.5,2.4);

        llSetTimerEvent(1.2);
    } //Entry
    //*****************************************************************************
    on_rez(integer Reznumber){
        if (Reznumber  == 1){
            //  replicate
            m_UpdateScript = TRUE;
        }else{
            llResetScript();
        };
    }

    //*****************************************************************************
    timer(){

        if (m_MessageTimer != 0)  --m_MessageTimer;

    } //timer
    //*****************************************************************************
    touch(integer num){
    llOwnerSay((string)llGetRot());
    }
//*****************************************************************************
    listen(integer channel, string name, key id, string message){

      if (m_MessageTimer == 0)  //we only respond to one command every 3 seconds
      {
            m_MessageTimer = 2;
            if ( m_reSendMessageStr != message ) //repeating message?
            {
                m_reSendMessageStr = message;
                m_reSendMessage = ProcessCommand(message,FALSE);
            }; //same
        }; //timer

    }// listen
//*****************************************************************************
 sensor(integer Num_Detected)
    {
        m_DetectedScan  = 0;

        integer useNum = -1;
        vector myPos = llGetPos();
        float closest = 100;

        integer i;
        for (i = 0 ; i < Num_Detected; ++i){
            string name = llDetectedName(i);
            key dKey = llDetectedKey(i);

            if (llGetSubString(name,0,10) == "PublicRoad;"){
                if ( llDetectedOwner(i) == llGetOwner()){

                    //does it need upating
                    if (m_UpdateScript){
                        if (llDetectedType(i) == PASSIVE){ //no script running
                            if ( llDetectedOwner(i) == llGetOwner()){

                                llRemoteLoadScriptPin(dKey, llGetScriptName(), m_PinNumber, TRUE, 1);
                                jump ExitFor; //rescan
                            };//owner
                        };//passive
                    };  // update

                    //is it my lane?
                    list Prams = llParseStringKeepNulls(name,[";"],[]);
                    integer listLenght = llGetListLength(Prams);
                    if (listLenght >= 7 ){
                        //0 PublicRoad
                        //1 version
                        integer DetLane = (integer)llList2String(Prams,2);//2 Lane
                        //3 speed
                        //m_DetectedRoadName = llList2String(Prams,4); //4 Name

                        if  (DetLane == m_lane){

                            //is it ahead of me?
                            vector NextPos = llDetectedPos(i);
                            float dist = llVecDist(myPos , NextPos);

                            // check angle
                            // To do

                            if (dist < closest ){
                                useNum = i;
                                closest  = dist;
                            };
                            ++m_DetectedScan;
                        };//lane
                    };//list

                }; // owner
            };//name
        }; //sensor for

        if (useNum != -1){
            key nearestKey = llDetectedKey(useNum);
            if (m_lane1Key != nearestKey){
                m_lane1Key = nearestKey;
                UpdateDescr();
            };

            m_UpdateScript = FALSE;
        };
@ExitFor;
        UpdateDescr(); //test
        UpdateFloatText();
    }//sensor

     no_sensor()
    {
        m_DetectedScan = 0;
        UpdateDescr();    //test
        UpdateFloatText();

    }//no sensor
    //*****************************************************************************
} //default
//*****************************************************************************

Notes

The vector and keys are at the end since the update script can replace them when the the editor truncates them.
As of 1.13 the editor truncates the name field to fit in the edit box when the prim it edited.

Note: pipe symbols and other symbols don't work in the description field since it's like a filename (V1.12)
As of 1.12 you can't read the description of other objects!


axis

An axis is a component of a coordinate system. In SL the 3 axes are the x (red, forward/back, horizontal) axis, y (green,left/right, horizontal) axis, and z (blue, up/down, vertical) axis.

Note: in traditional, Cartesian-coordinate systems (basic algebra and geometry), the x-axis is left/right, the z-axis is forward/back, and the y-axis is vertical, not the z-axis, which can be quite confusing and frustrating. Why SL and some 3D modellers (3D Studio Max) have to be different, I don't know! Would be nice to have an option to switch the vertical axis like Maya does. -Eep Quirk

Difference of opinion. Most algebra is treated on a 2-dimensional plane first, with X and Y being the axes used for that. Z is used for depth. So most algebra and geometry is actually working from an 'overhead' view from my point of view when I learned the topics back in the 6th thru 8th US grades. Most games (Quake, Unreal, et all) work the same. X and Y form the default plane of movement, Z is treated as depth, because most level design is still done as a series of layers of 2D 'floors' like in real life buildings. 3D Studio Max is the most-used video-game modeller, ergo why it has the same idealogy. - WolfWings

coordinate


banking

Vehicles

11.9. Banking

The vertical attractor feature must be enabled in order for the banking behavior to function. The way banking works is: a rotation around the vehicle's roll-axis will produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude of the yaw effect will be proportional to the VEHICLE_BANKING_COEF, the angle of the roll rotation, and sometimes the vehicle's velocity along it's preferred axis of motion.

The VEHICLE_BANKING_COEF can vary between -1 and +1. When it's positive then any positive rotation (by the right-hand rule) about the roll-axis will affect a (negative) torque around the yaw-axis, making it turn to the right--that is the vehicle will lean into the turn, which is how real airplanes and motorcycle's work. Negating the banking coefficient will make it so that the vehicle leans to the outside of the turn (not very "physical" but might allow interesting vehicles so why not?). When a vehicle such as a boat or hovercraft reverses it tends to lean the wrong way during turns with a positive coefficient. Theoretically, changing to a negative coefficient would make it lean the correct way.

The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making banking vehicles do what is desired rather than what the laws of physics allow. For example, consider a real motorcycle: it must be moving forward in order for it to turn while banking. However, video game motorcycles are often configured to turn in place when at a dead stop because they're often easier to control that way using the limited interface of the keyboard or game controller. The VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic banking by fuctioning as a slider between a banking that is correspondingly totally static (0.0) and totally dynamic (1.0). "Static" meaning that the banking effect depends only on the vehicle's rotation about it's roll-axis compared to "dynamic" where the banking is also proportional to its velocity along it's roll-axis. Finding the best value of the "mixture" will probably require trial and error.

The time it takes for the banking behavior to defeat a pre-existing angular velocity about the world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if the vehicle is to bank quickly then it should have a banking timescale of about a second or less, otherwise a sluggish vehicle can be made by giving it a timescale of several seconds.

Vehicles


Base64

Base64 Content-Transfer-Encoding

Base64 is a format that allows the storing of arbitrary sequences of octets (8 bits) using a "printable" chararacter set of 64 characters ([A-Z][a-z][0-9]+/). Or, in simpler terms, it allows safe transporting of binary data through a plaintext medium like email. Base64 is not encryption.

See RFC 1341 section 5.2 for the specification.

Example
Plaintext | Base64
"Never fear, Smith is here." | "TmV2ZXIgZmVhciwgU21pdGggaXMgaGVyZS4="

Functions
Function | Description
llBase64ToInteger | Decoding
llIntegerToBase64 | Encoding
llBase64ToString | Decoding
llStringToBase64 | Encoding
llXorBase64StringsCorrect | Encryption/Decryption (new version)
llXorBase64Strings | Encryption/Decryption (depreceated)

//Validate that a string is in fact a base64 string.
integer IsBase64(string a)
{
    return a == llXorBase64StringsCorrect(a, "AAAA");
}//written by Strife Onizuka

Note that unlike Base64 decoders in some other languages, llBase64ToString will stop decoding at the first whitespace character it encounters.

Functions | Crypto


Benchmarks

Here are some rough benchmarks of intra-object communication methods performed by HughPerkins.

Should be noted that script delays will effect the code as will minimum event delay.--BW

llMessageLinked: About 40 messages per second, when sending in bursts of 5 with llSleep(0.1) between bursts.
Strangely, sending as a single burst *reduced* the receive speed, and also caused a lot of message loss, presumably because the events queue in the receiving script was flooded.
Presumably the sending is *very* fast and not at all delayed, but the receiving script (a) is blocked by messages being added to the queue (?) and (b) is somewhat more delayed than the sending script, or possibly just (a).

IntraSL Email: Send delay: 20 seconds
Receive delay: about 1 second

llParseString2List: About 40 commands per second.

llStringToBase64/llBase64ToString: 3 to 4 commands per second(!). See the comments at llStringToBase64 for details.

Appendix: core scripts used for benchmarking

llMessageLinked, Sender script:
default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
        integer i;
        for( i = 0; i < 40; i++ )
        {
            llMessageLinked( LINK_SET, 0, (string)i, "" );
            llMessageLinked( LINK_SET, 0, (string)i, "" );
            llMessageLinked( LINK_SET, 0, (string)i, "" );
            llMessageLinked( LINK_SET, 0, (string)i, "" );
            llMessageLinked( LINK_SET, 0, (string)i, "" );
            llSleep(0.1);
        }
    }
}

llMessageLinked, Receiver Script:
integer iNumReceived = 0;

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }
    link_message( integer sendernum, integer num, string message, key id )
    {
        iNumReceived++;
        if( iNumReceived % 5 == 0 )
        {
            llSetText( (string)iNumReceived, <1,1,1>, 1.0 );
        }
    }
}

llParseString2List:
default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
        integer i;
        string sTestString = "TEST-=-" + (string)llGetKey() + "-=-1-=-asdfsdf";
        for( i = 0; i < 200; i++ )
        {
            list args;
            args = llParseString2List( sTestString, ["-=-"], [] );
            if( i % 5 == 0 )
            {
                llSetText( (string)i, <1,1,1>, 1.0 );
            }
        }
    }
}


Beta

A piece of software that is mostly working, but still under test. Software goes through in-house, or Alpha, testing and then is tested out-of-house or sometimes publically, also know as Beta testing.


Bit

A basic binary unit. A single '1' or '0' within a computer. 1/8th of a byte.

LSL has a bitwise operator that works with a bitfield made up of bitflags.

Bits can be mapped to form a bitmap.

Code | Bitmap


Bitfield

A bitfield is a method for storing a lot of yes/no, set/not_set, or on/off data in one integer. Because an integer is made up of 32 1s and 0s (bits), each of the 32 positions in an integer can be made to represent something. Essentially, it is a way of looking at an integer on the bit level where each bit is yes/no or on/off flag. Each of these bits in a bitfield is then a bitflag. When used with bitwise operators the results can be very powerful.

For example, if we want to describe fruit we might have a fruit bitfield. We could decide that the first bit (furthest right) is if it is round or not, the second bit is whether it is red or not and the third bit is whether it grows on a vine. Thus, an apple could be 011 or 3 (round, red and not grown on a vine), a grape could be 111 or 7 (round, red and grown on a vine), and a banana could be 000 or 0 (not round, not red and not grown on a vine). Each position is a power of 2 (remember 2^0 is 1).

If we wanted to see if a given fruit bitfield represented a round fruit we could use the following:
if (myFruit & 1)

To make it more legible we may define a set of global integers:
integer ROUND = 1; // 0001 binary
integer RED   = 2; // 0010 binary
integer VINE  = 4; // 0100 binary

Then the previous if can be stated in the very legible form:
if (myFruit & ROUND)

If we want to set a single field, or multiple fields, that is also easy:
myFruit = myFruit | ROUND

It's easier to work with bitfields with hex than decimal constants. (0x20000000 == 536870912)

(edit: s/then/than/ ; not sure if this is what was meant, but scanning this sentence 20 times it's the most likely one. I can see it be 'then', but that doesn't make much sense.)

Functions or events that use bitfields include: llSensor, llParticleSystem, llGetAgentInfo, control.

Boolean | Operators | Integer | Bitwise | Binary


Bitflag

A bitflag is an integer that is a power of 2 and can be OR'ed together to form a bitfield.

bitfield


bitflags

See bitfield.


Bitmap

A bitmap image is a bit-for-bit representation of an image (or texture). Each bit can (in theory) be mapped into the memory of a video device (of course, in practice, that may not be true because of endianness issues, different pixel formats, etc).

Texture


bitmask

See bitfield.


BitShift

See Bitwise


Block

A block of code--a codeblock, in LSL, as in other languages, can contain one or more statements delimited by { curly braces }.

Examples:

if (THIS_IS_A_BLOCK) {
    llSay(0,"This is an example. This is only an example. If this were actual code, it would do something more useful.");
    llDie();
}

for (x = 0; x < BIGNUM; x++) {
    llSay(0,"Once again, I am too lazy to come up with a decent example.");
}

A block is also another name for the "box" prim type.

script | prim


BMP

A device-independant bitmapped graphics format, usually uncompressed, of between 1 and 24 bits and without alpha channel support.

You can find more info here.


Body Part

A body part is a part of an avatar's body that can be shape, skin, hair, or eyes. A body part can be in an object's inventory and retrieved via LSL.

A script can't be attached to a body part--only an object. LSL can't rename or otherwise manipulate a body part.

A body part can be identified by llGetInventoryName and llGetInventoryNumber, using the inventory constant INVENTORY_BODYPART.

A body part is not an attachment.

Avatar | Inventory


BooleanLogic

Want Boolean or Bitwise?


Bounding Box

A "bounding box" refers to the collision shape of a prim or avatar as percieved by Second Life's physics engine, Havok. While you see a humanoid avatar, Havok sees your agent as a box--the dimensions of which can be determined by the llGetAgentSize and llGetBoundingBox functions.

Q: Does Havok use bounding boxes for prims also?
A: No, Havok calculates physics collisions based on the actual surface of the object. Unfortunately, in practice, this is somewhat broken. When Havok 2.x+ is added to SL, the precision will be much greater than it currently is.

The limitations of the precision of the physics engine will cause objects to 'float' over other objects, get 'stuck' between them or 'bounce' off them prematurely. It will also allow objects to pass through other objects, if they travel fast enough.

Physics | Havok | Collisions | Precision


brace

Braces

What is a brace? Simple, they're these little 'curly-q' brackety characters:

{}

Braces are used to define code blocks. Do not confuse them with brackets!

Indenting Styles

The two most common brace/indenting styles are as follows:

Allman/C-Style
default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

K&R/Sun-Style
default {
    state_entry() {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number) {
        llSay(0, "Touched.");
    }
}

C-style tends to be more common than K&R, and it's what the LSL editor uses for its default script.

The advantage of the K&R style is that it saves a lot of space. -?
How much space? A linefeed and a tab isn't a lot of space added up... -EepQuirk
The advantage of C-style is that Catherine likes it.
TreadWhiplash likes C-style a lot; and notes that C-style is a hell of a lot more readable in complex code!
ChristopherOmega disagrees with Tread. A smaller number of lines your eye has to scan through makes K&R code easier to read. It's all a matter of taste though.
It ends up being many MORE lines your eye has to scan through trying to find where a codeblock begins. -EepQuirk
As answered in my comment below, that depends upon how much you know (or want to know) your language. Also, when you gauge the scope from the distance you are from the margin, it doesnt matter which style you use in terms of how much more your eyes need to run thru. -ChristopherOmega
StefanNilsson also likes C-style and happily throws in the question of whether or not to explicitly add braces even when the code block consists of only one statement. The Swede says: yes!
I don't do this because it's more efficient to have it all on a single line. -EepQuirk
I tend towards indenting the line after the conditional statement, reguardless of whether it's enclosed in brackets. Saves a couple lines without sacrificing a significant amount of readability. -ChristopherOmega
DolusNaumova wonders if not putting spaces between a closing parenthesis and a brace is still K&R-style?
NornAlbion likes a mix of both styles, using C-style for default and K&R-style for the rest.
IoneLameth likes Python indenting so calls this moot (and seems to mix them in practice).
SkyHoney always adds the braces even for a single statement since she learned Perl, which requires them. Even though it adds an extra line for the closing brace, it makes the code even more beautiful. :)

Bracket | Syntax


braces

See Brace.


bracket

Brackets

Brackets are these characters:

[] {} <>

This discussion also includes something which are not brackets; parentheses. ()

The word "bracket" is frequently and imprecisely used to describe any of the four kinds of bracket-like characters used in LSL. All four types are used for different purposes and are not interchangable, so it's good to get their names straight. Some people will refer to parentheses as "brackets" while retaining the proper names for the other three types. This may be less confusing, but it's still wrong.

Name: Example: Used for: Example:
Parentheses

()

 1. Containing arguments within a function or event.
2. For containing code in a statement.
3. When typecasting a value from one type to another. 		1. llSay(0,"Hello.")
2. if ( (1 > 0) || (2 < 1) )
3. (string)random_number
Braces

{}

 Containing a code block. if (a) { // do stuff }
Square Brackets

[]

 Describing the contents of a list. list list_name = [1, 3.9, "Hello"];
Angle Brackets

<>

 Describing the values of a vector or rotation. Vector: <1.0, 2.0, 0.0>
Rotation: <0.0, 0.0, 0.0, 1.0>

Glossary


brackets

See bracket.


bug

Bugs

A "bug" or a "glitch" is an error or flaw in a computer program. They can be caused by a simple typo, by a deliberate (but poorly-implemented) design decision, or by simply not keeping track of what does what in an LSL script. commenting code can help keep problems in check.

If a script cannot be compiled, it will return a compiler error. This is not the same as a bug though, of course, a script may contain bugs after the problem leading to the error is resolved.

"Debugging" is the process of testing and correcting bugs in a program (or LSL script).

Etymology
The use of the word "bug" to describe a problem with a machine actually predates the existence of computers, with Thomas Edison having used the word to describe a problem with his inventions as early as 1878.

LSL Bugs
LSL itself contains several known bugs that can cause problems with a script, through no fault of the developer. Functions containing bugs will acknowledge the problem on their respective pages and advise a workaround where one exists.

Glossary | Known Bugs


build

Links

  • SL History Wiki: build


Builders' Buddy

This page is just getting started under construction, and will be replaced with the actual entry in the next day or so.

Keep your eyes peeled on this spot!

Thanks - Newfie


bumpiness

See bumpmap


bumpmap

Bumpmaps

A bumpmap maps a bumpy appearance on an object's surface. This effect is achieved by altering the shading of the surfaces by overlaying "embossed" textures. Second Life has a few predefined bumpmaps that can be applied to an object as well as the "Brightness" and "Darkness" maps that are rendered based on the bright and dark areas of the object's texture. Bumpmapping can be changed via the llSetPrimitiveParams function. The UUIDs of each bumpmap surface can be found in viewerart.ini, or here on the wiki at ClientAssetKeys.

Bumpmapped prims/sides cannot also have transparency¹ but can have shininess.

    ¹ Ben Linden (forums): "Right now, bump mapping in SL uses the alpha channel for the 8-bit greyscale "bumpmap", and since we only support single textures, and only 32-bit textures, this means that any texture with alpha (which is always already 32-bit) can't have a bumpmap.

    Now, we are reworking how we render SL, and we are considering allowing a face to have more than 1 texture associated with it - then we could allow you to have a 32-bit texture (so one with alpha) plus a bump map, or maybe something fancier, since bump maps are sooooo two years ago."

Texture


BVH

Biovision Hierarchy (BVH) files, created mainly from Curious Labs' Poser (but other 3D modellers can make them too), are used as Second Life avatar animations.

more info

animation | animations


Byte

A byte is a common measurement unit when dealing with memory. 1024 bytes make a kilobyte. A script has 16384 bytes of stack memory available to it, most of which can be used for storing variables.

LSL memory requirements for different types:
Type | Memory footprint
integer | 4 bytes
string | (llStringLength(string) * 2) + 5
float | 4 bytes
vector | 12 bytes
Rotation | 16 bytes


Bytecode

"Bytecode is a sort of intermediate code that is more abstract than machine code. It is often treated as a binary file containing an executable program much like an object module, which is a binary file containing a machine code produced by compilers." Wikipedia

The client compiles the script, temporarily stores the text script and the script together in the disk cache, and uploads them together when the sim is ready.

LSL is a language like Java in that they both compile into bytecode which can is interpreted (LSL is not compiled to machine code). LSL bytecode is high-level and does no optimization at compile time. LSL bytecode can be decompiled back into the original script with the only loss being the variable and user function names.

LL is moving to Mono for scripting so, in the future, LSL will be compiled to CIL, assembled into CLR assemblies and run in a Mono virtual machine (VM) in the simulator. (This also opens up the possibility of using other languages that target the CLR, such as C#, VB.NET, Python, and many others, though LL has yet to announce any plans to allow this.) Babbage Linden is doing the work of replacing the LSL VM with Mono; read about it in his blog. According to a post from April 2005, LSL scripts run 300 to 500 times faster in the Mono VM!

LSL | Script | Memory | Glossary


cache

The cache (usually in C:\Documents and Settings\<user>\Application Data\SecondLife\cache\ on Windows-based PCs) is where Second Life stores files downloaded from the asset server while in a simulator.

Some problems with the client (crashes/freezes/hangs, texture corruption, etc) can be solved by deleting the cache.

server | grid | simulator


call

See functions.


Calling Card

Calling cards are one of several types of inventory item. They cannot be given directly to other users, nor placed inside objects' inventories, and thus, cannot be manipulated by the inventory functions.

Inventory


CamelCase

Camel Case (also known as InterCaps) is the capitalising of each word in a function or variable name.

For example, MyFunction, NeverCall or TheSimpsons

WikiNames use CamelCase. On WakkaWiki, you don't even need to explicitly state a link if you use CamelCase.

WikiPedia:CamelCase

WikiFormatting


Cell

Cells (16m x 16m x infinity) divide up a simulator into more manageable sections for rendering prims, lights, shadow, clouds, etc. Cells can most easily be seen when there are too many lights in a cell: the light reflecting on the ground has a sharp edge along the cell's borders.

Simulator | Land


center

object position

The object position, as returned by llGetPos, is the geometric center (see below) of the root prim in the linked set. For a single prim, object center and geometric center are always the same.

geometric center

The geometric center of a linked set is the average of the geometric centers of all the prims in the set. This is what llRezObject uses. See llGetGeometricCenter.

center of gravity/mass

The center of gravity/mass is the balance point. If you take a symmetric shape such as a cube prim, the center of gravity/mass is in the geometric center. If you reduce the top size of the cube, the center of gravity/mass will shift towards the thick end because it is heavier. See llGetCenterOfMass.

object | prim | link | position | mass


Channel

The chat and listen functions use a "channel" system to send messages back and forth. Any script can intercept these messages if it knows the channel to listen on. There are 4,294,967,294 possible channels, represented as an integer in the range of (-2,147,483,648) to 2,147,483,647. Channel 0 is the only one sent to agents--only messages on it appear in the chat window. All other channels are for scripts only.

Chat channels are not to be confused with XML-RPC data channels.

When typing in the chat window, you can specify the channel with /<channel number>, as in:

/42 This goes to channel 42, but won't be heard by nearby agents

Chat can only be generated by agents on negative channel numbers through llDialog. This means that typing "/-13 Whatever" into your chat bar won't work.

This gets a channel from the name of the owner:
(If there's no other way for multiple objects to communicate, it's also not very secure):
list letters = [" ","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
integer ChannelFromName()
{
    integer x;
    integer length;
    integer channel;
    string owner = llToLower(llKey2Name(llGetOwner()));
    for(x=0, length=llStringLength(owner); x<length; ++x)
    {
               channel+=llListFindList(letters,[llGetSubString(owner,x,x)]);
    }
    return channel;
}

This will generate a random (positive or negative) channel number.
integer DlgChannel = 0;
{
    do
    {
        DlgChannel = ((integer) llFrand(3) - 1) * ((integer) llFrand(2147483647));
    }
    while(DlgChannel == 0);
}
Uses full range of available channels, excepting channels 0, 2147483647 (DEBUG_CHANNEL), -2147483647 and 2147483648. - DavidSoon

Channel Functions
llDialog
llListen
llListenControl
llListenRemove
llSay
llShout
llWhisper

Channel Events
listen

Constants

Name | Value
DEBUG_CHANNEL | 2147483647
PUBLIC_CHANNEL | 0

Communications | Chat


Character

A character is a single letter, digit, or other punctuation or typographical mark. Really, it is one of the symbols in a character set. The LSL code standard uses the ASCII standard for its characters but when the script is running it uses UTF-8.

Strings are composed of characters which make up text. Other types are described by characters when writing code but are represented differently when the script itself is compiled.

Glossary


characters

See character.


Chat

Chat functions allow objects and agents to communicate with each other and among themselves.

Chat in Second Life uses channels. Channel 0 is the public chat channel all users see as chat text. There is no way for users to listen to any channel other than channel 0 directly, though users can send messages on other channels using llDialog or by specifiying a channel in the chat text-entry field itself.

Users can send messages to other channels by typing "/num message", where "num" is the number of the channel and "message" is the desired chat text. So, "/10 Hello, script!" will say "Hello, script!" on channel 10. Additionally, "//message" can be used to send "message" to the last-used chat channel. Scripts make no distinction between channels. Users are unable to send messages on negative channels.

Channels are stored as signed integers, so the possible chat channels range from -2147483648 to 2147483647.

A script can only have up to 64 listens open at a time (if it goes over its limit, it crashes with a "Too Many Listens" run-time error). The functions listed at the bottom help manage situations where this error may occur.

If the string passed to the chat function is greater then 1023 bytes long, it is truncated (chopped off) at its 1023th byte.

Prims that output chat never receive their own output back in listen events. This applies to all scripts in that primitive, not just the one that called the chat function. This is a good thing--trust us. To communicate between scripts in the same primitive, use llMessageLinked. Prims in the same link-set can use chat to communicate with one another, but this practice is discouraged (the use of llMessageLinked is recommended instead).

Chat can be heard within a sphere centered on the sender:
Function | Radius
llWhisper | 10m
llSay | 20m
llShout | 100m

Other Chat Functions
llDialog
llListen
llListenControl
llListenRemove
llOwnerSay

Q: Is there a way to specify an arbitrary chat radius? What if I want to chat in a bigger range than 20m, but less than 100? Better yet, is there a way to limit or extend chat to cover a given parcel of land?
A: Unfortunately, no. You could do something with attachments and llOwnerSay, but there's no way to do it by default.

You could always use a sensor and llInstantMessage the detected users in a for() loop. -JacobxGoff
Functions | Communications


Child

In a linked object, a child primitive is highlighted in blue when selected in edit mode, whereas non-linked and parent (root) primitives are highlighted in gold/yellow when selected in edit mode. A child primitive is, in essence, fused to its linked set and has a set of rules to follow which set it apart from being a regular non-linked object.

Child prims can't move as freely as other objects and can only move non-physically using llSetRot(), llSetLocalRot, llSetPos(), and llSetScale().

Note: a child primitive can continue to move using the non-physical functions even if its parent is physical. An exception to this is that it can't scale itself, as that would change its mass, which is something SL doesn't want to change dynamically.

Object | Prim | Link | Parent


Child Rotation

Child rotation is the rotation of a child prim in relation to its parent (instead of in relation to the world). This comes into play when moving (rotating) parts are in a linked object that can itself be rotated.

Examples

An example of child rotation is a car door. No matter what direction the car faces, the doors are probably expected to always open outwards and rotate around their hinges.

The usual way of rotation using llSetRot is always in relation to the world axis. To make the door rotate in relation to the whole car it is linked to, the relative rotation for the door has to be combined with the current rotation of the whole car (in relation to the world axis). Luckily, the function llSetLocalRot does exactly that.

Or, if to do it manually for some reason (such as perform a local rotation with a llSetPrimitiveParams call), this function does the same:
SetLocalRot(rotation localrot) {
    llSetRot(localrot / ( (ZERO_ROTATION / llGetLocalRot()) * llGetRot()));
}
Notes:
  • The / operator rotates the rotation on the left by the inverse of the rotation on the right. It has nothing to do with division.
  • This does not work for attachments as llGetLocalRot and llGetRot behave differently when in a prim that is attached to the avatar.

The same in more detail (and with the inverting done manually):
rotation Inverse(rotation r)
{
    r.x = -r.x;
    r.y = -r.y;
    r.z = -r.z;
    return r;
}
rotation GetParentRot()
{
    return Inverse(llGetLocalRot())*llGetRot();
}
SetLocalRot(rotation x)
{
    llSetRot(x*Inverse(GetParentRot()));
}

Note: For child prim of an attachment, use this instead (where rootrot is rotation of root prim--use llMessageLinked to obtain that):
SetLocalRot(rotation localrot) {
    llSetRot(localrot / rootrot);
}

To rotate an object in two steps starting at its current orientation:

(1) first pi/2 about the world's z-axis... Qz
(2) then pi/2 about the world's x-axis... Qx

The final rotation would be:

Q = llGetRot() * Qz * Qx;
llSetRot(Q);

Note the order of operations.

To rotate from a current orientation:

(1) first pi/2 about the object's z-axis... Qz
(2) then pi/2 about the object's x-axis... Qx

The final rotation would be:

Q = Qx * Qz * llGetRot();
llSetRot(Q);

This last example is useful for when a script computes a small incremental rotation in the object's local frame (call it dQ) and add that slight rotation to the object's current orientation. Assuming dQ is known, the script calls that would rotate the object would be:

Q = dQ * llGetRot();
llSetRot(Q);

Below here needs its "you"/"your" reworded. -EepQuirk

Child Rotation How-To

Now, after that very exciting math explanation, I'm sure you just want to know how to get down and dirty with how to actually set up a child linked object for rotation. This portion of the page is dedicated to outlining step-by-step, how to make a child prim rotate.

Step 1: First, you need your basic linked object. Any linked object will do.
Step 2: Rotate your linked object, so that the parent of the set is at ZERO_ROTATION. In other words, in the edit window, make sure the object's rotation X = 0, Y = 0 and Z = 0.
Step 3: Locate your child object that needs to be rotated. Go into edit mode and check the box next to "Edit Linked Parts". This will allow you to select prims in the set individually
Step 4: Select the target prim, and unlink only it from the set. Now, right click the target prim again. The target prim should now have a gold halo around it, and should be movable seperately from the set.
Step 5: Rotate the target prim to the rotation you want it to be at. Then, put this script into it:
default {
state_entry() {
llSay(0, (string) llGetRot());
}
}
Step 6: Record the value the object said.
Step 7: Repeat steps 5 and 6 until all rotations of the object are covered.
Step 8: Link the child prim back on to the set.
Step 9: Rotate the parent of the set back into its original rotation.
Step 10: When passed the values you got in step 6, llSetLocalRot will now rotate your child prim correctly, relative to the rotation of the parent prim.

Attachments

Getting things to work out with attachment prim rotations is challenging because of trying to rotate child prims relative to the root prim of the attachment and not the avatar rotation. Here's the breakdown of the various functions in different situations:

script in the root prim of a normal detached object

llGetRot | object global rotation
llGetLocalRot | object global rotation
llGetRootRotation | object global rotation

script in the child prim of a normal detached object

llGetRot | object global rotation + prim rotation
llGetLocalRot | prim rotation
llGetRootRotation | object global rotation

script in root prim of attachment

llGetRot | avatar rotation
llGetLocalRot | attachment rotation
llGetRootRotation | avatar rotation

script in child prim of attachment

llGetRot | avatar rotation + prim rotation (BOGUS)
llGetLocalRot | prim rotation with respect to the attachment root prim
llGetRootRotation | avatar rotation

Basically, what's going with attachments is that the avatar sort of becomes the root prim and replaces its rotations for the attachment root prim rotations.

There's a couple things to take note of here. One is that there's no way to directly tell what the attachment rotation is from a script within an attachment child prim. That makes it a bit hard to make root prim relative adjustments to rotation.

The other is that llGetRot returns a completely bogus value from a script in an attachment child prim because it's actual rotation is avatar rotation * attachment rotation * prim rotation not avatar rotation * prim rotation. As the attachment is rotated this value becomes completely meaningless.

For example, create two prims and link them. Set the child to have a rotation of 30 deg in the Z axis. Attach the object to your skull or somewhere appropriate. Then rotate the whole attachment 45 deg in the Z.

Then using this handy script:

out(string message) {
    llOwnerSay((string) llGetLinkNumber() + "> " + message);
}

vector deg(rotation rot) {
    vector eul = llRot2Euler(rot);
    eul *= RAD_TO_DEG;
    return eul;
}

default {
    touch_start(integer count) {
        if (llDetectedKey(0) != llGetOwner()) return;

        out("llGetRot: " + (string) deg(llGetRot()));
        out("llGetLocalRot: " + (string) deg(llGetLocalRot()));
        out("llGetRootRotation: " + (string) deg(llGetRootRotation()));
    }
}

You will get results similar to these:

Root 1: llGetRot: <0.00000, 0.00000, -6.66063>
Root 1: llGetLocalRot: <0.00000, 0.00000, 45.00000>
Root 1: llGetRootRotation: <0.00000, 0.00000, -6.66063>
Child 2: llGetRot: <0.00000, 0.00000, 23.33937>
Child 2: llGetLocalRot: <0.00000, 0.00000, 30.00000>
Child 2: llGetRootRotation: <0.00000, 0.00000, -6.66063>

Looking at the child's llGetRot it is clear that you should be seeing -6.7 + 45.0 + 30.0 = 68.3 and not 23.3 as shown because that's what you are actually observing.

Having llSetPrimitiveParams use llSetLocalRot functionality instead of llSetRot would go a long way toward alleviating this problem. In the mean time there's no easy workaround. Harcoding or passing around the attachment rotation is probably a start.

Functions | Rotation


circumference

The distance around the edge of a circle. PI diameters or TWO_PI radiuses long.


Client

A client is software that receives data from a server. Second Life is a client, also known as secondlife.exe (formerly newview.exe), which features a propietary 3D engine, a GUI, the Havok physics engine, the FMOD sound API, JPEG2000 compression, and other technology (more details).

  • A Mac OS X version of the client is currently in beta phase.
  • A Linux port is currently in alpha phase.

Client-side is something that only affects the client and not the server. Some client-side functions are:

llParticleSystem
llSetTextureAnim
llTargetOmega

newview.exe | Server | Client Asset Keys


Client Asset Keys

These keys are from the %PROGRAMFILES%\Second Life\app_settings\viewerart.ini file and include textures (GUI, "missing", etc) and animations.

Categories:
Textures
Environment, Avatar, General UI, Inventory, Map, Find, Movement Controls, Edit Mode, Misc
Animations
Sounds

Textures

Environment

Filename | UUID | Description
alpha_gradient.tga | e97cf410-8e61-7005-ec06-629eba4cd1fb | Used for generating the texture for the ground dynamically. Also used for creating the "invisiprim" that hides avatars, prims with alpha values less than 1.0 and particle effects
alpha_gradient_2d.tga | 38b86f85-2575-52a9-a531-23108d8da837 | Used for generating the texture for the ground dynamically. Also used for creating the "invisiprim" that hides avatars, prims with alpha values less than 1.0 and particle effects
collision texture | 35f217a3-f618-49cf-bbca-c86d486551a9 | Sprite used to generate collision visual effect.
grass_texture_1.tga | 79504bf5-c3ec-0763-6563-d843de66d0a1 |
grass_texture_2.tga | 6c4727b8-ac79-ba44-3b81-f9aa887b47eb |
grass_texture_3.tga | 99bd60a2-3250-efc9-2e39-2fbcadefbecc |
grass_texture_4.tga | 7a2b3a4a-53c2-53ac-5716-aac7d743c020 |
noentrylines.tga | 5d3e196b-fd4d-ada7-e4c1-99f8e9f1cfbf | Red "No entry of property" lines.
noentrypasslines.tga | ac8f8627-6a30-8da8-d4bd-958668eea7a0 | Green "No entry of property, except with pass" lines.
propertyline.tga | e3548c46-8d5e-03da-fcab-4fc36ad818bb | Semi-transparent texture overlayed on the ground along the borders of a property when View>Show Property Lines is active.
terrain_dirt.tga | b8d3965a-ad78-bf43-699b-bff8eca6c975 | Ground texture.
terrain_dirt_detail.tga | 0bc58228-74a0-7e83-89bc-5c23464bcec5 | Hi-res ground texture.
terrain_grass.tga | abb783e6-3e93-26c0-248a-247666855da3 | Ground texture.
terrain_grass_detail.tga | 63338ede-0037-c4fd-855b-015d77112fc8 | Hi-res ground texture.
terrain_mountain.tga | 179cdabd-398a-9b6b-1391-4dc333ba321f | Ground texture.
terrain_mountain_detail.tga | 303cd381-8560-7579-23f1-f0a880799740 | Hi-res ground texture.
terrain_rock.tga | beb169c7-11ea-fff2-efe5-0f24dc881df2 | Ground texture.
terrain_rock_detail.tga | 53a2f406-4895-1d13-d541-d2e3b86bc19c | Hi-res ground texture.
tree_beach_grass_1.tga | 18fb888b-e8f1-dce7-7da7-321d651ea6b0 |
tree_cypress_1.tga | fb2ae204-3fd1-df33-594f-c9f882830e66 |
tree_cypress_2.tga | 30047cec-269d-408e-0c30-b2603b887268 |
tree_dogwood.tga | 64367bd1-697e-b3e6-0b65-3f862a577366 |
tree_eelgrass.tga | 96b4de31-f4fa-337d-ec78-451e3609769e |
tree_eucalyptus.tga | a6162133-724b-54df-a12f-51cd070ad6f3 |
tree_fern.tga | 8872f2b8-31db-42d8-580a-b3e4a91262de |
tree_kelp_1.tga | 2caf1179-7861-6ff3-4b7d-46e17780bdfa |
tree_kelp_2.tga | 2a4880b6-b7a3-690a-2049-bfbe38eafb9f |
tree_oak.tga | 8a515889-eac9-fb55-8eba-d2dc09eb32c8 |
tree_palm_1.tga | ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c |
tree_palm_2.tga | 2d784476-d0db-9979-0cff-9408745a7cf3 |
tree_pine_1.tga | 0187babf-6c0d-5891-ebed-4ecab1426683 |
tree_pine_2.tga | d691a01c-13b7-578d-57c0-5caef0b4e7e1 |
tree_plumeria.tga | 6de37e4e-7029-61f5-54b8-f5e63f983f58 |
tree_sea_sword.tga | 5894e2e7-ab8d-edfa-e61c-18cf16854ba3 |
tree_tropical_1.tga | 5bc11cd6-2f40-071e-a8da-0903394204f9 |
tree_tropical_2.tga | cdd9a9fc-6d0b-f90d-8416-c72b6019bca8 |
undergrowth_1.tga | 8f458549-173b-23ff-d4ff-bfaa5ea2371b |
water | 2bfd3884-7e27-69b9-ba3a-3e673f680004 | Semi-transparent blue texture shown on the surface of water.
winter_tree_aspen.tga | 7c0cf89b-44b1-1ce2-dd74-07102a98ac2a |
winter_tree_pine_1.tga | 10d2a01a-0818-84b9-4b96-c2eb63256519 |
winter_tree_pine_2.tga | 67931331-0c02-4876-1255-28770896c6a2 |
(Top)

Avatar

Filename | UUID | Description
silhouette.tga | da5d4079-7819-6b53-d2a4-dc9929381d7d | The constant shadow cast by the avatar's feet?
avatar_thumb_bkgrnd.tga | 3a7f4f0d-be14-ee78-29e3-fc8b0b2a68d3 |
eyes.tga | 6522e74d-1660-4e7f-b601-6f48c1659a77 |
hair.tga | 7ca39b4c-bd19-4699-aff7-f93fd03d3e7b |
pixiesmall.tga | 168e6813-096e-07ea-97ae-fd416826f627 | The particle texture used as both the default texture for particles and for avatar editing beams.
(Top)

General UI

Im not so sure the status_* textures (No script, no-rez, etc) icons should be here, as they're specific to a particular part of the UI. That also goes for the notify_* textures... it kinda seems more intuitive for this section to contain *only* the textures used in many parts of the UI (buttons, tabs, background colours, scroll bars), however, since the Nofitication area uses only notify_* textures, and the Status Bar only uses status_* textures, Im not sure if they deserve their own tables. -Chris
Filename | UUID | Description
button_disabled_32x128.tga | f8124d60-2875-c358-7847-2acb63e5400c |
button_enabled_32x128.tga | d8faf8cb-ee6e-b0b5-abd9-bde873ad3461 |
button_enabled_selected_32x128.tga | 1eddba75-b682-110a-104e-6cdcce616a25 |
cam_rotate_in.tga | 70bf2262-3eed-4996-88ac-076907e8921d |
cam_rotate_out.tga | 88745b46-da05-11d5-8ac0-0003477c4611 |
cam_tracking_in.tga | fe2fc73b-5a64-4a8e-aacc-46fa81faf96a |
cam_tracking_out.tga | 95c4ea0e-e3c2-4904-b847-7d7676139ebb |
cam_zoom_minus_in.tga | deed3f4b-93e9-4183-a3b0-a5a98a6de1bb |
cam_zoom_out.tga | bb02e941-cb3b-4dd3-892a-6841b5de6e45 |
cam_zoom_plus_in.tga | c7aefd32-ce13-4242-82cc-2631d44ff9d3 |
checkbox_disabled_false.tga | 7d94cb59-32a2-49bf-a516-9e5a2045f9d9 |
checkbox_disabled_true.tga | c817c642-9abd-4236-9287-ae0513fe7d2b |
checkbox_enabled_false.tga | 05bb64ee-96fd-4243-b74e-f40a41bc53ba |
checkbox_enabled_true.tga | cf4a2ed7-1533-4686-9dde-df9a37ddca55 |
closebox.tga | 47a8c844-cd2a-4b1a-be01-df8b1612fe5d |
close_in_blue.tga | e5821134-23c0-4bd0-af06-7fa95b9fb01a |
close_inactive_blue.tga | 779e4fa3-9b13-f74a-fba9-3886fe9c86ba |
combobox_arrow.tga | b31c1335-0e9c-4927-bc90-53277777d9c1 |
crosshairs.tga | 6e1a3980-bf2d-4274-8970-91e60d85fb52 | Texture for the mouselook crosshairs.
minimize.tga | 34c9398d-bb78-4643-9633-46a2fa3e9637 |
minimize_inactive.tga | 6e72abba-1378-437f-bf7a-f0c15f3e99a3 |
minimize_pressed.tga | 39801651-26cb-4926-af57-7af9352c273c |
notify_box_icon.tga | b2ef2d31-9714-a07b-6ca7-31638166364b |
notify_next.tga | 07d0ea4c-af0c-aad1-dbbf-c24020ff2b80 |
notify_tip_icon.tga | 74ba3584-58ea-9984-5b76-62d37942ab77 |
radio_active_false.tga | 7a1ba9b8-1047-4d1e-9cfc-bc478c80b63f |
radio_active_true.tga | 52f09e07-5816-4052-953c-94c6c10479b7 |
radio_inactive_false.tga | 90688481-67ff-4af0-be69-4aa084bcad1e |
radio_inactive_true.tga | 1975db39-aa29-4251-aea0-409ac09d414d |
resize_handle_bottom_right_blue.tga | e3690e25-9690-4f6c-a745-e7dcd885285a |
restore.tga | 111b39de-8928-4690-b7b2-e17d5c960277 |
restore_inactive.tga | 0eafa471-70af-4882-b8c1-40a310929744 |
restore_pressed.tga | 90a0ed5c-2e7b-4845-9958-a64a1b30f312 |
scrollbutton_down_in_blue.tga | d2421bab-2eaf-4863-b8f6-5e4c52519247 |
scrollbutton_down_out_blue.tga | b4ecdecf-5c8d-44e7-b882-17a77e88ed55 |
scrollbutton_left_in_blue.tga | ea137a32-6718-4d05-9c22-7d570d27b2cd |
scrollbutton_left_out_blue.tga | 43773e8d-49aa-48e0-80f3-a04715f4677a |
scrollbutton_right_in_blue.tga | b749de64-e903-4c3c-ac0b-25fb6fa39cb5 |
scrollbutton_right_out_blue.tga | 3d700d19-e708-465d-87f2-46c8c0ee7938 |
scrollbutton_up_in_blue.tga | a93abdf3-27b5-4e22-a8fa-c48216cd2e3a |
scrollbutton_up_out_blue.tga | dad084d7-9a46-452a-b0ff-4b9f1cefdde9 |
spin_down_in_blue.tga | a985ac71-052f-48e6-9c33-d931c813ac92 |
spin_down_out_blue.tga | b6d240dd-5602-426f-b606-bbb49a30726d |
spin_up_in_blue.tga | c8450082-96a0-4319-8090-d3ff900b4954 |
spin_up_out_blue.tga | 56576e6e-6710-4e66-89f9-471b59122794 |
square_btn_32x128.tga | b28df901-6b8d-d31c-7903-4eb9676d4bfc |
square_btn_selected_32x128.tga | c48c9e95-191b-96d3-08b2-6e8ada58b651 |
startup_logo.tga | 66864f3c-e095-d9c8-058d-d6575e6ed1b8 |
status_build.tga | 175a6b75-45c9-c2c2-4765-bf37a3909b53 | No-build land status symbol.
status_busy.tga | beb0d821-6725-abdf-032d-1f70cdabde82 | Busy mode symbol.
status_fly.tga | 0e058115-5b8f-c3d7-dcaa-9623d92885d1 | No-fly land status symbol.
status_health.tga | 4330e8ce-b39b-1eb8-c2ec-a97c0b3947b5 | Health-enabled area symbol.
status_money.tga | 5863eb7a-1546-6501-533a-6061f73a36b7 | L$ symbol.
status_scripts.tga | 4cc1afcd-04dd-178f-e074-0f9dc730ab45 | No-scripts land status symbol.
tab_bottom_blue.tga | bf0a8779-689b-48c3-bb9a-6af546366ef4 |
tab_bottom_selected_blue.tga | c001d8fd-a869-4b6f-86a1-fdcb106df9c7 |
tab_left.tga | 1097dcb3-aef9-8152-f471-431d840ea89e |
tab_left_selected.tga | bea77041-5835-1661-f298-47e2d32b7a70 |
tab_top_blue.tga | 1ed83f57-41cf-4052-a3b4-2e8bb78d8191 |
tab_top_selected_blue.tga | 16d032e8-817b-4368-8a4e-b7b947ae3889 |
(Top)

Inventory

Filename | UUID | Description
button_anim_pause.tga | db2d9c2d-0bbd-21e2-e83a-103ea2def7a8 |
button_anim_pause_selected.tga | ad65d67a-777b-fbfa-693d-4bdcfca2acca |
button_anim_play.tga | 2a7f6738-5d82-2ff3-d419-30ed09cbb72b |
button_anim_play_selected.tga | 119c37bb-24af-45fe-ae11-3a6bc3c85138 |
button_anim_stop.tga | e10c9e36-d9f6-c8b4-de96-557dccce9205 |
button_anim_stop_selected.tga | b8c0e0aa-2771-439e-c919-d2f5dad69a1c |
container_animation.tga | c4e657a1-4c86-0159-2da0-32ff948484e6 |
container_bodypart.tga | 770cb2df-758d-34d5-36c7-e3de06db5b5d |
container_clothing.tga | dd90406f-4c8f-a3f9-41df-d562f94f09e0 |
container_gesture.tga | 59cd31c0-2791-3c48-f740-f0a36c68653e | If a gesture is dragged out of inventory onto the ground, it appears contained in a cube with this texture applied to it.
container_landmark.tga | 24c63386-04f7-ce6f-4ff2-dfb215d2e21f | If a landmark is dragged out of inventory onto the ground, it appears contained in a cube with this texture applied to it.
container_many_things.tga | 849d3292-d9fa-7186-5465-dd7b5fc1ec48 | If multiple inventory items (or a folder is) are selected, and dragged on to the ground, they appear in the contents of a cube with this texture applied to it.
container_object.tga | ad887ae1-2bee-f2c9-6786-5599de3c95c4 |
container_script.tga | b93bd494-c4bd-bcdf-4a59-35a9497d03f3 | If a script is dragged out of inventory onto the ground, it appears contained in a cube with this texture applied to it.
container_sound.tga | 5ddea031-cfa3-2776-43e3-c7146c1b4cd6 | If a sound is dragged out of inventory onto the ground, it appears contained in a cube with this texture applied to it.
container_texture.tga | b3f95caf-bd62-bef3-0ded-dea752920629 | If a texture is dragged out of inventory onto the ground, it appears contained in a cube with this texture applied to it.
inv_folder_animation.tga | 4d59b3ee-f29d-b912-2bcc-9bb1f8a07ec6 | Icon used for the "Animations" inventory folder.
inv_folder_bodypart.tga | 1fe05580-1d2f-0345-b28b-52b6e3a20e5d | Icon used for the "Bodyparts" inventory folder.
inv_folder_callingcard.tga | a3735971-e2b2-d78a-580d-d265cd8f2484 | Icon used for the "Calling Cards" inventory folder.
inv_folder_clothing.tga | f1427d3d-b2e8-97c4-69ab-1f36d4c0e8f0 | Icon used for the "Clothing" inventory folder.
inv_folder_gesture.tga | 4de9129a-9fc1-d759-d739-364293906ba2 | Icon used for the "Gestures" inventory folder.
inv_folder_landmark.tga | 9f921155-7c8c-e276-d5ec-03ac9340584d | Icon used for the "Landmarks" inventory folder.
inv_folder_lostandfound.tga | 9a371a04-297d-bacf-0d16-5f49753efe1d | Icon used for the "Lost and Found" inventory folder.
inv_folder_notecard.tga | a9e75d84-5073-9cb7-10a9-1ca68ef5c7ba | Icon used for the "Notecards" inventory folder.
inv_folder_object.tga | 113e5133-fd0d-ee51-4a59-9d67ca10e8a7 | Icon used for the "Objects" inventory folder.
inv_folder_plain_closed.tga | 86f00960-c3e9-9680-145d-3beffd743e9c | Icon used for closed inventory folders.
inv_folder_plain_open.tga | d15dc243-2d0b-47af-0ce1-ec376464bdc8 | Icon used for open inventory folders.
inv_folder_script.tga | baa5c310-6a6d-cc48-51eb-65196ba31d77 | Icon used for the "Scripts" inventory folder.
inv_folder_snapshot.tga | 6efe85e7-800f-1843-296c-a5b7adffe091 | Icon used for the "Snapshots" inventory folder.
inv_folder_sound.tga | e10cb910-1e71-da47-bd12-8c53f7793714 | Icon used for the "Sounds" inventory folder.
inv_folder_texture.tga | 743f035b-a049-43f4-16c7-7ec8daa2c481 | Icon used for the "Textures" inventory folder.
inv_folder_trash.tga | 88ad072e-ea0b-aabd-5ac0-b37862a6eb66 | Icon used for the "Trash" inventory folder.
inv_item_animation.tga | b5cda0d6-d196-ce48-63db-d04323ef8931 | Icon used to represent a texture inventory item.
inv_item_attach.tga | 5bcae41e-aa5d-02f8-edf1-605ebdd875ab | Icon used to represent a texture inventory item.
inv_item_bodypart.tga | d2a5362d-5c55-57dd-a9e9-5c814d1ddc16 | Icon used to represent a texture inventory item.
inv_item_callingcard_offline.tga | d0afe86b-2489-7600-55b7-6abb0a63d9f9 | Icon used to represent an offline calling card.
inv_item_callingcard_online.tga | 672cc53e-8dc0-ba91-2a4e-574104cf071c | Icon used to represent an online calling card.
inv_item_clothing.tga | 34dfe476-8e26-0e3a-11cf-76cc4a7126ce | Icon used to represent a clothing inventory item.
inv_item_eyes.tga | eaa5fd96-5c25-06ef-2280-7ef20203e167 | Icon used to represent eye settings.
inv_item_gesture.tga | 5579245d-d5bf-5f13-46b0-8624490de24c | Icon used to represent a gesture inventory item.
inv_item_gloves.tga | 117b11cb-c04e-5081-13da-1a8846070fd0 | Icon used to represent a texture inventory item.
inv_item_hair.tga | 6bca3bf4-ed6d-d438-63a0-2a7066d03a0b | Icon used to represent hair settings.
inv_item_jacket.tga | 8df59386-56e0-c811-0443-840da3acb3a5 | Icon used to represent a jacket inventory item.
inv_item_landmark.tga | bf25a2a0-85da-7fa0-0993-e461768d0221 | Icon used to represent an unvisited landmark.
inv_item_landmark_visited.tga | 229fac85-5428-4ab7-adeb-eb8389e91092 | Icon used to represent a visited landmark.
inv_item_notecard.tga | 23ce8a2c-9ea2-d863-6572-806f0645b0c7 | Icon used to represent a notecard inventory item.
inv_item_object.tga | 0f0780a0-89c4-742a-ef28-26405a41cf85 | Icon used to represent a object inventory item.
inv_item_pants.tga | a87a58ca-f857-63b1-0acf-072711ed1bdb | Icon used to represent a pants inventory item.
inv_item_script.tga | 59a3df81-ed76-06c9-7264-6dada535e7a3 | Icon used to represent a script inventory item.
inv_item_shape.tga | 4463e433-4db5-79ef-c1b0-4821b03ddb07 | Icon used to represent a shape inventory item.
inv_item_shirt.tga | e2ffb62b-6abc-22d6-952d-764759b4d636 | Icon used to represent a shirt inventory item.
inv_item_shoes.tga | cf384fa5-1edd-c37c-2134-283dd4fe3396 | Icon used to represent a shoes inventory item.
inv_item_skirt.tga | 0b43f826-2abc-2944-7d72-10777a51d19b | Icon used to represent a skirt inventory item.
inv_item_snapshot.tga | 3810d584-b092-7caa-57e0-010f192b9659 | Icon used to represent a snapshot inventory item.
inv_item_socks.tga | 22137c6d-6ec5-6eee-9a2e-2d7a9e6cbcd4 | Icon used to represent a socks inventory item.
inv_item_sound.tga | eb414d69-c77d-d4e7-66e6-6c2e6f6c1976 | Icon used to represent a sound inventory item.
inv_item_texture.tga | 19f452d7-4eee-9f46-76cc-5497d17f1dd9 | Icon used to represent a texture inventory item.
inv_item_underpants.tga | 2f15dc09-4385-526c-aa5d-d9d516ec7d99 | Icon used to represent a underpants inventory item.
inv_item_undershirt.tga | f72ab629-a3ab-de0c-35c0-5285e27478ce | Icon used to represent a undershirt inventory item.
(Top)

Map

Filename | UUID | Description
compass markers | 79156764-de98-4815-9d50-b10a7646bcf4 |
direction_arrow.tga | 586383e8-4d9b-4fba-9196-2b5938e79c2c |
map_avatar_16.tga | db0dadd5-026a-88cf-f5c1-523a0a2daa3e |
map_avatar_8.tga | 0be58a91-8065-c02b-7a12-2cc14dddbc37 |
map_avatar_you_8.tga | 02fbdc40-5e07-a6e1-228b-58e10f8335b7 |
map_event.tga | 6008be5e-9267-2a3a-9798-e81b076c22ca | Map event icon
map_event_mature.tga | f9cdba28-a227-d613-2f16-ce06209314ae | Map mature event icon
map_home.tga | ae9b8f5f-03a1-2e71-db77-6eb27a1ba181 | Map telehub icon
map_telehub.tga | bf1b2bb0-13b1-40ae-3354-b1b93761bdb4 | Map telehub icon
map_track_16.tga | a3878395-ef00-a0e6-ee9a-f45ed6b9ce59 |
map_track_8.tga | bfdc7bf6-e2ee-1754-f4df-cc25887714ad |
(Top)

Find

Filename | UUID | Description
icon_auction.tga | 96abf5b1-335c-6b76-61e3-74ada07f3cb8 |
icon_avatar_offline.tga | 34648c67-5bfb-5790-e05e-8bd6600fd087 |
icon_avatar_online.tga | 529ed15b-3d41-dcc1-79de-90bf21770b5b |
icon_day_cycle.tga | 5b30a285-f1e3-92b1-dcd3-0d07366ced3e |
icon_event.tga | be235ae0-53cf-1d68-b3ae-cf375ed1fb58 |
icon_event_mature.tga | cc090999-1b3e-2e97-7a38-c9f4afd10297 |
icon_for_sale.tga | f20728fd-1670-3771-2293-e0dd3f0bcaab |
icon_group.tga | 04237108-a879-5a95-9b0c-b18fd09bc447 |
icon_land_for_landless.tga | c421ddf2-b9d7-b373-503c-f4c423f37f1c |
icon_place.tga | ba0bac4e-815e-14e1-2895-5065b8c703b3 |
icon_popular.tga | bdd47da5-5b5b-c906-37ad-16aaa64f096f |
icon_top_pick.tga | 77ca91a2-4431-aeaf-6249-3dd99c7dd86d |
(Top)

Movement Controls

Filename | UUID | Description
move_backward_in.tga | db11d956-5e7d-4aa5-b39d-7774d339fc5c |
move_backward_out.tga | 3ae8bb18-ed97-4cd3-ae5c-d54bc8479fe7 |
move_down_in.tga | b92a70b9-c841-4c94-b4b3-cee9eb460d48 |
move_down_out.tga | b5abc9fa-9e62-4e03-bc33-82c4c1b6b689 |
move_forward_in.tga | 54197a61-f5d1-4c29-95d2-c071d08849cb |
move_forward_out.tga | a0eb4021-1b20-4a53-892d-8faa9265a6f5 |
move_left_in.tga | 724996f5-b956-46f6-9844-4fcfce1d5e83 |
move_left_out.tga | 82476321-0374-4c26-9567-521535ab4cd7 |
move_right_in.tga | 7eeb57d2-3f37-454d-a729-8b217b8be443 |
move_right_out.tga | 1fbe4e60-0607-44d1-a50a-032eff56ae75 |
move_turn_left_in.tga | 95463c78-aaa6-464d-892d-3a805b6bb7bf |
move_turn_left_out.tga | 13a93910-6b44-45eb-ad3a-4d1324c59bac |
move_turn_right_in.tga | 5e616d0d-4335-476f-9977-560bccd009da |
move_turn_right_out.tga | 5a44fd04-f52b-4c30-8b00-4a31e27614bd |
move_up_in.tga | 49b4b357-e430-4b56-b9e0-05b8759c3c82 |
move_up_out.tga | f887146d-829f-4e39-9211-cf872b78f97c |
(Top)

Edit Mode

Filename | UUID | Description
bump_bark.tga | 6c9fa78a-1c69-2168-325b-3e03ffa348ce | Bumpieness
bump_bricks.tga | b8eed5f0-64b7-6e12-b67f-43fa8e773440 | Bumpieness
bump_checker.tga | 9deab416-9c63-78d6-d558-9a156f12044c | Bumpieness
bump_concrete.tga | db9d39ec-a896-c287-1ced-64566217021e | Bumpieness
bump_crustytile.tga | f2d7b6f6-4200-1e9a-fd5b-96459e950f94 | Bumpieness
bump_cutstone.tga | d9258671-868f-7511-c321-7baef9e948a4 | Bumpieness
bump_discs.tga | d21e44ca-ff1c-a96e-b2ef-c0753426b7d9 | Bumpieness
bump_gravel.tga | 4726f13e-bd07-f2fb-feb0-bfa2ac58ab61 | Bumpieness
bump_petridish.tga | e569711a-27c2-aad4-9246-0c910239a179 | Bumpieness
bump_siding.tga | 073c9723-540c-5449-cdd4-0e87fdc159e3 | Bumpieness
bump_stonetile.tga | ae874d1a-93ef-54fb-5fd3-eb0cb156afc0 | Bumpieness
bump_stucco.tga | 92e66e00-f56f-598a-7997-048aa64cde18 | Bumpieness
bump_suction.tga | 83b77fc6-10b4-63ec-4de7-f40629f238c5 | Bumpieness
bump_weave.tga | 735198cf-6ea0-2550-e222-21d3c6a341ae | Bumpieness
bump_woodgrain.tga | 058c75c0-a0d5-f2f8-43f3-e9699a89c2fc | Bumpieness
missing_asset.tga | 32dfd1c8-7ff6-5909-d983-6d4adfb4255d | MISSING IMAGE
object_cone.tga | c2b8c90a-7dca-26e3-1a63-7aa4a0389cf9 | Build tools Cone object type icon
object_cone_active.tga | cf69c64b-f19e-e1f3-a586-42fef31a23be | Build tools Cone object selected icon
object_cube.tga | 70c747ac-1de3-a8b9-514d-101753ca6ccb | Build tools Cube object type icon
object_cube_active.tga | f9c5e213-1076-7a7d-7889-52388aad2c1a | Build tools Cube object selected icon
object_cylinder.tga | 13e35d95-5f6c-9a91-1766-49dedf9b1267 | Build tools Cylinder object type icon
object_cylinder_active.tga | 3f3e4932-8412-e2a7-cfe9-92caf5978b1b | Build tools Cylinder object selected icon
object_grass.tga | 7ca8e672-920b-4653-3970-1abc91abef58 | Build tools Grass object type icon
object_grass_active.tga | d0fc7cc9-646a-6860-cf7c-1d9e58cd6dab | Build tools Grass object selected icon
object_hemi_cone.tga | 69d5e60c-739a-40b1-b526-84072121e394 | Build tools half-cone object type icon
object_hemi_cone_active.tga | 2e0c5435-95bb-1c0d-5da1-42336fb1cfc0 | Build tools half-cone object selected icon
object_hemi_cylinder.tga | f4be3e06-24a8-f86e-acc7-7daefc0572b7 | Build tools half-cylinder object type icon
object_hemi_cylinder_active.tga | 67279486-cfc1-3633-de42-85db65db373c | Build tools half-cylinder object selected icon
object_hemi_sphere.tga | b67251ab-1716-b9fb-f911-967ba3fe027b | Build tools half-sphere object type icon
object_hemi_sphere_active.tga | 6c489466-3058-6475-6b1b-e5fc1d49f1f3 | Build tools half-sphere object selected icon
object_prism.tga | 02935f3a-dcda-3b42-1874-da89d4c12870 | Build tools Prism object type icon
object_prism_active.tga | 223aac97-bd2f-ec2e-ad45-5641b77c78f9 | Build tools Prism object selected icon
object_pyramid.tga | 9dde8b56-2cc4-a932-b63a-38c3a83221ad | Build tools Pyramid object type icon
object_pyramid_active.tga | e7217b1a-e3d8-e339-d28a-d7714d0b5bee | Build tools Pyramid object selected icon
object_ring.tga | a7610e41-4647-16d8-0e0e-85a1211c1596 | Build tools Ring object type icon
object_ring_active.tga | 2c955a73-fa31-237b-a4a1-5c8ede3bae50 | Build tools Ring object selected icon
object_sphere.tga | 7fa122c0-b994-460e-8636-cdc451d67268 | Build tools Sphere object type icon
object_sphere_active.tga | f2c3bcbc-2904-41a5-1c22-688f176fd1ee | Build tools Sphere object selected icon
object_tetrahedron.tga | e17db404-9fc5-9534-1038-777c82b2771f | Build tools Tetrahedron object type icon
object_tetrahedron_active.tga | 2792ea3b-c052-85fe-d168-a62b2f4e9d7c | Build tools Tetrahedron object selected icon
object_torus.tga | 19e1f4c9-6aa6-4414-981d-59a1343a6472 | Build tools Torus object type icon
object_torus_active.tga | ef2bca77-5004-4547-b00a-3b96e463f89f | Build tools Torus object selected icon
object_tree.tga | 710d1bec-fb33-28f1-e77e-ddbb5b51f5ed | Build tools Tree object type icon
object_tree_active.tga | da4835c7-b12a-41dd-11db-dae452f040c2 | Build tools Tree object selected icon
object_tube.tga | 7ce81316-a478-480f-961c-435fcbdecaf0 | Build tools Tube object type icon
object_tube_active.tga | 55c3e4d1-cfdc-48a8-af32-a34844b91832 | Build tools Tube object selected icon
plywood (default object texture) | 89556747-24cb-43ed-920b-47caed15465f | The default texture prims are set to upon creation.
selected texture | a85ac674-cb75-4af6-9499-df7c5aaf7a28 | Texture of crosshairs that is overlayed onto the selected texture in "Select Texture" mode.
tool_dozer.tga | d2a0d4d4-54eb-4d16-be4b-4eae43845c74 |
tool_dozer_active.tga | d4afdbbe-1550-4b7d-91de-95731f47e8e3 |
tool_land.tga | 86fe4df4-0ecb-4382-b9ae-475925a92388 |
tool_land_active.tga | 34e60587-0791-4a07-8918-f5995fcc22a3 |
tool_orbit.tga | 06964fe4-033f-448a-95c9-30dc41d1be8b |
tool_orbit_active.tga | ee4e07db-3f72-4098-bd4c-aef34515a7bc |
tool_pan.tga | a32aa302-0a15-48d2-b2b1-4d69f1161173 |
tool_pan_active.tga | 24d9ad33-0b42-4eb5-99a3-659d838bc5c0 |
tool_zoom.tga | 27eb8829-fe65-45ed-a49a-73aac42f4b38 |
tool_zoom_active.tga | 69445f58-5c8e-44e0-9d2e-47408bb43b39 |
white.tga | 5748decc-f629-461c-9a36-a35a221fe21f | The "blank" texture.
(Top)

Misc

Texture | UUID | Description
alpha_noise.tga | b9e1cf8a-9660-c020-0c69-18f1ea27268a |
alpha_sizzle.tga | e121e2fc-7573-740f-edfd-0d45a9ba486e | Possibly a texture used to generate clouds? Also the texture used for unloaded textures in the texture picker
black.tga | e2244626-f22f-4839-8123-1e7baddeb659 |
darkgray.tga | 267e26d3-e0e1-41b8-91b1-3b337102928d |
legend.tga | ca7609c6-6ec6-32d9-332e-0d8f437ef644 |
lightning | f8e2c2f0-7d5e-bb9a-68d0-7a3e87984784 |
lightning (flipped horizontally) | eef34d99-4f5b-efa1-4586-b4853c430dd3 |
lightgray.tga | c520bf46-cc5d-412b-a60b-9f1bd245189f |
rounded_square.tga | 38ce8b3c-fb30-5c59-9926-bd643613f606 |
rounded_square_soft.tga | 4c95e6bc-fe77-9cb4-b58a-909848042c1e |
(Top)

Animations

Filename | UUID
avatar_aim_l_bow.bvh | 46bb4359-de38-4ed8-6a22-f1f52fe8f506
avatar_aim_r_bazooka.bvh | b5b4a67d-0aee-30d2-72cd-77b333e932ef
avatar_aim_r_handgun.bvh | 3147d815-6338-b932-f011-16b56d9ac18b
avatar_aim_r_rifle.bvh | ea633413-8006-180a-c3ba-96dd1d756720
avatar_angry_fingerwag.bvh | c1bc7f36-3ba0-d844-f93c-93be945d644f
avatar_angry_tantrum.bvh | 11000694-3f41-adc2-606b-eee1d66f3724
avatar_away.bvh | fd037134-85d4-f241-72c6-4f42164fedee
avatar_backflip.bvh | c4ca6188-9127-4f31-0158-23c4e2f93304
avatar_blowkiss.bvh | db84829b-462c-ee83-1e27-9bbee66bd624
avatar_bow.bvh | 82e99230-c906-1403-4d9c-3889dd98daba
avatar_brush.bvh | 349a3801-54f9-bf2c-3bd0-1ac89772af01
avatar_busy.bvh | efcf670c-2d18-8128-973a-034ebc806b67
avatar_clap.bvh | 9b0c1c4e-8ac7-7969-1494-28c874c4f668
avatar_courtbow.bvh | 9ba1c942-08be-e43a-fb29-16ad440efc50
avatar_crouch.bvh | 201f3fdf-cb1f-dbec-201f-7333e328ae7c
avatar_crouchwalk.bvh | 47f5f6fb-22e5-ae44-f871-73aaaf4a6022
avatar_dance1.bvh | b68a3d7c-de9e-fc87-eec8-543d787e5b0d
avatar_dance2.bvh | 928cae18-e31d-76fd-9cc9-2f55160ff818
avatar_dance3.bvh | 30047778-10ea-1af7-6881-4db7a3a5a114
avatar_dance4.bvh | 951469f4-c7b2-c818-9dee-ad7eea8c30b7
avatar_dance5.bvh | 4bd69a1d-1114-a0b4-625f-84e0a5237155
avatar_dance6.bvh | cd28b69b-9c95-bb78-3f94-8d605ff1bb12
avatar_dance7.bvh | a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6
avatar_dance8.bvh | b0dc417c-1f11-af36-2e80-7e7489fa7cdc
avatar_dead.bvh | 57abaae6-1d17-7b1b-5f98-6d11a6411276
avatar_drink.bvh | 0f86e355-dd31-a61c-fdb0-3a96b9aad05f
avatar_express_afraid.bvh | 6b61c8e8-4747-0d75-12d7-e49ff207a4ca
avatar_express_afraid_emote.bvh | aa2df84d-cf8f-7218-527b-424a52de766e
avatar_express_anger.bvh | 5747a48e-073e-c331-f6f3-7c2149613d3e
avatar_express_anger_emote.bvh | 1a03b575-9634-b62a-5767-3a679e81f4de
avatar_express_bored.bvh | b906c4ba-703b-1940-32a3-0c7f7d791510
avatar_express_bored_emote.bvh | 214aa6c1-ba6a-4578-f27c-ce7688f61d0d
avatar_express_cry.bvh | 92624d3e-1068-f1aa-a5ec-8244585193ed
avatar_express_cry_emote.bvh | d535471b-85bf-3b4d-a542-93bea4f59d33
avatar_express_disdain.bvh | d4416ff1-09d3-300f-4183-1b68a19b9fc1
avatar_express_embarrassed_emote.bvh | 0b8c8211-d78c-33e8-fa28-c51a9594e424
avatar_express_frown.bvh | fee3df48-fa3d-1015-1e26-a205810e3001
avatar_express_kiss.bvh | 1e8d90cc-a84e-e135-884c-7c82c8b03a14
avatar_express_laugh.bvh | 18b3a4b5-b463-bd48-e4b6-71eaac76c515
avatar_express_laugh_emote.bvh | 62570842-0950-96f8-341c-809e65110823
avatar_express_open_mouth.bvh | d63bc1f9-fc81-9625-a0c6-007176d82eb7
avatar_express_repulsed.bvh | 36f81a92-f076-5893-dc4b-7c3795e487cf
avatar_express_repulsed_emote.bvh | f76cda94-41d4-a229-2872-e0296e58afe1
avatar_express_sad.bvh | 0eb702e2-cc5a-9a88-56a5-661a55c0676a
avatar_express_sad_emote.bvh | eb6ebfb2-a4b3-a19c-d388-4dd5c03823f7
avatar_express_shrug.bvh | 70ea714f-3a97-d742-1b01-590a8fcd1db5
avatar_express_shrug_emote.bvh | a351b1bc-cc94-aac2-7bea-a7e6ebad15ef
avatar_express_smile.bvh | b7c7c833-e3d3-c4e3-9fc0-131237446312
avatar_express_surprise.bvh | 313b9881-4302-73c0-c7d0-0e7a36b6c224
avatar_express_surprise_emote.bvh | 728646d9-cc79-08b2-32d6-937f0a835c24
avatar_express_tongue_out.bvh | 835965c6-7f2f-bda2-5deb-2478737f91bf
avatar_express_toothsmile.bvh | b92ec1a5-e7ce-a76b-2b05-bcdb9311417e
avatar_express_wink.bvh | 869ecdad-a44b-671e-3266-56aef2e3ac2e
avatar_express_wink_emote.bvh | da020525-4d94-59d6-23d7-81fdebf33148
avatar_express_worry.bvh | 9f496bd2-589a-709f-16cc-69bf7df1d36c
avatar_express_worry_emote.bvh | 9c05e5c7-6f07-6ca4-ed5a-b230390c3950
avatar_falldown.bvh | 666307d9-a860-572d-6fd4-c3ab8865c094
avatar_female_walk.bvh | f5fc7433-043d-e819-8298-f519a119b688
avatar_fist_pump.bvh | 7db00ccd-f380-f3ee-439d-61968ec69c8a
avatar_fly.bvh | aec4610c-757f-bc4e-c092-c6e9caf18daf
avatar_flyslow.bvh | 2b5a38b2-5e00-3a97-a495-4c826bc443e6
avatar_hello.bvh | 9b29cd61-c45b-5689-ded2-91756b8d76a9
avatar_hold_l_bow.bvh | 8b102617-bcba-037b-86c1-b76219f90c88
avatar_hold_r_bazooka.bvh | ef62d355-c815-4816-2474-b1acc21094a6
avatar_hold_r_handgun.bvh | efdc1727-8b8a-c800-4077-975fc27ee2f2
avatar_hold_r_rifle.bvh | 3d94bad0-c55b-7dcc-8763-033c59405d33
avatar_hold_throw_r.bvh | 7570c7b5-1f22-56dd-56ef-a9168241bbb6
avatar_hover.bvh | 4ae8016b-31b9-03bb-c401-b1ea941db41d
avatar_hover_down.bvh | 20f063ea-8306-2562-0b07-5c853b37b31e
avatar_hover_up.bvh | 62c5de58-cb33-5743-3d07-9e4cd4352864
avatar_impatient.bvh | 5ea3991f-c293-392e-6860-91dfa01278a3
avatar_jump.bvh | 2305bd75-1ca9-b03b-1faa-b176b8a8c49e
avatar_jumpforjoy.bvh | 709ea28e-1573-c023-8bf8-520c8bc637fa
avatar_kick_roundhouse_r.bvh | 49aea43b-5ac3-8a44-b595-96100af0beda
avatar_kissmybutt.bvh | 19999406-3a3a-d58c-a2ac-d72e555dcf51
avatar_land.bvh | 7a17b059-12b2-41b1-570a-186368b6aa6f
avatar_laugh_short.bvh | ca5b3f14-3194-7a2b-c894-aa699b718d1f
avatar_motorcycle_sit.bvh | 08464f78-3a8e-2944-cba5-0c94aff3af29
avatar_musclebeach.bvh | 315c3a41-a5f3-0ba4-27da-f893f769e69b
avatar_no_head.bvh | 5a977ed9-7f72-44e9-4c4c-6e913df8ae74
avatar_no_unhappy.bvh | d83fa0e5-97ed-7eb2-e798-7bd006215cb4
avatar_nyanya.bvh | f061723d-0a18-754f-66ee-29a44795a32f
avatar_peace.bvh | b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9
avatar_point_me.bvh | 17c024cc-eef2-f6a0-3527-9869876d7752
avatar_point_you.bvh | ec952cca-61ef-aa3b-2789-4d1344f016de
avatar_prejump.bvh | 7a4e87fe-de39-6fcb-6223-024b00893244
avatar_punch_l.bvh | f3300ad9-3462-1d07-2044-0fef80062da0
avatar_punch_onetwo.bvh | eefc79be-daae-a239-8c04-890f5d23654a
avatar_punch_r.bvh | c8e42d32-7310-6906-c903-cab5d4a34656
avatar_rps_countdown.bvh | 35db4f7e-28c2-6679-cea9-3ee108f7fc7f
avatar_rps_paper.bvh | 0836b67f-7f7b-f37b-c00a-460dc1521f5a
avatar_rps_rock.bvh | 42dd95d5-0bc6-6392-f650-777304946c0f
avatar_rps_scissors.bvh | 16803a9f-5140-e042-4d7b-d28ba247c325
avatar_run.bvh | 05ddbff8-aaa9-92a1-2b74-8fe77a29b445
avatar_salute.bvh | cd7668a6-7011-d7e2-ead8-fc69eff1a104
avatar_shoot_l_bow.bvh | e04d450d-fdb5-0432-fd68-818aaf5935f8
avatar_shout.bvh | 6bd01860-4ebd-127a-bb3d-d1427e8e0c42
avatar_sit.bvh | 1a5fe8ac-a804-8a5d-7cbd-56bd83184568
avatar_sit_female.bvh | b1709c8d-ecd3-54a1-4f28-d55ac0840782
avatar_sit_generic.bvh | 245f3c54-f1c0-bf2e-811f-46d8eeb386e7
avatar_sit_ground.bvh | 1c7600d6-661f-b87b-efe2-d7421eb93c86
avatar_sit_to_stand.bvh | a8dee56f-2eae-9e7a-05a2-6fb92b97e21e
avatar_sleep.bvh | f2bed5f9-9d44-39af-b0cd-257b2a17fe40
avatar_smoke_idle.bvh | d2f2ee58-8ad1-06c9-d8d3-3827ba31567a
avatar_smoke_inhale.bvh | 6802d553-49da-0778-9f85-1599a2266526
avatar_smoke_throw_down.bvh | 0a9fb970-8b44-9114-d3a9-bf69cfe804d6
avatar_snapshot.bvh | eae8905b-271a-99e2-4c0e-31106afd100c
avatar_soft_land.bvh | f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57
avatar_stand.bvh | 2408fe9e-df1d-1d7d-f4ff-1384fa7b350f
avatar_stand_1.bvh | 15468e00-3400-bb66-cecc-646d7c14458e
avatar_stand_2.bvh | 370f3a20-6ca6-9971-848c-9a01bc42ae3c
avatar_stand_3.bvh | 42b46214-4b44-79ae-deb8-0df61424ff4b
avatar_stand_4.bvh | f22fed8b-a5ed-2c93-64d5-bdd8b93c889f
avatar_standup.bvh | 3da1d753-028a-5446-24f3-9c9b856d9422
avatar_stretch.bvh | 80700431-74ec-a008-14f8-77575e73693f
avatar_stride.bvh | 1cb562b0-ba21-2202-efb3-30f82cdf9595
avatar_surf.bvh | 41426836-7437-7e89-025d-0aa4d10f1d69
avatar_sword_strike_r.bvh | 85428680-6bf9-3e64-b489-6f81087c24bd
avatar_talk.bvh | 5c682a95-6da4-a463-0bf6-0f5b7be129d1
avatar_throw_r.bvh | aa134404-7dac-7aca-2cba-435f9db875ca
avatar_tryon_shirt.bvh | 83ff59fe-2346-f236-9009-4e3608af64c1
avatar_turn_180.bvh | 038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53
avatar_turnback_180.bvh | 6883a61a-b27b-5914-a61e-dda118a9ee2c
avatar_turnleft.bvh | 56e0ba0d-4a9f-7f27-6117-32f2ebbf6135
avatar_turnright.bvh | 2d6daa51-3192-6794-8e2e-a15f8338ec30
avatar_type.bvh | c541c47f-e0c0-058b-ad1a-d6ae3a4584d9
avatar_walk.bvh | 6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0
avatar_whisper.bvh | 7693f268-06c7-ea71-fa21-2b30d6533f8f
avatar_whistle.bvh | b1ed7982-c68e-a982-7561-52a88a5298c0
avatar_wink_hollywood.bvh | c0c4030f-c02b-49de-24ba-2331f43fe41c
avatar_yes_happy.bvh | b8c8b2a3-9008-1771-3bfc-90924955ab2d
avatar_yes_head.bvh | 15dd911d-be82-2856-26db-27659b142875
avatar_yoga_float.bvh | 42ecd00b-9947-a97c-400a-bbc9174c7aeb
(Method used to determine UUIDs)
(Top)

Sounds

Sound | UUID
bell ting | ed124764-705d-d497-167a-182cd9fa2e6c
click | 4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6
health reduction (female) | 219c5d93-6c09-31c5-fb3f-c5fe7495c115
health reduction (male) | e057c244-5768-1056-c37e-1537454eeb62
IM session, new incoming (2 bells) | 67cc2844-00f3-2b3c-b991-6418d01e1bb7
IM start | c825dfbc-9827-7e02-6507-3713d18916c1
instant message notification | 67cc2844-00f3-2b3c-b991-6418d01e1bb7
invalid operation | 4174f859-0d3d-c517-c424-72923dc21f65
keyboard loop | 5e191c7b-8996-9ced-a177-b2ac32bfea06
money change, down (coins) | 77a018af-098e-c037-51a6-178f05877c6f
money change, up (cash register bell) | 104974e3-dfda-428b-99ee-b0d4e748d3a3
null keystroke | 2ca849ba-2885-4bc3-90ef-d4987a5b983a
object collision | be582e5d-b123-41a2-a150-454c39e961c8
object collision (rubber) | 212b6d1e-8d9c-4986-b3aa-f3c6df8d987d
object collision (plastic) | d55c7f3c-e1c3-4ddc-9eff-9ef805d9190e
object collision (flesh) | 2d8c6f51-149e-4e23-8413-93a379b42b67
object collision (wood splintering?) | 6f00669f-15e0-4793-a63e-c03f62fee43a
object collision (glass break) | 85cda060-b393-48e6-81c8-2cfdfb275351
object collision (metal clunk) | d1375446-1c4d-470b-9135-30132433b678
object create (whoosh) | 3c8fc726-1fd6-862d-fa01-16c5b2568db6
object delete | 0cb7b00a-4c10-6948-84de-a93c09af2ba9
object rez (shake) | f4a0660f-5446-dea2-80b7-6482a082803c
pie menu appear (ding) | 8eaed61f-92ff-6485-de83-4dcc938a478e
pie menu slice highlight | d9f73cf8-17b4-6f7a-1565-7951226c305d
pie menu slice highlight1 (same as above) | f6ba9816-dcaf-f755-7b67-51b31b6233e5
pie menu slice highlight2 (same as above) | 7aff2265-d05b-8b72-63c7-dbf96dc2f21f
pie menu slice highlight3 (same as above) | 09b2184e-8601-44e2-afbb-ce37434b8ba1
pie menu slice highlight4 (same as above) | bbe4c7fc-7044-b05e-7b89-36924a67593c
pie menu slice highlight5 (same as above) | d166039b-b4f5-c2ec-4911-c85c727b016c
pie menu slice highlight6 (same as above) | 242af82b-43c2-9a3b-e108-3b0c7e384981
pie menu slice highlight7 (same as above) | c1f334fb-a5be-8fe7-22b3-29631c21cf0b
snapshot | 3d09f582-3851-c0e0-f5ba-277ac5c73fb4
teleport/texture apply | d7a9a565-a013-2a69-797d-5332baa1a947
thunder | e95c96a5-293c-bb7a-57ad-ce2e785ad85f
window close | 2c346eda-b60c-ab33-1119-b8941916a499
window open (same as above) | c80260ba-41fd-8a46-768a-6bf236360e3a
zipper? | 6cf2be26-90cb-2669-a599-f5ab7698225f
(Top)
Key | Asset


clothes

See clothing.


Clothing

In Second Life, clothing is a specific inventory item and consists of a texture and shape data added together. For some experienced designers new to SL, this can be counter-intuitive. Clothing shape types are: gloves, pants, jacket, shirt, shoes, skirt, socks, undershirt, and underpants.

Scripts can't be attached to clothing--only to objects. LSL can't rename or otherwise manipulate clothing.

Clothing can be identified by llGetInventoryName and llGetInventoryNumber, using the inventory constant INVENTORY_CLOTHING.

Were you thinking of attachments? Attachments are objects "worn" (to use the Second Life UI's parlance) or "attached" (to use LSL's). Attachments frequently take the form of prim hair, hats, shoes, jewelery, and so on. Attachments can contain scripts.

Inventory | Avatar


Code

"Code", short for "source code", is a sequence of specifically formatted characters, numbers, and symbols assembled to create software. In LSL, LSL is the code.

"Bytecode" is compiled code, readable only by a computer (and really crazy and/or smart people).

LSL | Script | Code Block


CodeBlock

A codeblock is code within a set of brackets; also known as simply a block.

Example:
default { // Start default's code block.

} // End default's code block.

Of course, code block statements can contain other code blocks.

Example:
default { // Start default's code block.
    state_entry() { // Start the state_entry event block

    } // End the state_entry event block
} // End default's code block.

Some prefer to place the 'opening brace' of the code block on the next following row.

for(iterator declaration; condition; increment operation)
{ // Start loop code block.

} // End loop code block.

Where to place that opening brace is a topic for religious fervor. See braces for more on this.

Usually, you indent the statements of the code block, that is, add spaces or tab-spaces to make an outline of the code - this highly improves readability.

if(someCondition)
{ // Start conditional code block.

    // Tabulated inner statement
    for(iterator declaration; condition; increment operation)
    {
        // Tabulated inner inner et.c.
    }

} // End conditional code block.

Glossary | Script | Brace


colour

See Color.


commandsexample

work in progress


Commons

The commons is the pool of backend server resources devoted to running LSL scripts, as well as public access to the skies of SL for travel and scripted objects--everything available to the general SL public that is not reserved for landowners.

The border between the commons and the private is the line where landowners rights cease. Specifically: landowners can specify "no outside scripts" up to 20 meters above the terrain of their land. Above that is common space for scripted objects. Landowners can return objects that are over their land... except TemporaryOnRez temp-on-rez objects--those are part of the commons. Also, the resources devoted to executing LSL scripts is all one big common pool--anyone can dip from the pool. In fact, landowners have to dip from the common pool in order to run their own scripts--there is no portion of LSL script resources that are reserved for them.

The only way to "withdraw" from the commons would be to buy a private island--owners of private islands have more control over their resources in that they have more control over who they allow access.

The commons are determined by the feature set of SL. This is key to the confusion about what is common and what is not--it is impossible for the commons to use up private resources--if the feature set allows you to use up some resource then it is common, by definition. Some people believe that a portion of the common resources are currently reserved for them--they are incorrect... sorta. Some resources should belong to them, but the feature set does not yet support it. (Andrew Linden, 3/20/05)

Land | Temporary On Rez


Communication

See Communications.


Compile

LSL source code won't work on its own. It must first be "compiled" into bytecode by a compiler that will run on the LSL virtual machine. It's all automatic, so you don't have to worry about the specifics of how it works.

The LSL compiler is contained within the SL client. When the "Save" button is clicked in the script editor window, the user's computer strips out comments, compiles the code, and sends it to the server where it creates a new script inventory item. (If editing an existing script, it will appear to overwrite the existing version though it's actually removing the existing copy and creating a new script with the same name and a different key.)

If source code cannot be properly compiled, an error will return, indicating which problem has happened (missing brackets, unknown variable names, etc.) and the location within the script's source code where it occured.

Glossary


Compiler

A "compiler" is the program that converts ("compiles") source code to a program that can be run. In SL, the compiler is integrated into the SL client's script editor tool. Pressing the "Save" button compiles the code, then uploads the resulting bytecode to the SL server.

The compiling process is described at the "compile" entry.

Glossary


Concatenate

To concatenate, means to put together. In the context of LSL, concatination applies to strings and lists - its how you fuse two strings or two lists together. It is done with the + operator.

Examples:
list a = ["Hello"];
list b = ["Goodbye"];
list ab = a + b; // ab is ["Hello", "Goodbye"]

string a = "Hello";
string b = "Goodbye";
string ab = a + b; // ab is "HelloGoodbye"
Why is the latter comment not orange?
String | List


ConceptOverview

LSL Concept Overview

(1/3 done - feel free to add stuff, but keep it terse)

Linden Scripting Language (LSL)

Scripts

One or more scripts can be included in the inventory of a prim to affect its appearance and behavior when it is rezzed in the world.

The script language has familiar operators flow control, and data types including the integer, float, string, and list. There are also a few types specific to SL. A key is a string used to identify objects. A vector is used to represent color, direction, velocity, force, impulse, position, scale, and Euler rotation. Quaterion rotation is used in many cases.

Scripts use constants, define and use local and global variables, user defined functions, and states. A large library of built-in functions provides access to things in SecondLife.

Scripts can handle many kinds of events:

(objects) changed, sensor, collision, on_rez
(agents/avatars) attach, touch, control, money, run_time_permissions
(communication) listen, email, remote_data, dataserver
(world) timer

After being compiled into bytecode, scripts are initially in the default state. Scripts can change the state to any locally defined state or back to the default state. A state change clears all pending events. Resetting a script not only resets the state to the default state, but also resets all global variables to their initial state.

Scripts can be turned on or off by another script in the same prim with llSetScriptState.
Scripts can be updated by other scripts with llRemoteLoadScriptPin.
Some functions cause a ScriptDelay after executing.

memory | errors

World

The SL world or grid consists of several sims positioned in cartesian global coordinates each one with its own regional coordinates. Each prim and object has its own own internal local coordinates.

Sims always have land, but may be submerged in water.

Sound
Video

Agents and Avatars

A user is represented by an agent and an avatar. An agent can be an object, creator and/or owner. Three or more users can form a group and own land and group objects.

The user controls an avatar, a visual representation of a user agent, via the client program. The avatar always plays an animation. An avatar can have attachments to parts of its body.

Objects

An object is composed of a non-hierarchical set of prims or primitive shapes that are linked together.

Each prim has a shape composed of several sides. Each side has a color, opacity, and texture.
Each prim has geometric properties position, scale, and rotation, and several additional parameters Particles

Objects (but not prims) have permissions which constrain what users, avatars, and other objects can do with them.
An object is either solid or phantom, and it either obeys physics or not.
When an object is rezzed it may be permanent or TemporaryOnRez.

Inventory
Vehicles


Condition

A condition is often used to reference statements within a flow-control statement's parenthesis. Conditions determine whether the code contained within the conditional statement's block should be executed.

Example:
if (isAwesome) {
    llOwnerSay("w00t! This is awesome!");
} else {
    llOwnerSay("Aww, this is lame :-|");
}

while(isAwesome) {
    llOwnerSay("Awesome!");
}
In this case, isAwesome is the condition.

If-Else | statement | code


ConditionalStatements

See IfElse.


ConsoleOption

Check this off and then restart your SL client to have a DOS window pop up and show you what's going on under the hood of Second Life.

debug


constant

See constants.


contribute

So you want to help improve the LSL Wiki Mirror 7-7-7? You found the right place!

Before you start, create an account. Very few pages are editable by users without an account. You need to create one even if you already have a Second Life account.

Once you've got an account, take at least a quick glance at the following pages:

  • WikiEtiquette: Here you'll find information that applies to Wikis in general.
  • What's To-Do
  • WikiFormatting: How to do it.
  • WikiStyleGuide: How to make it fit into this Wiki.
  • Other Contributors: Feel free to add yourself to the list.
  • Scripting Mentors. Feel free to add yourself to the list and contact those on it.

You don't have to read all that if you just want to make some minor edits; just put the information down and others will fix the rest.

If you're not sure about something or want to discuss it before making a change, add a comment or make a note on the page.


Coordinate

An SL coordinate is represented by a vector in the form <x,y,z> that indicates a position in a three-dimensional (3D) space defined by 3 linear axes at right (90-degree) angles to each other. Each axis has an associated unit length (meters in SL) and the combination forms a cubic grid.

The origin of a coordinate system is the point where the axes meet, represented by the vector <0,0,0>. In SL the position of any given object or effect can be simultaneously represented by 3 distinct coordinate system types:

  • Global coordinates represent the component distances in meters from a single point on the grid.
  • Region coordinates are meters relative to the southwest corner of a given simulator.
  • Local coordinates give the position of one object or effect with respect to another.

Note: coordinates also apply to textures via UV.

Axis | Vector | UV


coordinates

See coordinate.


cosine

Cosine is a trigonometric function, llCos, which represents the ratio of sides of a triangle.

You can pass cosine an angle to an object and it will return the ratio of the adjacent side (x value) and the hypotenuse (distance to object).

The inverse function is arccosine.

sine | tangent


CPU

Short for "central processing unit", the "brain" of a computer. Each sim in SL runs on its own CPU core, though there may be multiple CPUs per server.


Crash

A "crash" is when a program or script simply stops responding and cannot proceed. For examples of crashes in LSL, ("run-time errors"), see errors.

Bugs can lead to crashes.

Glossary


Crash Course

Here is an LSL-specific crash course in programming. Do not be intimidated, it really is easy! Work on one section at a time, and if you ever have questions, IM a Scripting Mentor in-world, or post a comment here for everyone's benefit.

You may read it in chapters, listed below. However, if you have a comment, please post it on this page, and not in a section.

You may be confused by some of the terminology used on these pages. If a word or phrase is not linked directly, consult the Glossary. If you don't understand something, post a comment on this page.

Chapters:
1) Comments
2) Values (Variables, Constants, and Literals)
3) Codeblocks
4) Functions
5) Events
6) Conditionals (Meat and potatoes 1)
7) Loops and Typecasting (Meat and potatoes 2)
8) States
9) Miscellaneous other hints and tips

Homepage | Tutorial | Glossary


CrashCourse1

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

1) Comments
Comments are lines prefixed with double-slash. (//)

Here is an example:
// this is a comment

The double-slash may appear almost anywhere, and anything following it on the same line will be ignored by the compiler. They're called "comments" for a reason -- they let you comment your code, explaining and labeling parts of your script. This is very useful, as it lets you explain what your scripts do, not only to others, but to yourself. It's surprising how much of your thought process can be forgotten in a few months.

The other use of comments is to "comment out" a line of code. By using comments when working on a script, you can prevent parts of your script from running, without actually having to delete them. Removing the comment restores the code. This can be useful when troubleshooting a script.

For example:
llOwnerSay("Hello"); // The code on this line will run.
//llSay(0,"Hi there.); This line is commented out. It will not run.

Try it out yourself. Create a new script, and see what happens when you comment out various lines. Most will return errors when you click "Save", but you can comment out either of the llSay lines or even the entire state_entry or touch_start code blocks to prevent them from running at all. (Remember to get every line, including the ones with braces!)

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse2

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

2) Values
Values (variables, "constants", literals, etc):

variable -- has a name, has a value, value can change.
constant -- has a name, has a value which is fixed -- LSL has no real constant support, but when they're necessary, you can fake a constant by using a variable, and never changing its value.
literal -- an actual value that can be assigned to a variable, or used as an argument. (you'll get to this in a sec)

Values have what's called a type as well. Integer, float and string are the most common ones. For a complete list, as well as a discussion of the differences, see the types page.

Examples of usage:

Declaring a variable:
integer score = 0;
string loginName;
The initial (or default) value of 0 has been assigned to a variable score of the type integer. The default value of the variable is optional. Some languages offer implicit default values (usually 0 for integers, the empty string for strings, etc). It's generally intuitive, if supported. The variable loginName of type string is declared, and is empty by implicit default.

Declaring a constant:
integer MAX_MOVES = 10;
It's a good idea to identify constants in a different style than variables, so give a visual reminder that they are not to be modified. I use ALL-CAPS, usually. Note: a default value is specified, and it should be expected by the programmer that this value will not change at runtime.
Technically, LSL does NOT have constants; rather this is a (good) formatting suggestion of a variable.
- I (author) agree; thanks for pointing that out. :-)

Using a literal:
loginName = "testuser";
Here, loginName from our earlier example is being re-assigned to contain the literal string "testuser". So, "testuser" is a literal.

You should take a brief look at the complete list of types. It explains the difference between each one.

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse3

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

3) Codeblocks
A codeblock is a well-formed sub-piece of a program/script. In LSL, it is enclosed in curly braces. It's important to keep curly braces: { }, brackets: ( ) and square brackets: [ ] straight. They're all used in different situations, which can be confusing.

Example:
if (0 == 1)
{
   // this could never occur! if it does, shoot me!
   llWhisper(0, "Houston, we have a problem.");
}
Notice that the block contains a comment and a function call. We are passing two arguments to the function llWhisper, enclosed in parentheses, namely the 0 (an integer literal), and "Houston, we have a problem." (a string literal), respectively.

You may declare variables inside a code block, and they will be useable within that block or sub-blocks inside it. Outside the context of that block (called "scope"), things are not meaningful. So the following is nonsense:
integer x = 0;
// ... (some other code)
if (x == 0)
{
   integer y = 1;
}
x = y; // this is not valid -- y is only in scope within the above sub-code-block
When we try to assign the variable x the value stored in y, we notice that y is no longer in scope, so we'll get an error. If we had declared y outside that block, in the parent block (which is implied), it would be okay. In other words, a code block inside another code block hides its contents from the outer block, but the inner block can "see" everything in the outer block, in addition to its own scope.

Now you may realize: variables declared near the top of the script, outside any code block, are called global -- they are in scope for the entire script, which can be useful, but can also be abused. In general, try to avoid using global variables if you don't need them. But of course, it's good to make constants global. Variables that are declared inside a code block are called local.

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse4

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

4) Functions
A function, in general, does some work, and returns a result of a particular type. In LSL, as in C, a function can return a meaningless or nonexistent result -- in C, this is called the "void" type; in LSL, one simply excludes a type from the function's prototype (explained in a moment). Pedants will say that a function that doesn't return a result is actually a "procedure", but we won't get too worked up about that.

Anyway, here's an example function in LSL:
integer sum(integer a, integer b)
{
   integer result = a + b;
   return result;
}
Here we see the basic layout of a function: its calling convention (that is, the name of the function, how many arguments (AKA parameters) it takes, in what order they appear, what their types are, and how they're identified), sometimes also called a "prototype", followed by a code block (which of course, can contain sub-code-blocks).

Now, there are a few abstract ideas to get down:

1) A function can, inside of its code, call another function. Program flow in the caller stops temporarily, and control is passed to the function being called (which is sometimes referred to as the callee -- hey, don't blame me, I don't make these things up); when it returns, flow continues after the call.

Example:
integer negate(integer x)
{
   return -x;
}

integer difference(integer a, integer b)    //This isn't intended for efficiency
{
   integer negB = negate(b);
   integer result = a + negB;
   return result;                //If you wanted efficiency, it would be "return a - b;"
}
So, here, when you call difference with two numbers, first, the second argument (b) has its value passed to negate, which takes control, and negates the value passed to it, then returns the negated value of b. Control returns to the difference function, and the result is stored in negB. Then, we add the two operands. Adding a negative is equivalent to subtracting, so the difference function really does compute the difference between the two original arguments (a and b). Fantastic!

2) A function can take almost any number of arguments, including none:

Examples:
doNothing()
{
   // takes no arguments, and does nothing, and returns nothing -- what a waste!
}
integer add4Numbers(integer w, integer x, integer y, integer z)
{
   return w + x + y + z;
}

3) Arguments passed into a function are copies of values, and can be modified with no effect on the caller (note: this is specific to LSL, not programming in general).

Example:
// assume we have the negate function from before already defined
integer foo()
{
   integer q = 1;
   integer negQ = negate(q);
   // what is the value of q now? that's right, still 1 -- can you explain why?
}
The variable q is still 1 because a copy of its value was passed to the negate function, so when the negate function modified its argument, it didn't modify q, but rather a copy of the value that q had when the function was called. This is a very important concept. If you do not understand it fully --I mean crystal clarity-- IM another scripter in-world and ask them to explain it better. If they can't help you, they'll generally be able to point you in the right direction. Most problems students have in learning are not caused by their lack of capability, but by poor teaching.

Okay, now you might be thinking "great, now I have to implement all kinds of basic functions before I can even get to real scripting," and if so, cheer up, because it's not the case. There are in fact many, many useful built-in functions, and to help you keep track of them, they all begin with double-lowercase-ell ("ll"). (It stands for Linden Lab.) You can call these functions to determine where your avatar is, to change the color of a prim, and much more. See the Functions page for a complete list of built-in functions.

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse5

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

5) Events
LSL is a modern language, and as such, it supports event-driven programming. This simply means that instead of having to write a program that anticipates all possibilities, it can react to things as they occur.

There is a special class of function called an event handler (though it's more frequently referred to as simply an "event", though when learning, that invariably leads to confusion -- the event should be the thing that happens, and the handler should be what deals with it. Anyway, I digress...). Normally, a program consists of a main (entry) function which in turn calls other functions, and runs in a fairly linear fashion. When you have event-driven programs, however, things can happen out of the blue, and interrupt your program flow. This is actually a good thing. Think about it like this:

Suppose you are reading a good book, but you want a cup of tea to sip while reading. You have at least two ways to manage this:

1) Put the book down, put the kettle on, and watch it until it boils. Make your tea, pick up your book, and enjoy.

2) Put the book down just for a moment, use a whistling kettle, put it on, and resume reading. Once it boils, you interrupt your reading just for a moment to make the tea, and then you can read and sip.

You can probably agree that option 2 is better because it allows you to keep reading while the water is coming to a boil, instead of staring balefully at the kettle until steam is shooting out of it before returning to your book.

If you were to translate this situation to a programming frame of mind, the kettle boiling would be an event, and you would have an event handler that would be called when the boiling event occurred. Simple, no?

Usually, you have to set up an interest in the event to be notified of it. In the case of the kettle, you use a whistle on the kettle, and tune your ears to the whistling sound, so you notice as soon as it happens and can take action.

In LSL, there are quite a few events you can watch for. One very simple event in LSL is the timer. You can set up a timer event, and every x seconds, the timer event handler will be called. Thus, you could make a script that does something on the hour, or whatever. If you want to learn about events practically, however, I recommend you start with touch_start. Lucky for you, it's part of the default code that is added when you create a new script for an object.

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse6

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

6) Statements, conditionals (meat and potatoes 1)
Now we get to the part of programming that actually accomplishes something -- the logic. Everything else is more of a framework to allow us to describe the organization of the code, more or less.

Fundamentally, a computer program takes input, make a decision based on some conditions, and produces output. Often that output is fed right back in for further decision-making.

Let's take a look at a simple, yet non-trivial, conditional block:
// assume x has already been declared, as an integer
if (x < 10)
{
   x = x + 1;
}
else
{
   x = 0;
}

Can you guess the range of values we expect to see in x? Indeed, it's likely that x would have a value between (and including) zero and 10, at any given time. Basically, it says "if we haven't reached ten with x, add one to x, and store the result back into x, effectively incrementing x by one; otherwise, reset x to zero." It's rather a bit like English, isn't it? Once the symbols become more familiar to you, it will be easy to read the code, and equally easy to write your own. Notice the statement x = x + 1;, which is basically saying "x now has a value of whatever it had plus one".

Notice also something else (pun intended, hah!) -- the else keyword. If the first condition fails (that is, if x is not less than 10), then the first code block is skipped, and control is passed to the next block. If that else is immediately followed by an if, then the following code-block will also be skipped if the condition is not met. And yes, you can set up "chains" of cascading conditionals like so:
// assume s has already been declared, as a string
if (s == ":-)")
{
   llOwnerSay("You seem to be in a good mood.");
}
else if (s == ":-(")
{
   llOwnerSay("Hey champ, what's got you down?");
}
else
{
   llOwnerSay("Your expression is mystifying.  What's on your mind, homie?");
}

Makes sense?

Right, so, basically, many scripts will figure out where things are, what color they are, how big they are, etc, and change some of those things. For instance, if you build a security door, it might "pay attention" for a command word from an avatar, and then it will react by modifying its position or rotation to allow passage. Naturally, it's going to need some conditionals -- it might check if the command came from the owner, rather than just some random passerby. Or it might check if it is already open. I'm sure you get the idea.

Two more things -- first, a function call that doesn't care about the return value (AKA a procedure call) can usually be placed anywhere a statement can, and sometimes can be loosely referred to as a statement; second, if you only have one statement after your conditional (and each of its else(s), if any), you can omit the curly braces. Here is the first example again, without the braces:
// assume x has already been declared, as an integer
if (x < 10)
   x = x + 1;
else
   x = 0;

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse7

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

7) Loops, typecasting (meat and potatoes 2)
Okay, so far, you've learned a fair bit, and with a bit of practice and effort, you can make a bunch of simple scripts already. But most of those scripts will just be reactions to things, without much sophistication. If you want to make more powerful scripts, you'll need to consider loops.

Looping is quite simple, really. It just means a block of code is repeated several times, based on various conditions, usually with something changing each time. Say, for example, that you need to write thank-you's for an event. You have a guest list in front of you, and you gradually work your way down the list, writing a thank you to each person on it. This is a loop.

One of the simplest loops is the while loop. It basically says "as long as this is true, do this".

Example:
while ( 1 == 1 )
{
    llOwnerSay( "Yes, yet again, 1 equals 1" );
}

This would cause the object to go into something called an infinite loop, which is not advisable. It would repeatedly IM the owner saying "Yes, yet again, 1 equals 1".
A slightly more complex loop is the do while loop.

Example:
do
{
    llOwnerSay( "Either this is the first execution of this loop, or 1 equals 1" );
} while ( 1 == 1 )

Have you guessed the difference? A do while loop will always execute the code inside it at least once, and then keep on repeating if the condition is true, while a while loop will only execute the code inside it if the condition is true.

The most advanced loop in LSL is the for loop. It's most common usage is to have a counter progress between two values, such as 0-9, executing code each time. However, it can be more complex than that. The sytax of a for loop is:

for (initialization; test; update)
{
    statements
}

The statement of initialization executes when it first begins, it continues so long as test is true, and after each iteration, the run statement is triggered. That is a pretty abstract level of thinking about it, so I'll give you a more complete example now.

integer n;
for ( n = 0 ; n < 5 ; n++ )
{
    llOwnerSay( "n = " + (string)n );
}

This would result in the following being messaged to you:

n = 0
n = 1
n = 2
n = 3
n = 4

Now, you probably noticed the (string)n, and wondered what it did. This is called typecasting. Remember that functions take only certain variable types, and sometimes you have one type that you need to pass to function that requires a different type; suppose you have an integer, and you need to pass a string along to a function, such as llOwnerSay. To convert it, simply put the desired new type in parentheses in front of the variable. A temporary copy of its value will be converted to the type you want. So, I know that llOwnerSay takes a string as its second argument. I typecast the integer n to a string, and then concatenated it with (tacked it onto) the literal string "n = ".

Is it always possible to typecast one thing to another? No. For instance, there is a type called vector, which is an aggregate (group) of 3 floats. (<1.0,1.0,1.0> is an example.) Does it make sense to typecast a vector to a single float? (1.0 is a float.) Nope. How could one number represent three distinct ones? That's nonsense (simply speaking). However, a vector variable could be rendered as a string, right? So, that's fine. In fact, almost everything can be typecast to a string.

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse8

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

8) States
Have you ever heard of a finite state machine? Well, it's just a fancy way to say that programs can behave differently depending on what state they're in, and that there's a limited number of states that a program can be in. Never mind the fancy terminology, it's really quite simple.

Let's take a real-world example again. A human can be said to have two main states -- awake, and asleep (or unconscious). When awake, a human will react to various stimuli in a particular way. When asleep, the very same stimuli might produce a different reaction. Yes, you guessed it -- the stimuli can be thought of as events, and the reactions as event handlers.

In LSL, there is a default state, and then you can define your own additional states. When a script is saved, it always resets to the default state. When a state is entered or exited, the state_entry and state_exit event handlers, respectively, are called. States are named, then followed by a code block. To change states, you just use the state keyword, followed by the name of the new state.

Here is how a simple human might be programmed in LSL, supposing we could make up our own additional events/event handlers: (This example won't compile, as we can't create our own event handlers, but it illustrates the point nicely.)
default // the "birth" state :-)
{
   state_entry()
   {
      llWhisper(0, "I'm alive!");
      state awake; // change to the "awake" state
   }
}

state awake
{
   state_entry()
   {
      open_eyes();
   }

   on_hunger_pangs(integer starving)
   { // called when stomach growls (not a real LSL event)
      eat();
   }

   on_getting_sleepy(float energyLeftPercentage)
   { // called periodically (not a real LSL event)
      llWhisper(0, "*yawn*");
      if (energyLeftPercentage <= 0.10)
      {
         state sleep;
      }
   }
}

state sleep
{
   state_entry()
   {
      close_eyes();
      llSetTimerEvent(60*60*7); // set timer for 7 hours
   }

   on_hunger_pangs(integer starving)
   {
      if (starving == TRUE)
      {
         state awake;
      }
   }

   timer()
   { // when the timer expires...
      state awake; // ...we wake up
   }
}

Notice that our fake event "on_hunger_pangs(integer starving)" behaves differently depending on our state. If awake, we eat whenever we are hungry. If asleep, we wait until we are starving before waking up. (I've left out the "llSetTimer(0);" to keep it simple -- hardcore scripters can argue about this one endlessly if they like). You'll notice that we don't have to implement a handler in each state; we can ignore an event if we like by simply not implementing a handler for it.

Many scripts won't need anything other than the default state, and you can make your own global state-indicating variable as an alternative, (For instance, by defining a variable named "ON"", then checking to see if ON is set to TRUE or FALSE when needed) but this can be both complicated and unnecessary. The support for states is already there, so if you need it, why not use it?

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


CrashCourse9

Chapter Listing
< 1 2 3 4 5 6 7 8 9 >

9) Other assorted stuff
Some general tips/hints -- feel free to add your own and place your identification after it if you wish:

  • When you save a script inside an object it automatically resets and enters the default state. Scripts saved into your inventory are not running, and as such, will only reset when placed in an object.
  • It is possible to have more than one script in an object, and all can run simultaneously without running into memory problems. Each script has 16k available in total.
  • One object cannot directly modify another, even if linked. You will need to implement a control protocol using the communications functions (link messages, or llSay/llListen on a special channel) to control the objects. The only exception to this rule is with regards to a linked prims color and alpha.
  • When using llListen, try to think about how it can be used most effectively, and most efficiently. There are discussions on the forum about it. Check 'em out. In many cases using a link message is prefered, and speeds code execution. The advantages and disadvantages of listen vs. link messages gets very political.
  • There are many ways to do cool things by using functions in ways they were not originally intended. This is an art, of sorts. Think creatively.
  • Comments are not bad, but good code should need few comments. This is part of the self-documenting-code theory: if it's not clear what you're doing, do yourself a favour and document it. you WILL forget your thought-flow from a few months back
  • First write your code for functionality and readability/expandability. Then worry about optimization. Once you're comfortable with programming, you should consider efficiency as you work, otherwise you may end up re-writing the program from scratch
  • Don't forget to check for appropriate permissions when applicable.
  • Remember that objects can change ownership while scripts are running. Make sure your scripts can handle this. There are other subtleties to consider as well. Try to be as thorough as possible when designing. Think about what will happen if your script is given to someone else, or attached, or placed on the ground. You may wish to test these possibilities so you're sure you understand what happens in each case.
  • Use strategically-placed llOwnerSay calls to help you debug code.
  • Like anything else, planning helps. So does smartly naming your variables. If you can, test in chunks. This gets harder and harder with bigger pieces of code and more complex operations, but you can often write little bits of code and then build them up into bigger and bigger sections. (-EloisePasteur)
  • Like any other coding environment, when you find a good piece of code, keep it! The first time I started handling lists, it was clunky and horrible. Gradually I evolved a piece of code that is far from perfect, but works quite efficiently. Suddenly, I'm using it to handle lists in several other scripts that I've written. It can be hard to know when to adapt and when to write from scratch. I still get it wrong! But generally adapting code, or at least looking at code for something you have that is similar helps quite a bit.
  • Ask in the scripting forum and look through the script library as well - there's a lot of code out there that people share freely, and some of it might do what you want.
  • If you see a script that does something you want, consider asking the creator for help. Most people in SL are friendly and will help, or will direct you to someone who can. They may even give you the entire script itself.

Previous | Next
Homepage | Tutorial | CrashCourse | Glossary


Creator

The creator of an object or script is the user who initially created the item. For objects, it is the user who rezzed the parent prim. For a texture, it is the user who first uploaded the texture. For a script, it is the user who started from a default script.

Modifying an existing object or script will not make the modifier the creator--even if the entire contents of the script is erased or the prim is reverted to its original shape. However, there are ways to hide the creator related to links and the parent-child relationship (and there have also been bugs that allowed the creator of an object to be changed or listed as "(no one)".

Functions

llGetCreator
llGetInventoryCreator

Link | Prim | Script | Owner


crosshair

Links

  • SL History Wiki: crosshair

User Interface | mouselook


Crypto

Cryptographic Functions

So far, there are only a few crypto functions, and they aid in securing data for transfer over the Internet.

llMD5String | Does an MD5 message digest (a checksum basically). This can be used to verify authenticity without having to know the plaintext at all.
llModPow | (a ^ b)%c Weak RSA encryption
llBase64ToString | Decodes a Base64 string. This is not encryption, it is merely encoding.
llStringToBase64 | Encodes a string. This is not encryption, it is merely encoding.
llXorBase64Strings(deprecated) llXorBase64StringsCorrect | Performs an xor on two Base64 strings.

Functions | Communications | Strings


CSS

Abbreviation for Cascading Style Sheets, a feature of HTML developed by the W3C. With Cascading Style sheets, both web designers and end users can create style templates (sheet) that specifies how different text elements (paragraphs, headings, hyperlinks, etc.) appear on a web page. Currently, not all browsers express CSS formatting in the same manner.


CSV

CSV stands for Comma Separated Value. Its the standard for displaying list information as a string. Since the days before llDumpList2String() a CSV (using llList2CSV()) was the only way to transfer list values over string interfaces (like object talk (whisper, say, shout) or link messages). Comma Separated Values are... well... separated by commas.

Examples

The, Quick, Brown, Fox, Jumped, Over, The, Lazy, Dog
Foo, Bar, The, And, Red, Goo

The space after the comma is optional, so the following is also a valid CSV string:

Mistress,Torrid,Aimee

CSV Functions

llList2CSV
llCSV2List

The CSV functions are nice for passing multiple parameters as a single string (using llMessageLinked for example). You can even pass complex types like vectors or rotations even though their string representations have commas. LSL handles the fuss for you, which indicates that the functions don't actually implement the most basic case of CSV, but some version of it with built in escaping. More specifically, commas inside angle brackets ("<" and ">") are not interpreted as separators.

BUT, one thing to be aware of when unpacking the string back into a list is that the type information associated with any given list entry is lost (there are ways around this, see the llCSV2List entry). It's up to you to know what the type of a given list item is and you must use llList2String and cast the result to the correct type as in:

link_message(integer sender, integer num, string str, key id)
{
    list tokens = llCSV2List(str);
    vector pos = (vector) llList2String(tokens, 1);
}

Lists that haven't been gone through CSV conversion work normally as described under lists.

llDumpList2String and llParseString2List perform the same functions, but allow variable separators, not just commas. This can be better when working with vectors, rotations, complete sentences, etc.

Lists / Functions


CVS

Stands for Concurrent Version System and has nothing to do with LSL. Perhaps you meant CSV? :)


Damage

On land parcels with the damage option enabled, avatars can be damaged and killed (which simply teleports them to their home location, similar to llTeleportAgentHome).

The current health of an avatar is displayed in the menu bar (only when over damage-enabled land) with a red heart and the current health percentage (100% is the maximum, 0% means death).

Damage is inflicted when the avatar collides with a physical object that has a damage percentage set (using llSetDamage). If the physical object collides with the avatar while it's sitting on a prim, no damage is inflicted.

Avatars also take damage when they collide with objects (that do not have a damage percentage set) or land at high velocities, so it's possible to use llPushObject to kill someone by pushing them into objects or the ground at high speed.

Agent/Avatar | Land


Damp

Damping, represented by a tau symbol for seconds, is a type of physical motion characterized by its non-constant velocity: the closer the object is to its target, the slower it will go. Because of this property of damped motion, damping is used in a few LSL dynamics movement functions to compensate for rounding errors and script asynchronicities. Damping relies on the principle that a slow-moving object is easier and more visually appealing to stop than a fast-moving one.

With Havok 2, damping will (hopefully) become less of an issue then it is now, and constant velocity motion, while still slightly more difficult to code, will become more accurate, and therefore more appealing.

Functions that cause damping include:
llGroundRepel
llLookAt
llMoveToTarget
llRotLookAt
llSetHoverHeight
some vehicle parameters

Dynamics | Physical | tau


Debate

This page is for debating of topics relevent to the wiki as a whole. If you think there should be a debate on some topic please add it. Please put all comments in the text of this page.

Topic | Description
Italic Page Names | page names in italics all thoughout the document
Lowercase Links | all WikiNames lowercase instead of CamelCase
Root Links | links vs. links

Italic Page Names

Pros | Cons
Easy to see the word in the text which the article is about | Tacky - Overkill
If you don't know what your reading about italics aren't going to help you; in limited use I can see it is beneficial but every occurance of the word is overkill. -- BW

I would agree. I'm used to italics being sparsely used, and only as a point of emphasis and stress, so I keep on thinking a specific contrast is being illustrated. I prefer simple and uncluttering conventions in references so I can read over without having to think about the intent ofthe wiki's or even author's style. Besides, if you don't have the short term memory to remember what page you're looking at... [complete sentence yourself] :) --IL

Lowercase Links

Pros | Cons
Simplifies caching | The wiki engine instructs the web browser not to cache pages, thus caching is a none issue
Mirrors how other case-sensitive wiki engines work | WakkaWiki isn't case sensitive.
Improves WakkaWiki's intended link design. | Goes against the intended design of the WakkaWiki engine (to use CamelCase).
CamelCase can still be used for multi-word links, if necessary. | Makes the vast majority of the older links harder to read.
 | Requires the recoding of every page.

SL requires a high speed internet connection and bandwidth to download gigibytes of data in short periods of time. Caching of a webpage is a non issue compaired. Wakka isn't case sensative, why should we have to conform, especialy when it makes the vast majority of links harder to read. If the links weren't made to be read by humans, why were they given human readable names? -- BW

I don't see a problem with how things are. Maybe minor inconsistency, but I hadn't noticed any, and I don't think people are going to attach much significance to slightly different casing. Since wikipedia/mediawiki convention cannot be adhered to (no spaces, case semi-sensitivity in redirects when unambiguous) another way of significantly splitting words is nice - I don't mind camelcase.
Please explain what does caching have to do with the difference. [...cut for space...]? --IL

The argument is based on that your browser would download both Debate & debate as seperate pages; and that this is wasteful. Mediawiki is case sensitive. There are places on Wikipedia where there are different pages for a term based on its case (like the lowercase would be about the term and the upper case about the movie). There has been some movement to get LL to move to MediaWiki but it hasn't happened :-/ (IMHO the web developers are pretty low on the totem pole at LL.) --BW

Then indeed it's fairly silly to hack around Wakka behaviour with a lot of editing (copy, paste, backlink check, deletes?, etc) when it's an order of magnitude or five less bandwidth than SL itself. Something to think about and put in the style guide of a new wiki perhaps, but a pretty gainless bother now.
I guess if someone has nothing better to do they could... Still, you'd need to settle on things like how to handle different types of nouns, differently cased links because of sentence position, etc; there's probably no significantly cleaner solution. I sort of like the current case insensitivity, oh-my-I-justdownloaded-5K-twice. Laziness, impatience, hubris, right?:P --IL

It's about GOOD habits, people. Developers are getting lazier and lazier as they have access to more and more memory, CPU/GPU power, etc. This needs to stop. Intelligent link design is only one small part of this overall developer laziness. SL is VERY bandwidth-wasteful; there's no reason the LSL Wiki Mirror 7-7-7 needs to be too. -EepQuirk

I've just looked at the HTTP headers that wakka sends, the pages are all 'no-cache'. Every time you view a page your browser downloads it; reguardless if it were visited 10 days ago or 10 seconds ago. As per this new evidence, the underlying assumption of reduced bandwidth has been overturned. On a side note, I cannot see how this impoves the design if it goes against the design. The key tennon of programing is lazyness. The greatest programers are lazy. If programers weren't lazy, they would all be programing in assembly because you can always write better code in assembly then can be produced by a compiler. The trouble with writing an application in assembly is it takes forever, it doesn't scale. A lazy programer recycles code, a lazy programer writes & uses libraries so they can use them in bigger projects. Lazyness is what makes programers great, it drives them to write more advanced languages and libraries. Time spent on plumbing, like getting your links rights, is wasted time. It's time that could be spent creating. Lazy programers are productive programers. If the language isn't case sensative, you shouldn't force a style that requires it. - BW

Are we talking about all links... as in function links included? LSL functions must be formatted in CamelCase to compile, so with a lowercase link policy, wouldn't that confuse people? And if LSL is the exception, wouldn't that also confuse people? - BurnmanBedlam

Root Links

Pros | Cons
easier/quicker to code | doesn't look pretty
I think pretty in this case is more important then easy/speed. It doesn't entail much extra work, simple copy and paste and a bit of trimming; if you can handle wiki tags and have the constitution & time to edit the wiki you can do it right. (Funny, you were argueing above against lazyness) -EepQuirk

I assume by "Root Links" what is ment is events vs events. Of course i advocate lazyness in programing but i do not think it should be all prevailing in authorship (because really isn't it just easier to just write nothing?). This is about something the user will notice (The above is about legislating something the user will most like never notice). Pretty is better IMHO - BW


Debug

To correct errors in a script, see debugging.

Although it doesn't have to do with LSL in general, the debug portion of the Second Life client is very mysterious and could probably use it's own wiki. For now, the LSL Wiki Mirror 7-7-7 will be used, which may change into a general SL wiki, or simply spin-off the debug section into a new wiki.

The debug menu can be enabled/disabled by hitting the Ctrl+Alt+D keys.

The options in the debug menu are rather elusive and quite complex. It's rather hard for the regular SL user to understand the power of these things which is why Linden Lab employees should feel free to add/correct this information as they see fit.

The Debug Menu

The first column gives the menu item, its depth in the menu, and its name exactly as shown, along with a link with more information (if it exists). The second column displays the shortcut key combination if it is applicable. The third contains general comments on the menu item (if any), and the default state. The fourth column makes an effort to show which items are safe to click on (some are not and will crash SL).

Debug:
The main menu, contains several general toggles and submenus, which you may click on below to learn more.
Menu Item | Shortcut | Notes | Safe?
Frame Console | Ctrl-Shift-2 | toggle (off) | yes
Texture Console | Ctrl-Shift-3 | toggle (off) | yes
Debug Console | Ctrl-Shift-4 | toggle (off) | yes
Audio > submenu | - | - | -
Fast Timers | Ctrl-Shift-9 | toggle (off) | yes
Memory | Ctrl-Shift-0 | toggle (does nothing) (off) | yes
Reload UI XML | Ctrl-Alt-R | sometimes the client doesn't properly reload a page but it's harmless | mostly harmless
ReloadSettingsReload settings/colors | - | not all colors can be changed on the fly | harmless
HUD Info > submenu | - | - | -
 | - | - | -
UI In Snapshot | - | toggle (off) | yes
High-res Snapshot | - | toggle (off) | yes
Show Mouselook Crosshairs | - | toggle (on) | yes
Debug Permissions | - | toggle (off) | yes
 | - | - | -
Rendering > submenu | - | - | -
World > submenu | - | - | -
UI > submenu | - | - | -
Character > submenu | - | - | -
 | - | - | -
Show Updates | Ctrl-Alt-Shift-U | - | yes
 | - | - | -
Console Window | - | - | yes
Debug > Fast Timers
todo

Debug > Memory
todo

Debug > Reload settings/colors
todo

Debug > UI In Snapshot
todo

Debug > High-res Snapshot
todo

Debug > Show Mouselook Crosshairs
Shows/hides crosshairs ("[]" image) in the camera's center when in mouselook mode.

Debug > Debug Permissions
Shows all permissions of an object on the bottom of "General" in the building menu and in inventory item properties.
The first letter is who the permission is for
O: owner, G: group, E: everyone, N: next owner, B: base, F: flagged. (Seems to be the same as owner?)
The other letters are the permissions
V: move, M: modify, C: copy, T: transfer
An asterisk (*) means that the permissions will be reset on next rez. This can happen when you change the permissions of something in your inventory, rather than changing the permissions on the object in-world, then taking the object into your inventory.

Debug > Show Updates
todo

Debug > Console Window
todo

Debug > Audio:
This submenu contains four options (they look like toggles), but they appear to do nothing.
Menu Item | Shortcut | Notes | Safe?
Global Pos | Ctrl-Shift-5 | unsure (does nothing) | yes
Cone | Ctrl-Shift-6 | unsure (does nothing) | yes
Local Pos | Ctrl-Shift-7 | unsure (does nothing) | yes
Duration | Ctrl-Shift-8 | unsure (does nothing) | yes

Debug > HUD Info:
This submenu contains four toggles, which display some basic client and avatar information.
Menu Item | Shortcut | Notes | Safe?
Velocity | - | toggle (off) | yes
Camera | - | toggle (off) | yes
Wind | - | toggle (off) | yes
FOV | - | toggle (off) | yes

Debug > HUD Info > Velocity
Displays a numeric readout of the avatar's linear velocity, and a bar graph that tops out at 6 m/s, in the lower left of the screen.

Debug > HUD Info > Camera
Displays several bits of information about the camera and agent in the lower right of the screen.
CameraAtAxis: a normalized vector that tells which direction the camera is pointing.
CameraCenter: a vector that gives the camera's current position in-world in meters.
AgentLeftAxis: a normalized vector that points to the avatar's immediate left.
AgentAtAxis: a normalized vector that points in the same direction the avatar is facing.
AgentRootCenter: a vector that gives the avatar's current position in world in meters.
AgentCenter: appears to be the same as AgentRootCenter but with a different z value.

Debug > HUD Info > Wind
Displays several bits of information about the wind, in the lower right of the screen, above the camera info.
Audio for wind: audio sample for the wind being played?
RWind vec: wind direction relative to the avatar, used for calculating how the wind sound is played.
RWind vel: wind speed relative to the avatar, used for calculating how the wind sound is played.
Wind vector: wind direction relative to the ground.
Wind velocity: wind speed relative to the ground.

Debug > HUD Info > FOV
Displays the client's field of view (FOV) in degrees in the lower right of the screen above the wind info.

Debug > Rendering
Menu Item | Shortcut | Notes | Safe?
Types > submenu | - | - | -
Features > submenu | - | - | -
Info Displays > submenu | - | - | -
 | - | - | -
Axes | - | toggle (off) | yes
Use VBO | - | - | ?
Use PBuffers | - | - | ?
Share Drawtables | - | - | ?
Light Glows | - | toggle (off) | yes
Local Tree Lighting | - | - | mostly harmless
 | - | - | -
Hide Selected | - | - | yes
 | - | - | -
Tangent Basis | - | - | ?
Tree Options... | - | - | ?
Fast UI | - | - | ?
Debug Fast UI | - | - | ?
Object-Object Occlusion | Ctrl-Alt-O | - | ?

Debug > Rendering > Axes
todo

Debug > Rendering > Use VBO
todo

Debug > Rendering > Use PBuffers
todo

Debug > Rendering > Share Drawtables
todo

Debug > Rendering > Light Glows
Shows coronas around light-emitting primitives. Useful for locating the ones that didn't intentionally have the light material set (obviating the need to check the material of each prim individually), and for improving the appearance of the ones that are intentional.

Debug > Rendering > Local Tree Lighting
Draws light-like lines and boxes (does nothing?)

Debug > Rendering > Hide Selected
todo

Debug > Rendering > Tangent Basis
Shows polygon normals only if prim has a touch event or is the root (with/without the touch event)

Debug > Rendering > Tree Options...
todo

Debug > Rendering > Fast UI
todo

Debug > Rendering > Debug Fast UI
todo

Debug > Rendering > Object-Object Occlusion
Toggles occlusion culling.

Debug > Rendering > Types
Menu Item | Shortcut | Notes | Safe?
Simple | Ctrl-Alt-Shift-1 | toggle (on) | yes
Alpha | Ctrl-Alt-Shift-2 | alpha, toggle (on) | yes
Tree | Ctrl-Alt-Shift-3 | toggle (on) | yes
Character | Ctrl-Alt-Shift-4 | avatars, toggle (on) | yes
SurfacePatch | Ctrl-Alt-Shift-5 | terrain, toggle (on) | yes
Sky | Ctrl-Alt-Shift-6 | toggle (on) | yes
Water | Ctrl-Alt-Shift-7 | toggle (on) | yes
Ground | Ctrl-Alt-Shift-8 | toggle (on) | yes
Volume | Ctrl-Alt-Shift-9 | prims, toggle (on) | yes
Grass | Ctrl-Alt-Shift-0 | toggle (on) | yes
Clouds | Ctrl-Alt-Shift-- | toggle (on) | yes
Particles | Ctrl-Alt-Shift-= | particles, toggle (on) | yes
Bump | Ctrl-Alt-Shift-\ | bumpmaps, toggle (on) | yes

Debug > Rendering > Features
Menu Item | Shortcut | Notes | Safe?
UI | Ctrl-Alt-1 | GUI, toggle (on) | yes
Selected | Ctrl-Alt-2 | toggle (on) | yes
Highlighted | Ctrl-Alt-3 | toggle (on) | yes
Dynamic Textures | Ctrl-Alt-4 | toggle (on) | yes
HW Lighting | Ctrl-Alt-5 | toggle (on) | yes
Fog | Ctrl-Alt-6 | toggle (on) | yes
Palletized Textures | Ctrl-Alt-7 | toggle (on) | yes
Test FRInfo | Ctrl-Alt-8 | toggle (on) | yes
Flexible Objects | Ctrl-Alt-9 | toggle (on) | yes
Chain Faces | Ctrl-Alt-0 | toggle (on) | yes

Debug > Rendering > Info Displays
Menu Item | Shortcut | Notes | Safe?
Verify | - | - | ?
AGP Map | - | - | ?
BBoxes | - | land bounding boxes | yes
Octree | - | occlusion tree nodes, etc | yes
Occlusion | - | occlusion blocks | yes
Face Chains | - | chained faces | yes
Composition | - | - | ?
ShadowMap | - | - | ?
LightTrace | - | light raytraces | ?
Pools | - | - | ?
Queues | - | - | ?
Map | - | - | ?
Show Depth Buffer | - | - | ?
Show Select Buffer | - | - | ?

Octree
Displays octree (occlusion tree) blocks (white outlines); blue lines point to prims/cells in a specific block that would be culled (removed), green outlines are prim bounding boxes.

Occlusion
Displays occlusion culled blocks as red boxes.

Face Chains
Improved face batching, which helps performance a little bit on some systems. Basically, instead of drawing like faces one at a time, it draws them in big batches. The green lines are static faces that got batched, the squares are faces that did not get batched, and the orange lines are moving faces that got batched. There should be no visible difference between chaining faces and not chaining faces.

Debug > World
Menu Item | Shortcut | Notes | Safe?
Force Sunset | Ctrl-Shift-N | forces sunrise | yes
Mouse Moves Sun | Ctrl-Alt-M | - | yes
Sim Sun Override | - | - | yes
Fixed Weather | - | - | yes

Debug > UI
Menu Item | Shortcut | Notes | Safe?
Async Keystrokes | - | - | ?
Dump SelectMgr | - | - | ?
Dump Inventory | - | - | ?
Dump Focus Holder | Ctrl-Alt-F | - | ?
Dump VolumeMgr | - | - | ?
Print Selected Object Info | Ctrl-Shift-P | - | ?
Print Agent Info | Shift-P | - | ?
Print Texture Memory Stats | Ctrl-Alt-Shift-M | - | ?
 | - | - | -
Debug SelectMgr | - | - | ?
Debug Clicks | - | - | ?
Debug Views | - | - | ?
Debug Mouse Events | - | - | ?
Debug Keys | - | - | ?
Debug WindowProc | - | - | ?
Debug Text Editor Tips | - | - | ?
Export Menus to XML... | - | - | yes
Edit UI... | - | - | yes
Load from XML... | - | - | ?
Save to XML... | - | - | yes

Debug > Character
Menu Item | Shortcut | Notes | Safe?
Grab Baked Texture > submenu | - | - | -
Force Params to Default | - | - | ?
Reload Vertex Shader | - | - | ?
Animation Info | - | - | yes
Flush Animations | - | - | no
Slow Motion Animations | - | - | yes
Show Look At | - | - | yes
Show Point At | - | - | yes
Debug Joint Updates | - | - | ?
Disable LOD | - | - | ?
Debug Character Vis | - | - | ?
Show Attachment Points | - | - | yes
Show Collision Plane | - | - | yes
Show Collision Skeleton | - | - | yes
Software Blending SSE | - | - | ?
Character Load Test | - | - | ?
Display Agent Target | - | - | ?
Debug Rotation | - | - | yes
Dump Attachments | - | - | ?
Dump Local Textures | Alt-Shift-M | - | ?

Debug > Character > Grab Baked Texture
Menu Item | Shortcut | Notes | Safe?
Iris | - | - | ?
Head | - | saves a texture of everything on your head (eyes, tattoos, etc.) | yes
Upper Body | - | - | ?
Lower Body | - | - | ?
Skirt | - | - | ?
Client


DebugConsole

This is what you see if you use the ConsoleOption option. It's basically what is happening in your Second Life, behind the scenes.

Debug


Declare

To "declare" a variable is to set up memory space for it. In more basic terms, it can be thought of as creating a name to identify a chunk of data.

In LSL, variable declaration looks like this:
integer a;
vector b;
list c;
s All of these were variable declarations. You must declare a variable in order to use it in a script.

Variables are declared within the scope of where they were initiated.

Optionally, in a declaration, an initial value may be assigned, like so:
integer a = 42;
vector b = <1,1,1>;
list c = ["C","Sea","See"];

LSL | Glossary | scope | variable | value


Deed

To deed something, in SL's case, is to make an object group-owned.

Group Objects


degrees

A measurement of angle where a full turn is 360 units. A half-turn is 180, a quarter-turn is 90, an eighth-turn is 45, and so on.

To convert these to radians, multiply by DEG_TO_RAD.
To convert to these from radians, multiply by RAD_TO_DEG.

Another unit of angle measurement is radians.

Types | Quaternions | Rotation | Constants


delay

See ScriptDelay.


Delete

To get a script to delete (de-rez) the object containing it, use llDie.

To get a script to delete an inventory item within the same object, use llRemoveInventory.

How Do I...? | Page Deletion | Comment Deletion


Deprecated

Deprecation is what happens when functions and methods in a programming language become outdated, are deemed too buggy or insecure, or are replaced with better functions.

For example, llParticleSystem replaced llMakeFire and others because it does the same thing, but better. Thus, llMakeFire is deprecated.

See all deprecated functions.

Functions | Depcrecated Functions


DeprecatedFunctions

These functions have been deprecated and should no longer be used.

Deprecated Function: Replaced By:
llSoundPreload llPreloadSound
llSound llPlaySound, llLoopSound, llTriggerSound
llMakeExplosion llParticleSystem
llMakeFire llParticleSystem
llMakeFountain llParticleSystem
llMakeSmoke llParticleSystem
llRemoteLoadScript | llRemoteLoadScriptPin and llSetRemoteScriptAccessPin
llPointAt | Nothing
llStopPointAt | Nothing

Functions | Deprecated


dialog

Aside from the obvious communication reference, a dialog is a shortened form of a "dialog box", which is a GUI window display that contains information (like errors), buttons, checkboxes, pull-down menus, scrollable lists, text-entry fields, etc.

functions

llDialog
llGiveInventory
llGiveInventoryList
llGiveMoney
llLoadURL
llRequestPermissions

communications | chat | user interface


DoS

Denial of Service

An attack whereby a host is sent thousands of internet packets in an attempt to bring it offline.
(sort of like trying to send 50 messages down a telegraph pole at once, so that no-one can use it)


DotOperator

The dot operator (.) is used to access individual members of structures. LSL only supports two types of structures: vector and rotation.

rotation a;
a.x = 0.0;

if you were to write them as classes in C++ it would look something like this
class rotation
{
  public:
    float x,y,z,s;
    rotation(void);
    rotation operator * (const rotation & q);
    rotation operator / (const rotation & q);
    list operator + (const list & q);
    rotation & operator *= (const rotation & q){this = this * q; return this;};
    rotation & operator /= (const rotation & q){this = this / q; return this;};
};

class vector
{
  public:
    float x,y,z;
    vector(void);
    vector operator + (const vector & q);
    vector operator - (const vector & q);
    vector operator % (const vector & q);
    vector operator * (const rotation & q);
    vector operator / (const rotation & q);
    vector operator * (const float & q);
    vector operator / (const float & q);
    vector operator * (const integer & q);
    vector operator / (const integer & q);
    list operator + (const LSLList & q);
    vector & operator += (const vector & q){this = this + q; return this;};
    vector & operator -= (const vector & q){this = this - q; return this;};
    vector & operator %= (const vector & q){this = this % q; return this;};
    vector & operator *= (const rotation & q){this = this * q; return this;};
    vector & operator /= (const rotation & q){this = this / q; return this;};
    vector & operator *= (const float & q){this = this * q; return this;};
    vector & operator /= (const float & q){this = this / q; return this;};
    vector & operator *= (const integer & q){this = this * q; return this;};
    vector & operator /= (const integer & q){this = this / q; return this;};
};


double

A double is a variable type supported by many programming languages and compilers, but not LSL (yet).

It is called a "double" because it has double the precision of a float, that is, a double is 64 bits, and a float is only 32 bits.


EcoAlgae094

Algae/Plant Script v0.94

by NeverRust

This open-source code is part of the Ecosystem project. Please read the EcosystemDisclaimer before using.
Please do not edit the code below. If you want to make changes, please create a new wiki page. Thanks.
Does not use the EcoName.

//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//
 //  Algae/Plant Script by Never Rust  //
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//

string VersionInfo = "Algae Script v0.94";

vector BirthSize = <0.3, 0.3, 0.3>;
vector MaxSize = <1, 1, 1>;       // Maximum size. only will pay attention to Z
vector FertileSize = <1, 1, 0.6>;   // Size that produces seeds. only will pay attention to Z
integer Age = 0;                    // current age (1 age = GrowthTimer seconds old)
integer OldAge = 80;                // age to start dying
integer MaxAge = 100;               // age to die
float GrowthTimer = 5;              // # of seconds between growth checks
float SeedTimer = 12;               // # of seconds to sleep after seed-shooting (make a multiple of Growthtimer)
float MaxGrowthStep = 0.05;          // max size increase per growth spurt
integer SeedNumber = 1;             // the number of seeds to birth at a time
integer MaxSeedNumber = 6;          // the max # of seeds per lifetime
integer CurrentSeedNumber = 0;      // currently how many seeds have been born
integer MyGeneration;               // generation of plant
integer MaxGeneration = 7;          // max # of generations to prevent
float MaxDepth = 100;               // depth / MaxDepth = % of sunlight to absorb
vector MyPos;
string MyProperName = "Algae";
float NonSurvivalRange = 1;         // can't survive within this range of another of it's type

//string daytolive = "2006-05-17";    //The one day that this will be alive for

//################## functions ######################//

talk (string m) {
    llWhisper(0, "/me "+ m);
}

TellAboutSelf() {
    talk("is generation "+ (string)MyGeneration + ", Age " + (string)Age + " ("+VersionInfo+")");
}

reset() {
    llVolumeDetect(TRUE);
    llSetText("", <0,0,0>, 0);
    llSetAlpha(0.95, ALL_SIDES);
    llSetAlpha(0.0, 0);
    Age = 0;
    CurrentSeedNumber = 0;
    ResetAtGround();
    MyPos = llGetPos();
}

CheckNearbySurvival() {
    llSensor("Algae", NULL_KEY, PASSIVE | SCRIPTED, NonSurvivalRange, TWO_PI);
}

ResetAtGround() {
        vector OgPos = llGetPos();
        float ground = llGround(ZERO_VECTOR);
        vector size = llGetScale();
        float zz = ground - (OgPos.z - (size.z / 2.0));

        llSetPos(<OgPos.x, OgPos.y, OgPos.z + zz>);
}

float WaterDepth() {
    vector MyPos = llGetPos();
    float depth = llWater(ZERO_VECTOR) - MyPos.z;
    return depth;
}

float Sunlight() {                      // return 0 to 1: 0=no sunlight, 1=sun & no clouds
    vector sun = llGetSunDirection();
    float clouds = llCloud(ZERO_VECTOR);
    float sunlight = 0;
    if (sun.z < 0) sunlight = 0;
    else sunlight = 1 - clouds;
    return sunlight;
}

float SunDepthFactor() {

    float depthfactor = 1 - (WaterDepth() / MaxDepth);
    if (depthfactor < 0) depthfactor = 0;
    //talk("depth: "+ (string)depth + " depthfactor: "+ (string)depthfactor);
    return depthfactor;
}

Grow(float GrowthFraction) {
    vector size = llGetScale();
    float GrowthFactor = 1 + (MaxGrowthStep * GrowthFraction);

    if (CanGrow(size * GrowthFactor)) {
        //talk("is growing. (GrowthFactor: " + (string)GrowthFactor+ ")");
        ResizeAll(GrowthFactor);
    }
}

integer CanGrow(vector newsize) {
    // if we haven't reached max size and we're not in old age and we're not doing to grow out of the water
    if (MaxSize.z > newsize.z && Age < OldAge && (newsize.z / 2.0) < WaterDepth())
        return TRUE;
    else return FALSE;
}

Reproduce() {
    vector size = llGetScale();

    if (CanReproduce(size)) {
        //talk("is reproducing (budding).");

        CurrentSeedNumber += SeedNumber;

        integer i;
        for (i = 0; i < SeedNumber; i++) {
            float distx = RandBetween(0, 3 * size.x) * RandNegative();
            float disty = RandBetween(0, 3 * size.x) * RandNegative();

            vector RezPos = <MyPos.x + distx, MyPos.y + disty, llGround(<distx, disty, 0>)>;

            //talk("is at "+ (string)MyPos + " and birthing at: "+ (string)RezPos);
            float RezDegree = llFrand(360);
            rotation RezRot = llEuler2Rot( <0, 0, RezDegree * DEG_TO_RAD> );

            llRezObject(llKey2Name(llGetKey()), RezPos, ZERO_VECTOR, RezRot, MyGeneration + 1);
        }

        llSleep(SeedTimer);
    }

}

integer CanReproduce(vector size) {
    // if we've reached fertile size and won't produce more than the max # of seeds, then make some seeds
    if (size.z >= FertileSize.z && (CurrentSeedNumber + SeedNumber) <= MaxSeedNumber && MyGeneration < MaxGeneration)
        return TRUE;
    else return FALSE;
}

float RandBetween(float min, float max)
{
    return llFrand(max - min) + min;
}

integer RandNegative() {        // returns 1 or -1 randomly
    return ( -1 + (llFloor(llFrand(2.0)) * 2) );
}

GetOlder() {
    if (IsAboveWater()) {                         // Age quickly if above water
        Age += 10;
    } else Age += 1;                            // otherwise age normally

    if (MyGeneration > 0 && Age > OldAge && llGetObjectName() != "Dying "+MyProperName) {

        llSetAlpha(0.5, ALL_SIDES);
        llSetAlpha(0.0, 0);
        llSetObjectName("Dying "+ MyProperName);
    }
    if (MyGeneration > 0 && Age > MaxAge) llDie();

}

integer IsAboveWater() {
    vector size = llGetScale();
    if ((MyPos.z + size.z / 2) > llWater(ZERO_VECTOR)) return TRUE;
    else return FALSE;
}

ResizeAll(float scale) {
    llMessageLinked(LINK_SET, -1, (string)scale, NULL_KEY);
}

//************** default ******************************//

default
{
    state_entry()
    {
        llSetAlpha(0.0, 0);
        //talk("is born.");
        llSetTimerEvent(GrowthTimer);
        reset();

    }

    touch_start(integer total_number)
    {
        TellAboutSelf();
        if (MyGeneration == 0) {
            CurrentSeedNumber = 0;
            talk(" is resetting seed number.");
        }
        //talk(" randomneg:" + (string)RandNegative());
        //ProduceSeeds();
    }

    on_rez(integer start_param)
    {

        MyGeneration = start_param;
        if (MyGeneration > 0) llSetScale(BirthSize);
        //talk("is born. Generation "+ (string)MyGeneration + " ("+ VersionInfo + ")");
        llSleep(1);
        reset();
        CheckNearbySurvival();

    }

    object_rez(key child)
    {
        // Give copy of child to child
        llGiveInventory(child, llKey2Name(llGetKey()));
    }

    timer()
    {

        float sun = Sunlight() * SunDepthFactor();
        //llSetText("Sunlight: "+ (string)sun, <1.0, 1.0, 0.0>, 0.9);
        if (sun > 0) {
            Grow(sun);
        }
            Reproduce();

       GetOlder();
        //if ( llGetDate() != daytolive ) llDie();    //failsafe
    }

    collision_start(integer num_detected) {

        if (llDetectedType(0) & AGENT) {
            llSay(0, llDetectedName(0) + " is disturbing the plantlife!");
            Age += 10;
        } else {
            llSay(0, llDetectedName(0) + " collided with me! Type:" + (string)llDetectedType(0));
            llEmail((string)llDetectedKey(0) + "@lsl.secondlife.com", "food", "20");
            if (Age < OldAge) Age = OldAge - 1;
            else Age = MaxAge;
        }
    }

    sensor(integer num_detected)
    {
        if (num_detected > 0) Age = OldAge - 1;
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        if (num == -1) {
            float scale = (float)str; // size factor
            list primparams = [PRIM_SIZE, llGetScale() * scale];    //resize

            llSetPrimitiveParams(primparams);

            ResetAtGround();
        }
    }
}

EcosystemWorkingGroup | Ecosystem


EcoAnimal09e

Animal Scripts v0.9e

by NeverRust

This open-source code is part of the Ecosystem project. Please read the EcosystemDisclaimer before using.
Please do not edit the code below on this page unless you've already tested it to ensure it works. If you make minor changes, please add a note describing the change. For major changes, it's better to start a new wiki page and add a link on the Ecosystem page. Thanks.

Script 1 - Main & Life

////////////////////////////////////////////
  //  Animal/Eco-Name Script by Never Rust  //
 ////////////////////////////////////////////
//  Modified by:                          //
///////////////////////////////////////////

// Modified version of the fish script i wrote for the Flounder
// Added functionality to use the Eco-Name
// To see how it works, see the specialized functions below and look for "!!!!!!!!!" comments below
// Also see the food code (in the "Spore Food" object)

string VersionInfo = "Animal Script v0.9e";
string NamingVersion = "0";

integer IsActive = FALSE;
integer IsBusy = FALSE;

vector BirthSize = <0.3, 0.3, 0.3>;
vector MaxSize = <1, 1, 1>;
float MaxGrowthStep = 0.1;          // max size increase per growth spurt

float Speed = 2.0;                  // meters per second
integer WanderDist = 5;             // how far does it wander
float TimerTime = 4;               // how often do we hit the timer
//float MaxWaterDepth = 100;           // the max depth the fish likes to swim
float MaxGroundHeight = 2;           // the max height the fish likes to go from the bottom

integer SeedNumber = 1;             // the number of seeds to birth at a time
integer MaxSeedNumber = 6;          // the max # of seeds per lifetime
integer CurrentSeedNumber = 0;      // currently how many seeds have been born
integer MyGeneration;               // generation of plant
integer MaxGeneration = 7;          // max # of generations to prevent

float ActionStartTime;
float ActionTimeMax = 12;
string ActionName;

integer Age;
integer FertileAge = 20;
integer OldAge = 600;
float Health = 100;
integer MaxHealth = 100;
float Energy = 100;
integer MaxEnergy = 100;

integer TargetHandle;

string MyProperName = "Plob";           //!!!!!!!!!! These need to be defined for the Eco-Name
string MyEcoNameVersion = "A0";
string MyEcoType = "A";                     //A=Animal, P=Plant, R=Robot
string MyDietType = "P";

/////////////////################## functions ######################/////////////

//************* F e e d b a c k ***************//

talk(string m) {
    llWhisper(0, "/me "+ m);
}

display(string m, float Wait) {
    llSetText(//ActionName +"("+ (string)IsBusy +")\n"+
    m, <0.5, 1.0, 1.0>, 0.8);
    if (Wait > 0.0) {
        llSleep(Wait);
        llSetText("", <0,0,0>, 0.0);
    }
}

debug(string m) {
    llOwnerSay("/me (debug): " + m);
}

TellAboutSelf() {
    talk("is generation " + (string)MyGeneration + " and Age " + (string)Age +". Energy="+ (string)Energy +"/"+ (string)MaxEnergy +", Health="+ (string)Health +"/"+ (string)MaxHealth + "Action: "+ ActionName);
}

//                        ~ ECO-NAME                                                                                     ~//
// These are the standard functions to access the Eco-Name.
// If you're going to make changes these to these, please speak with Never Rust or others working on the
// Eco-system project to establish a new version number.
//
// Version A0 ("alpha zero" / initial testing)
// 0 - Proper Name (unchanging species name)
// 1 - Eco-Name version
// 2 - Type: "P"lant, "A"nimal, "R"obot, etc.
// 3 - Food/Energy amount from root
// 4 - Food/Energy amount from leaf
// 5 - Action ("EAT")
// 6 - Target Key
// 7 - Requested Food (how much you intend on eatting)

SetEcoName(integer FoodRoot, integer FoodLeaf, string Action, key TargetKey, integer EnergyRequested) {
    list EcoName = [MyProperName, MyEcoNameVersion, MyEcoType, FoodRoot, FoodLeaf, Action, TargetKey, EnergyRequested];
    llSetObjectName(llList2CSV(EcoName));
}

string EcoName2ProperName(string name) {
    //if (EcoNameVersion(name) == MyEcoNameVersion)
    return llList2String(llCSV2List(name), 0);
    //else return "";
}

string EcoNameVersion(string name) {
    return llList2String(llCSV2List(name), 1);
}

integer EcoName2FoodRootAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 3);
    else return 0;
}

integer EcoName2FoodLeafAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 4);
    else return 0;
}
string EcoName2Action(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2String(llCSV2List(name), 5);
    else return "";
}

key EcoName2TargetKey(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Key(llCSV2List(name), 6);
    else return NULL_KEY;
}

integer EcoName2RequestedFood(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 7);
    else return 0;
}

//******************8******** M o v e m e n t *****************************8******//

MoveTo(vector T) {
    IsBusy = TRUE;

    AnimateMove(TRUE);

    llTargetRemove(TargetHandle);       // remove last target
    TargetHandle = llTarget(T, 0.1);

    vector MyPos = llGetPos();
    llRotLookAt(getRotToPointAxisAt(<1,0,0>, <T.x, T.y, MyPos.z>), 1.0, 1.0);

    float distance = llVecDist(MyPos, T);

    ModifyEnergy(-distance);
    ActionStartTime = llGetGMTclock();
    llMoveToTarget(T, distance / Speed);

}

StopMove() {

    llStopMoveToTarget();
    llTargetRemove(TargetHandle);

    llApplyImpulse(-llGetMass()*llGetVel(),FALSE);

    vector x = llRot2Euler(llGetRot());
    //talk(" --- MyRot ---" + (string)llGetRot() + " -- Euler: " + (string)x);

  //  vector dir = llVecNorm(targetPos - llGetPos());

    rotation rot = llRotBetween(<0.0, 0.0, 0.0>, <x.x, x.y, 0>);

    llRotLookAt(rot, 0.5, 0.5);
    AnimateMove(FALSE);
    llSleep(1);
    IsBusy = FALSE;

}

AnimateMove(integer T) {
    //llSetRot(ZERO_ROTATION);
    llMessageLinked(LINK_ALL_CHILDREN, T, "head down", NULL_KEY);

}

rotation getRotToPointAxisAt(vector axis, vector target) {
    return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}

vector CorrectTarget(vector PT) {       // Don't want a land animal to fly or a fish to get out of water
                                        // needs fixin'

        float ground = llGround(PT - llGetPos());
        float water = llWater(PT - llGetPos());
        //llSay(0, "PT in: "+ (string)PT + " / ground: "+ (string)ground + " / water" + (string)water + " / MyPos =" + (string)llGetPos());
        if (PT.z < (ground + 0.01))  PT = <PT.x, PT.y, ground + 0.01>;
        //if (PT.z > water)  PT = <PT.x, PT.y, water>;
        if (PT.z - ground > MaxGroundHeight)  PT = <PT.x, PT.y, ground + MaxGroundHeight>;
        //if (PT.z < (water - MaxWaterDepth))  PT = <PT.x, PT.y, water - MaxWaterDepth>;

        return PT;
}

//************* The Lifecycle: Birth, Growth, Aging & Reproduction ***************//

Birth() {
    llSetText("",<0,0,0>, 0);
    llSetObjectName(MyProperName);
    talk("is born (" + VersionInfo + " by Never Rust)");
    if (MyGeneration > 0) {
        //llSetScale(BirthSize);
        IsActive = TRUE;
        llSetTimerEvent(TimerTime);
    } else IsActive = FALSE;
    AnimateMove(FALSE);
    Age = 0;
    Energy = MaxEnergy * 0.3;
    CurrentSeedNumber = 0;
    llSetRot(ZERO_ROTATION);
    llSetStatus(STATUS_PHYSICS, TRUE);
    llSetBuoyancy(0.0);
    llGroundRepel(0.3, FALSE, 1.0);

}

Grow(float GrowthFraction) {
    vector size = llGetScale();
    float GrowthFactor = 1 + (MaxGrowthStep * GrowthFraction);

    if (CanGrow(size * GrowthFactor)) {
        //talk("is growing. (GrowthFactor: " + (string)GrowthFactor+ ")");
        ResizeAll(GrowthFactor);
    }
}

integer CanGrow(vector newsize) {
    // if we haven't reached max size and we're not in old age
    if (MaxSize.z > newsize.z && Age < OldAge)
        return TRUE;
    else return FALSE;
}

ResizeAll(float scale) {
    llMessageLinked(LINK_SET, -1, (string)scale, NULL_KEY);
}

integer Reproduce() {
    if (CanReproduce()) {
        // reproduce here
        return TRUE;
    } else return FALSE;
}

integer CanReproduce() {
    return FALSE;
}

MakeOlder(integer t) {                          // Increases age and checks for death
                                                // Generation zero is immortal
    if (t > 0) {
        Age += t;
        //if (MyGeneration > 0 && Age == OldAge) display("I'm getting old!", 2);
        if (MyGeneration > 0 && Age >= OldAge) ModifyHealth(-t);
        ModifyEnergy(-t);
    }
    if (MyGeneration > 0 && Health <= 0) Die();
}

Die() {
    //llMessageLinked(LINK_SET, TRUE, "DIE", NULL_KEY);
    llDie();
}

//************* Eating, Energy, Health & Combat ***************//

ModifyHealth(float n) {
    Health = Health + n;
    if (Health <= 0) {
        Health = 0;
        MakeOlder(0);
    }
    else if (Health > MaxHealth) Health = MaxHealth;
}

ModifyEnergy(float n) {
    Energy = Energy + n;
    if (Energy < 0) Energy = 0;
    else if (Energy > MaxEnergy) Energy = MaxEnergy;
}

integer IsHungry() {
    if (Energy < (0.8 * MaxEnergy)) return TRUE;
    else return FALSE;
}

float Eat(float FoodEnergy) {
    float Hunger = MaxEnergy - Energy;
    if (Hunger > 0) {
        float EatEnergy = 0;
        if (FoodEnergy < Hunger) EatEnergy = FoodEnergy;
        else EatEnergy = Hunger;

        ModifyEnergy(EatEnergy);
        display("Yum! (+" + (string)llFloor(EatEnergy) +" / "+ (string)llFloor(Energy) +")", 3);
        display("just ate", 0);
        return EatEnergy;
    } else return 0;
}

//************* Activity ***************//

DoSomething() {
    if (IsActive && Health > 0 && Energy > 0) {

        if (Energy == MaxEnergy) {
            AnimateMove(TRUE);
            display("Zzzz...", 10);
            MakeOlder(10);
            AnimateMove(FALSE);
        }

        llMessageLinked(LINK_SET, IsHungry(), "Look", NULL_KEY);
        //debug("sent look msg");

    }
}

//####################### D # E # F # A # U # L # T ###############################//

default
{
    state_entry()
    {
        Birth();

    }

    on_rez(integer start_param) {
        //llResetScript();
        MyGeneration = start_param;
        llSleep(2);

        Birth();
    }

    touch_start(integer total_number)       // pauses when touched
    {
        TellAboutSelf();

        if (llDetectedKey(0) == llGetOwner()) {
            llTargetRemove(TargetHandle);

            IsActive = !IsActive;

            if (IsActive) {
                display("", 0);
                //AnimateMove(TRUE);
                llSetTimerEvent(TimerTime);
                DoSomething();
            }
            else {
                ActionName = "";
                IsBusy = FALSE;
                display("INACTIVE", 0);

                AnimateMove(FALSE);
                StopMove();
                llSetTimerEvent(0);
                //AnimateSwim(FALSE);
            }
        }

    }

    object_rez(key child)
    {
        // Give copy of child to child
        llGiveInventory(child, llKey2Name(llGetKey()));
    }

    timer()
    {
        if (IsBusy && llGetGMTclock() > ActionStartTime + ActionTimeMax) {
            display("Action timed out", 1);
            ActionName = "";
            StopMove();
        }
        MakeOlder(llFloor(TimerTime));    // it's been at least TimerTime since the last call, so age
        ModifyEnergy(llFloor(TimerTime/2.0));
        if (!IsBusy) {
            ActionName = "";
            DoSomething();
        }

    }

    at_target(integer tnum, vector targetpos, vector ourpos)
    {
        llTargetRemove(tnum);
        display("reached target", 0.5);
        StopMove();
    }
    not_at_target()
    {
        llApplyImpulse(<0.0, 0.0, 0.01>, FALSE);
    }

    collision(integer num_detected)                                 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
    {
        if (EcoName2TargetKey(llKey2Name(llGetKey())) == llDetectedKey(0)   // if we hit the targeted obj
                && EcoName2Action(llKey2Name(llGetKey())) == "EAT")         // and eating it...
        {
            StopMove();

            integer FoodAmount = EcoName2FoodRootAmount(llDetectedName(0));
            if (FoodAmount > 0) {                                   // if it gives energy then eat and remember
                Eat(FoodAmount);

                llMessageLinked(LINK_SET, FoodAmount, (string)llDetectedPos(0), llDetectedKey(0));  // Send info to script 2 for memory
                //AddFoodMemory(EcoName2ProperName(llDetectedName(0)), FoodAmount, llDetectedPos(0));
            } else {
                llMessageLinked(LINK_SET, 0, "Inedible", llDetectedKey(0));     // remember what's not edible
                //RememberInedible(EcoName2ProperName(llDetectedName(0)));
            }

        } else if (FALSE) {
            // check predator list
        } else {

        }
    }

    link_message(integer sender_num, integer num, string str, key id)   // used for growing
    {
         if(sender_num == 1) {
             if (num == -2) {
                MoveTo((vector)(str));
            }

            if (num == -1) {
                float scale = (float)str; // size factor
                list primparams = [PRIM_SIZE, llGetScale() * scale];    //resize

                llSetPrimitiveParams(primparams);
            }
        }

    }

}

Script 2 - Scanning & Memory

//////////    2     ///////////

integer IsHungry = FALSE;

list FoodMemory = [];
list PredatorMemory = [];
list FriendsMemory = [];
list InedibleMemory = [];

string MyProperName = "Plob";           //!!!!!!!!!! These need to be defined for the Eco-Name
string MyEcoNameVersion = "A0";
string MyEcoType = "A";                     //A=Animal, P=Plant, R=Robot
string MyDietType = "P";

/////////////////################## functions ######################/////////////

//************* F e e d b a c k ***************//

debug(string m) {
    llOwnerSay("/me (debug): " + m);
}

display(string m, float Wait) {
    llSetText(//ActionName +"("+ (string)IsBusy +")\n"+
    m, <0.5, 1.0, 1.0>, 0.8);
    if (Wait > 0.0) {
        llSleep(Wait);
        llSetText("", <0,0,0>, 0.0);
    }
}

//                        ~ ECO-NAME                                                                                     ~//
// These are the standard functions to access the Eco-Name.
// If you're going to make changes these to these, please speak with Never Rust or others working on the
// Eco-system project to establish a new version number.
//
// Version A0 ("alpha zero" / initial testing)
// 0 - Proper Name (unchanging species name)
// 1 - Eco-Name version
// 2 - Type: "P"lant, "A"nimal, "R"obot, etc.
// 3 - Food/Energy amount from root
// 4 - Food/Energy amount from leaf
// 5 - Action ("EAT")
// 6 - Target Key
// 7 - Requested Food (how much you intend on eatting)

SetEcoName(integer FoodRoot, integer FoodLeaf, string Action, key TargetKey, integer EnergyRequested) {
    list EcoName = [MyProperName, MyEcoNameVersion, MyEcoType, FoodRoot, FoodLeaf, Action, TargetKey, EnergyRequested];
    llSetObjectName(llList2CSV(EcoName));
}

string EcoName2ProperName(string name) {
    //if (EcoNameVersion(name) == MyEcoNameVersion)
    return llList2String(llCSV2List(name), 0);
    //else return "";
}

string EcoNameVersion(string name) {
    return llList2String(llCSV2List(name), 1);
}

integer EcoName2FoodRootAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 3);
    else return 0;
}

integer EcoName2FoodLeafAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 4);
    else return 0;
}
string EcoName2Action(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2String(llCSV2List(name), 5);
    else return "";
}

key EcoName2TargetKey(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Key(llCSV2List(name), 6);
    else return NULL_KEY;
}

integer EcoName2RequestedFood(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 7);
    else return 0;
}

//************* Eating, Energy, Health & Combat ***************//

AddFoodMemory(string FoodName, float FoodEnergy, vector FoodPos) {
    if (llGetListLength(FoodMemory) > 6) FoodMemory = llDeleteSubList(FoodMemory, 0, 0);
    FoodMemory += [FoodName];
    // change to a more organized FoodMemory list of [FoodName, FoodEnergy, FoodPos]
    // check to make sure the foodname isn't already there
    // if it is, then update the pos and energy
}

RememberInedible(string FoodName) {
    if (llGetListLength(InedibleMemory) > 6) InedibleMemory = llDeleteSubList(InedibleMemory, 0, 0);
    InedibleMemory += [FoodName];
}

//************* Sensory & Communication ***************//

LookAround() {
    llSensor("", NULL_KEY, ACTIVE | PASSIVE, 10, TWO_PI);
}

PostScan(list SpottedFood, list SpottedUnknown, list SpottedPredators, list SpottedFriends) {

        //debug("--MEM:" + (string)llGetFreeMemory());
        //debug("--Food:" + llList2CSV(SpottedFood));
        //debug("--??:" + llList2CSV(SpottedUnknown));
        //debug("--Preds:" + llList2CSV(SpottedPredators));
        //debug("--Amigos:" + llList2CSV(SpottedFriends));

        display("Figuing out what to do...", 0.3);

        string PredatorToFear = IsDangerous(SpottedPredators);
        if (PredatorToFear != "") {
            // run away from him

        } else if (IsHungry) {
            if (llGetListLength(SpottedFood) > 0) {

                display("I see something I like to eat!", 1);

                integer FoodNum = 0;                                    // Should look for nearest
                                                                        // for now, just use first one
                vector TargetPos = llList2Vector(SpottedFood, FoodNum + 1);
                key TargetKey = llList2Key(SpottedFood, FoodNum + 2);

                                                                    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
                string ActionName = "EAT";
                SetEcoName(0, 0, ActionName, TargetKey, 100);       // before trying to eat an object, you
                                                                    // must set your Eco-Name so that
                                                                    // your action is "EAT" and you are
                                                                    // targeting the food's key
                llMessageLinked(LINK_SET, -2, (string)(TargetPos), NULL_KEY);

            } else if (llGetListLength(SpottedUnknown) > 0) {

                integer FoodNum = 0;                                    // Should look for nearest
                                                                        // for now, just use first one
                vector TargetPos = llList2Vector(SpottedUnknown, FoodNum + 1);
                key TargetKey = llList2Key(SpottedUnknown, FoodNum + 2);

                display("I'll try to eat the nearest unknown object...", 0);
                string ActionName = "EAT";
                SetEcoName(0, 0, ActionName, TargetKey, 100);
                llMessageLinked(LINK_SET, -2, (string)(TargetPos), NULL_KEY);
            }

        } //else if (want to socialize) {
          //  do something social
        //}

}

string IsDangerous(list SpottedPredators) {

    // go thru list, return the name of the predator to fear, if any
    return "";
}

//####################### D # E # F # A # U # L # T ###############################//

default
{
    state_entry()
    {
        //llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        //llSay(0, "Touched.");
    }

    sensor(integer num_detected)
    {

        display("I see " + (string)num_detected + " things.", 0.5);
        if (num_detected > 0) {

            list SpottedFood = [];
            list SpottedUnknown = [];
            list SpottedPredators = [];
            list SpottedFriends = [];

            display("Categorizing everything...", 0.3);
            integer i;
            integer t;
            for (i = 0; i < num_detected; i++) {                        // look at all things sensed
                string SeeName = EcoName2ProperName(llDetectedName(i));
                vector SeePos = llDetectedPos(i);
                key SeeKey = llDetectedKey(i);
                integer Found = FALSE;

                for (t = 0; t < llGetListLength(FoodMemory); t++) {    // compare to list of food
                    if (SeeName == llList2String(FoodMemory, t)) {
                        SpottedFood += [SeeName, SeePos, SeeKey];
                        Found = TRUE;
                    }
                }

                for (t = 0; t < llGetListLength(PredatorMemory) && !Found; t++) {    // compare to list of predators
                    if (SeeName == llList2String(PredatorMemory, t)) {
                        SpottedPredators += [SeeName, SeePos, SeeKey];
                        Found = TRUE;
                    }
                }

                for (t = 0; t < llGetListLength(FriendsMemory) && !Found; t++) {    // compare to list of friends
                    if (SeeKey == llList2String(FriendsMemory, t)) {
                        SpottedFriends += [SeeName, SeePos, SeeKey];
                        Found = TRUE;
                    }
                }

                for (t = 0; t < llGetListLength(InedibleMemory) && !Found; t++) {     // compare to list of things known to be inedible

                    if (SeeName == llList2String(InedibleMemory, t)) {
                        Found = TRUE;
                    }
                }

                if (!Found) SpottedUnknown += [SeeName, SeePos, SeeKey];
            }

           PostScan(SpottedFood, SpottedUnknown, SpottedPredators, SpottedFriends); // decide what to do!

        }
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        if (sender_num == 1) {
            if (str == "Look") {
                IsHungry = num;
                LookAround();

            } else if (str == "Inedible") {                                 // get inedible collisions from script 1 to remember
                RememberInedible( EcoName2ProperName(llKey2Name(id)) );

            } else if (id != NULL_KEY && num > 0) {                         // get food collisions from script 1 to remember
                AddFoodMemory( EcoName2ProperName(llKey2Name(id)), num, (vector)str );

            }
        }
    }
}

EcosystemWorkingGroup | Ecosystem


EcoCommV1

Eco Comm Version 1

This will be the root page for the new protocol covering interspecies communications and interactions. This protocol will replace EcoNameA0.

As a departure from EcoNameA0 we will be splitting communications into 2 portions. Name based communications and Listen based communications. This version is currently under development. Species currently continue to interact using EcoNameA0

Active Proposals

NamesProtocolProposal


EcoFoodSpore1

Food Spore Script

This open-source code is part of the Ecosystem project. Please read the EcosystemDisclaimer before using.
Please do not edit the code below on this page unless you've already tested it to ensure it works. If you make minor changes, please add a note describing the change. For major changes, it's better to start a new wiki page and add a link on the Ecosystem page. Thanks.

string MyEcoNameVersion = "A0";
integer FoodRootWorth = 20;
string MyProperName = "Food Spore";
string MyEcoType = "P";

//                        ~ ECO-NAME                                                                                     ~//

SetEcoName(integer FoodRoot, integer FoodLeaf, string Action, key TargetKey, integer EnergyRequested) {
    list EcoName = [MyProperName, MyEcoNameVersion, MyEcoType, FoodRoot, FoodLeaf, Action, TargetKey, EnergyRequested];
    llSetObjectName(llList2CSV(EcoName));
}

string EcoNameVersion(string name) {
    return llList2String(llCSV2List(name), 1);
}

integer EcoName2FoodRootAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 3);
    else return 0;
}

integer EcoName2FoodLeafAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 4);
    else return 0;
}
string EcoName2Action(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2String(llCSV2List(name), 5);
    else return "";
}

key EcoName2TargetKey(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Key(llCSV2List(name), 6);
    else return NULL_KEY;
}

integer EcoName2RequestedFood(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 7);
    else return 0;
}

//                                                                                                                //

reset() {
    llSetStatus(STATUS_PHYSICS, TRUE);
    llMinEventDelay(1.0);
    SetEcoName(FoodRootWorth, 0, "", NULL_KEY, 0);
}

default
{
    state_entry()
    {
        reset();
    }

    on_rez(integer start_param)
    {
        reset();
        if (start_param > 0) llSetTimerEvent(55);
    }

    collision (integer num_detected)
    {
        if (EcoName2TargetKey(llDetectedName(0)) == llGetKey()) {

            if (EcoName2RequestedFood(llDetectedName(0)) > 0) {
                llDie();
            }

        }
    }

    timer()
    {
        llSetAlpha(0.5, ALL_SIDES);
        llSleep(5);
        llDie();
    }

}

...by NeverRust

EcosystemWorkingGroup | Ecosystem


EcoName

The Eco-Name

Conceptually the eco-name is a way to allow for very efficient communication between ALife critters and food. It is currently being developed by the EcosystemWorkingGroup.
The eco-name is a list of comma-separated values stored in an object's name. This data can be read easily by Detected functions during collisions or scans, and prevents the need for potentially resource-hogging llListen calls.

Currently the EcosystemWorkingGroup is using version "A0" (alpha-zero) for testing in Terminus. This version supports variables needed for basic eating.
The standardized code is available here: EcoNameA0

Variables Currently Used in the Eco-Name

List # | Variable | What is it for? | Data type
0 | Proper Name | unchanging species name | 36 characters max
1 | Eco-Name version | ensure correct version | 2 characters, "A0"
2 | Type | determining if edible, other uses | 1 character: "P"lant, "A"nimal, "R"obot
3 | Root Food | Energy amount from root | integer 0-9999
4 | Leaf Food | Energy amount from non-root | integer 0-9999
5 | Action | determining what the organism is doing | 3 characters, capitalized; i.e. "EAT"
6 | Target Key | who the organism is doing Action to | 36 character key
7 | Requested Food | how much the organism intends on eating | integer 0-9999

Example Eco-Name: Shark, A0, A, 1000, 0, EAT, 133b98a7-b49a-47a1-91ea-35bf7a14810a, 340
Explanation: The shark is an animal that offers 1000 energy if eaten. He is looking to eat (me!) and would like to eat 340 units of energy/food if possible.

Proposed Variables

...will be implemented as needed and agreed upon

  • Sub-type (? characters) -- more specific type of creature (reptile, mammal, etc.)
  • Damage Min (integer, 0-999) -- how much damage to do on collision / fight
  • Damage Max (integer, 0-999) -- max damage (can be randomly decided)
  • Channel (integer, -#) -- personal/species communication channel
  • Color R (integer, 1-255) -- the organism's color, for visual recognition
  • Color G (integer, 1-255)
  • Color B (integer, 1-255)
  • Attractiveness (string, 2) -- two-letter code for mating beauty
  • Smell (string, 2) -- a two-letter code for whatever we might want
  • Taste (string, 2) -- 2-letter code; how does the organism taste
  • Touch (string, 2) -- 2-letter code for the texture/feel of the organism
  • Sound (string, 2) -- 2-letter code for the sound being made (barking, tweeting, shouting, ribbiting, etc.)
  • Visible State (string, 2) -- a two-letter code for whatever we might want -- will be useful to convey the health of the organism
  • Signs (string, 2) -- some sort of non-verbal communication; usually species-specific
  • Personal/2nd Name (string, 24) -- a personal name or other information
  • Parent Key (key/string, 36) -- the organism's parent
  • Generation (integer, 0-9999) -- generation #

Other Information

...that can be gotten from llDetected* functions and thus does not need to be in the Eco-Name

  • Group to possibly check to see if they are in the Ecosystem group
  • Key to identify the specific organism, and for sending email
  • Link Number to see if you're interacting with the root prim or a "leaf"/leg
  • Name to get the EcoName
  • Pos where this thing is
  • Rot is it looking at me? (probably better to use Target Key)
  • Type to check for active scripts
  • Vel to judge the critter's speed

Development and discussion: http://groups.google.com/group/SL-EcoSystem/browse_thread/thread/6512bdda94a3b269
Thanks to Laukosargas Svarog for the original ideas! (http://forums.secondlife.com/showpost.php?p=1042155&postcount=3)

EcosystemWorkingGroup | Ecosystem


EcoNameA0

Eco-Name version A0 (alpha-zero)

This open-source code is part of the Ecosystem project. Please read the EcosystemDisclaimer before using.
Please do not edit the code below. If you want to make changes, please create a new wiki page. Thanks.

//                        ~ ECO-NAME                                                                                     ~//
// These are the standard functions to access the Eco-Name.
// If you're going to make changes to these, please speak with Never Rust
// or others working on the Eco-system project to establish a new version number.
//
// Version A0 ("alpha zero" / initial testing)
// 0 - Proper Name (unchanging species name)
// 1 - Eco-Name version
// 2 - Type: "P"lant, "A"nimal, "R"obot, etc.
// 3 - Food/Energy amount from root
// 4 - Food/Energy amount from leaf
// 5 - Action ("EAT")
// 6 - Target Key
// 7 - Requested Food (how much you intend on eatting)

string MyProperName = "species name";        //!!! These need to be defined for the Eco-Name
string MyEcoNameVersion = "A0";
string MyEcoType = "A";                     //A=Animal, P=Plant, R=Robot
string MyDietType = "P";

SetEcoName(integer FoodRoot, integer FoodLeaf, string Action, key TargetKey, integer EnergyRequested) {
    list EcoName = [MyProperName, MyEcoNameVersion, MyEcoType, FoodRoot, FoodLeaf, Action, TargetKey, EnergyRequested];
    llSetObjectName(llList2CSV(EcoName));
}

string EcoName2ProperName(string name) {
    //if (EcoNameVersion(name) == MyEcoNameVersion)
    return llList2String(llCSV2List(name), 0);
    //else return "";
}

string EcoNameVersion(string name) {
    return llList2String(llCSV2List(name), 1);
}

integer EcoName2FoodRootAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 3);
    else return 0;
}

integer EcoName2FoodLeafAmount(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 4);
    else return 0;
}
string EcoName2Action(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2String(llCSV2List(name), 5);
    else return "";
}

key EcoName2TargetKey(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Key(llCSV2List(name), 6);
    else return NULL_KEY;
}

integer EcoName2RequestedFood(string name) {
    if (EcoNameVersion(name) == MyEcoNameVersion) return llList2Integer(llCSV2List(name), 7);
    else return 0;
}

EcosystemWorkingGroup | Ecosystem | EcoName


Ecosystem

Eco-system Concepts & Code

This page is for the dissemenation of concepts and code developed by the EcosystemWorkingGroup.
All code is open-source and as such must remain free to public in any form. (See the EcosystemDisclaimer)

Concepts

EcoName
RecursiveRezzing
ReservedChannels

Standardized Code

EcoNameA0 (deprecated)
EcoCommV1

Never Rust's Code

Algae - EcoAlgae094 - Plant that grows underwater in sunlight, reproduces quickly, but has a generation limit.
Animal Scripts - EcoAnimal09e - 2 scripts currently used in the plobs (and the lice?) on Terminus
Basic Food Spore - EcoFoodSpore1 - Currently spit out of the Foodshrooms on Terminus

EcosystemWorkingGroup


EcosystemDisclaimer

Disclaimer

The code provided by the EcosystemWorkingGroup is open-source. As such if you use and/or make any modifications to the code, you will need to provide it to the public for free (preferrably on this site). This way everyone can help contribute to advancing these scripts.

Note that many of these scripts utilize SelfReplication, and may be dangerous for an inexperienced scripter to use, especially in an uncontrolled sim. Use at your own risk!

Ecosystem


EcosystemWorkingGroup

The Ecosystem Working Group

The Ecosystem Working Group (EWG) is a SecondLife group dedicated to creating an open-source Artificial Life ecosystem. We are currently operating out of the Terminus sim, generously donated by Kerunix Flan.

Our discussions are primarily here: http://groups.google.com/group/SL-EcoSystem
Our open-source code is here: Ecosystem

Rules

1. No intentionally disrupting the eco-system by creating an organism that dominates. If you're going to use common interactions (such as the EcoName), then you should at least try to maintain the balance.
2. No intentionally crashing the sim by a mix of overpopulation and scripts that cause too much lag.
3. Any scripts developed or seriously tested on our land should be made available as open-source.
4. Self-replicating ALife should have a fail-safe mechanism to avoid growing unchecked. Do not let the organism grow into our neighbor's sim.
5. Have fun and experiment with interesting concepts!

List of Officers and Members

  • Never Rust (NeverRust)
  • Polymerase Enyme (PolymeraseEnzyme)
  • Sera Rawley (SeraRawley)
  • AnkhMorpork Salsman (AnkhMorporkSalsman)

...add your own name here only if you're a member

EcoSystem


else

See IfElse.


Encapsulation

Programmers refer to a style of coding, that partitions and protects areas of code from each other, as "encapsulation".

There are more technical definitions (as in object-oriented programming) but, for the purposes of LSL, encapsulation represents an approach to coding that can result in higher quality code. In this case, higher quality means easier to understand, easier to maintain, easier to re-use and less prone to error.

Encapsulation involves defining a block of code such that the internal workings of the code block are shielded from outside tampering; and the block has a consistent interface so that other code elements can efficiently refer to the encapsulated block. The primary means of encapsulation in LSL is the user-defined function.

Protection is achieved in a function by using local variables and interfacing with other code through the input and return parameters of the function rather than just using global variables for everything.

By placing a block of code that impliments a specific feature in a function, keeping any variables in the function local and using the input and return parameters as the interface with calling code, the code in the function forms a standalone unit. Other code can access the features provided but must use the defined parameter interface.

The benefits are that code within the function can change without affecting external code as long as the interface doesn't change. Also any problems in the function code can be easily found in that code, and you only need to look in the function definition to know what impact it has on the script as a whole. Code that uses the feature becomes easier to understand as well because a meaningful name which evokes the feature replaces a bunch of code. In this way, more complex features can be built out of simple blocks yet remain maintainable (i.e. easy to figure out what's going on when you look at your own code 6 months later and wonder who the heck wrote it).

By not using global variables one can be assured that no changes to variable values, that the function depends on, are happening outside the function. This makes finding problems and fixing them easier. By using global variables in multiple functions, it becomes much more difficult to determine where a problem is and how to fix it as multiple functions may be acting on the same variable.

There are cases where a function needs persistent data storage. Since all local variables are cleaned up when a function returns, successive calls to functions can't depend on each other. In terms of encapsulation, what would be required is to assign a global variable (i.e. persists after the function returns) to a function and make it private to that function so that other code could not access it directly. This is the essence of object-oriented programming: an object being a block(s) of code encapsulated along with private data and providing an abstract external interface.

In LSL this can be simulated by convention. That means defining a global variable that is marked by some method (prepending the function name to the variable name for example) so that it's clear that the variable should not be used by any other code except the function code. Then the global declaration can be placed right above the function definition so that they can be easily moved as a unit.

So, another benefit of a well encapsulated function is code re-use. With good encapsulation, a feature can be easily copied from one script to another speeding up development and making things more consistent so there's not as much to keep track of.

Part of good encapsulation is making functions that do one thing and do that thing well. That's not to say that the idea captured in the function name can't be a big idea made up of smaller ideas; it's that those smaller ideas should also be their own functions that the bigger idea uses. In general, if a chunk of code is longer than a screen height or two there's probably some parts of it that should be made into their own functions.

Ultimately, by encapsulating features and abstracting them under successively higher level names, we are extending the language via functional units and expanding the bounds of what can be expressed by recombining those units in new ways.

Functions | User-Defined Function | Scope


endian

The endianess (or byte-order) defines the way values are stored in memory.

Lets take an example value of 0x12345678 (hex).

On big-endian systems (MIPS, PPC, IP network packets, etc.) it's stored with the most significant byte first:: 0x12345678

On little-endian systems (x86) it's stored as with the least significant byte first, e.g. as: 0x78563412

You're making this up, right? (And I know you're not, you should just have a link in here linking to how this relates to LSL.
See Preview. I'll put links in here once 1.5 is released and we have pages for the functions it applies to. -Ez

Detailed explanation


Energy

An object's energy, as reported by llGetEnergy, is quite distinct from the RL physics definition of energy. SL energy is actually a throttle on how effectively scripts can change the motion of physical objects. The energy of an object ranges from 0.0 to 1.0. If an object's energy is 1.0, LSL functions that attempt to change the object's motion will have their full effect. If the energy of the object is 0, the same functions will have no effect. Intermediate energy values give intermediate effects.

An object gains energy through a stipend, which it earns merely by existing in the rezzed state. An object expends energy when scripts call functions to change its motion. SL's energy was implemented to limit the amount scripts can use certain dynamics functions.

Each time an object is rezzed, or its mass is changed (like by changing its size or shape), its energy is reset to 0.

Earning Energy

While rezzed, an object is credited with energy at a rate that is determined by its mass. The more massive the object is, the slower it earns energy. Specifically, the energy stipend is 200/mass units of energy per second. When an object's energy reaches 1.0, the stipend is suspended until the energy falls below 1.0.

Expending Energy

The following functions immediately reduce the amount of energy of the object running the script:

llApplyImpulse
llApplyRotationalImpulse
llPushObject

For the first two, the amount of energy expended is the magnitude of the impulse vector divided by 20,000. For llPushObject, it is the sum of the expenditures due to the two impulse vectors. Note: for llPushObject, the object being pushed does not lose energy.

These functions also reduces the amount of energy of the object being affected:

llMoveToTarget
llRotLookAt
llLookAt
llSetForce
llSetTorque
llSetForceAndTorque
llSetHoverHeight
llGroundRepel
llSetBuoyancy

The amount of energy used by calls to these functions is affected by the mass of the object in question. The different functions also seem to have varying levels of efficiency. Some tasks take more energy for a given mass than others. Persistent functions like llSetBuoyancy and llMoveToTarget constantly use energy and will not function if the mass of the object is above a limit determined by the rate of recharge vs. the efficiency of the function. Smaller objects (those with lesser mass) gain energy quicker.

The energy an object has can be determined by calling llGetEnergy.

Q: Is it by object or by prim?
A: The root prim has the combined energy of all the child prims and its own, as it does with mass. Scripts in child prims will reflect the energy of that prim, same with mass.

A lot isn't known about energy. If anyone has performed tests, please don't hesitate to post more information here.

I've done some experimenting, and energy has far greater effects on scripts. First off, energy potential is based off mass. If the prim has negative mass (LSL mass functions are kind of screwy) then scripts will not run; events won't register (but they will still be shown like for money & touch). If you try and pay an object with negative mass you will get an "out of energy" error box. But, fortunately, there is a workaround for negative-mass prims: make the bad prim the root prim and link in another prim to balance it out. I've concluded the reason scripts don't run (Lindens wouldn't spill the beans when I asked; they told me it was kind of like magic), is that negative-mass prims totally foul up the center-of-mass calculations in LSL and, more importantly, totally do a number on vehicles (I think vehicles are implemented through LSL more then Havok). With some very screwed up prims (don't ask) you can have prims on the other side of the mass equations. The interesting thing is that, under certain circumstances, a prim can be made to shift from positive mass to negative mass and vice versa by manipulating the twist, hollow, top size & top shear. Most interesting is the fact that some combinations of attributes on normally negative-mass prims result in super positive-mass prims. Haven't thought of a good use for them.

On a side note, center-of-mass & mass is used for link distances, not prim size. My findings with prim mass has lead me to conclude that LSL & Havok's mass calculations are done separately (negative-mass objects don't fly into the sky as expected). Will post more when I do the experiments; at the moment I'm trying to keep off LL radar, but I guess I just failed at doing that. :p

SL 1.5 has different rules with mass, and scripts now run in objects that once had negative mass. To read more on this topic read the notes on llSetPrimitiveParams -BW

Functions | Mass | Dynamics | Physics


Engine

An engine is a type of library that has its own API.

API


environment

See simulator.


Equality

Equality operators in LSL test a condition on two inputs and return a value of TRUE or FALSE.

Equality Operators

Operator | Name | Syntax | Description
> | Greater then | var1 > var2 | Returns TRUE if var1 is greater then var2, FALSE otherwise. var1 and var2 must be either an integer or a float.
< | Less then | var1 < var2 | Returns TRUE if var1 is less then var2, FALSE otherwise. var1 and var2 must be either an integer or a float.
>= | Greater then or equal to | var1 >= var2 | Returns TRUE if var1 is greater then or equal to var2, FALSE otherwise. var1 and var2 must be an integer or a float.
<= | Less then or equal to | var1 <= var2 | Returns TRUE if var1 is less then or equal to var2, FALSE otherwise. var1 and var2 must be an integer or a float.
!= | Not equal to | var1 != var2 | Returns TRUE if var1 is not equal to var2, FALSE otherwise. var1 and var2 can be any type.
== | Equal to | var1 == var2 | Returns TRUE if var1 is equal to var2, FALSE otherwise. var1 and var2 can be any type.
Note:
  • When using == or != on lists, it's important to remember that only the list lengths are compared. So "["a"] == ["b"]" would return TRUE, the same as "llGetListLength(["a"]) == llGetListLength(["b"])".
    • "["a"] != ["b"]" is the same as "llGetListLength(["a"]) - llGetListLength(["b"])"
  • Do not confuse the assignment operator "=" with the equality operator "=="! When comparing two values, use "==". When assigning a value, use "=". The compiler will not catch your error, as it's possible to assign values within an IF statement delibeartely. Compiling a script with "=" rather than "==" will probably cause the statement to evaluate to TRUE and the conditional variable to become corrupted.

These operators are often used in conditional statements.

Operators | Unary | Binary | Bitwise | Boolean | Equality | Assignment


Estate

An island simulator.

Simulator


Euler

A Euler is a representation of a rotation as a vector, using pitch, yaw, and roll. Euler rotations can be most easily visualized as a tank turret mounted on the Y-Z plane with its barrel pointed down the Z-axis. Second life first applies a rotation to the X-axis, which turns the turret. It then applies the Y rotation, which adjusts the angle of the barrel up and down. It then applies rotation to the Z-axis, which spins the barrel, kind of like a carriage bolt but without the in-and-out motion.

Anyone doing rotational math in SL is encouraged to learn how to use quaternions, but for those who are limited to basic trigonometry, the X and Y coordinate of Euler rotation can be derived between two points by finding the x,y, and z components of the distance between them, deriving the total distance between them, and using the following formulas:

X rotation = arctangent of (Y distance/Z distance) * -1
Y rotation = arcsine of (X distance/total distance)

Note (on rotation sign): In order to find the distances between the two points, it is necessary to subtract one point's coordinates from the others. For instance, X distance = P1.X - P2.X. Since subtraction isn't communtitive (x-y is not equal to y-x), which point is designated as P1 is important. For the X rotation, both the y and z components are affected by this, so regardless of their order, their bias cancels out as long as both signs are preserved. However, for the sign of the Y rotation to be correct the points must be ordered so Z1 is greater than Z2.

See rotations for more information.

Functions

llEuler2Rot
llRot2Euler

Rotation | Vector


Evaluate

In LSL, and in many other places, to evaluate an expression is to determine its value.

LSL | expression | value


Event

event is a compiler keyword that is not used.

See Events.


Event Handler

In LSL scripts, an event handler is possibly best thought of as a function the simulator executing the script may trigger in response to certain conditions. Like functions, event handlers have named parameter lists and return values, although they also differ in a few important regards.

Whereas the built-in and user-defined functions are global, event handlers are local--the only construct in LSL that is limited to a given state's scope. That is, event handlers are only valid within the state that contains them. State changes may only be made from event handlers.

Event handlers cannot be called directly from functions. Though event handlers may be exited with a return, they are return type void.

Events


ExampleKey2Channel

A constant problem in LSL is the constant use of the same channel for everything in scripts. This causes a fair deal of unnecessary lag, and leads to many a server instability.

Ever wish you could create an arbitrary channel that can be discovered by any script? You can!

The secret is converting a key into a channel in a predictable way. One tried and true method is to use the hex provided.

Example code:

integer Key2Channel(key given, integer num_digits)
{
    // Creates a private channel using a known key value given and num_digits
    // Interestingly, you can find this number in any object by using a sensor

    string known = (string)given;
    integer count = llStringLength(known) - 1;
    integer i;

    string channel = ""; // String for channel creation

    for(i = 0; i < count; i += 2)
    {
        channel += (string)((integer)("0x" + llGetSubString(known,i,i + 1)));

        if(llStringLength(channel) >= num_digits)
            i = count;
    }

    // Return the value
    return (integer)channel;
}

integer Key2Integer(string input, integer start, integer chars)
{
    return (integer)("0x"+llGetSubString(
            (string)llParseString2List(input,["-"],[]),
        start, start + chars - 1));
}

This trick also works with a script pin for even more script-fu.


ExamplellHTTPRequest

This is a minimalistic approach to setting up a simple request to a remote web server, and get the result.

Limitations: apparently PHP must be compiled as an Apache module (and not using FastCGI) for you to get access to apache_response_headers().
Note: There is an PHP based emulation to the apache_response_headers() while using FastCGI emu_getallheaders()

key requestid; // just to check if we're getting the result we've asked for; all scripts in the same object get the same replies

default
{
    touch_start(integer number)
    {
        requestid = llHTTPRequest("http://my.server.com/my-script.php",
            [HTTP_METHOD, "POST",
             HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
            "parameter1=hello&parameter2=world");
    }

    http_response(key request_id, integer status, list metadata, string body)
    {
        if (request_id == requestid)
            llWhisper(0, "Web server said: " + body);
    }
}

And here goes myscript.php:

<?
// Only works with PHP compiled as an Apache module
$headers = apache_request_headers();

$objectName = $headers["X-SecondLife-Object-Name"];
$objectKey     = $headers["X-SecondLife-Object-Key"];
$ownerKey     = $headers["X-SecondLife-Owner-Key"];
$ownerName = $headers["X-SecondLife-Owner-Name"];
$region        = $headers["X-SecondLife-Region"];
// and so on for getting all the other variables ...

// get things from $_POST[]
// Naturally enough, if this is empty, you won't get anything
$parameter1    = $_POST["parameter1"];
$parameter2    = $_POST["parameter2"];

echo $ownerName . " just said " . $parameter1 . " " . $parameter2 . "\n";
?>

Note: if for some reason $_POST[] doesn't return anything, you have to check for other alternatives, like the one described on the first comment on the page for llHTTPRequest (directly reading from the HTTP stream), as per VeloxSeverine's suggestion.

Suggestion: Use $_REQUEST[] instead of $_POST[] as it will work for all types, as well as, POST. SiRiSAsturias

Note: For greater server compatibility and efficiency, use $_SERVER[] instead of apache_request_headers() to access the SL header data. For Example, $_SERVER['HTTP_X_SECONDLIFE_OWNER_NAME']; (notice the format change of the data labels) ~PlexLum

Example: Here is the use of a emulated apache_request_headers() that I wrote; if you're using FastCGI and are used to using the apache function, use this! I hope this helps someone! (It'll also allow you to get the headers if you're not using FastCGI). FoxDiller
Small edit to correct the casing of X-SecondLife headers, as variable names and array keys in PHP are case sensitive. (And in case anyone is wondering, this function also works with PHPSuExec too) - EddyOfarrel
<?php
function emu_getallheaders()
{
    foreach($_SERVER as $name => $value)
    if(substr($name, 0, 5) == 'HTTP_')
        $headers[str_replace('X-Secondlife-', 'X-SecondLife-', str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))))] = $value;
    return $headers;
}

$headers = emu_getallheaders(); // Just replace any use of `apache_request_headers()` with `emu_getallheaders()`.

$objectName = $headers["X-SecondLife-Object-Name"];
$objectKey = $headers["X-SecondLife-Object-Key"];
// ..........................[~ETC ETC~]..........................//
?>

I have been asked to offer my source code for checking HTTP headers, so I present yet another example... the server and client code to check headers... - Ice
<?php
//This simply echoes the headers exactly as it gets them...
//This script requires PHP as an apache module to work

$headers=apache_request_headers();
foreach($headers as $key => $val)
{
     echo $key.": ".$val."\n";
}
?>
//Ice Brodie's http text output script
//This script requests from our data source (URL) and echoes out what it sends to the owner of the script
//I use this to test my PHP apps personally, it's a simple debugging application so I release it publicly
//You may use, redistribute, modify, copy however you feel would be useful

string URL="http://www.secondlife.com/httprequest/homepage.php";
//Replace URL with the path to your web server.
key http;//This stores the HTTP request we send.

default
{
    touch_start(integer foo)
    {
        http=llHTTPRequest(URL, [] ,"");
    }

    http_response(key id,integer status, list meta, string body)
    {
        if(http==id)
        {
            integer i;
            list lbody=llParseString2List(body,["\n"],[]);
            integer count=llGetListLength(lbody);
            //This turns newline characters char(10) into new lines in chat
            //These are Unix/LSL style end of lines, use \r\n in place of \n for
            //  Windows style end of line from web server.
            for(i=0;i<count;i++)
            {
                llOwnerSay(llList2String(lbody,i));
            }
        }
    }
}
--
Examples


ExampleNotice

**Joker Opus- Sensor Notice**

This is a script used to send a notice to anyone within the sensor range. Like an artificial linden notice.

///////////Created by Joker Opus, December///////////
//Revised by Strife Onizuka
default
{
    touch_start(integer num_detected)
    {
        llSensor("", NULL_KEY, AGENT, 96, PI);//This scans on touch
    }
    sensor(integer count)
    {
        integer i;
        for(i = 0; i < count; ++i)
        {
            llDialog(llDetectedKey(i), "Hi all, I am testing this!", ["Ok"], 12345); //This is dialog, it looks like a notice, and it says "Hi all, I am testing this!" and it has an [ok] for a button, indicating the notice look. and the OK gets rid of the pop up.
        }
    }
}

Q: Can I use this anywhere I want? So if i go to a sandbox, and I want to tell everyone something, I can just use this?

A: Yes, its sensor is limited though, so you can add alot around a parcel you own, and have them all activate when you tell the mother to using llsay on a channel or something.


ExampleScripts

See Examples.


exchangeHttpdb

Introduction

httpdb provides free, persistant, off-site storage for LSL Scripts via llHTTPRequest. You can store up to 250kb of data in the form of key/value pairs, and read or write the data from any script. You can share your data with other users, protect it with passwords, browse and manage your data on the web, and download backups. There is no need to register to use httpdb, you can just start saving and retrieving data at any time. http://w-hat.com/httpdb

Return to protocol exchange

libhttpdb.lsl
//----------------------------------------------------------------------------//
// httpdb constants                                                           //
//----------------------------------------------------------------------------//

// The full base url to use for httpdb requests.
string      HTTPDB_URL          = "http://w-hat.com/httpdb/";

//----------------------------------------------------------------------------//
// httpdb messages                                                            //
//----------------------------------------------------------------------------//

// If you want to use httpdb through link messages in another script, you should
// copy these constants. You then make requests with:
//   llMessageLinked( [link_number], [request_code], [value], [name] );
// ex:
//   llMessageLinked( LINK_SET, HTTPDB_SAVE, "some data", "log/example.log");

// Request codes. These are also used internally - do not remove them.
integer     HTTPDB_SAVE         = 1000;
integer     HTTPDB_LOAD         = 1100;
integer     HTTPDB_DELETE       = 1200;

// Response codes. If you don't want to use httpdb through link messages, you
// can remove these.
integer     HTTPDB_VALUE_LOADED = 5000;
integer     HTTPDB_FAILURE      = 5100;
integer     HTTPDB_SUCCESS      = 5200;

//----------------------------------------------------------------------------//
// httpdb variables                                                           //
//----------------------------------------------------------------------------//

// Strided [reqid, name, type] list of pending requests.
list        httpdb_reqids     = [];

// The amount of free space returned by last PUT/DELETE request.
integer     httpdb_free_space = -1;

//----------------------------------------------------------------------------//
// httpdb functions                                                           //
//----------------------------------------------------------------------------//

// Abstract httpdb request interface.
httpdb_request( integer type, string type_str, string name, string body ) {
    key reqid = llHTTPRequest(HTTPDB_URL + name, [HTTP_METHOD, type_str], body );
    if ( reqid == NULL_KEY )
        httpdb_failure( type, name, 0, "HTTP throttled" );
    else
        httpdb_reqids += [reqid, name, type];
}

// Save a value to httpdb.
httpdb_save( string name, string value ) {
    httpdb_request( HTTPDB_SAVE, "PUT", name, value );
}

/// Load a value from httpdb. The function httpdb_value_loaded() will be
/// called with the name and value when the request completes.
httpdb_load(string name) {
    httpdb_request( HTTPDB_LOAD, "GET", name, "" );
}

// Delete a value from httpdb.
httpdb_delete(string name) {
    httpdb_request( HTTPDB_DELETE, "DELETE", name, "" );
}

//----------------------------------------------------------------------------//
// httpdb "virtual" functions.                                                //
//----------------------------------------------------------------------------//
// If you don't want to put httpdb into a seperate script and use it through
// link messages, then you should replace the link message code with your own
// in these functions.

// Called when a value is successfully loaded.
httpdb_value_loaded(string name, string value) {

    llMessageLinked(LINK_SET, HTTPDB_VALUE_LOADED, value, name);

}

// Called when a request fails.
httpdb_failure( integer type, string name, integer status, string body) {

    // You can programatically handle errors here, or just report them.
    llOwnerSay( "[ERROR] httpdb returned status " + (string)status + " for " + (string)type + " on " + name );

    llMessageLinked(LINK_SET, HTTPDB_FAILURE, llList2CSV([type, status, body]), name );

}

// Called when a request was successful.
httpdb_success(integer type, string name, integer status, string body) {

    llMessageLinked(LINK_SET, HTTPDB_SUCCESS, llList2CSV([type, status, body]), name );

}

//----------------------------------------------------------------------------//
// httpdb events                                                              //
//----------------------------------------------------------------------------//

default {

    // httpdb link message handler. if you're not using httpdb in a seperate
    // script using link messages, you should remove this.
    link_message( integer sender, integer num, string str, key id ) {
        if ( num == HTTPDB_SAVE )
            httpdb_save( (string)id, str );
        else if ( num == HTTPDB_LOAD )
            httpdb_load( (string)id );
        else if ( num == HTTPDB_DELETE )
            httpdb_delete( (string)id );
    }

    // httpdb llHTTPRequest response handler. this should not be removed.
    // insert your own code at the bottom of the function.
    http_response( key reqid, integer status, list meta, string body ) {

        // See if it's an httpdb request
        integer httpdb_req_index = llListFindList( httpdb_reqids, [reqid] );
        if ( httpdb_req_index != -1 ) {
            // pull its info out the list
            string  name = llList2String( httpdb_reqids, httpdb_req_index+1 );
            integer type = llList2Integer(httpdb_reqids, httpdb_req_index+2 );

            // remove it from the list
            httpdb_reqids = llDeleteSubList( httpdb_reqids, httpdb_req_index, httpdb_req_index+2 );

            // only 2xx codes represent success
            if ( status < 200 || status >= 300 ) {
                httpdb_failure( type, name, status, body );
                return;
            }

            // if it's a load request, call httpdb_value_loaded
            if ( type == HTTPDB_LOAD )
                httpdb_value_loaded( name, body );
            // otherwise, the body should be the updated amount of free space
            else
                httpdb_free_space = (integer)body;

            httpdb_success( type, name, status, body );

            return;
        }

        // Not an httpdb request

        // ..add your own handlers here..

    }

}


exchangeLockGuardItem

LockGuard V2 Items

At the heart of the LockGuard System is the LockGuard V2 Script which is used to define your LockGuard Items. The LockGuard V2 Script is actually a script library dedicated to the linking, unlinking, and maintenance of the particle chains. Any item which contains the LockGuard V2 Script instantly becomes LockGuard compatible, and can then have its properties defined in the LockGuard V2 Config notecard which tells the LockGuard Item what its for, the behavior of its particle chain, etc.

If you have any questions, comments, or suggestions for LockGuard then send me, Lillani Lowell, a message and I will try my best to assist you.

Return to LockGuard

Return to Protocol Exchange

LockGuard Item Creation

There are two steps to creating a unique LockGuard Item.

Step 1. Copy the LockGuard V2 Script and LockGuard V2 Config notecard into the prim of the object where the chain will connect from.
Step 2. Edit the LockGuard V2 Config notecard, and change the ID [name] field.

LockGuard V2 Config

LockGuard can be used to make more than the cuffs which are included with the package. LockGuard can be used to make anything..... cuffs, collar leashes, fences, spider webs, riggings on ships, or anything that needs a particle chain linking from one object to another. But, because of the configurability of LockGuard, the one thing you have to do right is edit the LockGuard V2 Config notecard and give it an ID, which is really easy to do, but you still need to get it right!

Here's an example of a minimal LockGuard V2 Config notecard.....

ID leftwrist
ID wrists
ID allfour

..... this notecard allows the LockGuard V2 Script to configure itself with the ID of leftwrist. So, when someone makes a request from that avatar for the leftwrist item, then this is the LockGuard Item that will respond. If you wanted a right wrist, you would change the ID to rightwrist. If you wanted to create a floating sphere called George which links a particle chain then you'd use ID floatingspheregeorge. In this example, we also use ID wrists and ID allfour because according to the LockGuard Standard Tags they are associated, which allows for simple calling of multiple chains in one call. LockGuard is *unlimited* in the sense that you can create any kind of item you want to create simply by editing the LockGuard V2 Config notecard. For attachments, there is a standard list of common ID tags to use.

LockGuard V2 Standard ID Tags

LockGuard allows you to identify particle chains by ID, and you can specify any kind of ID you want, even multiple IDs in a single configuration file. Through these IDs you can then call a chain by its ID, or even multiple chains if they share the same ID. However.....

If you are making LockGuard-enabled attachments for avatars which will interact with other LockGuard devices (cuffs, collars, restraints, etc) then you should use the following ID tags which relate to your attachment. It is very important to use the proper tag, as failure to do so will result in your attachment not functioning as it should when a LockGuard device attempts to generate a chain from your attachment.

Here is the set of LockGuard V2 Standard ID tags which you should use when you are creating LockGuard-enabled attachments.

=========================
    Attachment Points
            ..... Head, Neck, Collar
=========================

ID collarfrontloop                  (the front loop of the collar, usually what leashes are attached to)
ID collarbackloop
ID collarleftloop
ID collarrightloop
ID leftearring
ID rightearring
ID nosering
ID leftnosering
ID rightnosering
ID upperlipring
ID lowerlipring
ID tonguering
ID gag
ID leftgag
ID rightgag
ID topheadharness
ID frontheadharness
ID backheadharness
ID leftheadharness
ID rightheadharness

=========================
    Attachment Points
            ..... Wrists, Ankles
=========================

ID leftwrist
ID rightwrist
ID leftankle
ID rightankle

    If you use leftwrist or rightwrist, also include:

ID wrists
ID allfour

    If you use leftankle or rightankle, also include:

ID ankles
ID allfour

=========================
    Attachment Points
            ..... Torso
=========================

ID harnessfrontloop
ID harnessbackloop
ID harnessleftloopfront
ID harnessrightloopfront
ID harnessleftloopback
ID harnessrightloopback
ID harnessleftshoulderloop
ID harnessrightshoulderloop
ID frontbeltloop
ID backbeltloop
ID leftbeltloop
ID rightbeltloop
ID bellyring
ID clitring
ID leftlabia
ID rightlabia
ID cockring
ID ballring
ID buttplug

=========================
    Attachment Points
            ..... Misc
=========================

ID leftupperarm
ID rightupperarm
ID leftupperthigh
ID rightupperthigh
ID leftknee
ID rightknee

    If you use leftupperarm or rightupperarm, also include:

ID arms

    If you use leftupperthigh or rightupperthigh, also include:

ID thighs

    If you use leftknee or rightknee, also include:

ID knees

Going into further detail about the LockGuard V2 Config notecard.....

id [tag]
texture [key]
size [#] [#]
life [#]
gravity [#]
speed [#]
color [#] [#] [#]

.....these are options you can use to define your particle chain with the LockGuard V2 Script. And they are defined in the same matter as the command line versions. Once loaded by the LockGuard Item Script, anything you define in the LockGuard V2 Config notecard becomes the item's default, and unless told otherwise through the commandline parse will use them as the defaults. If anything changes your particle chains through commands, the LockGuard V2 Script will restore the defaults you specified when it unlinks.

Command | Variable | Explaination
gravity | #grav | This command will change the effect gravity has on your particle chains.
life | #sec | Sending this command will tell LockGuard how long each particle chain link should live in seconds, or in other words, how fast it should move from origin to destination. For instance, a value of 1 will create a faster, tigher particle chain while a value of 5 would create a loose, slower particle chain. Chain life can be modified "on the fly" as needed.
speed | #min #max | Sending this command will tell LockGuard how fast each link of the particle chain should move, it directly relates to the life value above. If you leave out the #max value then all particles will flow at the speed of #min.
color | #r #g #b | Sending this command will change the color or tint of your particle chain. The values are red, green, blue, and each one is a value between 0 and 1, with 0 being dark, 0.5 being middle, and 1 being bright.
size | #x #y | Sending this command will tell LockGuard how large each particle chain link should be. The smaller the value, the smaller the particle links, and the less visible they are from a distance.
texture | UUID | If you want your furniture to change the default particle chain texture of the LockGuard Library then simply send this command and LockGuard will obediently swap out textures for you.

Return to LockGuard

Return to Protocol Exchange


exchangeLockGuardSource

LockGuard V2 Open Source

Here is a complete LockGuard V2 Script which implements the LockGuard V2 Protocol. This script is fully functional and well-commented. LockGuard V2 support is as simple as dropping this script into the prim you wish to generate particle chains from. The script is open source, so if you need to make any modifications for your own projects, feel free to do so..... however, while you are free to copy and modify this script to suit your own needs, I ask that you do not deviate from the LockGuard V2 Protocol.

Note: if your LockGuard anchor is to be used on an attachement (cuffs, collars, belts, etc), then it is better to add PSYS_PART_FOLLOW_SRC_MASK to nBitField in llLockGuardLink(): this will prevent the particles to trail behind the avatar when the latter moves...

Return to LockGuard

Return to Protocol Exchange

LockGuard V2 Script

//====================================================================================================
//  LockGuard V2 Script
//      ..... by Lillani Lowell
//
//      Special thanks (in alphabetical order) go to:
//
//          Tengu Tamabushi (testing/debugging/code support & rope texture)
//          Zi Ree (testing/debugging/code support & chain texture)
//
//              The chain and rope textures which LockGuard V2 uses are owned by Tengu and Zi.
//
//      And thanks to all the people who are using LockGuard!
//
//==================================================
//
// LockGuard V2 is a simple, powerful, programmable, and highly versatile particle chain
// link library. LockGuard V2 is a plug and play, multiple purple chain/rope/hose generator
// which can be used for fences, streamers, decorations, spiderwebs, and b&d related items.
//
// LockGuard chains are called by matching an avatar key with a particle chain ID tag, and
// by using this method particle chains can be called individually, in groups, or even all
// at once.
//
// Now.....
//
// I'd like to say, the way this source is written may seem a little odd to some people,
// but there *is* a rhyme and reason to my apparant madness, even
// if it doesn't seem entirely obvious at first glance. :)
//
// And, of course, code improvements are always welcome!
//
// Feel free to use this code in any derivitive works, a source mention would be
// nice, but not necessary. :)
//
// LockGuard Features:
//      -- One way communication, no need to haggle.
//      -- Plug and play functionality.
//      -- Built-in particle chain generator, configurable "on the fly".
//      -- 100% backwards compatibility with all previous furniture and devices.
//      -- Simplistic, furniture side scripting, LockGuard is your wheel, you don't have to reinvent it.
//      -- Call chains based on customizable ID tags, call multiple chains which share one ID.
//      -- Determine whether or not an item exists through a ping.

// LockGuard V2 Features:
//      -- Open source!
//      -- Multi-command parsing, include all your LockGuard commands in a single chatblock.
//      -- Multiple ID tags per item, call a chain individually, call it by a group tag, or all at once!
//      -- Determine whether or not a chain is linked from the item.
//      -- Ability to change channels for private projects (requires nPrivateProject=TRUE).
//      -- Ability to disable listen entirely, for "fire and forget" chains (requires nPrivateProject=TRUE).

//==================================================
//  program variables
//==================================================

        //
        // Changing nPrivateProject to TRUE will allow LockGuard V2 devices to change the LG channel
        // or even disable llListen entirely. nPrivateProject should *always* be false for items which
        // use non-static or linking with public items.
        //

    integer nPrivateProject = FALSE;

        //
        // Typical channel and handler.
        //

    integer         nChannel = -9119;
    integer         nHandle;

        //
        // Variables for sucking the data out of a notecard. See the default state.
        //

    string          fnLNCFilename = "LockGuard V2 Config";
    string          fnLNCFileData;
    list            fnLNCFileDataList;
    integer         fnLNCLine;
    key             fnLNCQueryID;

        //
        // Command line storage + parser count.
        //

    list            lCommandLine;
    integer         nParserCount;

        // Chat lines are converted into lists because
        // it is assumed the internal compiled FindList functions will work faster than
        // breaking it down into substrings and then having a bunch of intepreted if and thens
        // running amok on a virtual machine comparing them.
        //
        // Known Issue
        //
        // The only potential issue with the new V2 Script is that it now supports multiple LockGuard
        // commands in a single command block. This was included in the overhaul of the parser which old
        // LockGuard does not support. This will *not* affect LockGuard devices or furniture which are
        // already on the market, as they are still compatible with the LockGuard protocol.
        // Backwards compatibility has been thoroughly tested to make sure old LockGuard furniture/devices
        // operate as they should with the new LockGuard V2 Script.
        //
        // The only time this issue will present a problem is when new V2 furniture or devices use the
        // new multi-command format and try to communicate with the old LockGuard Item Script. The old
        // LockGuard Item Script will only recognize the first command, and ignore the rest. This is
        // easily solved by dropping in the LockGuard V2 Script and LockGuard V2 Config notecard in place
        // of the old LockGuard Item Script and LockGuard Item Config notecard into your item, etc.
        //
        // Again, this *does not* affect the use of current /furniture/ or /devices/.
        //
        // I had debated on adding the new multi-command parser because of this potential issue, but at
        // the last minute decided being able to configure AND link a chain in one single call
        // which significantly reduces LockGuard channel use and increases overall performance
        // outweighed the minor inconvenience of taking a moment to change out LockGuards scripts in items.
        //
        // Evolution is not without its little bumps in the road, but that which does not evolve gets left
        // behind.
        //

//==================================================
//  lockguard variables
//==================================================

        //
        // 0 - id
        // 1 - link
        // 2 - unlink
        // 3 - ping
        // 4 - free
        // 5 - texture
        // 6 - size
        // 7 - life
        // 8 - speed
        // 9 - gravity
        // 10 - color
        // 11 - unlisten    << only works when nPrivateProject = TRUE
        // 12 - channel     << only works when nPrivateProject = TRUE
        //
        // Do not modify or change the order of lLockGuardCommands unless you KNOW what you're doing!
        //

    list            lLockGuardCommands = [ "id", "link", "unlink", "ping", "free", "texture", "size", "life", "speed", "gravity", "color", "unlisten", "channel" ];

        //
        // These textures were granted by Zi and Tengu (see credits at top) for use in LockGuard V2.
        // Although they are being distributed with V2, they are still owned by their respective creators.
        //

    key             kDefaultChain = "40809979-b6be-2b42-e915-254ccd8d9a08";
    key             kDefaultRope = "bc586d76-c5b9-de10-5b66-e8840f175e0d";

    list            lLockGuardID = [];

    key             kTarget;

    //
    // Default particle chain values, if they're not loaded from the configuration notecard
    // these are what they will be. Don't change the defaults here, change them in the LockGuard V2
    // Config notecard instead.
    //

    key             kTextureDefault = "40809979-b6be-2b42-e915-254ccd8d9a08";
    float           fSizeXDefault = 0.07;
    float           fSizeYDefault = 0.07;
    float           fLifeDefault = 1;
    float           fGravityDefault = 0.3;
    float           fMinSpeedDefault = 0.005; // Not really used, life generally determines speed.
    float           fMaxSpeedDefault = 0.005; // Not really used, life generally determines speed.
    float           fRedDefault = 1;
    float           fGreenDefault = 1;
    float           fBlueDefault = 1;

    //
    // Particle chain values the program will actually use. Don't fill them in,
    // they'll only get written over later.
    //

    key             kTexture;
    float           fSizeX;
    float           fSizeY;
    float           fLife;
    float           fGravity;
    float           fMinSpeed;
    float           fMaxSpeed;
    float           fRed;
    float           fGreen;
    float           fBlue;

    integer         nLinked = FALSE;

//==================================================
//  filter
//==================================================

integer llLockGuardItemCheck()
{

    //
    // LockGuard will do the checks to ensure the command line meets the following critera:
    //      1. It's meant for LockGuard.
    //      2. It's meant for the avatar who owns the item this script is in.
    //      3. It's meant for all items OR.....
    //      4. It's meant for a corresponding ID tag which has been given to this item.
    //
    // While ALL is still supported as a tag for backwards compatibility with some old devices, it should
    // *never* be used in new devices.
    //

    if( llList2String( lCommandLine, 0 ) != "lockguard" )
        return FALSE;
    if( llList2String( lCommandLine, 1 ) != (string)llGetOwner() )
        return FALSE;
    if( llList2String( lCommandLine, 2 ) == "all" )
        return TRUE;
    if( llListFindList( lLockGuardID, llList2List( lCommandLine, 2, 2 ) ) == -1 )
        return FALSE;

    return TRUE;

}

//==================================================
//  particle chain
//==================================================

llRestoreLockGuardDefaults()
{

    //
    // Restore the chain defaults, LockGuard does this when the script first starts (after
    // loading from the notecard), when attached to an avatar, or when an unlink command
    // is issued.
    //

    kTexture = kTextureDefault;
    fSizeX = fSizeXDefault;
    fSizeY = fSizeYDefault;
    fLife = fLifeDefault;
    fGravity = fGravityDefault;
    fMinSpeed = fMinSpeedDefault;
    fMaxSpeed = fMaxSpeedDefault;
    fRed = fRedDefault;
    fGreen = fGreenDefault;
    fBlue = fBlueDefault;

}

llLockGuardLink( integer fn_nRelinking )
{

    //
    // The simple secret of a particle chain revealed! :)
    //

    integer nBitField = PSYS_PART_TARGET_POS_MASK|PSYS_PART_FOLLOW_VELOCITY_MASK;

    llParticleSystem( [] );

    if( fn_nRelinking == FALSE )
        kTarget = llList2Key( lCommandLine, ++nParserCount );

    if( fGravity == 0 )
        nBitField = nBitField|PSYS_PART_TARGET_LINEAR_MASK;

    llParticleSystem( [ PSYS_PART_MAX_AGE, fLife, PSYS_PART_FLAGS, nBitField, PSYS_PART_START_COLOR, <fRed, fGreen, fBlue>, PSYS_PART_END_COLOR, <fRed, fGreen, fBlue>, PSYS_PART_START_SCALE, <fSizeX, fSizeY, 1.00000>, PSYS_PART_END_SCALE, <fSizeX, fSizeY, 1.00000>, PSYS_SRC_PATTERN, 1, PSYS_SRC_BURST_RATE, 0.000000, PSYS_SRC_ACCEL, <0.00000, 0.00000, (fGravity*-1)>, PSYS_SRC_BURST_PART_COUNT, 10, PSYS_SRC_BURST_RADIUS, 0.000000, PSYS_SRC_BURST_SPEED_MIN, fMinSpeed, PSYS_SRC_BURST_SPEED_MAX, fMaxSpeed, PSYS_SRC_INNERANGLE, 0.000000, PSYS_SRC_OUTERANGLE, 0.000000, PSYS_SRC_OMEGA, <0.00000, 0.00000, 0.00000>, PSYS_SRC_MAX_AGE, 0.000000, PSYS_PART_START_ALPHA, 1.000000, PSYS_PART_END_ALPHA, 1.000000, PSYS_SRC_TARGET_KEY, kTarget, PSYS_SRC_TEXTURE, kTexture ] );

    nLinked = TRUE;

}

llLockGuardUnlink()
{

    //
    // Unlink the particle chain, restore the item's defaults, and move along.
    //

    llParticleSystem( [] );

    llRestoreLockGuardDefaults();

    nLinked = FALSE;

    kTarget = NULL_KEY;

}

llLockGuardTexture()
{

    //
    // Change the texture.
    //

    kTexture = llList2Key( lCommandLine, ++nParserCount );

    if( kTexture == "chain" )
        kTexture = kDefaultChain;
    if( kTexture == "rope" )
        kTexture = kDefaultRope;

    if( nLinked )
        llLockGuardLink( TRUE );

}

llLockGuardSize()
{

    //
    // Change the size.
    //

    fSizeX = llList2Float( lCommandLine, ++nParserCount );
    fSizeY = llList2Float( lCommandLine, ++nParserCount );

    if( nLinked )
        llLockGuardLink( TRUE );

}

llLockGuardLife()
{

    //
    // Change the life.
    //

    fLife = llList2Float( lCommandLine, ++nParserCount );

    if( nLinked )
        llLockGuardLink( TRUE );

}

llLockGuardSpeed()
{

    //
    // Change the speed.
    //

    fMinSpeed = llList2Float( lCommandLine, ++nParserCount );
    fMaxSpeed = llList2Float( lCommandLine, ++nParserCount );

    if( nLinked )
        llLockGuardLink( TRUE );

}

llLockGuardGravity()
{

    //
    // Change the amount of gravity.
    //

    fGravity = llList2Float( lCommandLine, ++nParserCount );

    if( nLinked )
        llLockGuardLink( TRUE );

}

llLockGuardColor()
{

    //
    // Change the color/tint.
    //

    fRed = llList2Float( lCommandLine, ++nParserCount );
    fGreen = llList2Float( lCommandLine, ++nParserCount );
    fBlue = llList2Float( lCommandLine, ++nParserCount );

    if( nLinked )
        llLockGuardLink( TRUE );

}

//==================================================
//  channel
//==================================================

llLockGuardUnlisten()
{

    //
    // Kill the listener. This command will not work unless nPrivateProject == TRUE.
    //

    llListenRemove( nHandle );

}

llLockGuardChannelChange()
{

    //
    // Swap channels. This command will not work unless nPrivateProject == TRUE.
    //

    llListenRemove( nHandle );

    nChannel = llList2Integer( lCommandLine, ++nParserCount );
    nHandle = llListen( nChannel, "", NULL_KEY, "" );

}

//==================================================
//  obedience
//==================================================

llLockGuardSetID()
{

    //
    // Assign a new ID to the item, an item can have multiple IDs.
    //

    nParserCount++;
    lLockGuardID += llList2List( lCommandLine, nParserCount, nParserCount );

}

llLockGuardPing()
{

    //
    // Do we exist?
    //

    llWhisper( nChannel, "lockguard " + (string)llGetOwner() + " " +  llList2String( lLockGuardID, 0 ) + " okay" );

}

llLockGuardFree()
{

    //
    // Are we free?
    //

    if( nLinked )
        llWhisper( nChannel, "lockguard " + (string)llGetOwner() + " " +  llList2String( lLockGuardID, 0 ) + " no" );
    else
        llWhisper( nChannel, "lockguard " + (string)llGetOwner() + " " +  llList2String( lLockGuardID, 0 ) + " yes" );

}

llLockGuardObey( integer fn_nBase )
{

    integer nCommands = llGetListLength( lCommandLine );
    integer nReturn;

    //
    // Let's parse! The script will poll through the commandline and compare it to any known commands
    // provided in the command list defined under the variables with lLockGuardCommands. If it finds a
    // match it'll call the command based on its number.
    //
    // In theory, searching commands this way using compiled/native functions should be faster than using
    // multiple functions to break the commandline down into substrings, storing the substrings, and then
    // comparing them on a virtual machine. Maybe someone can confirm/deny this.
    //
    // When fn_nBase == 3, it is being called from the listen block.
    // When fn_nBase == 0, it is being called from the notecard reader.
    //

    nParserCount = fn_nBase;

    do
    {

        nReturn = llListFindList( lLockGuardCommands, llList2List( lCommandLine, nParserCount, nParserCount ) );

        if( fn_nBase == 3 )
        {

            //
            // These commands can only be called via chat command blocks.
            //

            if( nReturn == 1 )
                llLockGuardLink( FALSE );
            else if( nReturn == 2 )
                llLockGuardUnlink();
            if( nReturn == 3 ) llLockGuardPing();
            if( nReturn == 4 ) llLockGuardFree();

        }

        //
        // These commands can be called anywhere, either by setting defaults through the notecards
        // or through chatblocks.
        //

        if( nReturn == 5 ) llLockGuardTexture();
        if( nReturn == 6 ) llLockGuardSize();
        if( nReturn == 7 ) llLockGuardLife();
        if( nReturn == 8 ) llLockGuardSpeed();
        if( nReturn == 9 ) llLockGuardGravity();
        if( nReturn == 10 ) llLockGuardColor();

        if( nPrivateProject == TRUE )
        {

            //
            // LockGuard willonly allow channel changing and unlistening if nPrivateProject == TRUE.
            //

            if( nReturn == 11 ) llLockGuardUnlisten();
            if( nReturn == 12 ) llLockGuardChannelChange();

        }

        if( fn_nBase == 0 )
        {

            //
            // These commands can only be called via the notecard reader.
            //

            if( nReturn == 0 ) llLockGuardSetID();

        }

        nParserCount++;

    } while( nParserCount < nCommands );

}

//==================================================
//  default
//==================================================

default
{

    //
    // The standard, "let's read the notecard" function.
    //

    state_entry()
    {

        fnLNCLine = 0;

        fnLNCQueryID = llGetNotecardLine( fnLNCFilename, fnLNCLine );

    }

    dataserver( key query_id, string data )
    {

        integer i;

        if( query_id == fnLNCQueryID )
        {

            if( data != EOF )
            {

                if( fnLNCLine > 0 )
                {

                    fnLNCFileData += " ";

                } else
                {

                    fnLNCFileDataList = [];

                }

                fnLNCFileDataList += [ data ];

                fnLNCLine++;

                fnLNCQueryID = llGetNotecardLine( fnLNCFilename, fnLNCLine );

            } else
            {

                do
                {

                    lCommandLine = llParseString2List( llToLower( llList2String( fnLNCFileDataList, i ) ), [ " " ], [] );

                    llLockGuardObey( 0 );

                    i++;

                } while( i < llGetListLength( fnLNCFileDataList ) );

                fnLNCFileDataList = [];

                state lockguardGo;

            }

        }

    }

    changed( integer change )
    {

        //
        // If anything in our inventory changes, reset.
        //

        if( change == CHANGED_INVENTORY )
            llResetScript();

    }

}

//==================================================
//  lockguardGo
//==================================================

state lockguardGo
{

    on_rez( integer num )
    {

        //
        // Kill any lingering chains and do a complete script reset during a new rez.
        //

        llLockGuardUnlink();

        llResetScript();

    }

    state_entry()
    {

        //
        // Load up the default chain values and listen up.
        //

        llRestoreLockGuardDefaults();

        nHandle = llListen( nChannel, "", NULL_KEY, "" );

    }

    listen( integer channel, string name, key id, string message )
    {

        //
        // Parse the command line.
        //

        lCommandLine = llParseString2List( llToLower( message ), [ " " ], [] );

        if( !llLockGuardItemCheck() )
            return;

        llLockGuardObey( 3 );

    }

    changed( integer change )
    {

        //
        // If anything in our inventory changes, reset.
        //

        if( change == CHANGED_INVENTORY )
            llResetScript();

    }

}


exchangeTLMLGenerator

This script generates TLML code from the prim it is running in. This script doesn't support prim torture, and due to limitations in SL it cannot reproduce hover text or client-side rotations that have been added to the prim with llTargetOmega.

// TLML Exporter v0.30, drop in a page's prim to get the corresponding TLML code

// ===How to get unicode characters in your llSetText_text===
// "\\uxxxx"     where xxxx are 4 hex characters, unicode character 0000xxxx
// "\\Uxxxxxxxx" where xxxxxxxx are 8 hex characters, unicode character xxxxxxxx
// "\\n"         new line do not use "\n"
// "\\\\"         \
// "\\\""         "
// "\\t"         same as "\t", which you may use
// ""

vector offset = <0.0,0.0,1.0>;

string llSetText_text     = "";
vector llSetText_color    = <1.0,1.0,1.0>;
float  llSetText_alpha    = 1.0;

vector llTargetOmega_axis       = ZERO_VECTOR;
float  llTargetOmega_spinrate   = 0.0;
float  llTargetOmega_gain       = 0.0;

//ANIM_ON    ==    0x01
//LOOP      ==    0x02
//REVERSE     ==    0x04
//PING_PONG ==    0x08
//SMOOTH     ==    0x10
//ROTATE      ==    0x20
//SCALE      ==    0x40

integer llSetTextureAnim_mode = 0;
integer llSetTextureAnim_face = ALL_SIDES;
integer llSetTextureAnim_x_frames = 2;
integer llSetTextureAnim_y_frames = 2;
float     llSetTextureAnim_start_frame = 0;
float     llSetTextureAnim_end_frame = 3;
float   llSetTextureAnim_rate = 0.1;

string TLML_URL                 = "";

list llParticleSystem_list      = [];

setParticles()
{
    llParticleSystem_list += [];
}

//////////////////////////////////////////////////////////////
//               DO NOT MODIFY ANYTHING BELOW               //
//////////////////////////////////////////////////////////////
//This script is a nightmare, only the brave should venture deeper
//adding support for new features is pretty easy
//but don't touch the structure of the script
//because of the complexities of TLML, the data layout is complex
//the *right* was to do this would be with 3 passes
//1) determin the mask
//2) parse the masks and filling in the data
//Instead we do it with basicly a single pass
//Then as the masks become aparent we insert them into the stream.
//This saves alot of time with minimal expense
//Unfortunately it's horribly complex and incredibly hard to read
//To grok this script in it's entirety is to be avoided.

//{

//}
//{

//}
//{
string byte2hex(integer x)
{//Helper function for use with unicode characters.
    integer x0  = (x & 0xF);
    return llGetSubString(hexc, x0 = ((x >> 4) & 0xF), x0) + llGetSubString(hexc, x0, x0);
}

string Unescape(string a)
{
    string  b = a;
    integer c = -1;
    integer d;
    integer e;
    integer f = 0;
    string g;
    while(d = llSubStringIndex(b, "\\") + 1)
    {
        g = llGetSubString(b,d,d);
        c += d;

        if((g == "\"") || (g == "\\"))
            a = llDeleteSubString(a,c,c);
        else if(g == "n")
            a = llInsertString(llDeleteSubString(a,c,c+1), c, "\n");
        else if(g == "t")
            a = llInsertString(llDeleteSubString(a,c,c+1), c, "\t");
        else if(g == "r")//rx[11,22,33,44,55,66,77,88,99,AA,BB,CC,DD,EE,FF]
        {
            g = "";
            if(d+(e = (integer)("0x"+llGetSubString(b,d+1,d+1)) * 2)+1 >= (f = llStringLength(b)))
                e = (f - d - 2) & -2;
            if((f = e))//this may look like a mistake, it's not $[E20002]
            {
                do
                    g = "%"+llGetSubString(b,d + e,d + e + 1) + g;
                while((e-=2) > 0);
            }
            a = llInsertString(llDeleteSubString(a,c, c + 2 + f),c, g = llUnescapeURL(g));
            c += llStringLength(g);//add to c so we don't accidentily unescape result
        }
        else if(g == "u" || (e = (g == "U")))// \uXXXX or  \UXXXXXXXX
        {
            a = llDeleteSubString(a, c, c + 5 + e *= 4);
            if(0 < e = (integer)("0x"+llGetSubString(b,d +1, d +4 + e)))
            {
                if (e >= 0x4000000)
                    f = 5;
                else if (e >= 0x200000)
                    f = 4;
                else if (e >= 0x10000)
                    f = 3;
                else if (e >= 0x800)
                    f = 2;
                else if (e >= 0x80)
                    f = 1;
                g = "%" + byte2hex((e >> (6 * f)) | ((0x3F80 >> f) * (0 != f)));
                while(f)
                    g += "%" + byte2hex((((e >> (6 * --f)) | 0x80) & 0xBF));
                a = llInsertString(a, c++, llUnescapeURL(g));
            }
        }
        b = llDeleteSubString(a,0,c);
    }
    return a;
}

//alternative licensensing exculsively granted for this script, acknowlegment is not required for use.
string Float2Hex(float a)
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
    if(a != 0)
    {
        float b = llFabs(a);
        string f = "";
        integer c = llFloor(llLog(b) / 0.69314718055994530941723212145818);//floor(log2(b))
        integer d = (integer)((b / llPow(2.0,c)) * 0x1000000);//shift up into integer range
        c -= 24;//the extra c used to make it an integer
        while(!(d & 0xf))
        {//strip extra zeros off before converting or they break "p"
            d = d >> 4;
            c+=4;
        }
        do
            f = llGetSubString(hexc,15&d,15&d) + f;
        while(d = d >> 4);
        if(a < 0)
            return "-0x" + f + "p"+(string)c;
        return "0x" + f + "p"+(string)c;
    }
    return "0";//zero would screw up the log.
}

string flo(float a)
{
    string b = (string)a;
    while(llGetSubString(b,-1,-1) == "0")
        b=llDeleteSubString(b,-1,-1);
    if(llGetSubString(b,-1,-1) == ".")
        return llDeleteSubString(b,-1,-1);
    if(llGetSubString(b,(a<0),(a<0)+1)=="0.")
        return llDeleteSubString(b,(a<0),(a<0));
    return b;
}

string vec(vector a)
{
    if(a == ZERO_VECTOR) return "";
    return "<"+flo(a.x)+","+flo(a.y)+","+flo(a.z)+">";
}

string vecF2H(vector a)
{
    if(a == ZERO_VECTOR) return "";
    list b = ["<",flo(a.x),",",flo(a.y),",",flo(a.z),">"];
    vector c = (vector)((string)b);
    if(c.x != a.x)
        b = llListReplaceList(b, [Float2Hex(a.x)], 1, 1);
    if(c.y != a.y)
        b = llListReplaceList(b, [Float2Hex(a.y)], 3, 3);
    if(c.z != a.z)
        b = llListReplaceList(b, [Float2Hex(a.z)], 5, 5);
    return (string)b;
}

string rot(rotation a)
{
    if(a == ZERO_ROTATION) return "";
    list b = ["<",flo(a.x),",",flo(a.y),",",flo(a.z),",",flo(a.s),">"];
    rotation c = (rotation)((string)b);
    if(c.x != a.x)
        b = llListReplaceList(b, [Float2Hex(a.x)], 1, 1);
    if(c.y != a.y)
        b = llListReplaceList(b, [Float2Hex(a.y)], 3, 3);
    if(c.z != a.z)
        b = llListReplaceList(b, [Float2Hex(a.z)], 5, 5);
    if(c.s != a.s)
        b = llListReplaceList(b, [Float2Hex(a.s)], 7, 7);
    return (string)b;
}

string int(integer a)
{
    if(a == 0) return "";
    return (string)a;
}

string TightListDump(list a, string b)
{
    string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) == -1)
            jump end;
    integer d = -llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b+(string)(a = []));
}

string TightListTypeDump(list a, string b) {
    b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ";
    string c = (string)a;
    integer d = 0;
    do
        if(1+llSubStringIndex(c,llGetSubString(b,d,d)))
            b = llDeleteSubString(b,d,d);
        else
            ++d;
    while(d<6);
    b = " " + c = llGetSubString(b,0,5);
    integer e;
    string f;
    if((d = -llGetListLength(a)))
    {
        do
        {
            e = llGetListEntryType(a,d);

            if((e > 0 && e < 3) || e > 4) f = (string)lis(llList2List(a,d,d));
            else

                f = llList2String(a,d);
            c+= llGetSubString(b,e,e) + f;
        }while(++d);
    }
    return c;
}

list lis(list a)
{
    integer b = -llGetListLength(a) - 1;
    list c;
    integer d;
    while(++b)
    {
        if((d = llGetListEntryType(a,b)) == TYPE_FLOAT)
        {
            float e = llList2Float(a,b);
            if(e != 0.0)
                c += flo(e);
            else
                c += "";
        }
        else if(d == TYPE_VECTOR)
            c += vec(llList2Vector(a,b));
        else if(d == TYPE_ROTATION)
            c += rot(llList2Rot(a,b));
        else if(d == TYPE_INTEGER)
            c += int(llList2Integer(a,b));
        else
            c += llList2String(a,b);
    }
    return c;
}

string hex(integer x)
{
    integer x0 = x & 0xF;
    string res = llGetSubString(hexc, x0, x0);
    x = (x >> 4) & 0x0FFFFFFF; //otherwise we get infinite loop on negatives.
    while( x != 0 )
    {
        x0 = x & 0xF;
        res = llGetSubString(hexc, x0, x0) + res;
        x = x >> 4;
    }
    return res;
}

string hexc="0123456789ABCDEF";
//}
add(string value, integer mask)
{
    if(llStringLength(llDumpList2String(header+params," ")+value) > 247)
    {
        store();
        if(mode & param_mask)
            mode = mode & attribute_mask;
    }
    mode = mode | mask;
    params += value;
}
store()
{
    break();
    if(mode & param_mask)
    {
        recycle_mask = recycle_mask & ~default_mask;
        ++cc;
    }
    commands += TightListDump(llDeleteSubList(params,-1,-1),(string)(params = []));
    if(llGetListLength(commands) == 1)
        header = llListReplaceList(header,[""],1,1);
    params = header + [sep];
}
break()
{
    list s = [sep];
    integer b = llListFindList(params, s);
    integer a;
    integer k;

    if(llList2String(params,-1) != sep)//it's possible we might be on a boarder already.
        params += mode;
    else
        params = llDeleteSubList(params,-1,-1);

    if(b + 1)
    {
        list r = [hex(recycle_mask)];
        string p = hex(a = recycle_mask & ~default_mask);
        if((recycle_mask && !cc) || (a && cc))
        {
            if(cc)
                r = [p];
            k = config_mask;
        }
        else
            r = [];
        while((b = 1 + llListFindList(llDeleteSubList(params + s, 0, a = b),s)))
        {
            b += a;
            params = llListReplaceList(llDeleteSubList(params,b - 1, b),[hex(llList2Integer(params, b - 1) | k)] + r, a, a);
            if(k)
                r = [p];
        }
    }
//    llOwnerSay("a"+(string)recycle_mask+TightListDump(params,""));
    params += sep;
}
burn()
{
    integer a = -llGetListLength(commands);
    integer k;
    string m = hex(recycle_mask);
    string p = hex(recycle_mask & ~default_mask);
    string n;
    integer j = llStringLength(sep) - 1;
    if(a)
    {
        do
        {
            if(1 + k = llSubStringIndex(n = llList2String(commands,a),sep))
            {
                do
                    n = llInsertString(llDeleteSubString(n, k, k+j),k,m);
                while(1 + k = llSubStringIndex(n,sep));
                commands = llListReplaceList(commands, [n], a, a);
            }
//            llOwnerSay("b"+(string)recycle_mask+n);
            m = p;
        }
        while(++a);
    }
    cc = 0;
}
//{
theFace(integer f)
{
    mode = (((multiplefaces >> 32) | f) & 0xF);

    if(multiplefaces & 0x40)
        mode = mode | 0x40 |
        (0x80 * (llList2Integer(llGetPrimitiveParams([PRIM_FULLBRIGHT, f]), 0) !=0));

    string t_s;
    if(multiplefaces & 0x100)
    {
        if((t_s = llGetTexture(f)) == "5748decc-f629-461c-9a36-a35a221fe21f")
            recycle_mask = recycle_mask | 0x1;
        else
            add(t_s, 0x100);
    }

    vector t_v;
    if(multiplefaces & 0x200)
    {
        if ((t_v = llGetColor(f)) != <1.0, 1.0, 1.0>)
            add(vec(t_v), 0x200);
        else
            recycle_mask = recycle_mask | 0x2;
    }

    float t_f;
    if(multiplefaces & 0x400)
    {
        if  ((t_f = llGetAlpha(f)) != 1.0)
            add(flo(t_f), 0x400);
        else
            recycle_mask = recycle_mask | 0x4;
    }

    if(multiplefaces & 0x800)
    {
        if ((t_v = llGetTextureScale(f)) != <1.0, 1.0, 0.0>)
            add(vec(t_v), 0x800);
        else
            recycle_mask = recycle_mask | 0x8;
    }

    if(multiplefaces & 0x1000)
    {
        if ((t_v = llGetTextureOffset(f)) != ZERO_VECTOR)
            add(vec(t_v), 0x1000);
        else
            recycle_mask = recycle_mask | 0x10;
    }

    if(multiplefaces & 0x2000)
    {
        if ((t_f = llGetTextureRot(f)) != 0.0)
            add(flo(t_f), 0x2000);
        else
            recycle_mask = recycle_mask | 0x20;
    }

    if(multiplefaces & (0x4000 | 0x8000))
    {
        list t_l = llGetPrimitiveParams([PRIM_BUMP_SHINY, f]);
        integer t_i;
        if(multiplefaces & 0x4000)
        {
            if ((t_i = llList2Integer(t_l, 0)) != PRIM_SHINY_NONE)
                add((string)t_i, 0x4000);
            else
                recycle_mask = recycle_mask | 0x40;
        }

        if(multiplefaces & 0x8000)
        {
            if ((t_i = llList2Integer(t_l, 1)) != PRIM_BUMP_NONE)
                add((string)t_i, 0x8000);
            else
                recycle_mask = recycle_mask | 0x80;
        }
    }
}

checkFaces()
{
    integer max = llGetNumberOfSides();
    multiplefaces = FALSE;
    string texture = llGetTexture(0);
    vector texture_scale = llGetTextureScale(0);
    vector texture_offset = llGetTextureOffset(0);
    float texture_rot = llGetTextureRot(0);

    vector color = llGetColor(0);
    float alpha = llGetAlpha(0);
    list fullbrights = llGetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES]);
    integer fullbright = llList2Integer(fullbrights, 0);
    list bump_shiny = llGetPrimitiveParams([PRIM_BUMP_SHINY, ALL_SIDES]);
    integer bump = llList2Integer(bump_shiny,1);
    integer shiny = llList2Integer(bump_shiny,0);

    integer i = 1;
    integer m = 0x40 | 0x100 | 0x200 | 0x400 | 0x800 |
                0x1000 | 0x2000 | 0x4000 | 0x8000;

    for (; i<max && multiplefaces != m; ++i)
    {
        if (llList2Integer(fullbrights, i) != fullbright)
            multiplefaces = multiplefaces | 0x40;

        if (llGetTexture(i) != texture)
            multiplefaces = multiplefaces | 0x100;

        if (llGetColor(i) != color)
            multiplefaces = multiplefaces | 0x200;

        if (llGetAlpha(i) != alpha)
            multiplefaces = multiplefaces | 0x400;

        if (llGetTextureScale(i) != texture_scale)
            multiplefaces = multiplefaces | 0x800;

        if (llGetTextureOffset(i) != texture_offset)
            multiplefaces = multiplefaces | 0x1000;

        if (llGetTextureRot(i) != texture_rot)
            multiplefaces = multiplefaces | 0x2000;

        if (llList2Integer(bump_shiny, i* 2 + 1) != bump)
            multiplefaces = multiplefaces | 0x4000;

        if (llList2Integer(bump_shiny, i* 2) != shiny)
            multiplefaces = multiplefaces | 0x8000;
    }
    if(multiplefaces != m)
    {
        mode = mode | 0x1;
        multiplefaces = ~(i = multiplefaces);
        theFace(0);
        multiplefaces = i;
    }
}

checkPrim()
{
    list type = llGetPrimitiveParams([PRIM_TYPE]);
    if (llList2Integer(type, 0) == 0)
        if (llList2Integer(type, 1) == 0)
            if (llList2Vector(type, 2) == <0.0, 1.0, 0.0>)
                if (llList2Float(type, 3) == 0.0)
                    if (llList2Vector(type, 4) == ZERO_VECTOR)
                        if (llList2Vector(type, 5) == <1.0, 1.0, 0.0>)
                            if (llList2Vector(type, 6) == ZERO_VECTOR)
                            {
                                recycle_mask = recycle_mask | 0x1;
                                return;
                            }
    add(TightListDump(lis(type),"*"), 0x4);
}
//}
string sep;

integer mode;

integer multiplefaces;

list params;
list header;

integer recycle_mask;

list commands;
integer cc;

integer default_mask;
integer config_mask;
integer param_mask;
integer attribute_mask;

default
{
    state_entry()
    {
        setParticles();
        llSetText(Unescape(llSetText_text), llSetText_color, llSetText_alpha);
        llTargetOmega(llTargetOmega_axis, llTargetOmega_spinrate, llTargetOmega_gain);
        llParticleSystem(llParticleSystem_list);
        llSetTextureAnim(llSetTextureAnim_mode, llSetTextureAnim_face, llSetTextureAnim_x_frames, llSetTextureAnim_y_frames,
                                llSetTextureAnim_start_frame,llSetTextureAnim_end_frame,llSetTextureAnim_rate);

        llOwnerSay("--------------------------------------");

        sep = llUnescapeURL("%01");
//        sep = "        ";

        if((llGetObjectPermMask(MASK_OWNER) & 0x0000E000) != 0x0000E000)
        {//If you remove this check the script will not function any better.
        //Many of the functions used to gather information do the same permissions check internaly
        //Those functions will cause error messages and an invalid TLML stream.
        //YOU HAVE BEEN WARNED.
            llOwnerSay("You cannot clone an object you do not have full permission on");
            return;
        }

        if(TLML_URL == "")
            TLML_URL = "-"+llGetObjectDesc();

        header = [llGetLinkNumber(), TLML_URL];

        params = header + [sep];
        default_mask = 0xF;
        param_mask = 0xffffFFFC;
        attribute_mask = 0x0;
        config_mask = 0x1;
//{
        checkPrim();

        add(vec(llGetScale()), 0x10);

        if (llGetLinkNumber() >= 2)// || rot_offset != ZERO_ROTATION)
        {
            vector pos = offset + (llGetLocalPos() );//* rot_offset);
            if(pos != ZERO_VECTOR)
                add(vecF2H(pos), 0x20);

            rotation local = llGetLocalRot();// * rot_offset; // or is it rot_offset * llGetLocalRot(); ?
            if (local != ZERO_ROTATION)
                add(rot(llGetLocalRot()), 0x80);
            else
                recycle_mask = recycle_mask | 0x2;
        }
        else
        {
            recycle_mask = recycle_mask | 0x2;
            if(offset != ZERO_VECTOR)
                add(vecF2H(offset), 0x20);
        }

        if(llSetText_text != "")
        {
            add(llSetText_text, 0x100);
            if(llSetText_color != <1.0,1.0,1.0> || llSetText_alpha != 1.0)
                add(rot(<llSetText_color.x, llSetText_color.y, llSetText_color.z, llSetText_alpha>), 0x200);
            else
                recycle_mask = recycle_mask | 0x8;
        }
        else
            recycle_mask = recycle_mask | 0x4 | 0x8;

        if(llParticleSystem_list != [])
        {
            add(TightListTypeDump(llParticleSystem_list,"*@#$%^&"), 0x400);
            recycle_mask = recycle_mask | 0x10;
        }

        if(llTargetOmega_axis != ZERO_VECTOR || llTargetOmega_spinrate != 0.0 || llTargetOmega_gain != 0.0)// $[E20011]
        {
            add(TightListDump([vec(llTargetOmega_axis),flo(llTargetOmega_spinrate),flo(llTargetOmega_gain)],"*"), 0x800);
            recycle_mask = recycle_mask | 0x20;
        }

        if(llSetTextureAnim_mode)
        {
            add(TightListDump([llSetTextureAnim_face, llSetTextureAnim_mode, llSetTextureAnim_x_frames, llSetTextureAnim_y_frames,
                    flo(llSetTextureAnim_start_frame),flo(llSetTextureAnim_end_frame),flo(llSetTextureAnim_rate)],"*"), 0x1000);
            recycle_mask = recycle_mask | 0x40;
        }
//}
        break();
        burn();
        header += "-";
        default_mask = 0xFF;
        recycle_mask = 0;
        param_mask = 0xffffFFF0;
        config_mask = 0x10;
        attribute_mask = 0x0CF;
        checkFaces();

        integer t = llGetNumberOfSides();
        integer c;
        if(multiplefaces)
        {
            while(c < t)
            {
                break();
                burn();
                recycle_mask = 0;
                theFace(c++);
            }
        }
        store();
        burn();

        t = -llGetListLength(commands);

//        llOwnerSay("-----------------------");
        while(t)
            llOwnerSay("T"+llList2String(commands,t++));

        llRemoveInventory(llGetScriptName());
    }
}


exchangeTLMLRenderer

exchangeTLML
exchangeTLTP
exchangeTLTPBrowser
exchangeTLMLGenerator

TLML Display Element - Main:
// TLML Display Element - Version 0.62 release candidate

list stack;
integer position;

//{

string pop()
{
    return llList2String(stack, ++position);
}

string peak()
{
    return llList2String(stack, position);
}

//}
//#undef STACK

string url;
string config;
string source_url;
integer config_mask;
float config_scale = 1.0;
string show_command;
string path;
string vecmag_1 = "<1,1,1>";

vector size = <0.05,0.05,0.05>;
vector pos = <0.0,0.0,0.0>;

string floatingtext;
vector floatingtextcolor = <1.0,1.0,1.0>;
float floatingtextalpha = 1.0;
//{

//}
integer status = 0x7F;

//{TightList
list TightListParse(string a) {
    string b = llGetSubString(a,((integer)""),((integer)""));//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,((integer)""),((integer)"")), [a=b],[]);
}

string TightListDump(list a, string b) {
    string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) & 0x80000000)
            jump end;
    integer d = 1 - llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}`[],\n\" qQxXzZ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b);
}

list tokens(string a, integer b)
{
    list c;
    if(b)
        c = tokens(llDeleteSubString(a,((integer)""),((integer)"")), b - 1);
    return [llGetSubString(a,((integer)""),((integer)""))] + c;
}

list TightListTypeParse(string a) {
    list b;
    if(llStringLength(a) > 6)
    {
        string m = llGetSubString(a,((integer)""),6);
        integer c = -llGetListLength(b = llDeleteSubList(llParseStringKeepNulls(llDeleteSubString(a,((integer)""),5), [a=""],tokens(m, 5)),((integer)""),((integer)"")));
        integer d;
        do
        {
            list f = [a = llList2String(b,c + 1)]; //TYPE_STRING || TYPE_INVALID (though we don't care about invalid)
            if((d = (1 + llSubStringIndex(m, llList2String(b,c)))) == TYPE_INTEGER)
                f = [(integer)a];
            else if(d == TYPE_FLOAT)
                f = [(float)a];
//            else if(d == TYPE_STRING)
//                already handled
            else if(d == TYPE_KEY)
                f = [(key)a];
            else if(d == TYPE_VECTOR)
                f = [(vector)a];
            else if(d == TYPE_ROTATION)
                f = [(rotation)a];
            b = llListReplaceList(b, f, c, c+1);
        }while((c+=2) & 0x80000000);
    }
    return b;
}
//}
//{

//#define DEBUG(a) llOwnerSay((string)(a))

//}
//#define SizeRange(x) (x)
//#define Unescape(x) (x)
//#define SetPrimTypeChained(a,b,c,d,e,f,g,h,i,j,k,l,m) ++position

float SizeRange(float a)
{
    if(0.01 > (a = llFabs(a)))
        a = 0.01;
    if(a > 10.0)
        a = 10.0;
    return a;
}

string Unescape(string a)
{
    string  b = a;
    integer c = -1;
    integer d = ((integer)"");
    integer e = ((integer)"");
    integer f = ((integer)"");
    while(d = llSubStringIndex(b, (b = "") + "\\") + 1)
    {
        if(2 < (e = llSubStringIndex("uUr\"\\nt", llGetSubString(a,d = 1 + (c+=d),d))))
            a = llInsertString(llDeleteSubString(a,c,d), c, llGetSubString("   \"\\\n\t",e, (e - (7 * (e == 6)))));
        else if(e == 2)
        {
            if(d+(e = (integer)("0x"+llGetSubString(a,d+1,d+1)) * 2)+1 >= (f = llStringLength(a)))
                e = (f - d - 2) & -2;
            if((f = e))//this may look like a mistake, it's not
            {
                do
                    b = "%"+llGetSubString(a,d + e,d + e + 1) + b;
                while((e-=2) > ((integer)""));
            }
            a = llInsertString(llDeleteSubString(a,c, c + 2 + f),c, b = llUnescapeURL(b));
            c += llStringLength(b) - 1;//add to c so we don't accidentily unescape result
        }
        else if(e + 1)// \uXXXX or  \UXXXXXXXX
        {
            b = llGetSubString(a, d + 1, d + 4 + e *= 4);
            a = llDeleteSubString(a, c, d + 4 + e);
            if(((integer)"") < e = (integer)("0x"+b))
            {
                f = (e >= 0x80) + (e >= 0x800) + (e >= 0x10000) + (e >= 0x200000) + (e >= 0x4000000);
                b = "%" + byte2hex((e >> (6 * f)) | ((0x3F80 >> f) * (!!f)));
                while(f)
                    b += "%" + byte2hex((((e >> (6 * --f)) | 0x80) & 0xBF));
                a = llInsertString(a, c, llUnescapeURL(b));
            }
        }
        b = llDeleteSubString(a,((integer)""),c);
    }
    return a;
}

string byte2hex(integer x)
{
    string hexc="0123456789ABCDEF";
    integer xa = (x & 0xF);
    return llGetSubString(hexc, xa = ((x >> 4) & 0xF), xa) + llGetSubString(hexc, xa, xa);
}

hideshow(integer a)
{
    list b = [PRIM_SIZE];
    if(a)
    {
        b += <    SizeRange(size.x * config_scale),
                SizeRange(size.y * config_scale * (1.0 + (0.1547344110854503464203233256351 * !!(status & 0x20)))),
                SizeRange(size.z * config_scale)>;

        if((status & (0x3)) || !(status & 0x800))//if something changed we refresh
            llSetText(floatingtext, floatingtextcolor, floatingtextalpha);
    }
    else
    {
        b += <0.05,0.05,0.05>;
        llSetText("", ((vector)""), ((float)""));
    }
    llSetPrimitiveParams([PRIM_POSITION, pos * config_scale * !!a] + b);
    status = (status & (0xffffF7FF)) | (0x800 * !!a);
}

integer parse_config(string c)
{
    stack = TightListParse(c);
    integer mask = (integer)peak();
    if(mask & 1)
        config_scale = (float)pop();
    position+=!!(mask & 2);
    if(mask & 4)
        pos = (vector)pop() + pos * !!(mask & 32);
    if(mask & 16)
        path = pop();
    return mask;
}

default
{
    link_message(integer t_int_1, integer t_int_2, string c, key d)
    {
        rotation t_rot = ((rotation)"");
        position = 3;
        if((t_int_2 & -2) == 1010)
        {
            if(llSubStringIndex(path, llGetSubString(d,0,0)) + 1)
            {
                if(t_int_2 & 1)
                    jump in3;
                d = llDeleteSubString(d,0,0);
                jump in1;
            }
        }
        else if(t_int_2 == 1001)
        {
            if(d == show_command)
                jump in4;
        }
        else if(t_int_2 == 1000)
            jump in2;
        jump out1;
        @in3;@in4;
        {
            position = 1;
            hideshow((integer)llList2String(stack,0) | ((status & 0x800) && (0x8 & parse_config(c))));
        }
        jump out2;
        @in2;@in1;
        {
            config_mask = parse_config(config = d);
            list config_list = stack;
//            config_root_local_rot = (rotation)llList2String(config_list, 1);
//            server = llList2String(config_list, 2);
            position = -llGetListLength(stack = TightListParse(c));
            if((t_int_2 = (llStringLength(c = peak()) | (t_int_1 = !((source_url = llList2String(config_list, ((integer)""))) == source_url)))))
            {
                url = "";
                if(t_int_1)
                    show_command = "";
                if(t_int_2 & 0xFFFFFFFE)
                    url = c;
                llSetObjectDesc(url);
            }
            integer source = (integer)("0x"+(c = pop()));
            integer t_int_3 = ((integer)"");
            list t_list_1;
            list t_list_2;
            vector t_vec = ((vector)"");
            integer settings_mask = 0xF; //default all
            if(c != (d="-"))
            {
                if(0x1 & source)
                {
                    if((c = pop()) == "-")
                        settings_mask = 0x1F0; //keep all
                    else if(c != "+")
                        settings_mask = (integer)("0x"+c);
                }
                else if(c!="+")
                    settings_mask = ((integer)""); //clear

                status = ((status | ((0x3)) | (0x400 * t_int_1)) & (((0xffffFFDF)) | (0x20 * !!(settings_mask & 0x80))));

                if(source & 0x2)
                    if(llStringLength(show_command = pop()) == 1)
                        show_command = "";

                if((t_int_1 = (0xC & source)) == 0x4)
                {
                    t_int_2 = (integer)llList2String(t_list_1 = TightListParse(pop()), ((integer)""));
                    if(t_int_2 >= ((integer)"") && t_int_2 <= 6)
                    {//PRIM_TYPE = 9
                        t_list_2 = [9, t_int_2, (integer)llList2String(t_list_1, 1),(vector)llList2String(t_list_1, 2),
                                            (float)llList2String(t_list_1, 3),(vector)llList2String(t_list_1, 4),
                                            (vector)llList2String(t_list_1, 5)];
                        if(t_int_2 != 3)//PRIM_TYPE_SPHERE
                        {
                            t_list_2 += (vector)llList2String(t_list_1, 6);
                            if(t_int_2 > 3)//== PRIM_TYPE_TORUS || e == PRIM_TYPE_TUBE || e == PRIM_TYPE_RING)
                                t_list_2 += [(vector)llList2String(t_list_1, 7),(vector)llList2String(t_list_1, 8),
                                             (float)llList2String(t_list_1, 9), (float)llList2String(t_list_1, 10),
                                             (float)llList2String(t_list_1, 11)];
                        }
                        llSetPrimitiveParams((t_list_1 = t_list_2 = []) + t_list_2);
                    }
                }
                else if(t_int_1 == 0x8)
                {//check defines to see if this function evaporates into TLML-L script.
                    llMessageLinked(LINK_THIS, 1002, pop(), "");
                }
                else if(t_int_1 == 0xC || settings_mask & 0x1)
                {//      [Type][9, 0, 0, <0,1,0>, 0.0, <0,0,0>, <1,1,0>, <0,0,0>]
                    c = "if__v_i9iiv<0,1,0>fvv<1,1,0>v";
                    if(t_int_1)
                        c = pop();
                    llSetPrimitiveParams(TightListTypeParse((c="")+c));
                }

                if(source & 0x10)
                    size = (vector)((c="")+c) + ((vector)vecmag_1 * (float)(c = pop()));
                if(source & 0x20)
                    pos = (vector)pop() + (pos * !!(settings_mask & 0x200));
                if(source & 0x40){
                    t_vec = (vector)llList2String(t_list_1 = TightListParse(pop()), ((integer)""));
                    pos = 0.5 * (t_vec + (size = (vector)llList2String(t_list_1, 1))) + (pos * !!(settings_mask & 0x200));
                    size -= t_vec;
                }

                if(source & 0x80){
                    t_rot = (rotation)(c = pop());
                    if(<t_rot.x, t_rot.y, t_rot.z> != (t_vec = (vector)c))
                        t_rot = llEuler2Rot(t_vec * DEG_TO_RAD);
                }else if(!(settings_mask & 0x2))
                    jump skip7;
                //t_rot hasn't been used yet so we don't have to zero it, isn't that wonderful?
                llSetLocalRot(t_rot * (rotation)llList2String(config_list, 1));
                @skip7;

                if(source & 0x100)
                    floatingtext = Unescape(pop());
                else if(settings_mask & 0x4)
                    floatingtext = "";
                else
                    status = status & (0xffffFFFE);

                if(source & 0x200)
                {
                    t_rot = (rotation)(c = pop());
                    if(<t_rot.x, t_rot.y, t_rot.z> == (floatingtextcolor = (vector)((c="")+c)))
                        floatingtextalpha = t_rot.s;
                }
                else if(settings_mask & 0x8)
                    floatingtextcolor = <1.0,1.0,(floatingtextalpha = 1.0)>;
                else
                    status = status & (0xffffFFFD);

                if(source & 0x400)
                {
                    t_list_2 = TightListTypeParse(pop());
                    status = status | 0x4;
                }
                else if(!(settings_mask & 0x10) && (status & 0x4))
                    status = status & (0xffffFFFB);//t_list_2 is clean
                else
                    jump skip8;
                llParticleSystem(t_list_2);
                @skip8;

                if(source & 0x800)
                {
                    llTargetOmega(    (vector)llList2String(t_list_1 = TightListParse(pop()), ((integer)"")),
                                    (float)llList2String(t_list_1, 1),         (float)llList2String(t_list_1, 2));
                    status = status | 0x8;
                }
                else if(!(settings_mask & 0x20) && (status & 0x8))
                {
                    status = status & (0xffffFFF7);
                    llTargetOmega(((vector)""), ((float)""), ((float)""));
                }

                if(source & 0x1000)
                {
                    llSetTextureAnim((integer)llList2String(t_list_1 = TightListParse(pop()), ((integer)"")),
                                     (integer)llList2String(t_list_1, 1),(integer)llList2String(t_list_1, 2),
                                     (integer)llList2String(t_list_1, 3),(float)llList2String(t_list_1, 4),
                                     (float)  llList2String(t_list_1, 5),(float)llList2String((t_list_1 = []) + t_list_1, 6));
                    status = status | 0x10;
                }
                else if(!(settings_mask & 0x40) && (status & 0x10))
                {
                    llSetTextureAnim(((integer)""), ((integer)""), ((integer)""), ((integer)""), ((float)""), ((float)""), ((float)""));
                    status = status & (0xffffFFEF);
                }

                if(source & 0x2000)
                {
                    t_int_1 = (integer)llList2String(t_list_1 = TightListParse(pop()), t_int_2 = ((integer)""));
                    t_list_2 = TightListParse(llList2String(config_list, 2));
                    if(t_int_1 & 0x10)//XyText_key
                    {
                        t_list_2 = llListInsertList(t_list_2, llList2List(TightListParse(llList2String(t_list_1, t_int_2 = 1)) + ["",""],((integer)""),1), 1);
                        t_list_2 = llDeleteSubList(llDeleteSubList(t_list_2, t_int_3 = (llList2String(t_list_2,1) == ""), t_int_3),
                                                                             t_int_3 = ((llList2String(t_list_2, -2) != "") - 2), t_int_3);
                    }
                    t_list_2 = [c = TightListDump(t_list_2, "*")];
                    if(!(t_int_1 & 0x18))//XyText_key
                        t_list_2 = [];
                    llMessageLinked(LINK_THIS, 3200,
                        TightListDump((t_list_1 = t_list_2 = [(0x10 * ((t_int_1 & 0x18) != ((integer)""))) | (t_int_1 & 0x3FFF7)]) +
                                        t_list_2 + llList2List(t_list_1, t_int_2 + 1, t_int_2 + 12),"|"), c);
                    status = status | 0x20;
                }
                !!(source & 0x40000)+!!(source & 0x20000)+!!(source & 0x10000)+!!(source & 0x8000)+position+=!!(source & 0x4000);
//                else if(!(settings_mask & BITPrimKeepXyText))
//                    status = status | BITStatus_XyText_default;
//
//                if(source & BITSetURL)
//                {
//                    llSetPrimURL(pop());
//                    status = status | BITStatus_SetURL;
//                }
//                else if(status & BITStatus_SetURL)
//                {
//                    if(settings_mask & BITPrimKeepURL)
//                        llRefreshPrimURL();
//                    else
//                    {
//                        status = status & BITStatus_SetURL_no;
//                        llSetPrimURL("");
//                    }
//                }
            }
//            llOwnerSay((string)llGetFreeMemory());
            c = (string)(t_list_2 = t_list_1 = []);
            if(!(config_mask & 0x8) || (status & 0x800))
                hideshow(!(source & 0x2));
            while((1+position) & (settings_mask = 0x80000000))
            {
                status = (status & ((0xffffFDFF))) | (0x180);
                if(0xF == (t_int_1 = (0xF & (source = (integer)("0x"+llList2String(stack = llDeleteSubList((stack = [])+stack, 0, position), ++position))))))
                {
                    t_int_1 = ALL_SIDES;
                    status = status | 0x200;
                }

                if(source & 0x10)
                {
                    settings_mask = (integer)("0x"+(c = pop()));
                    if(c == "+")
                        settings_mask = 0xFF; //default all
//                    else if(c == "-")
//                        settings_mask = 0x0; //keep all
                }

                position+=!!(source & 0x20);

                if((t_int_2 = (source & 0x100)) || (settings_mask & 0x1))
                {
                    if(t_int_2)
                        c = pop();
                    if(!t_int_2 || (c == ""))
                        c = "5748decc-f629-461c-9a36-a35a221fe21f";
                    t_int_2 = 5000;
                    if((t_int_2 = (integer)("0x"+llGetSubString(c,((integer)""),6)+"1")))//if it's non zero then its likely a key
                        llSetTexture(c, t_int_1);
                    llMessageLinked(LINK_THIS, 5900 - (!t_int_2 * 900), c, c = (string)t_int_1);

                }

                t_vec = (vector)vecmag_1;
                if(source & 0x200)
                    t_vec = (vector)pop();
                else if(!(settings_mask & 0x2))
                    jump skip5;
                llSetColor(t_vec, t_int_1);
                @skip5;

                t_vec.x = 1.0;
                if(source & 0x400)
                    t_vec.x = (float)pop();
                else if(!(settings_mask & 0x4))
                    jump skip6;
                llSetAlpha(t_vec.x, t_int_1);
                @skip6;

                t_vec = (vector)vecmag_1;
                if(source & 0x800)
                    t_vec = (vector)pop();
                else if(!(settings_mask & 0x8))
                    jump skip1;
                llScaleTexture(t_vec.x, t_vec.y, t_int_1);
                @skip1;

                t_vec = ((vector)"");
                if(source & 0x1000)
                    t_vec = (vector)pop();
                else if(!(settings_mask & 0x10))
                    jump skip2;
                llOffsetTexture(t_vec.x, t_vec.y, t_int_1);
                @skip2;

                t_vec.z = ((float)"");
                if(source & 0x2000)
                    t_vec.z = (float)pop();
                else if(!(settings_mask & 0x20))//5 + 5+1+5+1 + 5 + 5
                    jump skip3;
                llRotateTexture(t_vec.z, t_int_1);
                @skip3;

//                t_list_1 = [];//we don't need this since we haven't used it since the last wipe.
                if(source & 0x40)
                    t_list_1 = [PRIM_FULLBRIGHT, t_int_1, !!(source & 0x80)];

                if(source & 0x4000)
                    t_int_2 = (integer)pop();
                else if(settings_mask & 0x40)
                    t_int_2 = PRIM_SHINY_NONE;
                else
                    status = status & (0xffffFF7F);

                //To Save memory we have totaly screwed up the script
                //to add more flags, another integer will need to be added to the memory space
                if(source & 0x8000)
                    t_int_3 = (integer)pop();
                else if(settings_mask & 0x80)
                    t_int_3 = PRIM_BUMP_NONE;
                else if(!(0x100 & (status = (status & (0xffffFEFF)))))
                    jump skip4;

                if(status | 0x200)
                    t_int_1 = llGetNumberOfSides();

                do
                {
                    t_list_1 += t_list_2 + llGetPrimitiveParams(t_list_2 =
                        [PRIM_BUMP_SHINY, t_int_1 -= !!(status & 0x200)]);
                    if(status & 0x80)
                        t_list_1 = llListReplaceList(t_list_1, [t_int_2], -2, -2);
                    if(status & 0x100)
                        t_list_1 = llListReplaceList(t_list_1, [t_int_3], -1, -1);
                }
                while(t_int_1 && (status & 0x200));
//                if(status & BITStatus_ALL_SIDES)
//                    t_int_1 = ALL_SIDES;

                @skip4;
                if(t_list_1 != [])
                    llSetPrimitiveParams((t_list_1 = [])+ t_list_1);
                !!(source & 0x80000)+!!(source & 0x40000)+!!(source & 0x20000)+position+=!!(source & 0x10000);
            }
            stack = [];
        }
        @out1;@out2;
    }
    touch_start(integer a)
    {
        if (url)
            llMessageLinked(1, 2000, url, config);
    }
}

TLML Display Element - XTM:
//TLML Display Element - XyTextMod Version 0.62 release candidate
//Portions of this script were taken from XyText

//#define EASY_CONFIG

string gCharIndex3;

string gCharIndex6;

//integer faces = 0x16473425;
//list offsetsingle = [0.037,0.0,0.244,0.0,0.037,];
//list offsetdouble = [];

string  XyText_key;
integer XyText_line;
integer XyText_ver = 5;
string  XyText_font = "471a92ff-50f4-2858-2f36-deab4cc50276";
integer XyText_BlockPosition;
integer XyText_mode;
float   XyText_fade_delay;
float   max_Alpha;

list TightListParse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b], []);
}

string TightListDump(list a, string b) {
    string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) == -1)
            jump end;
    integer d = 1 - llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b);
}

//{XyText6 Keys

//}

//{XyText3 Functions
vector GetGridOffset3(string str, integer index) {
    return <-0.45 + 0.1 * ((index = llSubStringIndex(gCharIndex3, llGetSubString(str, index, index))) % 10), 0.45 - 0.1 * (index / 10), 0.0>;
}
//}

//{XyText6 Functions
integer gci6(string str, integer a)
{
    return llSubStringIndex(gCharIndex3 + gCharIndex6, llGetSubString(str, a, a));
}

vector GetGridPos6(string str, integer index2) {
    integer index1 = gci6(str, index2);
    // There are two ways to use the lookup table...
    if (index1 >= (index2 = gci6(str, index2 + 1)))
        return < index2 * 2, index1, 0.0>;
    return < index1 * 2 + 1, index2, 0.0>;
}

string GetGridTexture6(vector grid_pos) {
    integer GridRow = (integer)(grid_pos.y) / 10;

    return     llInsertString(
                llInsertString(
                    llInsertString(
                        llInsertString(
                            llGetSubString("00e9f9f70669181cc1927f8e67678c8d347a5cb600317ec02fcff298eebf3c0e4e7e689e37f19eca8596a958bbd2396319ea9c2167ba8f6f99db573b1b877eb1dde7b412cda1652f6fc273f4641f96e1af6fa3bb3a6c9c4f4bf5d1c126c830daa201d3a2364b43b686865881c0f82a94b674dec8fead99e5c28d2db8e4c51540366e05f3be6be5cfc33b731dff649caa75c4925c0427dc0cc71ce28674ff4d27dcbe166b6a97efb2fc8ee5bc6a8b1be60dca2febfc66a762db8589026a4ecd68a0fca76f503a946b93360a918e886f7a67fb375d89a15a4f8c7a0cd1c066ffc4300470b2da345470074c1b8464ca050cd1f8e91cce2bd85e2120930d3b6309462a190e447b29dadb0bffc31adaf5a17075d55e71f6f89835e746a45f189f30a1300fac332b303da326bce2d70428ec190747c776011a53ce13ee8b5bb9e87c1e85a855c3a94f01ca33e07dde92e727e2cbc1dab22d61298619497a5235c954e1f7aef047f266959616df641010edd8e14c34ebf7e5e12e1a579fe224d9d5e71b4a69e98c26a5ad05e92eb5b906ad9ef9462a92262a9791ac2d8957ab33334b7820b24b3a8c5782eec6ed555003f5dbcd9b481daa9ea8a9fa1ee4ab9a0d38e217c231dbdcc84215b07aa66da14745cfdcc97e3cbbc9a345dfa0ae955c1f4bf9cff1e7d030ff80a242cb69f6951d4eae3bed32d6c4d733c0f1f2426df1d222220d88f96a30dccf9b2031efda0dfeb23c72252f259558b84bcc6515fa274d0cfb65f2838c4fde80cceddff8195dfdf36b2ccc2594feadd2a3dfcdb3a61711badf53e0ce2972da00955c129e3289b36767763e0d336d321fddfa5c1be26131766f6ad43b1dc46b5176a78b9038865b82bf0606d16cbb1868fd1d5c93eae42164a37ddd5d98cf273e3fd0f03048be58ee3a0b0e47c89ede4a6233a2dacb852aad1b00fb9c4a550e13495b25c4f0b459dc06dee3ce8def312c735b0e48018b6799c8832f7132164e71d12303ed9c8554710c6b4a417d8a1f4f404b97836672f8527911ca5e21ec5b2059094c313f90d7316b3306a4fcc3e1c4296d881701f88fbd7367130ac0846f3c95deb5b6d25c8070347459d540a0ae9d36065ae04f2842b64cfa8612ae9af53c5bf428998174d7abc4fd12467401e9792c4934e06ac761542797d53c3eaa0404386006753e375596c3e39f5b26bd81d3b25e62fe5b671d1e3e79f57f0b64a050d617ee00c8e9e3adc9cbbeff166af5f3f05ee02098f2b00e27ed02278a6594ba6d5e0d2b93f2e4f4bf70a707197d449e5b58846c0c850c61f9d6021d4b1a9503a44fee2b976eb5d80e680ae2ffae7265524dcb76c2b691992706f6e41cf21104bd0b0190dffad1bac8132b4bb15e956d56ae69f5d26a20de0ce7f816da2c51f1612a2029a542db7db882345fea05c7be465c409f9dcb3bd2aa07b3017e02c0635185acd51ef5f9d79b894dcff36519713c2bd73c77e1dc54242a", GridRow = ((GridRow * (GridRow + 1) / 2 + ((integer)(grid_pos.x) / 20)) * 32), GridRow + 31)
                            ,8,"-"),
                        13,"-"),
                    18,"-"),
                23,"-");

}

vector GetGridOffset6(vector grid_pos) {
    return <-0.45 + 0.05 * (llRound(grid_pos.x) % 20), 0.45 - 0.1 * (llRound(grid_pos.y) % 10), 0.0>;
}
//}

string hexc="0123456789ABCDEF";

string byte2hex(integer x)
{//Helper function for use with unicode characters.
    integer x0  = (x & 0xF);
    return llGetSubString(hexc, x0 = ((x >> 4) & 0xF), x0) + llGetSubString(hexc, x0, x0);
}

string Unescape(string a)
{
    string  b = a;
    integer c = -1;
    integer d = ((integer)"");
    integer e = ((integer)"");
    integer f = ((integer)"");
    while(d = llSubStringIndex(b, (b = "") + "\\") + 1)
    {
        if(2 < (e = llSubStringIndex("uUr\"\\nt", llGetSubString(a,d = 1 + (c+=d),d))))
            a = llInsertString(llDeleteSubString(a,c,d), c, llGetSubString("   \"\\\n\t",e, (e - (7 * (e == 6)))));
        else if(e == 2)
        {
            if(d+(e = (integer)("0x"+llGetSubString(a,d+1,d+1)) * 2)+1 >= (f = llStringLength(a)))
                e = (f - d - 2) & -2;
            if((f = e))//this may look like a mistake, it's not
            {
                do
                    b = "%"+llGetSubString(a,d + e,d + e + 1) + b;
                while((e-=2) > ((integer)""));
            }
            a = llInsertString(llDeleteSubString(a,c, c + 2 + f),c, b = llUnescapeURL(b));
            c += llStringLength(b) - 1;//add to c so we don't accidentily unescape result
        }
        else if(e + 1)// \uXXXX or  \UXXXXXXXX
        {
            b = llGetSubString(a, d + 1, d + 4 + e *= 4);
            a = llDeleteSubString(a, c, d + 4 + e);
            if(((integer)"") < e = (integer)("0x"+b))
            {
                f = (e >= 0x80) + (e >= 0x800) + (e >= 0x10000) + (e >= 0x200000) + (e >= 0x4000000);
                b = "%" + byte2hex((e >> (6 * f)) | ((0x3F80 >> f) * (!!f)));
                while(f)
                    b += "%" + byte2hex((((e >> (6 * --f)) | 0x80) & 0xBF));
                a = llInsertString(a, c, llUnescapeURL(b));
            }
        }
        b = llDeleteSubString(a,((integer)""),c);
    }
    return a;
}

list faces()
{
    return llCSV2List("<3,2" + //"<-1,1,0>,<3,2" +
                llList2String([    ",.025>,<6,1,0>,<5,1,0>,<1,2,-.025>",
                                ".46,.037>,<7,1,0>,<4,-14.8,-.315>,<6,1,0>,<1,2.46,-.037>"],
                            !(XyText_ver % 5)    ));
}

display(string c, integer BlockPosition)
{
    list b;
    string m = "17,.83.020,.80.3";
    integer x = 8 * !(XyText_ver % 5);
    if((string)llGetPrimitiveParams([PRIM_TYPE]) != (string)
        (b = [2, 32, (vector)("<."+llGetSubString(m,x,x+5)+",0>"), (float)llGetSubString(m,x+6,x+7), ((vector)""), (vector)"<1,1,0>", ((vector)"")]))
            llSetPrimitiveParams([PRIM_TYPE] + b);
    if(XyText_mode & 1)
        llSetAlpha(0.00, ALL_SIDES);
    c = llGetSubString(Unescape(c), BlockPosition *= XyText_ver , BlockPosition + XyText_ver - 1);// + "  ";//spaces are the clear.

    b = faces();

    BlockPosition = (!(XyText_ver % 4)) - 5;//6;

    x = (XyText_ver > 5);

    vector t = (vector)"";

    list temp = [PRIM_TEXTURE, ALL_SIDES, "11ee27f5-43c0-414e-afd5-d7f5688c351f", (vector)"<1,1,0>", ((vector)""), ((float)"")];
    do
    {
        vector k = (vector)llList2String(b, BlockPosition);

        if(x)
            temp += [PRIM_TEXTURE, (integer)k.x, GetGridTexture6(t = GetGridPos6( c, XyText_ver + BlockPosition * 2 )),
                    <0.1 * k.y, 0.1, ((float)"")>, GetGridOffset6(t) + <2 * k.z,((float)""),((float)"")> , ((float)""),
                    PRIM_FULLBRIGHT, (integer)k.x, !!(XyText_mode & 2)];
        else

            temp += [PRIM_TEXTURE, (integer)k.x, XyText_font, <.05 * k.y, 0.1, ((float)"")>,
                    GetGridOffset3(c, XyText_ver + BlockPosition) + <k.z,((float)""),((float)"")> , ((float)""),
                    PRIM_FULLBRIGHT, (integer)k.x, !!(XyText_mode & 2)];
    }while(++BlockPosition);
    llSetPrimitiveParams(temp);

    if(XyText_mode & 1)
    {
        float Alpha = 0.05;
        for (; Alpha < max_Alpha; Alpha += 0.05)
            llSetAlpha(Alpha, ALL_SIDES);
        llSetAlpha(max_Alpha, ALL_SIDES);
        if (XyText_fade_delay >= 0.0)
        {
            llSleep(XyText_fade_delay);
            for (Alpha = max_Alpha; Alpha >= 0.05; Alpha -= 0.05)
                llSetAlpha(Alpha, ALL_SIDES);
            llSetAlpha(0.0, ALL_SIDES);
        }
    }
}

key getline;
key getlines;
key card;
list badpos;
integer pos;
integer lines;
string TN_ID;
integer direction;

getNextTexture()
{
    integer c = ((pos + lines) % lines);
    integer d;
    while((llListFindList(badpos, [((c + d + lines + lines) % lines)]) + 1) && (d < lines) && (d > -lines))
        d += direction/llAbs(direction);
    pos = (pos + d) % lines;
    if(llAbs(d) < lines)
        getline = llGetNotecardLine(card, ((pos + lines) % lines));
}

checkBadPos(integer a, integer c)
{
    if(llListFindList(badpos, [a = ((a % lines) + lines ) % lines]) == -1)
    {
        badpos = llListSort(badpos + [a], 1, 1);
        if(c)
            llMessageLinked(LINK_SET, 5100, (string)a, card);
        if(direction)
        {
            integer b = ((pos % lines) + lines ) % lines;
            c = direction/llAbs(direction);
            if(pos*c >= ((integer)"") && a*c <= b*c)
            {
                if(b)
                    pos += c;
                getNextTexture();
            }
        }
    }
}

default
{
    state_entry()
    {
        gCharIndex3 = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
                    + "abcdefghijklmnopqrstuvwxyz{|}~"
                    + llUnescapeURL("%CF%80%CE%B8%C3%97%C2%B0%E2%88%9A");

        gCharIndex6 = llUnescapeURL("%C2%B2%C2%B3%C2%BC%C2%BD%C2%BE%C2%A7%C2%A9%C2%AE%E2%80%A2%E2%80%A6");

    }
    link_message(integer a, integer b, string c, key d)
    {

        list info;

        if(b == 3000)
        {
            if(d == XyText_key && d != "")
                for(a = llGetListLength(info = TightListParse((c = d = "") + c)), b = ((integer)""); b < a; b+= 2)
                    if((integer)llList2String(info,b) == XyText_line)
                        display(llList2String(info,b + 1),XyText_BlockPosition);
        }
        else if(b == 3100 || b == 3200)
        {
            if((b = (b != 3100)) || d != XyText_key)
                if(!b || a != llGetLinkNumber())
                    jump end;
            info = TightListParse(c);
            if((integer)llList2String(info,((integer)"")) == XyText_line || b)
            {
                integer e = !b;
                integer g = 7 & (a = (integer)llList2String(info,e));
                vector t = ((vector)"");
//                if(b == XyText_Group)
//                {

                if(g && (g < 5))
                    XyText_ver = (integer)("0x"+llGetSubString("-458A",g,g));

//                }
                list f = faces();
                if(a & 0x10)
                    XyText_key = llList2String(info, ++e);
                if(a & 0x20)
                    XyText_line = (integer)llList2String(info, ++e);
                if(a & 0x40)
                {// XyText_BlockPosition
                    ++e;
                    if(b)
                        XyText_BlockPosition = (integer)llList2String(info, e);
                }
                if(a & 0x80)
                {// XyText_font
                    XyText_font = llList2String(info, ++e);
                    if(!(a & 0x3000) && XyText_ver < 6)
                    {
                        g = !(XyText_ver % 4) - 5;
                        do
                        {
                            t = (vector)llList2String(f,g);
                            llSetTexture(XyText_font, (integer)t.x);
                        }
                        while(++g);
                    }
                }
                if(!b)
                    XyText_mode = (XyText_mode & -2) | !!(a & 0x100);
                if(a & 0x200)
                {// XyText_fade_delay
                    ++e;
                    if(!b)
                        XyText_fade_delay = (float)llList2String(info, e);
                }
                if(a & 0x400)
                {// max_Alpha
                    max_Alpha = (float)llList2String(info, ++e);
                    g = !(XyText_ver % 4) - 5;
                    do
                    {
                        t = (vector)llList2String(f,g);
                        llSetAlpha(max_Alpha, (integer)t.x);
                    }
                    while(++g);
                }
                if(a & 0x800)
                {
                    vector color = (vector)llList2String(info, ++e);
                    g = !(XyText_ver % 4) - 5;
                    do
                    {
                        t = (vector)llList2String(f,g);
                        llSetColor(color, (integer)t.x);
                    }
                    while(++g);
                }
                if(a & 0x1000)
                    XyText_mode = (XyText_mode & -3) | ((!!(integer)llList2String(info, ++e)) << 1);
                if(a & 0x30000)
                {// display
                    display(c = llList2String(info, ++e), XyText_BlockPosition * !!(a & 0x10000));
                    if(d == XyText_key && b && (a & 0x30000) == 0x30000)
                        llMessageLinked(LINK_ALL_OTHERS, 3000, TightListDump([XyText_line, c],"|"), d);
                }
            }
            @end;
        }
        else if(b == 3300 || b == 8999)
        {
            if(d == XyText_key || a == llGetLinkNumber() || b == 8999)
            {
                XyText_key = "";
                XyText_line = -2147483648;
                XyText_BlockPosition = ((integer)"");
            }
        }

        else if(b == 5000)
        {
            info = TightListParse(c);
            pos = llList2Integer(info,2);
            if(!direction = (integer)llList2String(info,3))
                direction = 1;
            badpos = [];
            getlines = llGetNumberOfNotecardLines(card = llList2String(info,0));
            TN_ID = TightListDump([card,llList2String(info,1)],"*");
        }
        else if(b == 5100)
        {
            if(d == card)
                checkBadPos((integer)c,0);
        }
        else if(b == 5200)
        {
            if(d == TN_ID)
            {
                b = pos + (direction = (integer)c);
                a = direction/llAbs(direction);
                do
                    if(llListFindList(badpos, [(((pos += a) % lines) + lines) % lines]) + 1)
                        b+=a;
                while(pos*a<b*a);
                getNextTexture();
            }
        }

        else if(b == 5900)
        {
            if(a == llGetLinkNumber())
            {
//                llSetTexture(d, (integer)c);
                XyText_line = (integer)(XyText_key = "");
            }
        }

    }

    dataserver(key a, string b)
    {
        if(a == getlines)
        {
            getlines = "";
            lines = (integer)b;
            getNextTexture();
        }
        else if(a == getline)
        {
            getline = "";
            if(llSubStringIndex(hexc+"abcdef", llGetSubString(b,0,0)) + 1)
            {
                list e = llCSV2List(b);
                if(llGetListLength(e) == 1)
                    e = llParseString2List(b, [llGetSubString(b,36,36)],[]);
                integer c = ALL_SIDES;
                if((a = llList2String(e,2)) != "")
                    c = (integer)((string)a);
                llSetTexture(llGetSubString(b,0,35), c);
            }
            else
                checkBadPos(pos,1);
        }
    }

}

TLML-L & Updater
// TLML-L & Updater - Version 0.62

list TightListParse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}

vector v_one = <1.0,1.0,1.0>;
string config;
key cardrequest;
string path;

hide(list a)
{
    llSetPrimitiveParams(a + [PRIM_POSITION, (v_one * 0.0), PRIM_SIZE, v_one * 0.05]);
}

SetPrimTypeChained(list versions, list types,
            integer holeshape, vector cut, float hollow, vector twist, vector topsize, vector topshear,
            vector advancedcut, vector taper, vector stuff)//float revolutions, float radiusoffset, float skew)
{
    if(versions == types)
    {
        integer version;
        integer type;
        integer len = -llGetListLength(versions);
        while(len)
        {
            list r = [version = llList2Integer(versions,len), type = llList2Integer(types,len++)];
            if(1 == version)
            {//legacy PRIM_TYPE interface, still active for backwords compatability. introduced in 1.3 fazed out in 1.5
                r += [cut, hollow];
                if(type != 3)//PRIM_TYPE_BOX,PRIM_TYPE_CYLINDER,PRIM_TYPE_PRISM
                    r += twist.y;
                if(4 == type)//PRIM_TYPE_TORUS
                    r += [topsize.y, topshear, advancedcut];
                if(5 == type)//PRIM_TYPE_TUBE
                    r += topshear.x;
    //            else if(PRIM_TYPE_RING == p) ring shape not arround when this interface was implemented
            }
            if(version == 9)
            {//new PRIM_TYPE interface. introduced in 1.5, various revisions have added new restrictions
                r += [holeshape, cut, hollow, twist];
                if(type >= 4)//PRIM_TYPE_TORUS,PRIM_TYPE_TUBE,PRIM_TYPE_RING
                    r +=[topsize, topshear, advancedcut, taper, stuff.x, stuff.y, stuff.z];// revolutions, radiusoffset, skew];
            }
            if(type <= 2)
                r += [topsize, topshear];
            if(3 == type)//PRIM_TYPE_SPHERE
                r += advancedcut;
            llSetPrimitiveParams(r);
        }
    }
}

list stack;

clear()
{
    llMessageLinked(LINK_THIS, 1000, "|-|31|+|<.05,.05,.05>||1F|+", path = cardrequest = "");
}

default
{
    link_message(integer a, integer b, string c, key d)
    {
        if(b == 1100)
        {//email, we use the elements as a giant email multi-thread.
            if(llList2Integer(stack = TightListParse(c),0) == llGetLinkNumber()) //keeps bad things from happening
            {
                if(llSubStringIndex(c = llList2String(stack,1),"@") == -1)
                    c += "@lsl.secondlife.com";
                llEmail(c, llList2String(stack,2), d);
                llSetTimerEvent((float)llList2String(stack,3));
            }
        }
        else if(b == 8999)
            clear();
        else if(b == 8998)
            llSetRemoteScriptAccessPin((integer)c);
//        else if(b == 8994)
//            llSetScriptState(d,(integer)c);
        else if(b == 8993)
        {
            if(INVENTORY_NONE != llGetInventoryType(c))
                llRemoveInventory(c);
        }
        else if(b == 8992)
            llSetTimerEvent((float)c);
        else if(b == 6000)
        {//begin,end,offset,visible-begin,visible-end
            stack = TightListParse(config = d);
            cardrequest = d = llList2String(stack, 2);
            b = 0;
            if((a = (integer)llList2String(stack,3)) & 2)
                b = (integer)llList2String(stack, 4 | (a & 1));
            a = llGetLinkNumber() - 2 - b;
            stack = TightListParse(c);
            b = (integer)llList2String(stack, 0);
            integer e = (integer)llList2String(stack,1);
            if( a >= b  && a <= e)
            {
                if(d)
                    cardrequest = llGetNotecardLine(d, a - b + (integer)llList2String(stack, 2));
            }
            else if(a < b)
            {
                if(a < e || ((llGetSubString(c, 0, 0) != "0") && !(e = (integer)(c = llList2String(stack, 3)))))
                    hide([]);
            }
            else if(a > (integer)llList2String(stack, 4))
                hide([]);
        }
        else if(b == 1000)
        {
            if(32 & b = (integer)llList2String(stack = TightListParse(d),3))
                path = llList2String(stack,3 + (1 & b) + !!(b & 4) + !!(b & 16));
            cardrequest = "";
        }
        else if(b == 1002)
        {
            if(a == llGetLinkNumber())
                SetPrimTypeChained(TightListParse(llList2String(stack = TightListParse(c), 0)),
                                 TightListParse(llList2String(stack, 1)),  (integer)llList2String(stack, 2),
                                        (vector)llList2String(stack, 3),     (float)    llList2String(stack, 4),
                                        (vector)llList2String(stack, 5),    (vector)llList2String(stack, 6),
                                        (vector)llList2String(stack, 7),    (vector)llList2String(stack, 8),
                                        (vector)llList2String(stack, 9),   <(float)    llList2String(stack, 10),
                                        (float)    llList2String(stack, 11),    (float)    llList2String(stack, 12)>);
        }
        stack = [];
    }
    changed(integer a)
    {
        if(a & CHANGED_LINK)
            if(!llGetLinkNumber())
                llDie();
    }
    dataserver(key request, string data)
    {
        if(cardrequest == request)
            llMessageLinked(LINK_THIS, 1000, data, config);
    }
    email(string t, string a, string s, string m, integer n)
    {
        llMessageLinked(LINK_ROOT,1200, "", m);
    }
    timer()
    {
        llGetNextEmail("","");
    }
}


exchangeTLTPBrowser

exchangeTLTP
exchangeTLML
exchangeTLMLRenderer

Sample Code:

Sample TLML browser:
// TLTP Browser Engine
// Version 0.51

// Supports: TLTP v0.4

integer debug = 0;

// constants
key nullk = NULL_KEY;
vector zerov = ZERO_VECTOR;

string tltp_str = "TLTP";        // for email comm
float max_sleep = 10.0;            // for the W commands

key source;                // if set to null key, listen anyone, else only listen this key

// info about browser
list current_url;            // maintained into full URL format
integer status = 2;            // browser's display is visible, and listening to adverts and other stuff
integer browsing_channel;        // channel for receiving answers if chat
integer browsing_handle;        // handle for receiving answers if chat

// info about inventory reading
key page_key;                // remote notecard key
key page_query;                // for reading remote notecards
integer page_line;
key joint_page_key;            // remote joint notecard key
key joint_page_query;            // for reading remote joint notecards
integer joint_page_line;

// info about display
integer link_offset = 2;        // link number of first display prim
integer max_prim_used;            // highest index of display prim in use
integer emailer;            // index of unused display prim for email sending

// courtesy of Strife Onizuka
list TightListParse(string a)
{
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [b],[]);
}
string TightListDump(list a, string b)
{     string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) == -1)
            jump end;
    integer d = -llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b);
}

go(string url)
{
    if(debug)//$[E20012]
        llOwnerSay(url);
    list t = TightListParse(url) + current_url;
    integer argc = llGetListLength(t);
    if (argc < 5) { llOwnerSay("Missing arguments in URL: " + url); return;    } // sanity check

    // extracting info from the URL
    current_url = llDeleteSubList(t, -4, argc - 9);
    request(current_url);
}

request(list u)
{
    if (llGetListLength(u) != 4) { llOwnerSay("Bad URL format"); return; }

    string thename = llList2String(u, 3);
    if (llStringLength(thename))
    {
        llSetText("Browsing " + thename, <0,1,1>, 0.8);
    } else llSetText("", zerov, 0.0);

    if (llList2String(u, 1) == "0"){
        // requesting a page to a chat server

        if ((integer)llList2String(u, 2) == 0)
        {
            llOwnerSay("Bad server channel.");
            return;
        }

        source = "";

        llListenRemove(browsing_handle);
        do;while(!(browsing_channel = (integer)llFrand(65536.0) | ((integer)llFrand(65536.0) << 16)));
        browsing_handle = llListen(browsing_channel, "", "", "");

        // object name is optional and only included here because of the specifications
        llShout((integer)llList2String(u, 2), "U" + TightListDump([llList2String(u, 0), "0", (string)browsing_channel, llGetObjectName()], "&"));

    }
    else if (llList2String(u, 1) == "1"){
        // requesting a page to an email server

        source = "";

        // using the last unused display prim as remailer
        if (emailer <= max_prim_used + link_offset) emailer = llGetNumberOfPrims();

        // same as above, name is included for following the specifications
        llMessageLinked(emailer, 1100, TightListDump([emailer, llList2String(u, 2), tltp_str], ";"), (key)("U" + TightListDump([llList2String(u, 0), "1", llGetKey(), llGetObjectName()], "&") ) );
        emailer = emailer - 1;
    }
    else if (llList2String(u, 1) == "2"){
        // read a joint notecard page
        joint_page_key = llList2String(u, 0);
        joint_page_line = (integer)llList2String(u, 2);
        joint_page_query = llGetNotecardLine(joint_page_key, joint_page_line);
    } else {
        llOwnerSay("Unknown transport method for URL: " + llList2CSV(u));
        return;
    }
}

parse(string command, string hook)
{
    string a = llGetSubString(command, 0, 0);
    command = llDeleteSubString(command, 0, 0);
    integer b = llListFindList(["A","a",2,"K","k",5,"P","p"],[a]);
    if (b+1)    //animations, keys, permissions all handled by a specialized script.
    {
        if ((~status & 4) && ((b == 0) || (b == 1)))
            return;
        llMessageLinked(LINK_THIS, b + 2010, command, llGetOwner());
    } else if (a == "U") {
        // received URL
        go(command);
    } else if ((b = (a == "T")) || a == "J") {
        // received TLML
        if(hook == "")
            hook = TightListDump([TightListDump(current_url, "#"), llGetLocalRot(), llList2String(current_url, 0), llList2String(current_url, 3)], "&");
        if(b)
            tlml(command, hook);
        else
        {
            // this is an internal command that only works for TLML-L pages
            // It is used to navigate a TLML-L page.
            llMessageLinked(LINK_SET, 6000, command, hook);
        }
    } else if (a == "t") {
        // received TLML key
        page_key = command;
        page_line = 0;
        page_query = llGetNotecardLine(page_key, page_line);
    } else if ((b = (a == "x")) || a == "X") {
        // received XTM or XTMP
        llMessageLinked(LINK_SET, (!b) * 3000 + b * 3100, command,
                            (key)TightListDump([llList2String(current_url, 2),
                            llList2String(current_url, 0)], "*"));
    } else if (a == "c") {
        // received owner chat request
        llOwnerSay(command);
    } else if (a == "W") {
        // received sleep request
        if ((float)command > max_sleep) { llSleep(max_sleep); } else
            llSleep((float)command);
    } else if (a == "s") {
        llStopSound();
    } else {
        list com = TightListParse(command);
        if (a == "C") {
            // received chat request
            if (llGetListLength(com) > 1)
                llShout((integer)llList2String(com, 0), llList2String(com, 1));
            else llSay(0, llList2String(com, 0));
        } else if (a == "D") {
            if(0 > b = (integer)llList2String(com,0))
                b = 0;
            integer c = b;
            if (llGetListLength(com) > 1)
            {
                c = (integer)llList2String(com,1);
                if(c < b)
                    c = b;
            };
            do    //this is intentional.
                llMessageLinked(b + link_offset, 8999,"individual","");
            while(++b < c);
        } else if (1 + b = llListFindList(["S", "L","l"],[a])) {
            if(status & 2)
            {    // received sound key
                float v = (float)llList2String(com, 1);
                a = llList2String(com, 0);
                if (v <= 0.0)
                    v = 0.5;
                if(b == 2)
                    llLoopSound(a, v);
                else if(b == 1)
                    llPlaySound(a, v);
                else
                    llTriggerSound(a, v);
            }
        } else if (a == "N") {
            // texture notecard go
            llMessageLinked(LINK_SET, 5200, llList2String(com,0),
                                TightListDump(llDeleteSubList(com, 0, 0), "*"));
        } else if (a == "R") {
            // received RPC command
            if (status & 8)
                llMessageLinked(LINK_THIS, 4400, llList2String(com,0),
                                TightListDump(llDeleteSubList(com, 0, 0), "*"));
        } else if (a == "Z") {
            // received RemoteLoad command
            if (status & 0x10)
                llMessageLinked(LINK_SET, 8998, "", "");
        } else if (a == "M") {
            b = -llGetListLength(com);
            while(b)
                parse(llList2String(com,b++), hook);
        }
    }
}

tlml(string t, string hook)
{
    // extracting the target display prim number
    integer ind = llSubStringIndex(llDeleteSubString(t, 0, 0), llGetSubString(t, 0, 0));
    integer target = (integer)llGetSubString(t, 1, ind);

    if (target >= 0) {
        // sending the TLML to the display
        if (target > llGetNumberOfPrims() - link_offset)
        {
            llOwnerSay("Not enough prims to display this page.");
            return;
        }
        if (target > max_prim_used) max_prim_used = target;
        llMessageLinked(target + link_offset, 1000, llDeleteSubString(t, 0, ind), hook);
    } else {
        if (target == -99) { clear(); } else
        if (target == -1) { llOwnerSay("Server is unavailable !"); } else
        if (target == -2) { llOwnerSay("Server error !"); } else
        if (target == -3) { llOwnerSay("Access denied !"); } else
        if (target == -4) { llOwnerSay("Page not found !"); } else
        if (target == -97)
        {
            float delay = (float)llDeleteSubString(t, 0, ind);
            if (delay > max_sleep) delay = max_sleep;
            llSleep(delay);
        } else if (target != -98) {
            llOwnerSay("Server response not recognized: " + t);
        }
    }
}

clear()
{
    llMessageLinked(LINK_ALL_OTHERS, 8999, "nevermind", nullk);
    max_prim_used = 0;
}

string generate_hook(list page, string server)
{
    return TightListDump([TightListDump(page , "#"), llGetLocalRot(), server], "&");
}

default
{
    state_entry()
    {
        llSetTimerEvent(6.0);
        browsing_handle = 0;
        current_url = ["", "", "", ""];
        llSetText("", zerov, 0.0);
    }

    dataserver(key id, string data)
    {
        if (id == page_query)
        {
            // reading of remote notecards
            if (data != EOF)
            {
                parse(data, "");
                page_line = page_line + 1;
                page_query = llGetNotecardLine(page_key, page_line);
            }
        } else if (id == joint_page_query)
        {
            // reading of remote joint notecards
            llMessageLinked(LINK_ALL_OTHERS, 6000, data,
                generate_hook([joint_page_line, 2, joint_page_key, llList2String(current_url,3)], joint_page_key));
        }
    }

    timer()
    {
        llGetNextEmail("", "");
    }

    email(string time, string address, string subj, string message, integer left)
    {
        integer n = 0;
        if (debug) llOwnerSay("Received email: " + subj + ": " + message);//$[E20012]
        if (subj == tltp_str)
        {
            n = llSubStringIndex(message, "\n\n");
            message = llDeleteSubString(message, 0, n  + 1);
            key newsource = (key)llGetSubString(address, 0, llStringLength(message) - 20);
            if (llStringLength(source))
            {
                if (source != newsource)
                {
                    llOwnerSay("Message from unknown source, discarded.");
                }
            } else {
                source = newsource;
                newsource = "";
            }
            if(llGetSubString(address,-19, -1) == "@lsl.secondlife.com")
            {
                current_url = llListReplaceList(current_url, [(string)source], 2, 2);
            }
            else
                current_url = llListReplaceList(current_url, [address], 2, 2);

            list commands = llParseStringKeepNulls(message, ["\n"], []);

            integer m = llGetListLength(commands);
            for(n = 0; n < m; ++n)
                parse(llList2String(commands, n), "");

            if (left > 0) llGetNextEmail("", "");
        }
    }

    listen(integer chan, string name, key id, string msg)
    {
        if (source)
        {
            if (source == id)
            {
                parse(msg, "");
            } else {
                llOwnerSay("Message from unknown source, discarded.");
            }
        } else {
            source = id;
            parse(msg, "");
        }
    }

    link_message(integer part, integer code, string msg, key id)
    {
        if (code == 2000)
        {
            parse(msg, id);
        } else if (code == 12345)
        {
            status = (integer)msg;
            if (~status & 2)
            {
                llStopSound();
            }
        }
    }
}

// TLTP Browser Interface
// Version 0.5

integer debug = 0;

// constants
key nullk = NULL_KEY;
vector zerov = ZERO_VECTOR;

// info about browser
integer status = 2;            // browser's display is visible, and listening to adverts and other stuff
                    // 0x1: listening to adverts (used in this script)
                    // 0x2: sound on
                    // 0x4: anims on (off by default)
                    // 0x8: RPC/Plugins on (off by default)
                    // 0x10: RemoteScript on (off by default)

integer advert_channel = -9;
integer advert_handle;
integer channel;            // channel for menu
integer handle;                // handle for menu
integer action;                // selected menu option
float discovery_timeout = 6.0;        // halftime before forgetting a server

// info about available servers
list advertised_names;
list advertised_urls;
list advertised_still;
list bookmarked_names;
list bookmarked_urls;
integer bkm_part;            // slice of the bookmarks to display
integer adv_part;            // slice of the adverts to display

string current_url;

// info about inventory reading
key query;
integer line;

// colors
vector color_flash = <1,0,0>;
vector color_on = <1,1,0>;
vector color_off = <0.2, 0.2, 0.2>;

// courtesy of Strife Onizuka
list TightListParse(string a)
{
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [b],[]);
}
string TightListDump(list a, string b)
{
    string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) == -1)
            jump end;
    integer d = -llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b);
}

clear()
{
    llMessageLinked(LINK_THIS, 2000, "T/-99", nullk);
}

go(string url)
{
    llOwnerSay("Fetching page.");
    current_url = url;
    llMessageLinked(LINK_THIS, 2000, "U" + url, nullk);
}

on()
{
    status = status | 1;
    llSetColor(color_on, ALL_SIDES);
    llListenRemove(advert_handle);
    advert_handle = llListen(advert_channel, "", "", "");
    llSetTimerEvent(discovery_timeout);
}

off()
{
    status = status & -2;
    llSetColor(color_off, ALL_SIDES);
    llListenRemove(advert_handle);
    llSetTimerEvent(0.0);
    advertised_names = [];
    advertised_still = [];
    advertised_urls = [];
    current_url = "";
}

config(string msg)
{
    if(action && msg == "Back")
    {
        action = 0;
        menu();
    }
    else if (action && msg == "More")
    {
        if(action == 1)
            bkm_part = (bkm_part + 1) % llGetListLength(bookmarked_names);
        else
            adv_part = (adv_part + 1) % llGetListLength(advertised_names);
        menu();
    }
    else
    {
        if (action == 1)
        {
            // from bookmarks menu
            integer i = llListFindList(bookmarked_names, [msg]);
            if (i < 0)
                llOwnerSay("Bookmark not found.");
            else
                go(llList2String(bookmarked_urls, i));
        }
        else if (action == 2)
        {
            // from adverts menu
            integer i = llListFindList(advertised_names, [msg]);
            if (i < 0)
                llOwnerSay("Server not found.");
            else
                go(llList2String(advertised_urls, i));
        } else {
            if (action = (1 + llListFindList(["Bookmarks", "Connect"],[msg])))
            {
                menu();
                return;
            }
            else
            {
                if (msg == "Reload")
                    go(current_url);
                else if (msg == "On")
                {
                    on();
                }
                else if (msg == "Off")
                {
                    off();
                }
                else if (msg == "Close") {
                    current_url = "";
                    llSetText("", zerov, 0.0);
                    clear();
                }
                else if (msg == "Sound Off" || msg == "Sound On")
                {
                    status = (status & -3) | (2 * (msg == "Sound On"));
                    update(status);
                }
                else if (msg == "Anims Off" || msg == "Anims On")
                {
                    status = (status & -5) | (4 * (msg == "Anims On"));
                    update(status);
                }
                else if (msg == "Plugins Off" || msg == "Plugins On")
                {
                    status = (status & -9) | (8 * (msg == "Plugins On"));
                    update(status);
                }
            }
        }
        llListenRemove(handle);
    }
}

update(integer u)
{
    llMessageLinked(LINK_THIS, 12345, (string)u, "");
}

menu()
{
    string opt;
    list btns;
    if (action == 0)
    {
        // main menu
        opt = "Detected servers: " + (string)llGetListLength(advertised_urls);
        if (status & 1)
        {
            btns = ["Off"];
        } else {
            btns = ["On"];
        }
        btns += ["ExitMenu", "Options"];
        if (llGetListLength(bookmarked_names) > 0) btns += ["Bookmarks"];
        if (llGetListLength(advertised_urls) > 0) btns += ["Connect"];
        if (llStringLength(current_url)) btns += ["Close", "Reload"];
    } else if (action == 1) {
        // display bookmarks
        opt = "Connect to:";
        if (llGetListLength(bookmarked_names) < 12)
        {
            btns = bookmarked_names + ["Back"];
        } else {
            btns = llList2List(bookmarked_names, bkm_part * 10, bkm_part * 10 + 9) + ["More", "Back"];
        }
    } else if (action == 2) {
        // display adverts
        opt = "Connect to:";
        if (llGetListLength(advertised_names) < 12)
        {
            btns = advertised_names + ["Back"];
        } else {
            btns = llList2List(advertised_names, adv_part * 10, adv_part * 10 + 9) + ["More", "Back"];
        }
    } else {
        // display options
        opt = "Sound:\t";
        if (status & 2)
        {
            opt += "On";
            btns = ["Sound Off"];
        } else {
            opt += "Off";
            btns = ["Sound On"];
        }
        opt = "\nAnimations:\t";
        if (status & 4)
        {
            opt += "On";
            btns = ["Anims Off"];
        } else {
            opt += "Off";
            btns = ["Anims On"];
        }
        opt = "\nPlugins:\t";
        if (status & 8)
        {
            opt += "On";
            btns = ["Plugins Off"];
        } else {
            opt += "Off";
            btns = ["Plugins On"];
        }
        btns += ["Back", "ExitMenu"];
    }
    llDialog(llGetOwner(), opt, btns, channel);
}

default
{
    state_entry()
    {
        bkm_part = 0;
        adv_part = 0;
        current_url = "";
        llSetText("", zerov, 0.0);
        if (status & 1) on();
    }

    changed(integer c)
    {
        if (c & CHANGED_INVENTORY)
        {
            if (llGetInventoryNumber(INVENTORY_NOTECARD) > 0)
            {
                line = 0;
                llOwnerSay("Adding bookmarks...");
                query = llGetNotecardLine(llGetInventoryName(INVENTORY_NOTECARD, 0), line);
            }
        }
    }

    dataserver(key id, string data)
    {
        if (id == query)
        {
            if (data != EOF)
            {
                // adding a bookmark from notecard
                if (llGetSubString(data, 0, 0) != "U") return;
                list l = TightListParse(llDeleteSubString(data, 0, 0));
                if (llGetListLength(l) != 4)
                {
                    llOwnerSay("Bad bookmark skipped: " + data);
                } else {
                    if (llListFindList(bookmarked_urls, [data]) < 0)
                    {
                        string name_to_bkm = llList2String(l, 3);
                        if (llStringLength(name_to_bkm) > 24)
                            name_to_bkm = llGetSubString(name_to_bkm, 0, 23);
                        bookmarked_names += [name_to_bkm];
                        bookmarked_urls += [llDeleteSubString(data, 0, 0)];
                    } else llOwnerSay("Redundant bookmark skipped: " + data);
                }
                line = line + 1;
                query = llGetNotecardLine(llGetInventoryName(INVENTORY_NOTECARD, 0), line);
            } else {
                llRemoveInventory(llGetInventoryName(INVENTORY_NOTECARD, 0));
                if (llGetInventoryNumber(INVENTORY_NOTECARD) > 0)
                {
                    line = 0;
                    llOwnerSay("Adding more bookmarks...");
                    query = llGetNotecardLine(llGetInventoryName(INVENTORY_NOTECARD, 0), line);
                } else llOwnerSay("Done adding bookmarks.");
            }
        }
    }

    timer()
    {
        if (llGetTime() > discovery_timeout)
        {
            integer i;
            integer max = llGetListLength(advertised_names);
            llResetTime();
            for (i = 0; i < max; ++i)
            {
                if (llList2Integer(advertised_still, i))
                {
                    // server still present
                    advertised_still = llListReplaceList(advertised_still, [0], i, i);
                } else {
                    // server wasn't there last time, remove it
                    advertised_names = llDeleteSubList(advertised_names, i, i);
                    advertised_urls = llDeleteSubList(advertised_urls, i, i);
                    advertised_still = llDeleteSubList(advertised_still, i, i);
                }
            }
            if (llGetListLength(advertised_names) == 0)
                llSetColor(color_on, ALL_SIDES);
        }
    }

    touch_start(integer c)
    {
        if (llDetectedKey(0) != llGetOwner()) return;
        action = 0;
        adv_part = 0;
        bkm_part = 0;
        llListenRemove(handle);
        channel = (integer)llFrand(500000000) - 800000000;
        handle = llListen(channel, "", llGetOwner(), "");
        llResetTime();
        menu();
    }

    listen(integer chan, string name, key id, string msg)
    {
        if (chan == channel)
        {
            // configuration command received
            llResetTime();
            config(msg);
        } else if (chan == advert_channel)
        {
            if (llGetSubString(msg, 0, 0) == "U")    // only URLs
            {
                integer inde = llListFindList(advertised_urls, [llDeleteSubString(msg, 0, 0)]);
                if (inde < 0)// only URLs we don't have
                {
                    list temp = TightListParse(llDeleteSubString(msg, 0, 0));
                    if (llGetListLength(temp) >= 4) // skip incomplete URLs
                    {
                        llSetColor(color_flash, ALL_SIDES);
                        string na = llList2String(temp, 3);
                        llOwnerSay("Found server: " + na);
                        if (llStringLength(na) > 24)
                            na = llGetSubString(na, 0, 23);
                        advertised_names += [na];
                        advertised_urls += [llDeleteSubString(msg, 0, 0)];
                        advertised_still += [1];
                    }
                    else if(debug)
                        llOwnerSay("Incomplete broadcasted URL");
                } else {
                    // refreshing the server if it exists in the list
                    advertised_still = llListReplaceList(advertised_still, [1], inde, inde);
                }
            }
        }
    }
}

TLTP Browser - Permissions Handler:
Untested, you don't need this but if you want to play animations etc then you need this.
Not to mention that the key handler hasn't been writen, nor the specs for how it should be writen
// TLTP Browser - Permissions Handler
// Version 0.11
// Script handles anything that requires permissions.

list ANIMATIONS;
list keys;
list anim;
list ranim;
list sanim;
integer perms;
integer request_perms;
integer status;
list ianim;
integer ianimi;
list key_masks;
list key_urls;
key user;

list TightListParse(string a)
{
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [a=b],[]);
}
string TightListDump(list a, string b)
{
    string c = (string)a;
    if(llStringLength(b)==1)
        if(llSubStringIndex(c,b) == -1)
            jump end;
    integer d = -llStringLength(b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" ");
    while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
        ++d;
    b = llGetSubString(b,d,d);
    @end;
    c = "";//save memory
    return b + llDumpList2String(a, b);
}

string getAnim(string c)
{
    if(llGetInventoryType(c) == INVENTORY_ANIMATION);
    else if(llListFindList(ANIMATIONS,[c = llToLower(c)]) != -1);
    else
    {
        //Don't use case sensative seperators!!!
        integer a = llListFindList(TightListParse(llToLower(TightListDump(ianim,""))),[c]);
        c = llList2String(ianim + [""], a);
    }
    return c;
}

integer PermCheck(integer a, integer b, key d)
{
    if(d == "")
        d = llGetOwner();
    if(user != d)
        request_perms = perms = 0;
    if(perms & a)
        return 1;
    else
    {
        status = status | b;
        user = d;
        request_perms = request_perms | perms | a;
        if(!(status & 16))
            llSetTimerEvent(1.0);
        status = status | 16;
    }
    return 0;
}

stopAllAnim(key d)
{
    if (PermCheck(PERMISSION_TRIGGER_ANIMATION, 1, d))
    {
        integer a = -llGetListLength(ranim);
        integer b;
        string c;
        while(a)
        {
            llStopAnimation(c = llList2String(ranim, a++));
            if(1 + b = llListFindList(ranim, [llToLower(c)]))
                ranim = llDeleteSubList(ranim,b,b);
        }
        sanim = ranim = [];
    }
}

stopAnim(string c, key d)
{
    if (PermCheck(PERMISSION_TRIGGER_ANIMATION, 2, d))
    {
        llStopAnimation(c);
        integer b = llListFindList(ranim, [llToLower(c)]);
        if(1 + b)
            ranim = llDeleteSubList(ranim,b,b);
    }
    else
        sanim += c;
}

startAnim(string c, key d)
{
    if (PermCheck(PERMISSION_TRIGGER_ANIMATION, 4, d))
    {
        if(llGetInventoryType(c) == INVENTORY_ANIMATION);
        else if(llListFindList(ANIMATIONS,[c = llToLower(c)]) != -1);
        else
        {
//            llOwnerSay("Animation \"" + c + "\"not found");
            jump skip;
        }
        llStartAnimation(c);
        if(-1 == llListFindList(ranim, [c = llToLower(c)]))
            ranim += c;
    } else {
        anim += c;
    }
    @skip;
}

takeKeys()
{//will write this later
    keys = [];
}

release_key(string c, key d)
{;}
release_keys()
{;}

refresh()
{
    integer a = llGetInventoryNumber(INVENTORY_ANIMATION);
    if(a != ianimi)
    {//we coud do a better check then this but i'm too lazy :P
        ianim = [];
        ianimi = a;
        while(--a)
            ianim += llGetInventoryName(INVENTORY_ANIMATION,a);
    }
}

default
{
    state_entry()
    {
        ANIMATIONS = ["aim_l_bow","aim_r_bazooka","aim_r_handgun","aim_r_rifle",
                "angry_fingerwag","angry_tantrum","away","backflip","blowkiss","bow","brush",
                "busy","clap","courtbow","crouch","crouchwalk","dance1","dance2","dance3",
                "dance4","dance5","dance6","dance7","dance8","dead","drink","express_afraid",
                "express_afraid_emote","express_anger","express_anger_emote","express_bored",
                "express_bored_emote","express_cry","express_cry_emote","express_disdain",
                "express_embarrased","express_embarrassed_emote","express_frown","express_kiss",
                "express_laugh","express_laugh_emote","express_open_mouth","express_repulsed",
                "express_repulsed_emote","express_sad","express_sad_emote","express_shrug",
                "express_shrug_emote","express_smile","express_surprise","express_surprise_emote",
                "express_tongue_out","express_toothsmile","express_wink","express_wink_emote",
                "express_worry","express_worry_emote"," falldown","female_walk","fist_pump","fly",
                "flyslow","hello","hold_l_bow","hold_r_bazooka"]+["hold_r_handgun","hold_r_rifle",
                "hold_throw_r","hover","hover_down","hover_up","impatient","jump","jumpforjoy",
                "kick_roundhouse_r","kissmybutt","land","laugh_short","motorcycle_sit","musclebeach",
                "no_head","no_unhappy","nyanya","peace","point_me","point_you","prejump","punch_l",
                "punch_onetwo","punch_r","rps_countdown","rps_paper","rps_rock","rps_scissors","run",
                "salute","shoot_l_bow","shout","sit","sit_female","sit_generic","sit_ground",
                "sit_to_stand","sleep","smoke_idle","smoke_inhale","smoke_throw_down","snapshot",
                "soft_land","","stand","stand_1","stand_2","stand_3","stand_4","standup","stretch",
                "stride","surf","sword_strike_r","talk","throw_r","tryon_shirt","turn_180",
                "turnback_180","turnleft","turnright","type","walk","whisper","whistle",
                "wink_hollywood","yes_happy","yes_head","yoga_float"];
        refresh();
    }
    link_message(integer a, integer b, string c, key d)
    {
        if(b == 2012)
            stopAllAnim(d);
        else if(b == 2010)
            startAnim(c,d);
        else if(b == 2011)
            stopAnim(c,d);
        else if(b == 2013)
        {
            keys += c;
            if(PermCheck(PERMISSION_TAKE_CONTROLS, 8, d))
                takeKeys();
        }
        else if(b == 2014)
            release_key(c,d);
        else if(b == 2015)
            release_keys();
        else if(b == 2016)
            PermCheck((integer)c, request_perms = perms = 0, d);
        else if(b == 2017)
            PermCheck((integer)c, 0, d);
    }
    run_time_permissions(integer p)
    {
        user = llGetPermissionsKey();
        perms = p;
        if(p & PERMISSION_TAKE_CONTROLS && (status & 8))
            takeKeys();
        if (p & PERMISSION_TRIGGER_ANIMATION)
        {
            integer a = -llGetListLength(anim);
            string b;
            if(a)
            {
                do
                {
                    llStartAnimation(b = llList2String(anim, a));
                    if(-1 == llListFindList(ranim, [b = llToLower(b)]))
                        ranim += b;
                }
                while(++a);
                anim = [];
            }
            if(a = -llGetListLength(sanim))
            {
                integer c;
                do
                {
                    llStopAnimation(b = llList2String(sanim, a));
                    if(1 + c = llListFindList(ranim, [llToLower(b)]))
                        ranim = llDeleteSubList(ranim,c,c);
                }
                while(++a);
                sanim = [];
            }
        }
    }
    changed(integer a)
    {
        if(a & CHANGED_INVENTORY)
            refresh();
    }
    timer()
    {
        llRequestPermissions(user, request_perms);
        request_perms = 0;
        llSetTimerEvent(0.0);
        status = status & -17;
    }
//    control(key a, integer b, integer c)
//    {
//        llMessageLinked(LINK_THIS,url_code,"","")
//    }
}


ExperimentllRezObject

Experiments on llRezObject

StefanNilsson tried to vary mass and velocity, and got these delays (in seconds) per rez:

Mass (kg) | 20m/s | 15m/s | 10m/s | 5m/s
0.001 | 0.83 | 0.87 | 0.83 | 0.87
0.01 | 0.93 | 0.90 | 0.83 | 0.83
0.1 | 0.93 | 0.90 | 0.87 | 0.83
0.2 | 4.77 | 3.73 | 2.80 | 1.80
0.3 | 6.77 | 5.33 | 3.87 | 2.33
0.4 | 8.73 | 6.73 | 4.77 | 2.80
0.5 | 10.07 | 8.23 | 5.80 | 3.30
0.6 | 12.77 | 9.73 | 6.77 | 3.80
0.7 | 14.20 | 11.27 | 7.80 | 3.67
0.8 | 16.77 | 12.83 | 8.90 | 4.87
Note that mass < 0.1 kg is treated as 0.1 - which suggests that mass has a precision of 100g. It's also evident that the delay varies with mass as well as inital velocity. - StefanNilsson

This seems to be fitting the function "delay = llVecMag(velocity) * mass + e" where 0 < e < 1 is the error, i think for low mass objects they treat the mass as zero. i would estimate that the value of e is 0.85?0.05 - BW


expression

The result of arithmetic/math operations.

dictionary.com entry

math


F32

Some functions (llLookAt, llRotLookAt, etc.) have tooltips within the LSL editor that say "F32" (indicating a 32-bit float) rather than simply "float". This is technically correct, however all floats in LSL are 32-bit. So if it says "F32", just pretend it says "float", okay?

Types


Face

A face is a shortened form of a prim's "surface". In LSL this is commonly referred to as side, though there are some differences. In normal 3D usage, a face is a single polygon.

For example, a newly created sphere has one face: the outside. It is the only surface that can be seen from any camera angle and that can have a texture applied to it so it is referred to as face 0 (zero).

A sphere that has been hollowed out has 2 faces: the outside (face 0) and the inside (face 1).

Other face possibilites exist, including the edges of a cut, hollowed out prim, and other such possible surfaces. See side for a list of faces available on an axis-aligned cube (including hollow and cut adjustments).

See debug for more info on the chain faces option.

Prim | Side


FeatureRequest

While LSL feature requests are not to be housed on the LSLWiki (the wiki is for documenting existing functionality only) you may post links to them on this page. Please post new functions at the top of the table. This page is intended to be reviewed by KellyLinden (linky).

Name | Description | Status
llSegmentString | Divide a string into a list so each segment is no longer then a given length | New
llRequestMailWhois new mail headers | Whois to help secure email comms in SL | New
llGetInventoryDesc | Return the description of an inventory item in an object, like llGetInventoryKey and llGetInventoryType | New
llWriteNotecard llModifyNotecard | Write/modify a notecard with a script | Rejected

Status | Meaning
New | Yet to be reviewed/commented on by LL.
Rejected | Proposal reject by LL.
Pending | LL has thought it was a good idea but nothing has not been implemented
Preview | Function is available in preview.
Implemented | Should be removed from the table.


FIFO

FIFO is an acronym for "First In, First Out". It is a programming data structure in which the oldest data added is processed first. In other words, think of a line of people (a queue): the first person in line is generally the first person to get through the line. In a perfect world, folks would get through the line in the same order in which they entered it.

LSL | LIFO


fileformat

File Formats

Animation

BVH

Audio

MP3
OGG
WAV

Image

BMP
GIF
JPEG
TGA

Other

XML

Video

QuickTime MOV
MPEG/MP4
ShockWave Flash SWF

Cache

DSF:
RIFF-WAVs (sounds) temporarily stored in the cache but will eventually be put into the larger data.db2.x.... cache database files. You can often find your current (and recent) SL session's sounds here.
LSLTEXT:
scripts you have recently edited
SLC:
objects in the sim

Audio | Image | Video | Cache


Firefox

Firefox (note the correct capitalization) is the current name of Mozilla's browser-only package, formerly known as "Firebird" and "Phoenix". Mozilla's Gekko web browsing engine (what Firefox is based off of) is being implemented into SL and, so far, is used in its help menu.

Firefox is the best web browser there is. You should be using it to read the LSL Wiki Mirror 7-7-7.

Get it at http://www.mozilla.org/products/firefox/.


Flexible

Since SL 1.9.1, a prim can be made flexible. Flexible prims ("flex prims") are a client-side effect and are automatically made phantom and non-physical. However, flex prims can be linked to a solid, physical root prim and will, thus, take on those properties as well (though it's buggy).

Constants
llSetPrimitiveParams PRIM_FLEXIBLE parameter to toggle a prim's flexibility on/off, softness (rigidity), gravity, friction/drag, wind, tension, and force:
llSetPrimitiveParams([PRIM_FLEXIBLE,1,2,.3,2,0,1,<0,0,0>]);

Notes:
  • Flexibility ignores prim shear but the force PRIM_PARAMETER parameter can simulate shearing to a degree.
  • Flexible objects add virtually zero burden on the simulator, which sees them as identical to any other primitive; on the viewer, they are very tightly time-sliced so as to scale with the capabilities of the machine and never bring down framerate in a significant way. (Yedwab Linden, 4/14/06)
  • Flexible objects already have an LOD system built into them, so things which are further away update a lot less frequently, and are given much less priority than nearer flexible objects.
    • If you enable the Debug Console in the Debug menu, it currently spams the various bins ("0: " being the farthest bin and "9: " being the nearest avatar attachments) and the number of flexible objects rebuilt per frame, how many total in the bin, and the time per frame in seconds spent on that bin. So you can try to figure out what the algorithm *actually* does, as opposed to what you think it does. (Yedwab Linden, 4/15/06)
  • In order to attach objects to the end of a flexible object, we need complete hierarchy. This is a separate feature which we're not currently working on, but should happen sometime in the future and which will enable attaching things to the end of flexible objects automatically.
    • Flexible objects are currently not elastic (that is, they maintain their initial length). To make both ends fixed creates a problem either with excessive elasticity or instability, and these are issues which were not in the scope of "Flexible Objects 1.0". (hint, hint) (Yedwab Linden, 4/15/06)
  • Linking things the to the end of flexible objects requires the notion of a link hierarchy with true parents and children. This will come later, but the extension to flexible objects will be automatic. (Yedwab Linden, 4/17/06)

Prim


Flying

To determine if an agent is flying, use llGetAgentInfo.

Note: you cannot force an agent to fly on its own. The user must initiate flight on their client.


FlyingTakoTips

The Flying Tako is a free ($1) in world sailing boat that is driven by the wind. The scripts are also public domain at LibrarySailboat (are you sure they have been released into the public domain?). As pointed out in the comments section this page discusses how to use the scripts and the scripted object in world, and hopefully with the scripts shows some of the ways that it is possible to model RL activities in world. This page is more about sailing than scripting, to complement the scripting pages.

Sailing the Flying Tako

Sailing the Flying Tako is much like sailing a dingy IRL. The skill of the sailor and their ability to use the winds and their skills in combination is critical to sailing it well.

The winds in SL are random and fickle in the extreme. This makes up, to some extent, for the fact that the Flying Tako won't spill you if you make a mistake, although it can also mean frantic work as you try to sail around in the ever-shifting winds.

Sailing, obviously, uses the wind and the sails to propel you through the water. But once you're moving things change a little as momentum will make you tend to keep moving.

Let's imagine you're completely still in the water (just starting to sail say).

If the wind is blowing from straight ahead of you, you won't be able to go forwards, and will, in fact, be blown backwards. In fact if the wind is within 35 degrees of straight ahead you won't be able to gain any forwards impetus from the winds. This gives you a V-shape from which you need to avoid the winds if possible, shown in Figure A.

Image hosted by Photobucket.com

Now imagine the other situation, the wind is blowing from dead astern (behind you). Obviously you can get forward impetus from this situation: the wind will push the sail and the boat along. The amount of impetus and thus your maximum speed is defined by the angle of the sail to the vessel. You can't control that angle directly, but using the sheets (the ropes to the sail speaking very loosely) you can limit the maximum angle of the sail between +/- 5 and +/- 90 degrees. If you set the sheet to 90 in this situation the sail will also move out to 90 degrees and catch the maximum wind and push you forwards as fast as possible.

Adjusting the sheet angle to keep the sail angle optimal is what will keep you occupied throughout the race. Thanks to the smart way the HUD is configured working out the optimal angle is easy: you want the sail at -1/2 the wind angle. You can get that by keeping the sheet at half the wind angle if you ignore the sign. Actually, because the winds change faster than the HUD updates you will come to rely on your pennant too, the particle stream from the top of your mast that shows the wind direction more quickly than the HUD, but that will come with practise.

You can adjust the sheet angle by saying 'sheet 10' or 'sheet -10' or similar, or by using the up and down arrow keys. Saying sheet and a positive number e.g. sheet 45 will let the sheet out, as will using the up arrow. Letting the sheet out means the maximum sail angle will increase: the sail can point more out to the side. Saying a negative number, e.g. sheet -50 or the down arrow will decrease the sheet angle, limiting the sail to closer to pointing back along the boat.

Although it may appear perverse in fact you sail fastest with the wind at about +/-40 degrees (that's to one side of ahead, but pretty much ahead still) because the curve of the sail in such circumstances creates aerodynamic thrust (at least, In Real Life), which enables you to sail faster than the wind. You don't have to understand that, but it is worth knowing that sailing at about 40 degrees to the wind is the fastest way to sail.

OK, that's the basics, and for general sailing that's all you need. Find one of those nice empty void sims (Balance actually has a public beach where you can put your boat in Balance (250,240)) and sail on it for a while. You can practise reading the wind and its changes and adjusting the sheets and so on without crashing into anything. Experiment to see where you sail best.

There are other factors. If you are racing there is a distinct 'racing line' that you may want to follow, the shortest route around the course. Actually you should be looking for the quickest route around the course, not the shortest. Imagine starting a race into a headwind. You couldn't start along the shortest course, but sailing at 40 degrees to the shortest route will give you the maximum thrust from that wind and you will sail most quickly over the ground. Judging where and when to turn to keep to that fastest course is part of the art of racing. Figure B shows a tacking course which will let you sail fastest along a course into a headwind.

Image hosted by Photobucket.com

You can, actually, appear to sail into a headwind, and at certain times you may have to if there is a very constricted part of the course. You can achieve this seeming miracle because of momentum. If you are sailing at, say, 2 m/s and you turn to sail into a headwind you will slow down, but you will keep moving, so you can coast through a constricted area into a headwind, or turn across the wind so you sail on the other tack (offset at 40 degrees on the other side) so you sail as close to straight ahead as possible.

Keeping moving, keeping moving quickly, is always an advantage, because when the wind sudden swirls and is against you it gives you a few moments to respond to the changed wind without it being disastrous.

For those of you with a background in physics and engineering it may not surprise you to hear of something called leeway. This is a sideways movement of the boat as the force of the wind on the sail is not totally directed forwards. Many factors contribute to this, wind angle and strength and sail angle being the main two. In most circumstances, especially in sailing in SL, this is not too significant, but when sailing in tight spaces the tendency of the boat to drift sideways results in you have to make another level of continuous adjustments to your heading and sheets.

In a race, as IRL, there is a flying start option. You are allowed to hit the line already moving when the timer hits 0. Of course getting it wrong imposes a penalty, if you cross early you have to turn around and recross the line going the right way in order to start your race. Getting this just right is a tricky balancing act, but the two people in the most recent practise race who managed this (at 6 and 8 seconds respectively) placed first and second too.

Tips for practise:

Start sailing on a nice empty sim, it will help! Play with the controls and experiment to find what suits you best. Once you're used to handling the boat practise sailing on rivers too. This lets you get used to working out how to sail in restricted water whilst still getting places.

Remember, however, your dingy is a physical object and will scrape on bridges, buildings etc. In fact it is surprisingly wide at the top: there is a prim up there that you can't see for the HUD, and that can get caught on things too.

There is a practise version of the Flying Tako, in which the wind is constant in both speed and direction. This may not be suitable for river travel, but will let you experiment with relating ground speed to wind speed easily, as well as getting used to the controls.

http://en.wikipedia.org/wiki/Sailing is the wiki page that will contain useful hints, tips and ideas. Google searching will also find many places that supplement their real world sailing classes with web-based resources which can be useful.


Follow Cam

FollowCam functionality allows scripts to control the camera
  • Scripts on attachments and vehicles can now control camera behavior through a call to llSetCameraParams(), once they have acquired the PERMISSION_CONTROL_CAMERA permission
  • The script will control the user's camera as long as the resident doesn't override it (use Alt-zoom or enter mouselook, for example) and the object remains attached or the avatar continues to sit on it:
    • When multiple scripts attempt to control the camera, the last script to set the CAMERA_ACTIVE flag will have control
    • Older scripts will regain control as soon as the newer one is deactivated
  • The new FollowCam scripts enable a huge variety of vehicle-following behaviors:
- For instance, a loose camera setting allows sudden changes in vehicle acceleration to happen without causing dizzying jolts in the camera
    • By turning off the "behindness" feature, the camera will be simply dragged along, but it will not try to stay exactly behind the vehicle
    • Other parameters, like pitch (the angle at which it "looks down" at the vehicle), distance, and others, allow you to customize the cinematics of vehicular fun

FollowCam family of camera functions
  • llRequestPermissions(agent_key, PERMISSION_CONTROL_CAMERA)
    • Requests permissions
      • Note, PERMISSION_CONTROL_CAMERA permissions are only supported for attachments and vehicles at this time
      • Accepting permissions via a dialog is not currently implemented.

  • llClearCameraParams(): Resets all camera values to their default.

  • llSetCameraParams([]): sets up the camera movement rules based on the parameter list
    • CAMERA_ACTIVE (TRUE or FALSE) Default = FALSE
      • Turns on or off scripting control of Scripted Camera
    • CAMERA_DISTANCE (0.5 to 10.0 meters) Default = 3.0
      • Sets how far away the Scripted Camera wants to be from its subject
    • CAMERA_PITCH (-45.0 to 80.0 degrees) Default = 0.0
      • Adjusts the angular amount that the Scripted Camera aims straight ahead vs. straight down, maintaining the same distance. Analogous to 'incidence'
    • CAMERA_FOCUS_OFFSET (<-10,-10,-10> to <10,10,10> meters) Default = <0.0, 0.0, 0.0>
      • Adjusts the position of the Scripted Camera focus position relative to the subject
    • CAMERA_POSITION_LAG (0.0 to 3.0 seconds) Default = 0.1
      • How much the Scripted Camera lags as it tries to move towards its 'ideal' position
    • CAMERA_FOCUS_LAG (0.0 to 3.0 seconds) Default = 0.1
      • How much the Scripted Camera lags as it tries to aim towards the subject
    • CAMERA_BEHINDNESS_ANGLE (0.0 to 180.0 degrees) Default = 10.0
      • Sets the angle in degrees within which the Scripted Camera is not constrained by changes in subject rotation. 180 effectively turns behindness off.
    • CAMERA_BEHINDNESS_LAG (0.0 to 3.0 seconds) Default = 0.0
      • How much the Scripted Camera lags as it tries to stay behind the target if outside of behindness angle
    • CAMERA_POSITION_THRESHOLD (0.0 to 4.0 meters) Default = 1.0
      • Sets the radius of a sphere around the Scripted Camera's ideal position within which it is not affected by subject motion
    • CAMERA_FOCUS_THRESHOLD (0.0 to 4.0 meters) Default = 1.0
      • Sets the radius of a sphere around the Scripted Camera's subject position within which its focus is not affected by subject motion
    • CAMERA_POSITION (vector position within the region)
      • Sets the position of the Scripted Camera
    • CAMERA_FOCUS (vector position within the region)
      • Sets the focus position of the Scripted Camera
    • CAMERA_POSITION_LOCKED (TRUE or FALSE) Default = FALSE
      • Locks the camera position so it will not move
    • CAMERA_FOCUS_LOCKED (TRUE or FALSE) Default = FALSE
      • Locks the camera focus so it will not move

Known issues
  • The camera appears to jitter back and forth. This is a problem with our avatar motion model that will be worse at lower framerate. We are continuing to investigate potential solutions.
  • Free standing objects cannot control the user camera. This will continue to be the case until we can implement a robust mechanism for letting residents cancel scripted camera behaviors.

//Linden Labs
//Dan Linden
integer CHANNEL; // dialog channel
list MENU_MAIN = ["Default", "Overhead Cam", "Spin Cam", "Trap Toggle", "Spaz Cam", "Drop Cam", "Worm Cam", "Side Cam", "Driving Cam", "More...", "Cam ON", "Cam OFF"]; // the main menu
list MENU_2 = ["More...", "...Back"]; // menu 2

integer on = FALSE;
integer flying;
integer falling;
integer spaz = 0;
integer trap = 0;

take_camera_control(key agent)
{
    llOwnerSay("take_camera_control"); // say function name for debugging
    llOwnerSay( (string)agent);
    llRequestPermissions(agent, PERMISSION_CONTROL_CAMERA);
    llSetCameraParams([CAMERA_ACTIVE, 1]); // 1 is active, 0 is inactive
    on = TRUE;
}

release_camera_control(key agent)
{
    llOwnerSay("release_camera_control"); // say function name for debugging
    llSetCameraParams([CAMERA_ACTIVE, 0]); // 1 is active, 0 is inactive
    llReleaseCamera(agent);
    on = FALSE;
}

focus_on_me()
{
    llOwnerSay("focus_on_me"); // say function name for debugging
//    llClearCameraParams(); // reset camera to default
    vector here = llGetPos();
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 0.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.0, // (0 to 3) seconds
        CAMERA_DISTANCE, 0.0, // ( 0.5 to 10) meters
        CAMERA_FOCUS, here, // region relative position
        CAMERA_FOCUS_LAG, 0.0 , // (0 to 3) seconds
        CAMERA_FOCUS_LOCKED, TRUE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) meters
//        CAMERA_PITCH, 80.0, // (-45 to 80) degrees
        CAMERA_POSITION, here + <4,4,4>, // region relative position
        CAMERA_POSITION_LAG, 0.0, // (0 to 3) seconds
        CAMERA_POSITION_LOCKED, TRUE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, ZERO_VECTOR // <-10,-10,-10> to <10,10,10> meters
    ]);
}

default_cam()
{
//    llOwnerSay("default_cam"); // say function name for debugging
    llClearCameraParams(); // reset camera to default
    llSetCameraParams([CAMERA_ACTIVE, 1]);
}

driving_cam()
{
    llOwnerSay("driving_cam"); // say function name for debugging
    default_cam();
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 45.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.5, // (0 to 3) seconds
        CAMERA_DISTANCE, 8.0, // ( 0.5 to 10) meters
        //CAMERA_FOCUS, <0,0,5>, // region relative position
        CAMERA_FOCUS_LAG, 0.05 , // (0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_PITCH, 20.0, // (-45 to 80) degrees
        //CAMERA_POSITION, <0,0,0>, // region relative position
        CAMERA_POSITION_LAG, 0.1, // (0 to 3) seconds
        CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, <3,0,2> // <-10,-10,-10> to <10,10,10> meters
    ]);
}

side_cam()
{
    llOwnerSay("side_cam"); // say function name for debugging
    llClearCameraParams(); // reset camera to default
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 0.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.0, // (0 to 3) seconds
        CAMERA_DISTANCE, 0.0, // ( 0.5 to 10) meters
        //CAMERA_FOCUS, <0,0,5>, // region relative position
        CAMERA_FOCUS_LAG, 0.0 , // (0 to 3) seconds
//        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) meters
//        CAMERA_PITCH, 80.0, // (-45 to 80) degrees
        //CAMERA_POSITION, <0,0,0>, // region relative position
        CAMERA_POSITION_LAG, 0.0, // (0 to 3) seconds
//        CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, <0,3,0> // <-10,-10,-10> to <10,10,10> meters
    ]);
}

rearview_cam()
{
    llOwnerSay("rearview_cam"); // say function name for debugging
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 180.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.0, // (0 to 3) seconds
//        CAMERA_DISTANCE, 10.0, // ( 0.5 to 10) meters
        //CAMERA_FOCUS, <0,0,5>, // region relative position
        CAMERA_FOCUS_LAG, 3.0 , // (0 to 3) seconds
//        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) meters
//        CAMERA_PITCH, 80.0, // (-45 to 80) degrees
        //CAMERA_POSITION, <0,0,0>, // region relative position
//        CAMERA_POSITION_LAG, 0.0, // (0 to 3) seconds
//        CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, <5,2,-2> // <-10,-10,-10> to <10,10,10> meters
    ]);
}

overhead_cam()
{
    llOwnerSay("overhead_cam"); // say function name for debugging
    default_cam();
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 180.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.5, // (0 to 3) seconds
        CAMERA_DISTANCE, 10.0, // ( 0.5 to 10) meters
        //CAMERA_FOCUS, <0,0,5>, // region relative position
        CAMERA_FOCUS_LAG, 0.05 , // (0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_PITCH, 80.0, // (-45 to 80) degrees
        //CAMERA_POSITION, <0,0,0>, // region relative position
        CAMERA_POSITION_LAG, 0.0, // (0 to 3) seconds
        CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, <0,0,0> // <-10,-10,-10> to <10,10,10> meters
    ]);
}

drop_camera_5_seconds()
{
    llOwnerSay("drop_camera_5_seconds"); // say function name for debugging
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 0.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.5, // (0 to 3) seconds
        CAMERA_DISTANCE, 3.0, // ( 0.5 to 10) meters
        //CAMERA_FOCUS, <0,0,5>, // region relative position
        CAMERA_FOCUS_LAG, 2.0, // (0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_PITCH, 0.0, // (-45 to 80) degrees
        //CAMERA_POSITION, <0,0,0>, // region relative position
        CAMERA_POSITION_LAG, 0.05, // (0 to 3) seconds
        CAMERA_POSITION_LOCKED, TRUE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, <0,0,0> // <-10,-10,-10> to <10,10,10> meters
    ]);
    llSleep(5);
    default_cam();
}

worm_cam()
{
    llOwnerSay("worm_cam"); // say function name for debugging
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 180.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.0, // (0 to 3) seconds
        CAMERA_DISTANCE, 8.0, // ( 0.5 to 10) meters
        //CAMERA_FOCUS, <0,0,5>, // region relative position
        CAMERA_FOCUS_LAG, 0.0 , // (0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 4.0, // (0 to 4) meters
        CAMERA_PITCH, -45.0, // (-45 to 80) degrees
        //CAMERA_POSITION, <0,0,0>, // region relative position
        CAMERA_POSITION_LAG, 1.0, // (0 to 3) seconds
        CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 1.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, <0,0,0> // <-10,-10,-10> to <10,10,10> meters
    ]);
}

spaz_cam()
{
    llOwnerSay("spaz_cam for 5 seconds"); // say function name for debugging
    float i;
    for (i=0; i< 50; i+=1)
    {
        vector xyz = llGetPos() + <llFrand(80) - 40, llFrand(80) - 40, llFrand(10)>;
//        llOwnerSay((string)xyz);
        vector xyz2 = llGetPos() + <llFrand(80) - 40, llFrand(80) - 40, llFrand(10)>;
        llSetCameraParams([
            CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
            CAMERA_BEHINDNESS_ANGLE, 180.0, // (0 to 180) degrees
            CAMERA_BEHINDNESS_LAG, llFrand(3), // (0 to 3) seconds
            CAMERA_DISTANCE, llFrand(10), // ( 0.5 to 10) meters
            //CAMERA_FOCUS, xyz, // region relative position
            CAMERA_FOCUS_LAG, llFrand(3), // (0 to 3) seconds
            CAMERA_FOCUS_LOCKED, TRUE, // (TRUE or FALSE)
            CAMERA_FOCUS_THRESHOLD, llFrand(4), // (0 to 4) meters
            CAMERA_PITCH, llFrand(125) - 45, // (-45 to 80) degrees
            CAMERA_POSITION, xyz2, // region relative position
            CAMERA_POSITION_LAG, llFrand(3), // (0 to 3) seconds
            CAMERA_POSITION_LOCKED, TRUE, // (TRUE or FALSE)
            CAMERA_POSITION_THRESHOLD, llFrand(4), // (0 to 4) meters
            CAMERA_FOCUS_OFFSET, <llFrand(20) - 10, llFrand(20) - 10, llFrand(20) - 10> // <-10,-10,-10> to <10,10,10> meters
            ]);
            llSleep(0.1);
    }
    default_cam();
}

spin_cam()
{
    llSetCameraParams([
        CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 180.0, // (0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.5, // (0 to 3) seconds
        //CAMERA_DISTANCE, 10.0, // ( 0.5 to 10) meters
        //CAMERA_FOCUS, <0,0,5>, // region relative position
        CAMERA_FOCUS_LAG, 0.05 , // (0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_PITCH, 30.0, // (-45 to 80) degrees
        //CAMERA_POSITION, <0,0,0>, // region relative position
        CAMERA_POSITION_LAG, 0.0, // (0 to 3) seconds
        CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) meters
        CAMERA_FOCUS_OFFSET, <0,0,0> // <-10,-10,-10> to <10,10,10> meters
    ]);

    float i;
    vector camera_position;
    for (i=0; i< 2*TWO_PI; i+=.05)
    {
        camera_position = llGetPos() + <0, 4, 0> * llEuler2Rot(<0,0,i>);
        llSetCameraParams([CAMERA_POSITION, camera_position]);
    }
    default_cam();
}

setup_listen()
{
    llListenRemove(1);
    CHANNEL = llRound(llFrand(1) * 100000);
    integer x = llListen(CHANNEL, "", "", ""); // listen for dialog answers
}

default
{
    state_entry()
    {
        llSitTarget(<0,0,0.1>,ZERO_ROTATION);
        setup_listen();
        llSetTimerEvent(2);
    }

    touch_start(integer total_number)
    {
integer perm = llGetPermissions();
if (perm & PERMISSION_DEBIT)
{ llOwnerSay(llGetScriptName() + " has PERMISSION_DEBIT perms for "+(string)llGetPermissionsKey()); }
if (perm & PERMISSION_TAKE_CONTROLS)
{ llOwnerSay(llGetScriptName() + " has PERMISSION_TAKE_CONTROLS perms for "+(string)llGetPermissionsKey()); }
if (perm & PERMISSION_TRIGGER_ANIMATION)
{ llOwnerSay(llGetScriptName() + " has PERMISSION_TRIGGER_ANIMATION perms for "+(string)llGetPermissionsKey()); }
if (perm & PERMISSION_ATTACH)
{ llOwnerSay(llGetScriptName() + " has PERMISSION_ATTACH perms for "+(string)llGetPermissionsKey()); }
if (perm & PERMISSION_CHANGE_LINKS)
{ llOwnerSay(llGetScriptName() + " has PERMISSION_CHANGE_LINKS perms for "+(string)llGetPermissionsKey()); }
if (perm & PERMISSION_TRACK_CAMERA)
{ llOwnerSay(llGetScriptName() + " has PERMISSION_TRACK_CAMERA perms for "+(string)llGetPermissionsKey()); }
if (perm & PERMISSION_CONTROL_CAMERA)
{ llOwnerSay(llGetScriptName() + " has PERMISSION_CONTROL_CAMERA perms for "+(string)llGetPermissionsKey()); }
if (perm == 0)
{ llOwnerSay(llGetScriptName() + " has NO perms for "+(string)llGetPermissionsKey()); }

        llDialog(llDetectedKey(0), "What do you want to do?", MENU_MAIN, CHANNEL); // present dialog on click
    }

    listen(integer channel, string name, key id, string message)
    {
        if (llListFindList(MENU_MAIN + MENU_2, [message]) != -1)  // verify dialog choice
//        if (llListFindList(MENU_MAIN, [message]) != -1)  // verify dialog choice
        {
//            llOwnerSay(name + " picked the option '" + message + "'."); // output the answer
            if (message == "More...")
                llDialog(id, "Pick an option!", MENU_2, CHANNEL); // present submenu on request
            else if (message == "...Back")
                llDialog(id, "What do you want to do?", MENU_MAIN, CHANNEL); // present main menu on request to go back

    else if (message == "Cam ON")
    {
        take_camera_control(id);
    }

    else if (message == "Cam OFF")
    {
        release_camera_control(id);
    }

    else if (message == "Default")
    {
        default_cam();
    }

    else if (message == "Driving Cam")
    {
        driving_cam();
    }

    else if (message == "Worm Cam")
    {
        worm_cam();
    }

    else if (message == "Overhead Cam")
    {
        overhead_cam();
    }

    else if (message == "Spaz Cam")
    {
        spaz_cam();
    }

    else if (message == "Side Cam")
    {
        side_cam();
    }

    else if (message == "Drop Cam")
    {
        drop_camera_5_seconds();
    }

    else if (message == "Trap Toggle")
    {
        trap = !trap;
        if (trap == 1) {
            llOwnerSay("trap is on");
        }
        else {
            llOwnerSay("trap is off");
        }
    }

    else if (message == "Spin Cam")
    {
        spin_cam();
    }

        } else
            llOwnerSay(name + " picked invalid option '" + llToLower(message) + "'."); // not a valid dialog choice
    }

    run_time_permissions(integer perm) {
        if ((perm & PERMISSION_CONTROL_CAMERA) == PERMISSION_CONTROL_CAMERA) {
            llSetCameraParams([CAMERA_ACTIVE, 1]); // 1 is active, 0 is inactive
            llOwnerSay("Camera permissions have been taken");
        }
    }

    changed(integer change)
    {
        if (change & CHANGED_LINK)
        {
            key agent = llAvatarOnSitTarget();
            if (agent)
            {
                setup_listen();
                llRequestPermissions(agent, PERMISSION_CONTROL_CAMERA);
            }
        }
    }

    attach(key agent)
    {
        if (agent != NULL_KEY)
        {
            setup_listen();
            llRequestPermissions(agent, PERMISSION_CONTROL_CAMERA);
        }
    }

    timer()
    {
        if (trap == 1)
        {
            focus_on_me();
        }
    }
}


FooBar

Of the metasyntactic variables, which are generally used as placeholders when describing a system, or as temporary names for works-in-progress, "foo" and "bar" are probably the two most popular, followed closely by "baz".

For instance, it could be described that a function "foo" takes two integer arguments, "bar" and "baz", and "foo" returns their sum. In doing so, metasyntactics have been used to describe a concept wherein the actual names are of little importance.

Read more on metasyntactics (history, etc).

Code


Force

Force is a physical property.

Functions

llGetForce
llSetForce
llSetForceAndTorque
llPushObject

Physics


Fork

"There is no spoon" - A student of the Oracle, The Matrix

As the child tells Keanu Reaves that there is no spoon, this page is unfortunately in place to tell you that there is no specific fork function in LSL. There are however methods to handle multiple scripts in a single prim, each script counting as a seperate process. Though this does allow multiple events and functions to occur simultaniously, they are still taken from the same single server timeslice, therefore, more scripts will slow down others as the task manager for the sim checks all it's scripts, the sim itself working in a Debian Linux task manager to find out when it can go compared with the kernel, postfix, squid proxies and often another simulator. This means that the processes have a limited ammount of time to work.
Methods to communicate between scripts include:

llMessageLinked | link_message
llEmail | sends e-mail, use llGetNextEmail to recieve
llGetNextEmail | email
llSay llShout llWhisper | chats on specified channel, use llListen to recieve
llListen | listen
llSensor llSensorRepeat | sensor

See Communications for a more thorough discussion of these functions.

A way to create a fork though not recomended in large numbers (think of Fish8) involves adding a prim to the inventory of the object and having it give that object a script using the following code.
default
{
     state_entry()
     {
          llOwnerSay("Compiled.");
     }

     on_rez(integer number)
     {
          if(number==1)
               llOwnerSay("Rezzed.");
     }

     touch_start(integer touched)
     {
          llRezObject("cube", <0.0, 0.0, 0.5>, <0.0,0.0,0.0>, ZERO_ROTATION,1);
     }

     object_rez(key object)
     {
          llGiveInventory(object, llGetScriptName());
     }
}
Communication


formatSetText

//FORMAT MESSAGE

string fn_format_settext(string message)
{
     return llDumpList2String( llParseString2List(message, [gs_delim], []) , "\n");
}


Forum References

Pages that link to the Second Life forums followed by that link. The purpose of these is so that users (most notably Teen Grid users) who don't have access to them can get the information.

I cleaned this page up a bit and pointed out the links that still need to be clarified.-Dolus

llDialog - 1
The forum message is now old, many of the suggested features have been implemented.

llModPow - 1
I've cut and pasted this thread into the sandbox . As you can see, it is not formatted well enough to be wiki page, unless your willing to do a lot of work. And the actual amount of information in the thread is relatively small (This one actually has a hi info to junk ratio). I understand the frustration, but in the majority of cases, your not missing anything (Like a lot of things in life I'm afraid).

llList2ListStrided - 1
Awaiting resolution.

llGetDate - 1
Relevant information is already in the comments.

llSetStatus - 1
Summary of thread: Q. Can you make some prims in an object phantom and some solid? A. No. I cant find this link on the llSetStatus page to remove it

ChristopherOmega - 1 2 3
Awaiting resolution.

PetProtocol - 1
Is a thread thats suggests creating a pet protocol and refers back to the wiki.

Comment965 - 1
The bug described no longer exists.

llGiveInventory - 1
Link to the Linden release notes for version 1.7.1.3 that says "Removed the limit on llGiveInventory."

This page may, and probably will be, updated. Check often.
Dolus, the problem is that most of these links lead to threads that sometimes, but not always, contain useful information. Often the information is out of date. The only way thing I could think of doing would be to text dump each message thread to a separate Wiki page. Given the small amount of information in the messages as a whole, I'm reluctant to do that. Was there a particular one you were interested in? - TiPBaKeR

Just in general, all of them. Sometimes the information may be useful, others may not. In many cases wiki pages refer to the forum post for more information. For those of us on the Teen Grid, we won't be able to access those references. - Dolus


FOV

FOV, or field-of-view, has to do with camera angle. SL's default FOV is at 60 degrees and can be changed via the "View" menu's "Zoom In", "Zoom Default", and "Zoom Out" commands.

The FOV changes automatically when the camera zooms in on something (prim/object, avatar, land, etc). This can be disorienting but can also be helpful in seeing detail. -EepQuirk

Camera


Frame Console

The frame console displays a graph of how much time the SL engine spends on certain tasks in each frame. Many of the states are self-explanatory, but others aren't so obvious. It can be useful to see what is slowing your framerate down, if you're curious and would like to take some corrective action.

Clicking anywhere inside the console will change the scale of the scrolling bar graph from its default of 0.00-0.05 seconds to a 0-100% scale, and back again.

The upper part of the window gives a short description of what each color band displays. Below that is a constant 100% scale of the current frame, and how the time used to render it was divided. Note, that in this section, the scale reads properly, from top to bottom corresponding with bars from left to right. The lower section is the history scale, listing the most recent 31 frames (before the current one). It is slightly different in presentation from the current frame scale, as RenderUI and MiscEnd are placed at the beginning of the display, rather than at the end.

It is fairly common for the following bands to expand greatly when the system slows down:
ObjectUpdate
UpdateMove
UpdateGeom
RenderGeom
Others may spike at different times.

Debug


Framerate

Framerate, or frames per second (FPS), is a measure of how smoothly a graphics display is running.

To simulate motion, many images are shown each second, fooling the brain into thinking that there is a moving object. Film has 30 FPS, which is the passing level for a computer game. Film is actualy more complicated then that, there are different standards which use different frame rates, converting between them results in degraded images

The Simulator has a physics FPS. In an ideal world physics would be calculated continuously and with calculus. Do to computer restrictions this is not possible to do in real-time. So the physics engine devides a second up into frames, each frame it moves all physical objects the distance they would go in that time period and checks to see if as a result of that movement they are currently embedded in another object. If they are, they push away from each other. This solution gives good enough physics in real-time without the need for expensive custom hardware. SL has a hard-coded (or maybe it's in a settings file) max physics FPS of 45.


Friction

Friction is the effect of multiple objects colliding continuously with each other. Lighter (less massive) objects are affected to a lesser extent by friction; heavier (more massive) ones are affected to a greater extent.

An object's friction can be changed by modifying its material type. llSetBuoyancy also affects friction.

VEHICLE_ANGULAR_FRICTION_TIMESCALE vehicle constant

References

  • There is no air friction, or water friction for that matter. Havok-1 has a built-in 'friction' method however it is numerically unstable, especially for small objects, and requires a lot of tuning to the physics engine and parameter space to make it work right. Someday there may be air friction, but it would have to be enabled on a per-object basis -- some people won't want their stuff getting blown away by the wind. (Andrew Linden, 5/2/06)

Physics | Mass


fullbright

See lighting.


Function

See functions.


functionLibrary

beginning of little chunks library.
I am not putting this in the main wiki stream for now, because I intend to over-comment them, as well as it is work in progress. Will see if it gets off ground.

Here is a library of functions providing little blocks of code to speed up the programing process. Brevity is not the object here. Neither will be explaining every aspect of the code - the focus will be on what they do. These little blocks can be used in a LEGO-ish / cookbook fashion to help learn some of the more advanced areas of LSL. They will be heavily commented, but ready to use first, and ask questions later.

Please prefix pages for the Function Library with "Function" to keep the Wiki namespace clean. Example: [[FunctionFormatSetText]] whether Function is capitalized is of no importance

Name | Description
randomVector | little functions to generate random colour and vectors, with examples
formatSetText | little snippets to format text for settext output
Unescape | function to unescape "\" style escape sequances
Rez All Objects | just a little snippet to loop through objects contents, and rez all objects
Max what do you think of the fromating? --BW

Not sure. What's fromating ;) No strong thoughts on the matter. Open to suggesitons. --MaxCase


FunctionRezAllObjects

// THis loops through all objects in inventory, and rezzes them.
//

fn_rez_all_objects()
{
    integer TOTAL_OBJECTS = llGetInventoryNumber(-1);
    integer i; //counter

    for(i = 0; i < TOTAL_OBJECTS; i++)
    {
        string inv_name = llGetInventoryName(-1, i); //the name of inv item on this loop
        if(llGetInventoryType(inv_name) == 6)//checks if its an object
        {
            llRezObject( inv_name, llGetPos(), <0,0,0>, ZERO_ROTATION, 0 );//start parm to trigger rezzed script
        }
    }
}

default
{
    state_entry()
    {
        llSay(0, "Touch me to rez all the objects in my inventory...");

        //
    }

    touch_start(integer total_number)
    {
        if(llDetectedKey(0) == llGetOwner())
        {
            fn_rez_all_objects();
        }
    }


FunctionStateChangeHack

To change state within a function, insert the state change statement within a conditional statment that always evaluates to TRUE.
For example:
stateChange() {
    state foo;
}

default {
    state_entry() {
        stateChange();
    }
}

state foo {
    state_entry() {
    }
}
The above doesn't compile, but the below does:
stateChange() {
    if (TRUE)
        state foo;
}

default {
    state_entry() {
        stateChange();
    }
}

state foo {
    state_entry() {
    }
}

Comparisons

Compared to a "normal" state change, there are no differences between changing states using the "state" statement, and changing states from within a function. The differences occur when viewing where, when and how code runs inside the function changing state.

When a function triggers a state change, it is popped off the stack (the state directive acts like a return statement). Any functions calling the function that changed state are allowed to complete, until focus returns to an event. After all code within the event finishes executing, then state changes normally.

The only difference occurs when comparing precedence of one state directive over another. An event's state change directive has precedence over a function's. That means, if an event executes the state change directive after a function does, the event's directive will be used. If a function executes a state change directive after another function does, then the first directive will be used. The same applies to two (or more) directives within an event; the first will take precedence over the others.

Examples of directive precedence differences:
default {
    state_entry() {
        state foo; // Only this one will work.
        state bar; // Ignored.
    }
}
// Pretend foo and bar are defined below...

changeState() {
    if (TRUE)
        state foo; // Ignored.
}

default {
    state_entry() {
        changeState();
        state bar; // Only this one will work.
    }
}
// Pretend foo and bar are defined below...

changeStateFoo() {
    if (TRUE)
        state foo; // Only this one will work.
}

changeStateBar() {
    changeStateFoo();
    if (TRUE)
        state bar; // Ignored.
}

default {
    state_entry() {
        changeStateBar();
    }
}
// Pretend foo and bar are defined below...

Adverse effects

When changing states from within functions, the main things to watch out for are return values.
string changeState() {
    if (TRUE)
        state foo;
    return "Hello";
}

default {
    state_entry() {
        string result = changeState();
    }
}
state foo {
    state_entry() {

    }
}
Calling changeState, because it returns a string, will trigger a run-time bounds check error. Returning a list triggers the same effect. The bounds check error results when you try to use the value returned from changeState (which we're doing in this case when we assign the result of the function to the variable 'result'). Because changeState changed state, this value does not exist, even though the SL script interpretor believes it should.

When changing state from within functions that return other types of variables, the function returns a null/zero value (0 for integers, 0.0 for floats, <0, 0, 0> for vectors and <0, 0, 0, 0> for rotations).

The point is: Never return a value from a function that changes state.
Hacks


Geometry

Geometry is the general term to describe what makes up a 3D model/object, which consists of polygons and vertices, and how a model's surface looks: solid, wireframe (outlined polygons), vertices only, etc.

object | polygon | vertex


Gesture

A gesture allows a user to combine animation, sound, and chat into a single "action" that can be triggered with a function key or by typing chat text. A gesture can be contained in an agent's or object's inventory.

A gesture can't be triggered by a script--only by user input and has a limit of 20 "steps".

To animate an avatar, use the animation functions (which won't be limited by how many animations can be performed at once).

Animation


Ghost

A ghost is an object or avatar that is visible to a user on the user's screen, but that doesn't actually exist on the SL servers. It happens when the SL client doesn't get a command from the simulator to remove the object or avatar.

Attachments can also ghost--or not even rez at all ("anti-ghosting"). According to Vektor Linden, a "good way to force attachments to rez is to put a simple script in them...scripted items have priority for rezzing."

Ghosts can also occur when the sim has crashed (or is frozen) and the user hasen't been logged out of SL.

A ghost will then have "Retrieving..." in its tooltip and, when selected in edit mode, will never display its name, owner, or permissions information. To get rid of a ghosted object, select and drag it offscreen with the position handles. (If a user doesn't have permission to move it, the user must relog or teleport to get rid of it.)

A ghosted object/attachment can be edited (if a user has permission) but its changes won't be saved and it can't be detached or taken into inventory.

SL Wiki's "Ghost"

Rez | Object | Agent/Avatar


GIF

GIF stands for Graphics Interchange Format. It is an old image format that was introduced by the CompuServe online service back in the late 1980s. GIF images may contain up to 256 unique colors, support a "background" or transparency mask, and can contain multiple frames for a sort of rudimentary "animation."

The PNG format is considered generally superior to GIF and is preferred wherever possible.

PNG and GIF have one trait in common--neither are supported upload formats for the SecondLife client, which only imports the TGA, BMP, or JPEG image file formats.

Another trait PNG and GIF share is that they are in no way related to LSL.

file formats


glitch

See "bug".


global

In general, global is something that applies or exists in all cases, compared to local.

item | explanation
global function | Any user-defined function or LSL function.
global variable | A global variable is declared outside of a state, event, or function.
global coordinate | A global coordinate differs from a local coordinate in that their origin point is not relative to any sim. Local coordinates are either relative to the region or object in question.
global axes | Unrotated world space (that objects are positioned in), compared to local object axes which can be rotated relative to the global one.
script | scope | local


GlobalCoordinate

Global Coordinates

Global coordinates are used to describe a position relative to one point on the entire Second Life world, hence the word "global". This means that every place on the grid has a different vector value when represented in global coordinates. This allows global coordinates to describe a position without adverse effects occuring when a sim border is crossed.

A region coordinate can be converted into a global coordinate by adding to it the region corner of the simulator the coordinate is relative to.

vector currentGlobalPos = llGetRegionCorner() + llGetPos();

Using global coordinates, it's easy to create an object that can traverse the world. llSetPos and llMoveToTarget use region coordiantes; to use these functions to travel to a particular global coordinate, pass the result of subtracting the current region's corner from the global coordinate to the function. This converts the global coordinate into a region coordinate.

Example:
// Pass a global position to this function, and the object
// will move there.
setGlobalPos(vector globalDest) {
    vector localDest;
    do {
        localDest = globalDest - llGetRegionCorner();
        llSetPos(localDest);
    } while (llVecDist(llGetPos(), localDest) > 0.1);
}

default {
    state_entry() {
        vector DABOOM_CORNER = <256000, 256000, 0>; // DaBoom's region corner.
        setGlobalPos(DABOOM_CORNER + <128, 128, 128>); // Travel to <128,128,128> in DaBoom.
    }
}

Q & A:

Q: Is the global coordinate origin point (<0, 0, 0>) located somewhere in the sim Da Boom?
A: No, Da Boom is merely the sim from which it all started. See coordinate.

Q: How do I convert the landmark positions (retrieved using llRequestInventoryData) to global coordinates?
A: Add the dataserver's return value to llGetRegionCorner before moving; the coordinate is relative to the simulator where you made the dataserver request.

Now to complicate things. Say you want to move a prim from one sim into another sim that is diagonal from it. An example situation would be calling llSetPos(<257,257,50>); from <254,254,50>. Your prim will first transfer into the sim on the x axis of the move and then into the sim on the y axis. If there is no sim on the x axis your prim will go offworld. So if you move a prim from Tan to Kissling it will go off world. But if you move a prim from Kissling to Tan it won't. -BW

(map)

Coordinates | Local Coordinates | Region Coordinates | Region Corner | Grid/World


GlobalCoordinates

Redirecting to GlobalCoordinate...


Global Variable

A global variable is a variable declared before the default state declaration of a script; it has a global scope. These variables are accessible everywhere in the script, and anything can set them. However, they are not accessible outside of the script, in other scripts.

It is good coding practice to prefix global variables with a "g". Since LSL doesn't allow defining constants, variables can be used as pseudo-constants, usually with names that are all UPPERCASE. Just remember never to change their value anywhere else in the script.

Note: in global variable declarations, the initial values can not be expressions; they can either be a constant, another global variable, or a literal value (like the number 3).

Example:
// Stuff you can do:

integer gMyInteger = 3; // Valid declaration of a global variable.
integer gAnotherInteger = CHANGED_INVENTORY; // Valid declaration of a global variable.
integer gCopiedInteger = gAnotherInteger; // Valid declaration of a global variable.

integer CHAT_CHANNEL = 0; // Valid declaration of a pseudo-constant.

// Stuff you **can't** do:

string gAnotherIntegerAsString = (string)gAnotherInteger;
// ^^^^ ILLEGAL! Global variable declarations are not evaluated. (You can't typecast here.)

float gMyFloat = llFabs(482.334);
// ^^^^ ILLEGAL! Global variable declarations are not evaluated. (You can't call a function here.)

string gMyString = "Hello!" + "Goodbye!";
// ^^^^ ILLEGAL! Global variable declarations are not evaluated. (You cant use operators here.)

// Global functions:

myFunction() {
    gMyInteger = 45; // Valid.
}

integer anotherFunction () {
    return gMyInteger; // Valid.
}

default {
    state_entry() {
        gMyFloat = llFabs(482.334); // Valid.
        gMyInteger = 12; // Valid.
    }
}

state foo {
    touch_start(integer n) {
        llSay(CHAT_CHANNEL, "Touched.");
        gMyInteger = n; // Valid.

        CHAT_CHANNEL = 4;
        // ^^^^ BAD CODING PRACTICE! Changing the value of a pseudo-constant is legal, since LSL doesnt
        // have user-defined constants, but you will go to hell for this - after some very nasty debugging
    }
}

Global variables should be used with some care - see http://c2.com/cgi/wiki?GlobalVariablesAreBad. If you are tight on memory one technique is to store all your local variables in globals and then reduce the number of globals to the number of unique variables you actualy need; turning your global variables into registers. Of course, unless your really careful and have omni presence over your code; debugging will be maddening. Insanity will not be far behind.

Compare with local variables.

Tutorials | Variable


GMT

Greenwich Mean Time, or GMT, is the standard time zone; another name for UTC.

To get the current time in GMT, use llGetGMTclock or llGetTimestamp.

time | PDT | PST | UTC


godmode

An administrative mode allowing Lindens to not be killed in damage-enabled sims. An example of use for this mode would be stopping out-of-control scripts, dealing with griefers, and killing users for pure enjoyment.

Specifically, scripts do not detect collisions with godmode Lindens.


Goto

In LSL, GOTOs are called jumps.


grab

Grabbing an object means click-holding it to drag/move it around. In 3rd-person view, only physical objects can be grabbed but in 1st-person view (mouselook mode), any object that's yours or is set to "allow anyone to move" can be grabbed.

llDetectedGrab
llSetStatus: STATUS_BLOCK_GRAB parameter

object | physical | mouselook


gravity

The acceleration under gravity in SL is 9.8 m/s^2. A prim with physics enabled essentially has a constant force of its mass * 9.8 m/s^2 applied in the negative z (global coordinates) direction.

llSetBuoyancy can repel the effects of gravity.

physics | units


Grey Goo Fence

As an attempt to combat mass-replicating grid crashers, starting in SL 1.10 a "grey goo fence" was put up that attempts to detect and put a stop to objects that rez objects or give inventory "excessively" and place a quick stop to them. Pragmatically speaking, this may stop any object that calls llRezObject, llGiveInventory, or llGiveInventoryList more than 240 times within 6 seconds will have all subsequent calls to these functions blocked until no further attempts are made for at least 12 seconds.

The rules that trigger the "grey goo fence" are subject to change without notice. Andrew Linden intends this limit to become a fairly complex heuristic that supposedly limits "excessive" scripts while affecting "normal" scripts as little as possible. Practically speaking these rules are liable to affect auto-rezzers that rez fairly complex objects fairly quickly and those that rez a large number of scripted objects.

The Rules

The calls to llRezObject, llGiveInventory, or llGiveInventoryList (possibly llRezAtRoot?) for each "group of objects" is counted. Rezzing a physical object counts as two calls.

If more than 240 calls are counted among the group of objects within 6 seconds then:

  • All temp-on-rez objects are immediately deleted
  • Some scripted unlink operations may fail (this is not intended)
  • Any further rez or give attempts will result in a script error
  • All objects in the group will have their energy set to zero, reducing their ability to physically affect themselves and the world.

The group that an object belongs to is based on how it was rezzed:

  • Attachments are not part of any group, they are not affected by these rules
  • Objects rezzed by an agent are grouped by their owner and the asset id of the object that is being rezzed.
  • Objects rezzed by another object (including attachments) are grouped based on the object that rezzed them. This means that if a rezzer is responsible for rezzing a large number of scripted objects, if those objects at any point rez/give more than 240 objects at a time then the group will be throttled

Sources and Quotes

The information in this article comes from two forum threads:

  • Grey Goo Fence
  • Grey Goo Fence Status?

"If necessary I'll add a per-owner throttle, perhaps it will have different magic numbers instead of 240/6. I hope to eventually increase the rate limits when the fence gets smarter, and maybe reduce or remove the implicit sleep built into the llGiveInventory() call which was really just an anti-grief hobble feature that may be made obsolete by the new fence."

"The XyObject is a known example where a build-seed runs into the fence. Not because it is rezzing > 40 objects a second, but because it uses a few levels of self-replication and delegation to construct a giant surface."

Miscellaneous

The term "Grey Goo" comes from science fiction and nanotechnology and refers to how an out of control self-replicating nanobot "army" might look like to an outside observer before it consumes the entire world.

Attachments | Object | Rez


Grid

A grid is a graphical division of units. In Second Life, the grid is the general term for the entire world. More specifically, the grid is the simulators visible on the world map and, even more specifically, the grid are the servers which run SL and have a symbolic relationship to the world map, with one server supporting one sim (though multiple sims can and are run on the same physical server hardware).

Da Boom was the first sim on the grid. Its SW (southwest) corner is at <1000,1000,0> sim coordinates, or <256000,256000,0> global coordinate meters. Some users refer to Da Boom as the center of the grid, but there's no technical reason to do so.

A gridmonkey takes care of the grid.

Simulator | Global Coordinate


Grid Monkey

The Linden who is on-call (carrying a pager and/or cell phone) in case of SL server problems.

The gridmonkey is a rotating, week-long duty for all of the LL developers during which they are on-call whenever there is a technical problem with the grid. (source)

Linden | Grid


Grief

Grief: (n) Something that causes great unhappiness.
Grief/Griefing: (v) To give others grief.
Griefer: (n) Someone that causes great unhappiness.

An overactive security system can cause grief for others which can lead to abuse reports being filed against the owner.
Do not delude yourself into thinking that protecting your privacy gives you the right to bounce (push) people. Read the Terms of Service and Community Standards.

Script functions used to combat griefing and cause grief:
llAddToLandBanList
llEjectFromLand
llPushObject
llRezObject
llRezAtRoot
llTeleportAgentHome This one is particularly nasty.
llUnsit

Any script that interacts with a user in a fashion that is contrary to what the user expects or is invasive, is potentially griefing. Scripted grief includes but not limited to:
  • Spam
    • Chat
    • IM
    • Sounds
    • Particles
    • Inventory
  • Harassment
  • Pushing
  • Orbiting
  • Grey Goo

For this type of griefing LL suspends accounts and bans repeat offenders. LL typically will not suspend people if it is an isolated incident and was an accident.

Also known under the name Grey Goo, self replicating objects can cause grief in a number of ways. Not only can they take down the grid, they can also be used as a platform to cause other types of grief (see previous list).

Releasing a self replicating object into the wild is risky. LL typically suspends users for releasing troublesome self replicating objects, even if the release was by accident. If the attack was done on purpose, LL turns over their records to the FBI.

Agent/Avatar | Land


griefing

See grief.


GroupObjects

Group-Owned Objects

"Group" in this case refers to user groups (as in Liaisons, Instructors, etc), not linked objects. However, there are some interesting LSL things to note about objects which have been deeded to a group.

  • There is no way to grant permissions (PERMISSION_DEBIT etc) to an object deeded to a group.
  • Money paid to an object deeded to a group goes into the group treasury, which is split evenly among all members nightly or as soon as there is total_members L$ in the group treasury.
  • Functions that only work over the script owner's land (llSetParcelMusicURL etc) will only work over group owned land if on an object that has been deeded to the group.
  • An object deeded to a group will be deleted if "returned" (via autoreturn etc) because there is no group inventory for it to go to.
  • llGetOwner returns the group's key, which means things like (llDetectedKey(0) == llGetOwner()) can never turn true.
  • llOwnerSay("foo") will fail silently.
  • When using such group keys, certain functions that work for agent keys will not work. (llRequestAgentData silently fails, llKey2Name returns "", etc.)

To deed an object to a group:
  • Set the object to a group by clicking the "set" button and selecting a group.
  • Check the "Share with group" box.
  • Have a group officer click the "Deed" button. If you are a group officer you will see the "Deed" button become active and you can click it.

Scripts run in group-owned objects give the group key as the owner (llGetOwner == groupkey). There are issues with functions that require permissions (ie permissions only an owner can grant).

Group | Object


groups

see group.


GUI

GUI (Graphical User Interface) is what lets a user interact with a program if it has graphics. For example, the chat history window in SL is part of its GUI.

user interface


gun

Moved to ExampleGun.


Havok

Havok is the physics engine that performs collision and dynamics calculations for SL. It should not be confused with the rendering engine, which was developed in-house (propietary). Havok is created by Havok.com, Inc., and is licensed by Linden Lab for use in SL.

Second Life currently uses Havok 1.8.3, which is no longer supported by Havok.com, Inc. The latest version of Havok is 4, released on July 11, 2006. The move to Havok 2.x was slated to occur in SL version SL 1.7, unfortunately, it did not occur. Andrew Meadows, a.k.a Andrew Linden, wrote a fantastic article discussing how Havok currently works in SL, and detailing how the upcoming migration to Havok 2.x will benefit us. Havok 2.x offers faster and more accurate physics, ultimately allowing for greater object complexity and better sim performance, though the immediate benefits may be minor.

Though Havok 2 does indeed support rag-doll physics for simulating realistic-looking behavior of human bodies, Havok 1.x does as well, yet SL does not take advantage of it. Andrew Linden has stated that a post-Havok 2.x SL will not either, at least not without a significant rewrite of the avatar code. Havok also supports destructable objects, but again, this is unlikely to be implemented into Second Life because of the prim limit.

Havok is frequently misspelled as "Havoc".

In a recent public meeting, Andrew Linden admited that Havok 2 will not be in the initial SL 1.7 release. He went on to say that it will be added when it is ready. When ever that is, Havok 2 has been a promised imminent feature since October, 2003 (becoming the biggest inside joke in SL).

A public roadmap of LL's upgrade of the Havok engine has not yet been found.

The 0.01m minimum size of primitives is definitely due to the limitations of the physics engine rather than for being "too small to see". However, Havok-2 will not save the day -- Havok-2 still has the default 0.1 meter "collision tolerance" as Havok-1, which means the odds of smaller prims in the debut of H2 in SL is almost zero. Nevertheless, I expect that we will eventually be able to reduce the minimum size allowed for primitives. (Andrew Linden, 8/17/05)

Physics | Dynamics


Heads-Up Display

Introduced in SL 1.7, the 8 heads-up display (HUD for short) attachment slots are visible only to the user to which they're attached. They're intended to allow scripters to create user interface elements, such as speedometers, game buttons, health readouts, etc.

Despite being invisible to other users, HUD attachments are just like any other attachment: they can be scripted, can automatically attach to an avatar, and can communicate via chat and other methods.

Values for llGetAttached and llAttachToAvatar:
Value | Position
31 | HUD Center 2
32 | HUD Top Right
33 | HUD Top
34 | HUD Top Left
35 | HUD Center
36 | HUD Bottom Left
37 | HUD Bottom
38 | HUD Bottom Right

HUD attachments can be moved around the user's screen:

llGetLocalPos can be used to get the HUD attachment's current position. (Notice that this isn't the same as llGetPos which seems to tell the avatar coordinates instead of the HUD attachment coordinates.)
llSetPos sets position.

How HUD Positioning Coordinates Work

(Being familiar with vectors will help.)

Each HUD attachment point has its own origin point, <0,0,0>. Only the .y and .z components are used for positioning, so an example position might be <0.0, 0.2, -0.8>, where 0.2 is the value of the horizontal axis and -0.8 the value of the vertical axis. The .x component is used to determine which HUD overprints when two HUDs overlap. More negative .x HUDs will render on top of less negative ones.

Rather than the more traditional coordinate layout, where numbers get bigger the farther up and right they go on the grid, the numbers on the horizontal axis of HUD attachment grid get larger as they move towards the left side of the screen. (Fortunately, the vertical axis is normal -- larger numbers are higher on the screen, smaller numbers are lower.)

The grid the HUD uses is square, not scaled relative to the size of the SL window, so setting an attachment in the HUD bottom right slot to a position of <0.0, 1.0, 1.0> will not put it in the upper left corner. However, it will put it at the top of the screen -- the HUD grid scales with the height of the SL window.

HUD attachments can still be positioned vertically without any trouble, as long as it is known where they're attached, but horizontally is another story. Because each attachment point is relative to window size, it'll end up vertically where desired.

This means that you can use the upper or bottom corner attachment slots to position an attachment in the corner above or below it, and be certain that it'll end up there on everyone's computer.

However, an attachment can't be positioned to one of the corner slots to a position on the opposite side of the screen with any reliability, as the dimensions of the SL window will change from computer to computer. (Note: there is no way to determine a user's window dimensions or aspect ratio.)

The range of possible coordinates for HUD attachments is greater than <0.0, -1.0, -1.0> to <0.0, 1.0, 1.0>, but the actual position llSetPos will move the attachment to appears to be unpredictable when the origin point of the attachment passes offscreen.

HUD Quirks and Workarounds

"Solid" HUD attachments
One frequent complaint about the HUD system is that even when fully transparent, HUD attachments still don't allow clicking "through" them to the world behind. One workaround for this is to use llSetPos to reposition the attachment offscreen when necessary for it to be "invisible". Note that in SL 13.0.8 (not sure which version this started in) you can use the mouse-wheel to shrink the HUD layer to the center of the screen (making the majority of the screen useful again).

Particles appear at <0,0,0>
When using llParticleSystem in a HUD attachment, particles will appear to come from <0,0,0> in the sim, not the HUD attachment. There is no workaround. Don't use particles in HUDs.

Q: Is it possible to trigger a sound in a HUD attachment that only the owner hears?
A: Yes. llPlaySound in a HUD attachment will only be heard by the owner.

Attachment | User Interface | AgentAndAvatar


Heap

The heap is an area of memory reserved for data that is created at run-time (the time when the script actually executes, or is run). In contrast, the stack is an area of memory used for data where its size is determined when the script is compiled.

LSL scripts are currently allocated 16kb of memory, in which the compiled script's bytecode, stack, and heap are stored. To check the available memory, before doing any memory-intensive calls, use llGetFreeMemory.

memory | stack


Hello World

Hello World programs/scripts are snipits of code used to test compiling engines and interpreters.
In LSL, we recieve a varient called Hello Avatar! when we create a new script, however individuals might have their own code to test functions they are interested in and run this script at the beginning of a new version. I persoanlly use one of my mapping tools as a hello world, in preview grid, and then on main grid, to make sure everything's working right with functions I use regularly - Ice

Hello World has a long history in programming, best read here.

What follows are Hello Worlds from various languages, as users contribute, first the Hello World that is standard for LSL, due to the nature of this Wiki, then a listing of other languages as users contribute them.

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

Multi-User Forth Example

(This is a Hello World compatable with TinyMUCK, FuzzballMUCK, NeonMUCK, and ProtoMUCK)
(MUF stands for Multi-User Forth, a language based upon the Reverse Polish notation system)
(This system was credited to HP calculators, as being the reason it's the language in MUCK varients)
(The notation, simply stated is 2 2 + would give you 4, as a language read from left to right, it is fairly simple)
(  Once you get the hang of the notation)

: main me @ "Hello World!" notify ;
Of all the languages to learn to code in, if anyone wonders why my code looks wierd, this and MPI are why - Ice

Java Example

class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

C++ Example

#include <stdio>
int main() {
    cout << "Hello World!";
    return 0;
}

PHP Example

<?php
  print("Hello, World!\n");
  echo("Hello, World!\n");
  phpinfo();
?>
This shows both ways to print out and also pulls up the PHPInfo() page. It's a bit more extensive, but the best way to test a new PHP install (in my opinion).

Perl Example

map { print chr ord($_) + 1 } split(//, "Gdkkn+\x1FVnqkc \x09")
Or this slightly less readable variation:
print "Hello, World!\n";


Hex

Abbreviation for hexadecimal.


Hexadecimal

Regular decimal numbers are what's known as base 10: 10 number symbols (0123456789) and make all other numbers out of them. Hexadecimal (or "hex)") is another numbering system, which is useful when coding, because it converts easily to binary. Hex uses 16 symbols (0123456789abcdef) and is therefore a base 16 system. As in all other number systems, such as decimal, adding zeroes to the left side of a number has no effect on its value: 0xF is equal to 0x0F.

Decimal | Hex | Binary
0 | 0 | 0000
1 | 1 | 0001
2 | 2 | 0010
3 | 3 | 0011
4 | 4 | 0100
5 | 5 | 0101
6 | 6 | 0110
7 | 7 | 0111
8 | 8 | 1000
9 | 9 | 1001
10 | A | 1010
11 | B | 1011
12 | C | 1100
13 | D | 1101
14 | E | 1110
15 | F | 1111
To figure out the value of a number you do a sum, for example, the number 5043 is
(5 * 1000) + (0 * 100) + (4 * 10) + (3 * 1)
another way of writing this is
(5 * 10^3) + (0 * 10^2) + (4 * 10^1) + (3 * 10^0)

Hexidecimal works the same way except with 16 instead of 10, to figure out the value of, for example 18B3:
(1 * 16^3) + (8 * 16^2) + (B * 16^1) + (3 * 16^0)
Converting this to decimal we have
(1 * 4096) + (8 * 256) + (11 * 16) + (3 * 1)
or 6323.

(Binary numbers work exactly the same way, incidentally, but using powers of 2)

A binary 8-bit byte converts easily to a 2-digit hex number, with the first digit representing the first 4 bits, and the second digit representing the second 4 bits.

So, for example, the binary number: 01101101 becomes 0110 1101, then using this chart we convert this to 6D.

Here's an example for converting up to an 8-digit hex number to decimal (there are several ways to do this, but this should be one of the fast kind):
integer hex2int(string hex) {
    if(llGetSubString(hex,0,1) == "0x")
        return (integer)hex;
    if(llGetSubString(hex,0,0) == "x")
        return (integer)("0"+hex);
    return(integer)("0x"+hex);
}

Here's an example for converting an integer into hexadecimal:
string hexc="0123456789ABCDEF";

string int2hex(integer x)
{
    integer x0 = x & 0xF;
    string res = llGetSubString(hexc, x0, x0);
    x = (x >> 4) & 0x0FFFFFFF; //otherwise we get infinite loop on negatives.
    while( x != 0 )
    {
        x0 = x & 0xF;
        res = llGetSubString(hexc, x0, x0) + res;
        x = x >> 4;
    }
    return res;
}

See ExampleNumberConversion for a more sophisticated LSL function.

llMD5String


Hippo

Hippos!

(Try hitting Ctrl+Alt+Shift+H in SL repeatedly.)

Hippo originated as a beta discussion about a petting zoo in Second Life, Hippo was eventually chosen, and became wildly popular.
There have been several Hippo creations, as it had become adopted as a mascot for Second Life.

Hippos, a refrence to a Linden toy that still floats about to this day.
Ask an old Linden for more infomation.

Looking up the members of the Second Life Mentor group in find will cause (Hippos) to be displayed instead of (waiting). Seemingly no other group does this (Even Live Help).

Hippos were also the name of a Linden created matchup system, since removed, some people may still have the old accessories, though presence is severely limited after the removal of telehubs.

A Hippo art project was created by Altruma Linden for the infohubs that replaced telehubs.

Linden Lab has a hippo soap dispenser in the office rest rooms.

Humor


Home

Home is the arbitrary location in the Second Life world that a user can identify as his or her "default" point-of-creation location. This location can be set in the "World" menu, by clicking on the "Set home to here" menu item. A user can only set his or her home location on land he or she owns or on land owned by a group the user is in. The home location used to be the only arbitary position in the world that can be teleported to directly (a telehub or another user is not necessary to teleport there).

When a user is killed, he or she "respawns" at their home location. When llTeleportAgentHome is invoked on a user, that user is teleported to his or her home location. A user can choose to teleport to his or her home by clicking the "Go Home" button in the "World Map" window or by selecting the "Teleport Home" menu item in the "World" menu. A new user's home location is the Welcome Area. A user can choose to login at his or her home location by selecting "My Home" as the "Login Location" in the "Preferences" window's "General" tab.

HomePage | Telehub | Teleport


HTTP

LSL includes functions that utilize HTTP, the HyperText Transfer Protocol, for communicating with web servers on the outside internet. HTTP requests incur no speed penalty, but there is a limit on how many requests per owner per sim can be made.

Functions
llEscapeURL | Returns the UTF-8 encoded escape codes for selected characters.
llHTTPRequest | Requests information from an HTTP server.
llLoadURL | Loads a web page in an external browser.
llUnescapeURL | Returns the UTF-8 characters without escape codes.

Events
http_response Called when an HTTP request is complete.

Communications


HUD

An acronym for heads-up display.


identifier

An identifier is something that identifies, or names/indicates/labels, something to another thing. In LSL, an identifier can be any of the following:

  • constant
  • variable
  • function including user-defined functions and event handlers
  • function argument/parameter
  • state
  • type
  • flow control keywords and other LSL keywords (I could find no definitive list)
  • jump label

An identifier is usable within a certain scope of code in a script. LSL keywords are global.

Many other things have string names in SL, such as objects and scripts, but those names are not identifiers in LSL. Conversely, identifiers in LSL are not values that may be operated on; their string names are not available, you cannot look them up by their string names, and you cannot create identifiers given their string names.

script | code | scope


IF

See IfElse.


IM

An IM is an instant message.

communications


Impulse

Impulse is a physical property dealing with force and rotation.

Functions

llApplyImpulse
llApplyRotationalImpulse

Physics | Force | Rotation


indent

Good code must be indented!

See the LSL StyleGuide and the entry on braces for more.


index

Welcome to the LSL Wiki, an effort to document the LSL scripting language of Second Life. This wiki is intended as an open documentation resource to help scripters of all skill levels. Anyone can contribute, so feel free to jump in.

Looking for information about Second Life in general? Try Linden Lab's official Second Life Wiki.
Are you new to LSL scripting? Try our LSL Tutorials.

There are other forks of the LSL Wiki in various states of development. See the 'Link to Useful Resources' at Linden Lab's LSL Portal.

Getting Started: LSL Reference: Function Categories:
Whether you're new to scripting or an experienced programmer, these pages should help you learn to use LSL.

Introduction
Search the Wiki
Tutorials
How Do I...
Glossary
Humor

Scripting Mentors
Scripting Groups
Scripting Teachers

Alternate Editors
Errors
Hacks
Known Bugs & Debugging Tips
Annoyances/Gotchas
Lag
Revisions - What's new in LSL?
LSL Style Guide

Script Library
Examples
Protocol Exchange 		Already know how to script, and just need the reference?

Constants
Events
Flow Control:
for, do-while, if-else, jump, return, state, while
Functions (see categories to right)
Operators:
assignment, binary, bitwise, boolean, unary
States
Types:
integer, float, vector, rotation, key, string, list
Variables
  • Agent/Avatar
  • Camera
  • Collision
  • Color
  • Communications
  • Controls
  • Dataserver
  • Detection
  • Dynamics/Movement/Physics
  • Group
  • Inventory
  • Link
  • Land (Ground/Terrain)
  • Light
  • List
  • Math
  • Particle (llParticleSystem)
  • Primitive/Object (llSetPrimitiveParams)
  • Script
  • Sensor
  • Simulator (Environment/World)
  • Sound
  • String (Text/Name)
  • Teleport
  • Texture
  • Time
  • Transformation:
    Rotation/Scaling/Translation
  • Vehicle
  • Video
  • Weather
  • XML-RPC
The LSL Wiki is an independent resource and is not affiliated with Linden Lab. Second Life and LSL are the property of Linden Lab. Portions of this site are copyright © Linden Lab.
By posting here, you agree to allow other users to copy, reproduce and alter your contribution for non-profit use. For additional information on the LSL Wiki's copyright policy, please see LSL Wiki Copyright.


InputOutput

See Communications.


Instance

An instance may be a familar concept to scripters with background in object-oriented programming (OOP). LSL is a procedural, event-based language; scripts are not classes and objects (in an OOP sense) cannot be instantiated from them.

Note: The term "object" used above does not refer to a Second Life prim-based object.

Glossary


Instant Message

Instant messages (IMs) are direct communication between objects and agents called from a script with llInstantMessage. IMs are especially useful for privacy or when the parties are not within say, shout, or whisper range, i.e., not in the same simulator.

Objects cannot receive IMs and they can only send to agents, which can send and receive messages from other agents.

IMs are saved if the user is not online, which they will receive upon next login, or, if the option is chosen, via email.

IMs can be quite slow for script-owner communication, so if speed is an issue, llOwnerSay may be better suited.

Communications


Instruction

An instruction is the most basic piece of code the Second Life virtual machine (script runner) can operate. When source code is compiled, it is translated into a sequence of binary operations that the Second Life simulator executes in response to events.

Operation


interpenetrate

The state when two object's geometries intermingle, intersect, or similarly fails to appear to follow the laws of real-world physics.

See penetrate.

physics | physical | penetrate


Interpolate

Interpolation is the process of smoothly changing one value to another.

These functions attempt to visually interpolate to the values passed to them:

llSetLocalRot
llParticleSystem (alpha/color)
llSetPos
llSetPrimitiveParams (during scale, rotation and position changes)
llSetRot
llSetScale

Wikipedia's definition

value


Introduction

See LSL.


Invisiprim
Invisiprim is a script that makes an object completely invisible using a texture, or a UUID. When you put this in an object, It makes the object invisible, and everything behind it from the view.

This Makes the prim COMPLETELY invis, so you cannot see it even in Highlight transparent view.

///////Created by Joker Opus, November 8th//////
       //Feel free to mod this, do not steal//

default
{
    state_entry()
    {
        key owner = llGetOwner();
        llListen(0,"",owner,"");
        llSetTexture("38b86f85-2575-52a9-a531-23108d8da837", ALL_SIDES);
        llSetTexture("e97cf410-8e61-7005-ec06-629eba4cd1fb", ALL_SIDES);                     //This is all the textures and functions that formulate the invisiprim
        llSetPrimitiveParams([PRIM_BUMP_SHINY, ALL_SIDES, PRIM_SHINY_NONE, PRIM_BUMP_BRIGHT]);
        llOffsetTexture(0.468, 0.0, ALL_SIDES);
        llScaleTexture(0.0, 0.0, ALL_SIDES);
        llSetAlpha(1.0, ALL_SIDES);
        llSetTimerEvent(5);
        }
     listen(integer channel, string name, key id, string msg)
    {
        if( msg== "uncloak" )
        {                                          //When you say uncloak, it will go back to a non-invisiprim.
       llSetTexture("4c1ce202-4196-f1c1-0409-367b3a71543e", ALL_SIDES);
        }
    }
}


IO

See Communications.


Island

An island is a simulator detached from the mainland.

Simulator | Grid


Item

An "item" could be considered to be roughly equivalent to a file on a normal computer system. Items exist within a user's or object's inventory. There are various functions and event handlers used to deal with them.

When referring to objects that exist in-world, and are made up of prims, see objects. Unrezzed objects (those that exist in the user's inventory) are considered to be items. Notecards, textures, and scripts are some other examples of items.

Constants

The functions llGetInventoryName and llGetInventoryNumber apply the following constants to the various inventory item types:

Constant | Value | Look At | Hex
INVENTORY_ALL | -1 | all inventory items |
INVENTORY_TEXTURE | 0 | textures | 0x00
INVENTORY_SOUND | 1 | sounds | 0x01
INVENTORY_LANDMARK | 3 | landmarks | 0x03
INVENTORY_CLOTHING | 5 | clothing | 0x05
INVENTORY_OBJECT | 6 | objects | 0x06
INVENTORY_NOTECARD | 7 | notecards | 0x07
INVENTORY_SCRIPT | 10 | scripts | 0x0A
INVENTORY_BODYPART | 13 | body parts | 0x0D
INVENTORY_ANIMATION | 20 | animations | 0x14
INVENTORY_GESTURE | 21 | gestures | 0x15
Note: item names may be up to 63 characters long.

Glossary | Crash Course | Inventory


Joint

A joint is a way to link ("join") moving physical prims together in SL.

Andrew Linden once wrote (source?; slightly edited to match the current state of SL):

All About Joints

Here is a quick rundown of joints.

To fully understand this quick tutorial you should already know all about linking. That is, you should know what the "root primitive" of a linked set is.

Making Joints

Each joint connects two objects. For this discussion I will call one of them the arm and the other the pivot.

To make a joint, select one of the objects, then hold down the shift key and select the second object. Go to the menu "Tools -> Make Joint" and select the type of joint you want.

The first object you selected will be the arm and the second object you selected will be the pivot.

For best results, disable "physics" for the two pieces before the joint is created. Then enable "physics" on the appropriate pieces.

Note that until one of the pieces has "physics" enabled, selecting one will not necessarily select the entire jointed object. However, when one of the pieces has "physics" enabled then selecting one should automatically select the other piece.

Hinge

If you enable "physics" just for the arm, then the arm will rotate about the pivot's local Z-axis (i.e. the blue one) that passes through the center-of-mass of the pivot's root primitive.

If you enable "physics" just for the pivot, then the pivot will rotate about its own local Z-axis that passes through its center-of-mass.

This is how you make a "wheel-like" joint: secure the arm and use a cylindrical pivot that is free to rotate. Don't even bother trying to make a car using 4 wheels. Joints are not stable enough to make vehicles yet, but you can make doors, propellers (maybe), and some other stuff that doesn't suffer too many complicated collisions.

If you enable "physics" for both, well... you get both.

Point-to-Point

If you enable "physics" for just the arm then the arm will swing freely from the center of mass of the pivot's root primitive. The arm will also be able to spin about the axis that connects it to the pivot.

If you enable "physics" for just the pivot then... uh... oops, that's bugged. The point of swing is not right... I'll have to fix it.

The wind-chimes were made using point-to-point hinges.

Limited Point-to-Point

Don't use limited point-to-point joints for things you want to last for very long. The limited point-to-point joint has some problems surviving the save-load cycle which we haven't had time to fix yet. However, making a fresh one should work.

Basically, it is the same as the point-to-point except the center of the joint is the midpoint between the two centers of mass, and the amount of swing to the side is limited. Think of it like your shoulder (except with spin)... you can't swing your arm to arbitrary orientations behind you.

Andrew Linden explains the joint removal process in this forum post.

Physics | Physical | Link


JPEG

JPEG (Joint Photographic Experts Group, the standards body that created the image format) is a lossy image compression file format which offers a variety of quality levels and the image's file size are generally proportional. JPEG images inhabit the YUV color space, which uses brightness, hue, and saturation to represent an image.

SL textures and heightmaps use the JPEG2000 format, which is actually altogether different from JPEG.

See also: JPEG article at Wikipedia.


JPEG2000

SL uses the JPEG2000 image compression format, provided by the Kakadu library, to store textures on the asset server. The image is converted from a JPEG, TGA, or BMP-format image uploaded via the client. Uploading an image from the JPEG format, will cause the JPEG2000 encoder to use lossy compression. For the other two formats lossless compression is used.

JPEG2000 is managed by the JPEG (Joint Photographic Experts Group) Group. JPEG2000 is the JPEG Group's new standard that may end up replacing JPEG.

http://jj2000.epfl.ch/jj_tutorials/jj2000/
http://jj2000.epfl.ch - Java Implementation of Jpeg2000 standard (JJ2000)
http://jpeg2000.epfl.ch/ - Extended version of JJ2000
J2K-Codec - An easy-to-use JPEG2000 decoder.
Texture | File Formats


keys

Disambiguation Page
Where would you like to go:
key | The key LSL data type.
Client Asset Keys | Frequently used and SL client UI Linden asset UUIDs.


Keyword

A keyword is a reserved word in a programming language that often carries with it special meaning. Keywords may not be used as identifiers.

LSL keywords:
Flow Control:
if, else, for, while, do, return, default, state, event
Types:
vector, rotation, float, integer, key, string, list, quaternion
Event names:
state_entry, state_exit, control, changed
"ll"-prefixed function names:
llDie, llFrand, llLog
Compiler-implemented constants:
ZERO_VECTOR, NULL_KEY, TRUE, PRIM_BUMP_SHINY
FlowControl | Functions | Types | Events


KiB

Kibibyte (KiB)

In 1998, the IEC decreed to call 1,024 bytes a kibibyte ? a term which is only slowly catching on. During the transition period, the IEEE suggests using a lower-case "k" for the decimal kilo (1,000) and, if properly pointed out, an upper-case "K" for the binary kilo (1,024), thereby taking the historical development into account. This is versus the kilobyte, where kilo is commonly defined as 1000.

It should be noted that many reject the IEC's binary prefixes (kibi-, mebi-, gibi-, tebi-, pebi-, exbi-) on the basis that they sound really, really dumb.


kilobyte

A kilobyte (KB or K--capital letters; lower-case "k" and "kb" are kiloBITS) is an unofficial term for 1024 bytes, or 8192 bits. It is a common file size measurement unit, but not part of SI like these other units, because "kilo" normally refers to the number 1000, not 2^10 (1024). "Kibibyte" (KiB), the "official" term for 1024 bytes, has been slow to catch on.

There is nothing "official" about KiB. It was coined to help alleviate the confusion brought on by computer industry marketing. In the beginning there was KB, which was 1024 bytes, then marketing got hold of things and figured that if they redefined "KB" to "1000 bytes" they would have an edge over their competitors ("your 160GB HD is really 149GB"; can you say false advertising?). - BW

Just FYI, the ugly-but-precise "i" notations are part of a relatively recent IEC standard; see this for some detail. Compare this. So "official" isn't too much of a stretch. --AlexanderDaguerre

grumbles after reading links provided. "I suppose" - BW

units | byte | bit


Landmark

A landmark is one of several inventory items that points to a specific location on the grid. The coordinates to which a landmark specifies can be found using the llRequestInventoryData function, if the landmark is in an object's inventory.

llGetInventoryName and llGetInventoryNumber identify landmark inventory objects with the constant INVENTORY_LANDMARK.

There are two versions of landmarks. The original version (those created prior to SL 1.6) stored its data as global coordinates. The version introduced in SL 1.6 stores the sim name and local sim position. The newer version has the advantage that, even if a sim is moved, the landmark will still point to the same location in that sim. The older version would, instead, point to the old location. This will typically be a void in the case of an island sim that has been relocated.

Inventory | Position


LaserPascal

Copyright stuff:
I give permission for my contributions and edits to be applied to the official Linden Labs LSL Wiki Mirror 7-7-7/repository, with attribution where appropriate.

Wish List

Idea | Description
llGetInventoryDesc | string llGetInventoryDesc(string name) Similar to llGetInventoryKey, could be used for pulling the description from items that cannot be scripted to return it, like notecards and textures. Since anyone can see the description of the contained objects without any permissions, there should be no perm problems with this function. Benefits would include simplifying vending (less reading from notecards for instance) and better script inventory management.


LexFile

This is the lex and yacc file used by the LSL compiler as posted by CoryLinden.

There is a bug in the lex file that results in weirdness. See the fix here.

lsl.l
D			[-]?[0-9]
N			[0-9]
L			[a-zA-Z_]
H			[a-fA-F0-9]
E			[Ee][+-]?{D}+
FS			(f|F)

% %
"//"					{  }

"integer"			{ return(INTEGER); }
"float"				{ return(FLOAT_TYPE); }
"string"			{ return(STRING); }
"key"				{ return(LLKEY); }
"vector"			{ return(VECTOR); }
"quaternion"		{ return(QUATERNION); }
"rotation"			{ return(QUATERNION); }
"list"				{ return(LIST); }

"default"			{ yylval.sval = new char[strlen(yytext) + 1]; strcpy(yylval.sval, yytext); return(STATE_DEFAULT); }
"state"				{ return(STATE); }
"event"				{ return(EVENT); }
"jump"				{ return(JUMP); }
"return"			{ return(RETURN); }
"if"				{ return(IF); }
"else"				{ return(ELSE); }
"for"				{ return(FOR); }
"do"				{ return(DO); }
"while"				{ return(WHILE); }

"state_entry"			{ return(STATE_ENTRY); }
"state_exit"			{ return(STATE_EXIT); }
"touch_start"			{ return(TOUCH_START); }
"touch"					{ return(TOUCH); }
"touch_end"				{ return(TOUCH_END); }
"collision_start"		{ return(COLLISION_START); }
"collision"				{ return(COLLISION); }
"collision_end"			{ return(COLLISION_END); }
"land_collision_start"	{ return(LAND_COLLISION_START); }
"land_collision"		{ return(LAND_COLLISION); }
"land_collision_end"	{ return(LAND_COLLISION_END); }
"timer"					{ return(TIMER); }
"listen"				{ return(CHAT); }
"sensor"				{ return(SENSOR); }
"no_sensor"				{ return(NO_SENSOR); }
"control"				{ return(CONTROL); }
"print"					{ return(PRINT); }
"at_target"				{ return(AT_TARGET); }
"not_at_target"			{ return(NOT_AT_TARGET); }
"at_rot_target"			{ return(AT_ROT_TARGET); }
"not_at_rot_target"		{ return(NOT_AT_ROT_TARGET); }
"money"					{ return(MONEY); }
"email"					{ return(EMAIL); }
"run_time_permissions"	{ return(RUN_TIME_PERMISSIONS); }
"changed"				{ return(INVENTORY); }
"attach"				{ return(ATTACH); }
"dataserver"			{ return(DATASERVER); }
"moving_start"			{ return(MOVING_START); }
"moving_end"			{ return(MOVING_END); }
"link_message"			{ return(LINK_MESSAGE); }
"on_rez"				{ return(REZ); }
"object_rez"			{ return(OBJECT_REZ); }
"remote_data"			{ return(REMOTE_DATA); }
"."						{ return(PERIOD); }

0[xX]{H}+			{ yylval.ival = strtoul(yytext, NULL, 0);  return(INTEGER_CONSTANT); }
{D}+				{ yylval.ival = strtoul(yytext, NULL, 10); return(INTEGER_CONSTANT); }
"TRUE"				{ yylval.ival = 1; return(INTEGER_TRUE); }
"FALSE"				{ yylval.ival = 0; return(INTEGER_FALSE); }
"STATUS_PHYSICS"		{ yylval.ival = 0x1; return(INTEGER_CONSTANT); }
"STATUS_ROTATE_X"		{ yylval.ival = 0x2; return(INTEGER_CONSTANT); }
"STATUS_ROTATE_Y"		{ yylval.ival = 0x4; return(INTEGER_CONSTANT); }
"STATUS_ROTATE_Z"		{ yylval.ival = 0x8; return(INTEGER_CONSTANT); }
"STATUS_PHANTOM"		{ yylval.ival = 0x10; return(INTEGER_CONSTANT); }
"STATUS_SANDBOX"		{ yylval.ival = 0x20; return(INTEGER_CONSTANT); }
"STATUS_BLOCK_GRAB"		{ yylval.ival = 0x40; return(INTEGER_CONSTANT); }
"STATUS_DIE_AT_EDGE"	{ yylval.ival = 0x80; return(INTEGER_CONSTANT); }
"STATUS_RETURN_AT_EDGE"	{ yylval.ival = 0x100; return(INTEGER_CONSTANT); }

"AGENT_FLYING"			{ yylval.ival = AGENT_FLYING; return(INTEGER_CONSTANT); }
"AGENT_ATTACHMENTS"		{ yylval.ival = AGENT_ATTACHMENTS; return(INTEGER_CONSTANT); }
"AGENT_SCRIPTED"		{ yylval.ival = AGENT_SCRIPTED; return(INTEGER_CONSTANT); }
"AGENT_MOUSELOOK"		{ yylval.ival = AGENT_MOUSELOOK; return(INTEGER_CONSTANT); }
"AGENT_SITTING"			{ yylval.ival = AGENT_SITTING; return(INTEGER_CONSTANT); }
"AGENT_ON_OBJECT"		{ yylval.ival = AGENT_ON_OBJECT; return(INTEGER_CONSTANT); }
"AGENT_AWAY"			{ yylval.ival = AGENT_AWAY; return(INTEGER_CONSTANT); }
"AGENT_WALKING"			{ yylval.ival = AGENT_WALKING; return(INTEGER_CONSTANT); }
"AGENT_IN_AIR"			{ yylval.ival = AGENT_IN_AIR; return(INTEGER_CONSTANT); }
"AGENT_TYPING"			{ yylval.ival = AGENT_TYPING; return(INTEGER_CONSTANT); }
"AGENT_CROUCHING"		{ yylval.ival = AGENT_CROUCHING; return(INTEGER_CONSTANT); }
"AGENT_BUSY"			{ yylval.ival = AGENT_BUSY; return(INTEGER_CONSTANT); }
"AGENT_ALWAYS_RUN"		{ yylval.ival = AGENT_ALWAYS_RUN; return(INTEGER_CONSTANT); }

"ANIM_ON"				{ yylval.ival = 0x1; return(INTEGER_CONSTANT); }
"LOOP"					{ yylval.ival = 0x2; return(INTEGER_CONSTANT); }
"REVERSE"				{ yylval.ival = 0x4; return(INTEGER_CONSTANT); }
"PING_PONG"				{ yylval.ival = 0x8; return(INTEGER_CONSTANT); }
"SMOOTH"				{ yylval.ival = 0x10; return(INTEGER_CONSTANT); }
"ROTATE"				{ yylval.ival = 0x20; return(INTEGER_CONSTANT); }
"SCALE"					{ yylval.ival = 0x40; return(INTEGER_CONSTANT); }

"ALL_SIDES"				{ yylval.ival = LSL_ALL_SIDES; return(INTEGER_CONSTANT); }
"LINK_ROOT"				{ yylval.ival = LSL_LINK_ROOT; return(INTEGER_CONSTANT); }
"LINK_SET"				{ yylval.ival = LSL_LINK_SET; return(INTEGER_CONSTANT); }
"LINK_ALL_OTHERS"		{ yylval.ival = LSL_LINK_ALL_OTHERS; return(INTEGER_CONSTANT); }
"LINK_ALL_CHILDREN"		{ yylval.ival = LSL_LINK_ALL_CHILDREN; return(INTEGER_CONSTANT); }
"LINK_THIS"				{ yylval.ival = LSL_LINK_THIS; return(INTEGER_CONSTANT); }

"AGENT"					{ yylval.ival = 0x1; return(INTEGER_CONSTANT); }
"ACTIVE"				{ yylval.ival = 0x2; return(INTEGER_CONSTANT); }
"PASSIVE"				{ yylval.ival = 0x4; return(INTEGER_CONSTANT); }
"SCRIPTED"				{ yylval.ival = 0x8; return(INTEGER_CONSTANT); }

"CONTROL_FWD"			{ yylval.ival = AGENT_CONTROL_AT_POS; return(INTEGER_CONSTANT); }
"CONTROL_BACK"			{ yylval.ival = AGENT_CONTROL_AT_NEG; return(INTEGER_CONSTANT); }
"CONTROL_LEFT"			{ yylval.ival = AGENT_CONTROL_LEFT_POS; return(INTEGER_CONSTANT); }
"CONTROL_RIGHT"			{ yylval.ival = AGENT_CONTROL_LEFT_NEG; return(INTEGER_CONSTANT); }
"CONTROL_ROT_LEFT"		{ yylval.ival = AGENT_CONTROL_YAW_POS; return(INTEGER_CONSTANT); }
"CONTROL_ROT_RIGHT"		{ yylval.ival = AGENT_CONTROL_YAW_NEG; return(INTEGER_CONSTANT); }
"CONTROL_UP"			{ yylval.ival = AGENT_CONTROL_UP_POS; return(INTEGER_CONSTANT); }
"CONTROL_DOWN"			{ yylval.ival = AGENT_CONTROL_UP_NEG; return(INTEGER_CONSTANT); }
"CONTROL_LBUTTON"		{ yylval.ival = AGENT_CONTROL_LBUTTON_DOWN; return(INTEGER_CONSTANT); }
"CONTROL_ML_LBUTTON"	{ yylval.ival = AGENT_CONTROL_ML_LBUTTON_DOWN; return(INTEGER_CONSTANT); }

"PERMISSION_DEBIT"				{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_DEBIT]; return(INTEGER_CONSTANT); }
"PERMISSION_TAKE_CONTROLS"		{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_TAKE_CONTROLS]; return(INTEGER_CONSTANT); }
"PERMISSION_REMAP_CONTROLS"		{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_REMAP_CONTROLS]; return(INTEGER_CONSTANT); }
"PERMISSION_TRIGGER_ANIMATION"	{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_TRIGGER_ANIMATION]; return(INTEGER_CONSTANT); }
"PERMISSION_ATTACH"				{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_ATTACH]; return(INTEGER_CONSTANT); }
"PERMISSION_RELEASE_OWNERSHIP"	{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_RELEASE_OWNERSHIP]; return(INTEGER_CONSTANT); }
"PERMISSION_CHANGE_LINKS"		{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_LINKS]; return(INTEGER_CONSTANT); }
"PERMISSION_CHANGE_JOINTS"		{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_JOINTS]; return(INTEGER_CONSTANT); }
"PERMISSION_CHANGE_PERMISSIONS"	{ yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_PERMISSIONS]; return(INTEGER_CONSTANT); }

"INVENTORY_TEXTURE"					{ yylval.ival = LLAssetType::AT_TEXTURE; return(INTEGER_CONSTANT); }
"INVENTORY_SOUND"					{ yylval.ival = LLAssetType::AT_SOUND; return(INTEGER_CONSTANT); }
"INVENTORY_OBJECT"					{ yylval.ival = LLAssetType::AT_OBJECT; return(INTEGER_CONSTANT); }
"INVENTORY_SCRIPT"					{ yylval.ival = LLAssetType::AT_LSL_TEXT; return(INTEGER_CONSTANT); }
"INVENTORY_LANDMARK"				{ yylval.ival = LLAssetType::AT_LANDMARK; return(INTEGER_CONSTANT); }
"INVENTORY_CLOTHING"				{ yylval.ival = LLAssetType::AT_CLOTHING; return(INTEGER_CONSTANT); }
"INVENTORY_NOTECARD"				{ yylval.ival = LLAssetType::AT_NOTECARD; return(INTEGER_CONSTANT); }
"INVENTORY_BODYPART"				{ yylval.ival = LLAssetType::AT_BODYPART; return(INTEGER_CONSTANT); }
"INVENTORY_ANIMATION"				{ yylval.ival = LLAssetType::AT_ANIMATION; return(INTEGER_CONSTANT); }
"INVENTORY_GESTURE"					{ yylval.ival = LLAssetType::AT_GESTURE; return(INTEGER_CONSTANT); }
"INVENTORY_ALL"						{ yylval.ival = LLAssetType::AT_NONE; return(INTEGER_CONSTANT); }

"CHANGED_INVENTORY"		{ yylval.ival = 0x1; return(INTEGER_CONSTANT); }
"CHANGED_COLOR"			{ yylval.ival = 0x2; return(INTEGER_CONSTANT); }
"CHANGED_SHAPE"			{ yylval.ival = 0x4; return(INTEGER_CONSTANT); }
"CHANGED_SCALE"			{ yylval.ival = 0x8; return(INTEGER_CONSTANT); }
"CHANGED_TEXTURE"		{ yylval.ival = 0x10; return(INTEGER_CONSTANT); }
"CHANGED_LINK"			{ yylval.ival = 0x20; return(INTEGER_CONSTANT); }
"CHANGED_ALLOWED_DROP"	{ yylval.ival = 0x40; return(INTEGER_CONSTANT); }
"CHANGED_OWNER"			{ yylval.ival = 0x80; return(INTEGER_CONSTANT); }

"TYPE_INTEGER"			{ yylval.ival = LST_INTEGER; return(INTEGER_CONSTANT); }
"TYPE_FLOAT"			{ yylval.ival = LST_FLOATINGPOINT; return(INTEGER_CONSTANT); }
"TYPE_STRING"			{ yylval.ival = LST_STRING; return(INTEGER_CONSTANT); }
"TYPE_KEY"				{ yylval.ival = LST_KEY; return(INTEGER_CONSTANT); }
"TYPE_VECTOR"			{ yylval.ival = LST_VECTOR; return(INTEGER_CONSTANT); }
"TYPE_ROTATION"			{ yylval.ival = LST_QUATERNION; return(INTEGER_CONSTANT); }
"TYPE_INVALID"			{ yylval.ival = LST_NULL; return(INTEGER_CONSTANT); }

"NULL_KEY"				{ yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, "00000000-0000-0000-0000-000000000000"); return(STRING_CONSTANT); }
"EOF"					{ yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, "\n\n\n"); return(STRING_CONSTANT); }

"PI"					{ yylval.fval = F_PI; return(FP_CONSTANT); }
"TWO_PI"				{ yylval.fval = F_TWO_PI; return(FP_CONSTANT); }
"PI_BY_TWO"				{ yylval.fval = F_PI_BY_TWO; return(FP_CONSTANT); }
"DEG_TO_RAD"			{ yylval.fval = DEG_TO_RAD; return(FP_CONSTANT); }
"RAD_TO_DEG"			{ yylval.fval = RAD_TO_DEG; return(FP_CONSTANT); }
"SQRT2"					{ yylval.fval = F_SQRT2; return(FP_CONSTANT); }

"ZERO_VECTOR"			{ return(ZERO_VECTOR); }
"ZERO_ROTATION"			{ return(ZERO_ROTATION); }

"ATTACH_CHEST"		{ yylval.ival = 1; return(INTEGER_CONSTANT); }
"ATTACH_HEAD"		{ yylval.ival = 2; return(INTEGER_CONSTANT); }
"ATTACH_LSHOULDER"	{ yylval.ival = 3; return(INTEGER_CONSTANT); }
"ATTACH_RSHOULDER"	{ yylval.ival = 4; return(INTEGER_CONSTANT); }
"ATTACH_LHAND"		{ yylval.ival = 5; return(INTEGER_CONSTANT); }
"ATTACH_RHAND"		{ yylval.ival = 6; return(INTEGER_CONSTANT); }
"ATTACH_LFOOT"		{ yylval.ival = 7; return(INTEGER_CONSTANT); }
"ATTACH_RFOOT"		{ yylval.ival = 8; return(INTEGER_CONSTANT); }
"ATTACH_BACK"		{ yylval.ival = 9; return(INTEGER_CONSTANT); }
"ATTACH_PELVIS"		{ yylval.ival = 10; return(INTEGER_CONSTANT); }
"ATTACH_MOUTH"		{ yylval.ival = 11; return(INTEGER_CONSTANT); }
"ATTACH_CHIN"		{ yylval.ival = 12; return(INTEGER_CONSTANT); }
"ATTACH_LEAR"		{ yylval.ival = 13; return(INTEGER_CONSTANT); }
"ATTACH_REAR"		{ yylval.ival = 14; return(INTEGER_CONSTANT); }
"ATTACH_LEYE"		{ yylval.ival = 15; return(INTEGER_CONSTANT); }
"ATTACH_REYE"		{ yylval.ival = 16; return(INTEGER_CONSTANT); }
"ATTACH_NOSE"		{ yylval.ival = 17; return(INTEGER_CONSTANT); }
"ATTACH_RUARM"		{ yylval.ival = 18; return(INTEGER_CONSTANT); }
"ATTACH_RLARM"		{ yylval.ival = 19; return(INTEGER_CONSTANT); }
"ATTACH_LUARM"		{ yylval.ival = 20; return(INTEGER_CONSTANT); }
"ATTACH_LLARM"		{ yylval.ival = 21; return(INTEGER_CONSTANT); }
"ATTACH_RHIP"		{ yylval.ival = 22; return(INTEGER_CONSTANT); }
"ATTACH_RULEG"		{ yylval.ival = 23; return(INTEGER_CONSTANT); }
"ATTACH_RLLEG"		{ yylval.ival = 24; return(INTEGER_CONSTANT); }
"ATTACH_LHIP"		{ yylval.ival = 25; return(INTEGER_CONSTANT); }
"ATTACH_LULEG"		{ yylval.ival = 26; return(INTEGER_CONSTANT); }
"ATTACH_LLLEG"		{ yylval.ival = 27; return(INTEGER_CONSTANT); }
"ATTACH_BELLY"		{ yylval.ival = 28; return(INTEGER_CONSTANT); }
"ATTACH_RPEC"		{ yylval.ival = 29; return(INTEGER_CONSTANT); }
"ATTACH_LPEC"		{ yylval.ival = 30; return(INTEGER_CONSTANT); }

"LAND_LEVEL"		{ yylval.ival = E_LANDBRUSH_LEVEL; return(INTEGER_CONSTANT); }
"LAND_RAISE"		{ yylval.ival = E_LANDBRUSH_RAISE; return(INTEGER_CONSTANT); }
"LAND_LOWER"		{ yylval.ival = E_LANDBRUSH_LOWER; return(INTEGER_CONSTANT); }
"LAND_SMOOTH"		{ yylval.ival = E_LANDBRUSH_SMOOTH; return(INTEGER_CONSTANT); }
"LAND_NOISE"		{ yylval.ival = E_LANDBRUSH_NOISE; return(INTEGER_CONSTANT); }
"LAND_REVERT"		{ yylval.ival = E_LANDBRUSH_REVERT; return(INTEGER_CONSTANT); }

"LAND_SMALL_BRUSH"	{ yylval.ival = 1; return(INTEGER_CONSTANT); }
"LAND_MEDIUM_BRUSH"	{ yylval.ival = 2; return(INTEGER_CONSTANT); }
"LAND_LARGE_BRUSH"	{ yylval.ival = 3; return(INTEGER_CONSTANT); }

"DATA_ONLINE"		{ yylval.ival = 1; return(INTEGER_CONSTANT); }
"DATA_NAME"			{ yylval.ival = 2; return(INTEGER_CONSTANT); }
"DATA_BORN"			{ yylval.ival = 3; return(INTEGER_CONSTANT); }
"DATA_RATING"		{ yylval.ival = 4; return(INTEGER_CONSTANT); }
"DATA_SIM_POS"		{ yylval.ival = 5; return(INTEGER_CONSTANT); }
"DATA_SIM_STATUS"	{ yylval.ival = 6; return(INTEGER_CONSTANT); }

"REMOTE_DATA_CHANNEL"	{ yylval.ival = 1; return(INTEGER_CONSTANT); }
"REMOTE_DATA_REQUEST"	{ yylval.ival = 2; return(INTEGER_CONSTANT); }
"REMOTE_DATA_REPLY"		{ yylval.ival = 3; return(INTEGER_CONSTANT); }

"PSYS_PART_FLAGS"		{ yylval.ival = LLPS_PART_FLAGS; return(INTEGER_CONSTANT); }
"PSYS_PART_START_COLOR"	{ yylval.ival = LLPS_PART_START_COLOR; return (INTEGER_CONSTANT); }
"PSYS_PART_START_ALPHA"	{ yylval.ival = LLPS_PART_START_ALPHA; return (INTEGER_CONSTANT); }
"PSYS_PART_START_SCALE"	{ yylval.ival = LLPS_PART_START_SCALE; return (INTEGER_CONSTANT); }
"PSYS_PART_END_COLOR"	{ yylval.ival = LLPS_PART_END_COLOR; return (INTEGER_CONSTANT); }
"PSYS_PART_END_ALPHA"	{ yylval.ival = LLPS_PART_END_ALPHA; return (INTEGER_CONSTANT); }
"PSYS_PART_END_SCALE"	{ yylval.ival = LLPS_PART_END_SCALE; return (INTEGER_CONSTANT); }
"PSYS_PART_MAX_AGE"		{ yylval.ival = LLPS_PART_MAX_AGE; return (INTEGER_CONSTANT); }

"PSYS_PART_WIND_MASK"				{ yylval.ival = LLPartData::LL_PART_WIND_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_INTERP_COLOR_MASK"		{ yylval.ival = LLPartData::LL_PART_INTERP_COLOR_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_INTERP_SCALE_MASK"		{ yylval.ival = LLPartData::LL_PART_INTERP_SCALE_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_BOUNCE_MASK"				{ yylval.ival = LLPartData::LL_PART_BOUNCE_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_FOLLOW_SRC_MASK"			{ yylval.ival = LLPartData::LL_PART_FOLLOW_SRC_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_FOLLOW_VELOCITY_MASK"	{ yylval.ival = LLPartData::LL_PART_FOLLOW_VELOCITY_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_TARGET_POS_MASK"			{ yylval.ival = LLPartData::LL_PART_TARGET_POS_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_EMISSIVE_MASK"			{ yylval.ival = LLPartData::LL_PART_EMISSIVE_MASK; return(INTEGER_CONSTANT); }
"PSYS_PART_TARGET_LINEAR_MASK"		{ yylval.ival = LLPartData::LL_PART_TARGET_LINEAR_MASK; return(INTEGER_CONSTANT); }

"PSYS_SRC_MAX_AGE"					{ yylval.ival = LLPS_SRC_MAX_AGE; return(INTEGER_CONSTANT); }
"PSYS_SRC_PATTERN"					{ yylval.ival = LLPS_SRC_PATTERN; return(INTEGER_CONSTANT); }
"PSYS_SRC_INNERANGLE"				{ yylval.ival = LLPS_SRC_INNERANGLE; return(INTEGER_CONSTANT); }
"PSYS_SRC_OUTERANGLE"				{ yylval.ival = LLPS_SRC_OUTERANGLE; return(INTEGER_CONSTANT); }
"PSYS_SRC_ANGLE_BEGIN"				{ yylval.ival = LLPS_SRC_ANGLE_BEGIN; return(INTEGER_CONSTANT); }
"PSYS_SRC_ANGLE_END"				{ yylval.ival = LLPS_SRC_ANGLE_END; return(INTEGER_CONSTANT); }
"PSYS_SRC_BURST_RATE"				{ yylval.ival = LLPS_SRC_BURST_RATE; return(INTEGER_CONSTANT); }
"PSYS_SRC_BURST_PART_COUNT"			{ yylval.ival = LLPS_SRC_BURST_PART_COUNT; return(INTEGER_CONSTANT); }
"PSYS_SRC_BURST_RADIUS"				{ yylval.ival = LLPS_SRC_BURST_RADIUS; return(INTEGER_CONSTANT); }
"PSYS_SRC_BURST_SPEED_MIN"			{ yylval.ival = LLPS_SRC_BURST_SPEED_MIN; return(INTEGER_CONSTANT); }
"PSYS_SRC_BURST_SPEED_MAX"			{ yylval.ival = LLPS_SRC_BURST_SPEED_MAX; return(INTEGER_CONSTANT); }
"PSYS_SRC_ACCEL"					{ yylval.ival = LLPS_SRC_ACCEL; return(INTEGER_CONSTANT); }
"PSYS_SRC_TEXTURE"					{ yylval.ival = LLPS_SRC_TEXTURE; return(INTEGER_CONSTANT); }
"PSYS_SRC_TARGET_KEY"				{ yylval.ival = LLPS_SRC_TARGET_UUID; return(INTEGER_CONSTANT); }
"PSYS_SRC_OMEGA"					{ yylval.ival = LLPS_SRC_OMEGA; return(INTEGER_CONSTANT); }

"PSYS_SRC_OBJ_REL_MASK"				{ yylval.ival = LLPartSysData::LL_PART_SRC_OBJ_REL_MASK; return(INTEGER_CONSTANT); }

"PSYS_SRC_PATTERN_DROP"				{ yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_DROP; return(INTEGER_CONSTANT); }
"PSYS_SRC_PATTERN_EXPLODE"			{ yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE; return(INTEGER_CONSTANT); }
"PSYS_SRC_PATTERN_ANGLE"			{ yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE; return(INTEGER_CONSTANT); }
"PSYS_SRC_PATTERN_ANGLE_CONE"		{ yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE; return(INTEGER_CONSTANT); }
"PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY"	{ yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE_EMPTY; return(INTEGER_CONSTANT); }

"VEHICLE_TYPE_NONE"		{ yylval.ival = VEHICLE_TYPE_NONE; return(INTEGER_CONSTANT); }
"VEHICLE_TYPE_SLED"		{ yylval.ival = VEHICLE_TYPE_SLED; return(INTEGER_CONSTANT); }
"VEHICLE_TYPE_CAR"		{ yylval.ival = VEHICLE_TYPE_CAR; return(INTEGER_CONSTANT); }
"VEHICLE_TYPE_BOAT"		{ yylval.ival = VEHICLE_TYPE_BOAT; return(INTEGER_CONSTANT); }
"VEHICLE_TYPE_AIRPLANE"	{ yylval.ival = VEHICLE_TYPE_AIRPLANE; return(INTEGER_CONSTANT); }
"VEHICLE_TYPE_BALLOON"	{ yylval.ival = VEHICLE_TYPE_BALLOON; return(INTEGER_CONSTANT); }

"VEHICLE_REFERENCE_FRAME"			{ yylval.ival = VEHICLE_REFERENCE_FRAME; return(INTEGER_CONSTANT); }
"VEHICLE_LINEAR_FRICTION_TIMESCALE"	{ yylval.ival = VEHICLE_LINEAR_FRICTION_TIMESCALE; return(INTEGER_CONSTANT); }
"VEHICLE_ANGULAR_FRICTION_TIMESCALE" { yylval.ival = VEHICLE_ANGULAR_FRICTION_TIMESCALE; return(INTEGER_CONSTANT); }
"VEHICLE_LINEAR_MOTOR_DIRECTION"	{ yylval.ival = VEHICLE_LINEAR_MOTOR_DIRECTION; return(INTEGER_CONSTANT); }
"VEHICLE_ANGULAR_MOTOR_DIRECTION"	{ yylval.ival = VEHICLE_ANGULAR_MOTOR_DIRECTION; return(INTEGER_CONSTANT); }
"VEHICLE_LINEAR_MOTOR_OFFSET"	    { yylval.ival = VEHICLE_LINEAR_MOTOR_OFFSET; return(INTEGER_CONSTANT); }

"VEHICLE_HOVER_HEIGHT"		{ yylval.ival = VEHICLE_HOVER_HEIGHT; return(INTEGER_CONSTANT); }
"VEHICLE_HOVER_EFFICIENCY"	{ yylval.ival = VEHICLE_HOVER_EFFICIENCY; return(INTEGER_CONSTANT); }
"VEHICLE_HOVER_TIMESCALE"	{ yylval.ival = VEHICLE_HOVER_TIMESCALE; return(INTEGER_CONSTANT); }
"VEHICLE_BUOYANCY"			{ yylval.ival = VEHICLE_BUOYANCY; return(INTEGER_CONSTANT); }

"VEHICLE_LINEAR_DEFLECTION_EFFICIENCY"	{ yylval.ival = VEHICLE_LINEAR_DEFLECTION_EFFICIENCY; return(INTEGER_CONSTANT); }
"VEHICLE_LINEAR_DEFLECTION_TIMESCALE"	{ yylval.ival = VEHICLE_LINEAR_DEFLECTION_TIMESCALE; return(INTEGER_CONSTANT); }
"VEHICLE_LINEAR_MOTOR_TIMESCALE"		{ yylval.ival = VEHICLE_LINEAR_MOTOR_TIMESCALE; return(INTEGER_CONSTANT); }
"VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE"	{ yylval.ival = VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE; return(INTEGER_CONSTANT); }

"VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY" { yylval.ival = VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY; return(INTEGER_CONSTANT); }
"VEHICLE_ANGULAR_DEFLECTION_TIMESCALE"	{ yylval.ival = VEHICLE_ANGULAR_DEFLECTION_TIMESCALE; return(INTEGER_CONSTANT); }
"VEHICLE_ANGULAR_MOTOR_TIMESCALE"		{ yylval.ival = VEHICLE_ANGULAR_MOTOR_TIMESCALE; return(INTEGER_CONSTANT); }
"VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE"	{ yylval.ival = VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE; return(INTEGER_CONSTANT); }

"VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY"	{ yylval.ival = VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY; return(INTEGER_CONSTANT); }
"VEHICLE_VERTICAL_ATTRACTION_TIMESCALE"		{ yylval.ival = VEHICLE_VERTICAL_ATTRACTION_TIMESCALE; return(INTEGER_CONSTANT); }

"VEHICLE_BANKING_EFFICIENCY"	{ yylval.ival = VEHICLE_BANKING_EFFICIENCY; return(INTEGER_CONSTANT); }
"VEHICLE_BANKING_MIX"			{ yylval.ival = VEHICLE_BANKING_MIX; return(INTEGER_CONSTANT); }
"VEHICLE_BANKING_TIMESCALE"		{ yylval.ival = VEHICLE_BANKING_TIMESCALE; return(INTEGER_CONSTANT); }

"VEHICLE_FLAG_NO_FLY_UP"			{ yylval.ival = VEHICLE_FLAG_NO_DEFLECTION_UP; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_NO_DEFLECTION_UP"			{ yylval.ival = VEHICLE_FLAG_NO_DEFLECTION_UP; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_LIMIT_ROLL_ONLY"		{ yylval.ival = VEHICLE_FLAG_LIMIT_ROLL_ONLY; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_HOVER_WATER_ONLY"		{ yylval.ival = VEHICLE_FLAG_HOVER_WATER_ONLY; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_HOVER_TERRAIN_ONLY"	{ yylval.ival = VEHICLE_FLAG_HOVER_TERRAIN_ONLY; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT"	{ yylval.ival = VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_HOVER_UP_ONLY"		{ yylval.ival = VEHICLE_FLAG_HOVER_UP_ONLY; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_LIMIT_MOTOR_UP"		{ yylval.ival = VEHICLE_FLAG_LIMIT_MOTOR_UP; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_MOUSELOOK_STEER"		{ yylval.ival = VEHICLE_FLAG_MOUSELOOK_STEER; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_MOUSELOOK_BANK"		{ yylval.ival = VEHICLE_FLAG_MOUSELOOK_BANK; return(INTEGER_CONSTANT); }
"VEHICLE_FLAG_CAMERA_DECOUPLED"		{ yylval.ival = VEHICLE_FLAG_CAMERA_DECOUPLED; return(INTEGER_CONSTANT); }

"PRIM_TYPE"				{ yylval.ival = LSL_PRIM_TYPE; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL"			{ yylval.ival = LSL_PRIM_MATERIAL; return(INTEGER_CONSTANT); }
"PRIM_PHYSICS"			{ yylval.ival = LSL_PRIM_PHYSICS; return(INTEGER_CONSTANT); }
"PRIM_TEMP_ON_REZ"		{ yylval.ival = LSL_PRIM_TEMP_ON_REZ; return(INTEGER_CONSTANT); }
"PRIM_PHANTOM"			{ yylval.ival = LSL_PRIM_PHANTOM; return(INTEGER_CONSTANT); }
"PRIM_POSITION"			{ yylval.ival = LSL_PRIM_POSITION; return(INTEGER_CONSTANT); }
"PRIM_SIZE"				{ yylval.ival = LSL_PRIM_SIZE; return(INTEGER_CONSTANT); }
"PRIM_ROTATION"			{ yylval.ival = LSL_PRIM_ROTATION; return(INTEGER_CONSTANT); }
"PRIM_TEXTURE"			{ yylval.ival = LSL_PRIM_TEXTURE; return(INTEGER_CONSTANT); }
"PRIM_COLOR"			{ yylval.ival = LSL_PRIM_COLOR; return(INTEGER_CONSTANT); }
"PRIM_BUMP_SHINY"		{ yylval.ival = LSL_PRIM_BUMP_SHINY; return(INTEGER_CONSTANT); }

"PRIM_TYPE_BOX"			{ yylval.ival = LSL_PRIM_TYPE_BOX; return(INTEGER_CONSTANT); }
"PRIM_TYPE_CYLINDER"	{ yylval.ival = LSL_PRIM_TYPE_CYLINDER; return(INTEGER_CONSTANT); }
"PRIM_TYPE_PRISM"		{ yylval.ival = LSL_PRIM_TYPE_PRISM; return(INTEGER_CONSTANT); }
"PRIM_TYPE_SPHERE"		{ yylval.ival = LSL_PRIM_TYPE_SPHERE; return(INTEGER_CONSTANT); }
"PRIM_TYPE_TORUS"		{ yylval.ival = LSL_PRIM_TYPE_TORUS; return(INTEGER_CONSTANT); }
"PRIM_TYPE_TUBE"		{ yylval.ival = LSL_PRIM_TYPE_TUBE; return(INTEGER_CONSTANT); }
"PRIM_TYPE_RING"		{ yylval.ival = LSL_PRIM_TYPE_RING; return(INTEGER_CONSTANT); }

"PRIM_HOLE_DEFAULT"		{ yylval.ival = LSL_PRIM_HOLE_DEFAULT; return(INTEGER_CONSTANT); }
"PRIM_HOLE_CIRCLE"		{ yylval.ival = LSL_PRIM_HOLE_CIRCLE; return(INTEGER_CONSTANT); }
"PRIM_HOLE_SQUARE"		{ yylval.ival = LSL_PRIM_HOLE_SQUARE; return(INTEGER_CONSTANT); }
"PRIM_HOLE_TRIANGLE"	{ yylval.ival = LSL_PRIM_HOLE_TRIANGLE; return(INTEGER_CONSTANT); }

"PRIM_MATERIAL_STONE"	{ yylval.ival = LSL_PRIM_MATERIAL_STONE; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL_METAL"	{ yylval.ival = LSL_PRIM_MATERIAL_METAL; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL_GLASS"	{ yylval.ival = LSL_PRIM_MATERIAL_GLASS; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL_WOOD"	{ yylval.ival = LSL_PRIM_MATERIAL_WOOD; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL_FLESH"	{ yylval.ival = LSL_PRIM_MATERIAL_FLESH; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL_PLASTIC"	{ yylval.ival = LSL_PRIM_MATERIAL_PLASTIC; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL_RUBBER"	{ yylval.ival = LSL_PRIM_MATERIAL_RUBBER; return(INTEGER_CONSTANT); }
"PRIM_MATERIAL_LIGHT"	{ yylval.ival = LSL_PRIM_MATERIAL_LIGHT; return(INTEGER_CONSTANT); }

"PRIM_SHINY_NONE"		{ yylval.ival = LSL_PRIM_SHINY_NONE; return(INTEGER_CONSTANT); }
"PRIM_SHINY_LOW"		{ yylval.ival = LSL_PRIM_SHINY_LOW; return(INTEGER_CONSTANT); }
"PRIM_SHINY_MEDIUM"		{ yylval.ival = LSL_PRIM_SHINY_MEDIUM; return(INTEGER_CONSTANT); }
"PRIM_SHINY_HIGH"		{ yylval.ival = LSL_PRIM_SHINY_HIGH; return(INTEGER_CONSTANT); }

"PRIM_BUMP_NONE"		{ yylval.ival = LSL_PRIM_BUMP_NONE; return(INTEGER_CONSTANT); }
"PRIM_BUMP_BRIGHT"		{ yylval.ival = LSL_PRIM_BUMP_BRIGHT; return(INTEGER_CONSTANT); }
"PRIM_BUMP_DARK"		{ yylval.ival = LSL_PRIM_BUMP_DARK; return(INTEGER_CONSTANT); }
"PRIM_BUMP_WOOD"		{ yylval.ival = LSL_PRIM_BUMP_WOOD; return(INTEGER_CONSTANT); }
"PRIM_BUMP_BARK"		{ yylval.ival = LSL_PRIM_BUMP_BARK; return(INTEGER_CONSTANT); }
"PRIM_BUMP_BRICKS"		{ yylval.ival = LSL_PRIM_BUMP_BRICKS; return(INTEGER_CONSTANT); }
"PRIM_BUMP_CHECKER"		{ yylval.ival = LSL_PRIM_BUMP_CHECKER; return(INTEGER_CONSTANT); }
"PRIM_BUMP_CONCRETE"	{ yylval.ival = LSL_PRIM_BUMP_CONCRETE; return(INTEGER_CONSTANT); }
"PRIM_BUMP_TILE"		{ yylval.ival = LSL_PRIM_BUMP_TILE; return(INTEGER_CONSTANT); }
"PRIM_BUMP_STONE"		{ yylval.ival = LSL_PRIM_BUMP_STONE; return(INTEGER_CONSTANT); }
"PRIM_BUMP_DISKS"		{ yylval.ival = LSL_PRIM_BUMP_DISKS; return(INTEGER_CONSTANT); }
"PRIM_BUMP_GRAVEL"		{ yylval.ival = LSL_PRIM_BUMP_GRAVEL; return(INTEGER_CONSTANT); }
"PRIM_BUMP_BLOBS"		{ yylval.ival = LSL_PRIM_BUMP_BLOBS; return(INTEGER_CONSTANT); }
"PRIM_BUMP_SIDING"		{ yylval.ival = LSL_PRIM_BUMP_SIDING; return(INTEGER_CONSTANT); }
"PRIM_BUMP_LARGETILE"	{ yylval.ival = LSL_PRIM_BUMP_LARGETILE; return(INTEGER_CONSTANT); }
"PRIM_BUMP_STUCCO"		{ yylval.ival = LSL_PRIM_BUMP_STUCCO; return(INTEGER_CONSTANT); }
"PRIM_BUMP_SUCTION"		{ yylval.ival = LSL_PRIM_BUMP_SUCTION; return(INTEGER_CONSTANT); }
"PRIM_BUMP_WEAVE"		{ yylval.ival = LSL_PRIM_BUMP_WEAVE; return(INTEGER_CONSTANT); }

"MASK_BASE"				{ yylval.ival = 0; return(INTEGER_CONSTANT); }
"MASK_OWNER"			{ yylval.ival = 1; return(INTEGER_CONSTANT); }
"MASK_GROUP"			{ yylval.ival = 2; return(INTEGER_CONSTANT); }
"MASK_EVERYONE"			{ yylval.ival = 3; return(INTEGER_CONSTANT); }
"MASK_NEXT"				{ yylval.ival = 4; return(INTEGER_CONSTANT); }

"PERM_TRANSFER"			{ yylval.ival = PERM_TRANSFER; return(INTEGER_CONSTANT); }
"PERM_MODIFY"			{ yylval.ival = PERM_MODIFY; return(INTEGER_CONSTANT); }
"PERM_COPY"				{ yylval.ival = PERM_COPY; return(INTEGER_CONSTANT); }
"PERM_MOVE"				{ yylval.ival = PERM_MOVE; return(INTEGER_CONSTANT); }
"PERM_ALL"				{ yylval.ival = PERM_ALL; return(INTEGER_CONSTANT); }

"PARCEL_MEDIA_COMMAND_STOP"		{ yylval.ival = PARCEL_MEDIA_COMMAND_STOP; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_PAUSE"	{ yylval.ival = PARCEL_MEDIA_COMMAND_PAUSE; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_PLAY"		{ yylval.ival = PARCEL_MEDIA_COMMAND_PLAY; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_LOOP"		{ yylval.ival = PARCEL_MEDIA_COMMAND_LOOP; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_TEXTURE"	{ yylval.ival = PARCEL_MEDIA_COMMAND_TEXTURE; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_URL"		{ yylval.ival = PARCEL_MEDIA_COMMAND_URL; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_TIME"		{ yylval.ival = PARCEL_MEDIA_COMMAND_TIME; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_AGENT"	{ yylval.ival = PARCEL_MEDIA_COMMAND_AGENT; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_UNLOAD"	{ yylval.ival = PARCEL_MEDIA_COMMAND_UNLOAD; return(INTEGER_CONSTANT); }
"PARCEL_MEDIA_COMMAND_AUTO_ALIGN"	{ yylval.ival = PARCEL_MEDIA_COMMAND_AUTO_ALIGN; return(INTEGER_CONSTANT); }

{L}({L}|{N})*		{ yylval.sval = new char[strlen(yytext) + 1]; strcpy(yylval.sval, yytext); return(IDENTIFIER); }

{D}+{E}					{ yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); }
{D}*"."{D}+({E})?{FS}?	{ yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); }
{D}+"."{D}*({E})?{FS}?	{ yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); }

L?\"(\\.|[^\\"])*\"	{ parse_string(); return(STRING_CONSTANT); }

"++"				{ return(INC_OP); }
"--"				{ return(DEC_OP); }
"+="				{ return(ADD_ASSIGN); }
"-="				{ return(SUB_ASSIGN); }
"*="				{ return(MUL_ASSIGN); }
"/="				{ return(DIV_ASSIGN); }
"%="				{ return(MOD_ASSIGN); }
";"					{ return(';'); }
"{"					{ return('{'); }
"}"					{ return('}'); }
","					{ return(','); }
"="					{ return('='); }
"("					{ return('('); }
")"					{ return(')'); }
"-"					{ return('-'); }
"+"					{ return('+'); }
"*"					{ return('*'); }
"/"					{ return('/'); }
"%"					{ return('%'); }
"@"					{ return('@'); }
":"					{ return(':'); }
">"					{ return('>'); }
"<"					{ return('<'); }
"]"					{ return(']'); }
"["					{ return('['); }
"=="				{ return(EQ);  }
"!="				{ return(NEQ);  }
">="				{ return(GEQ);  }
"<="				{ return(LEQ);  }
"&"					{ return('&');  }
"|"					{ return('|');  }
"^"					{ return('^');  }
"~"					{ return('~');  }
"!"					{ return('!');  }
"&&"				{ return(BOOLEAN_AND);	}
"||"				{ return(BOOLEAN_OR);	}
"<<"				{ return(SHIFT_LEFT);  }
">>"				{ return(SHIFT_RIGHT); }

[ \t\v\n\f]			{ }
.					{ /* ignore bad characters */ }

% %

lsl.y
lsl.y

%{
    int yylex(void);
    int yyerror(const char *fmt, ...);
%}

%union
{
    S32								ival;
    F32								fval;
    char							*sval;
    class LLScriptType				*type;
    class LLScriptConstant			*constant;
    class LLScriptIdentifier		*identifier;
    class LLScriptSimpleAssignable	*assignable;
    class LLScriptGlobalVariable	*global;
    class LLScriptEvent				*event;
    class LLScriptEventHandler		*handler;
    class LLScriptExpression		*expression;
    class LLScriptStatement			*statement;
    class LLScriptGlobalFunctions	*global_funcs;
    class LLScriptFunctionDec		*global_decl;
    class LLScriptState				*state;
    class LLScritpGlobalStorage		*global_store;
    class LLScriptScript			*script;
};

%token					INTEGER
%token					FLOAT_TYPE
%token					STRING
%token					LLKEY
%token					VECTOR
%token					QUATERNION
%token					LIST

%token					STATE_DEFAULT
%token					STATE
%token					EVENT
%token					JUMP
%token					RETURN

%token					STATE_ENTRY
%token					STATE_EXIT
%token					TOUCH_START
%token					TOUCH
%token					TOUCH_END
%token					COLLISION_START
%token					COLLISION
%token					COLLISION_END
%token					LAND_COLLISION_START
%token					LAND_COLLISION
%token					LAND_COLLISION_END
%token					TIMER
%token					CHAT
%token					SENSOR
%token					NO_SENSOR
%token					CONTROL
%token					AT_TARGET
%token					NOT_AT_TARGET
%token					AT_ROT_TARGET
%token					NOT_AT_ROT_TARGET
%token					MONEY
%token					EMAIL
%token					RUN_TIME_PERMISSIONS
%token					INVENTORY
%token					ATTACH
%token					DATASERVER
%token					MOVING_START
%token					MOVING_END
%token					REZ
%token					OBJECT_REZ
%token					LINK_MESSAGE
%token					REMOTE_DATA

%token <sval>			IDENTIFIER
%token <sval>			STATE_DEFAULT

%token <ival>			INTEGER_CONSTANT
%token <ival>			INTEGER_TRUE
%token <ival>			INTEGER_FALSE

%token <fval>			FP_CONSTANT

%token <sval>			STRING_CONSTANT

%token					INC_OP
%token					DEC_OP
%token					ADD_ASSIGN
%token					SUB_ASSIGN
%token					MUL_ASSIGN
%token					DIV_ASSIGN
%token					MOD_ASSIGN

%token					EQ
%token					NEQ
%token					GEQ
%token					LEQ

%token					BOOLEAN_AND
%token					BOOLEAN_OR

%token					SHIFT_LEFT
%token					SHIFT_RIGHT

%token					IF
%token					ELSE
%token					FOR
%token					DO
%token					WHILE

%token					PRINT

%token					PERIOD

%token					ZERO_VECTOR
%token					ZERO_ROTATION

%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE

%type <script>			lscript_program
%type <global_store>	globals
%type <global_store>	global
%type <global>			global_variable
%type <assignable>		simple_assignable
%type <assignable>		simple_assignable_no_list
%type <constant>		constant
%type <assignable>		special_constant
%type <assignable>		vector_constant
%type <assignable>		quaternion_constant
%type <assignable>		list_constant
%type <assignable>		list_entries
%type <assignable>		list_entry
%type <type>			typename
%type <global_funcs>	global_function
%type <global_decl>		function_parameters
%type <global_decl>		function_parameter
%type <state>			states
%type <state>			other_states
%type <state>			default
%type <state>			state
%type <handler>			state_body
%type <handler>			event
%type <event>			state_entry
%type <event>			state_exit
%type <event>			touch_start
%type <event>			touch
%type <event>			touch_end
%type <event>			collision_start
%type <event>			collision
%type <event>			collision_end
%type <event>			land_collision_start
%type <event>			land_collision
%type <event>			land_collision_end
%type <event>			at_target
%type <event>			not_at_target
%type <event>			at_rot_target
%type <event>			not_at_rot_target
%type <event>			money
%type <event>			email
%type <event>			run_time_permissions
%type <event>			inventory
%type <event>			attach
%type <event>			dataserver
%type <event>			moving_start
%type <event>			moving_end
%type <event>			rez
%type <event>			object_rez
%type <event>			remote_data
%type <event>			link_message
%type <event>			timer
%type <event>			chat
%type <event>			sensor
%type <event>			no_sensor
%type <event>			control
%type <statement>		compound_statement
%type <statement>		statement
%type <statement>		statements
%type <statement>		declaration
%type <statement>		';'
%type <statement>		'@'
%type <expression>		nextforexpressionlist
%type <expression>		forexpressionlist
%type <expression>		nextfuncexpressionlist
%type <expression>		funcexpressionlist
%type <expression>		nextlistexpressionlist
%type <expression>		listexpressionlist
%type <expression>		unarypostfixexpression
%type <expression>		vector_initializer
%type <expression>		quaternion_initializer
%type <expression>		list_initializer
%type <expression>		lvalue
%type <expression>		'-'
%type <expression>		'!'
%type <expression>		'~'
%type <expression>		'='
%type <expression>		'<'
%type <expression>		'>'
%type <expression>		'+'
%type <expression>		'*'
%type <expression>		'/'
%type <expression>		'%'
%type <expression>		'&'
%type <expression>		'|'
%type <expression>		'^'
%type <expression>		ADD_ASSIGN
%type <expression>		SUB_ASSIGN
%type <expression>		MUL_ASSIGN
%type <expression>		DIV_ASSIGN
%type <expression>		MOD_ASSIGN
%type <expression>		EQ
%type <expression>		NEQ
%type <expression>		LEQ
%type <expression>		GEQ
%type <expression>		BOOLEAN_AND
%type <expression>		BOOLEAN_OR
%type <expression>		SHIFT_LEFT
%type <expression>		SHIFT_RIGHT
%type <expression>		INC_OP
%type <expression>		DEC_OP
%type <expression>		'('
%type <expression>		')'
%type <expression>		PRINT
%type <identifier>		name_type
%type <expression>		expression
%type <expression>		unaryexpression
%type <expression>		typecast

%right '=' MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN
%left 	BOOLEAN_AND BOOLEAN_OR
%left	'|'
%left	'^'
%left	'&'
%left	EQ NEQ
%left	'<' LEQ '>' GEQ
%left	SHIFT_LEFT SHIFT_RIGHT
%left 	'+' '-'
%left	'*' '/' '%'
%right	'!' '~' INC_OP DEC_OP
%nonassoc INITIALIZER

% %

lscript_program
    : globals states
    {
    }
    | states
    {
    }
    ;

globals
    : global
    {
    }
    | global globals
    {
    }
    ;

global
    : global_variable
    {
    }
    | global_function
    {
    }
    ;

name_type
    : typename IDENTIFIER
    {
    }
    ;

global_variable
    : name_type ';'
    {
    }
    | name_type '=' simple_assignable ';'
    {
    }
    ;

simple_assignable
    : simple_assignable_no_list
    {
    }
    | list_constant
    {
    }
    ;

simple_assignable_no_list
    : IDENTIFIER
    {
    }
    | constant
    {
    }
    | special_constant
    {
    }
    ;

constant
    : INTEGER_CONSTANT
    {
    }
    | INTEGER_TRUE
    {
    }
    | INTEGER_FALSE
    {
    }
    | FP_CONSTANT
    {
    }
    | STRING_CONSTANT
    {
    }
    ;

special_constant
    : vector_constant
    {
    }
    | quaternion_constant
    {
    }
    ;

vector_constant
    : '<' simple_assignable ',' simple_assignable ',' simple_assignable '>'
    {
    }
    | ZERO_VECTOR
    {
    }
    ;

quaternion_constant
    : '<' simple_assignable ',' simple_assignable ',' simple_assignable ',' simple_assignable '>'
    {
    }
    | ZERO_ROTATION
    {
    }
    ;

list_constant
    : '[' list_entries ']'
    {
    }
    | '[' ']'
    {
    }
    ;

list_entries
    : list_entry
    {
    }
    | list_entry ',' list_entries
    {
    }
    ;

list_entry
    : simple_assignable_no_list
    {
    }
    ;

typename
    : INTEGER
    {
    }
    | FLOAT_TYPE
    {
    }
    | STRING
    {
    }
    | LLKEY
    {
    }
    | VECTOR
    {
    }
    | QUATERNION
    {
    }
    | LIST
    {
    }
    ;

global_function
    : IDENTIFIER '(' ')' compound_statement
    {
    }
    | name_type '(' ')' compound_statement
    {
    }
    | IDENTIFIER '(' function_parameters ')' compound_statement
    {
    }
    | name_type '(' function_parameters ')' compound_statement
    {
    }
    ;

function_parameters
    : function_parameter
    {
    }
    | function_parameter ',' function_parameters
    {
    }
    ;

function_parameter
    : typename IDENTIFIER
    {
    }
    ;

states
    : default
    {
    }
    | default other_states
    {
    }
    ;

other_states
    : state
    {
    }
    | state other_states
    {
    }
    ;

default
    : STATE_DEFAULT '{' state_body '}'
    {
    }
    ;

state
    : STATE IDENTIFIER '{' state_body '}'
    {
    }
    ;

state_body
    : event
    {
    }
    | event state_body
    {
    }
    ;

event
    : state_entry compound_statement
    {
    }
    | state_exit compound_statement
    {
    }
    | touch_start compound_statement
    {
    }
    | touch compound_statement
    {
    }
    | touch_end compound_statement
    {
    }
    | collision_start compound_statement
    {
    }
    | collision compound_statement
    {
    }
    | collision_end compound_statement
    {
    }
    | land_collision_start compound_statement
    {
    }
    | land_collision compound_statement
    {
    }
    | land_collision_end compound_statement
    {
    }
    | timer compound_statement
    {
    }
    | chat compound_statement
    {
    }
    | sensor compound_statement
    {
    }
    | no_sensor compound_statement
    {
    }
    | at_target compound_statement
    {
    }
    | not_at_target compound_statement
    {
    }
    | at_rot_target compound_statement
    {
    }
    | not_at_rot_target compound_statement
    {
    }
    | money compound_statement
    {
    }
    | email compound_statement
    {
    }
    | run_time_permissions compound_statement
    {
    }
    | inventory compound_statement
    {
    }
    | attach compound_statement
    {
    }
    | dataserver compound_statement
    {
    }
    | control compound_statement
    {
    }
    | moving_start compound_statement
    {
    }
    | moving_end compound_statement
    {
    }
    | rez compound_statement
    {
    }
    | object_rez compound_statement
    {
    }
    | link_message compound_statement
    {
    }
    | remote_data compound_statement
    {
    }
    ;

state_entry
    : STATE_ENTRY '(' ')'
    {
    }
    ;

state_exit
    : STATE_EXIT '(' ')'
    {
    }
    ;

touch_start
    : TOUCH_START '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

touch
    : TOUCH '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

touch_end
    : TOUCH_END '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

collision_start
    : COLLISION_START '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

collision
    : COLLISION '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

collision_end
    : COLLISION_END '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

land_collision_start
    : LAND_COLLISION_START '(' VECTOR IDENTIFIER ')'
    {
    }
    ;

land_collision
    : LAND_COLLISION '(' VECTOR IDENTIFIER ')'
    {
    }
    ;

land_collision_end
    : LAND_COLLISION_END '(' VECTOR IDENTIFIER ')'
    {
    }
    ;

at_target
    : AT_TARGET '(' INTEGER IDENTIFIER ',' VECTOR IDENTIFIER ',' VECTOR IDENTIFIER ')'
    {
    }
    ;

not_at_target
    : NOT_AT_TARGET '(' ')'
    {
    }
    ;

at_rot_target
    : AT_ROT_TARGET '(' INTEGER IDENTIFIER ',' QUATERNION IDENTIFIER ',' QUATERNION IDENTIFIER ')'
    {
    }
    ;

not_at_rot_target
    : NOT_AT_ROT_TARGET '(' ')'
    {
    }
    ;

money
    : MONEY '(' LLKEY IDENTIFIER ',' INTEGER IDENTIFIER ')'
    {
    }
    ;

email
    : EMAIL '(' STRING IDENTIFIER ',' STRING IDENTIFIER ',' STRING IDENTIFIER ',' STRING IDENTIFIER ',' INTEGER IDENTIFIER ')'
    {
    }
    ;

run_time_permissions
    : RUN_TIME_PERMISSIONS '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

inventory
    : INVENTORY '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

attach
    : ATTACH '(' LLKEY IDENTIFIER ')'
    {
    }
    ;

dataserver
    : DATASERVER '(' LLKEY IDENTIFIER ',' STRING IDENTIFIER')'
    {
    }
    ;

moving_start
    : MOVING_START '(' ')'
    {
    }
    ;

moving_end
    : MOVING_END '(' ')'
    {
    }
    ;

timer
    : TIMER '(' ')'
    {
    }
    ;

chat
    : CHAT '(' INTEGER IDENTIFIER ',' STRING IDENTIFIER ',' LLKEY IDENTIFIER ',' STRING IDENTIFIER ')'
    {
    }
    ;

sensor
    : SENSOR '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

no_sensor
    : NO_SENSOR '(' ')'
    {
    }
    ;

control
    : CONTROL '(' LLKEY IDENTIFIER ',' INTEGER IDENTIFIER ',' INTEGER IDENTIFIER ')'
    {
    }
    ;

rez
    : REZ '(' INTEGER IDENTIFIER ')'
    {
    }
    ;

object_rez
    : OBJECT_REZ '(' LLKEY IDENTIFIER ')'
    {
    }
    ;

link_message
    : LINK_MESSAGE '(' INTEGER IDENTIFIER ','  INTEGER IDENTIFIER ',' STRING IDENTIFIER ',' LLKEY IDENTIFIER ')'
    {
    }
    ;

remote_data
    : REMOTE_DATA '(' INTEGER IDENTIFIER ','  LLKEY IDENTIFIER ','  LLKEY IDENTIFIER ','  STRING IDENTIFIER ',' INTEGER IDENTIFIER ',' STRING IDENTIFIER ')'
    {
    }
    ;

compound_statement
    : '{' '}'
    {
    }
    | '{' statements '}'
    {
    }
    ;

statements
    : statement
    {
    }
    | statements statement
    {
    }
    ;

statement
    : ';'
    {
    }
    | STATE IDENTIFIER ';'
    {
    }
    | STATE STATE_DEFAULT ';'
    {
    }
    | JUMP IDENTIFIER ';'
    {
    }
    | '@' IDENTIFIER ';'
    {
    }
    | RETURN expression ';'
    {
    }
    | RETURN ';'
    {
    }
    | expression ';'
    {
    }
    | declaration ';'
    {
    }
    | compound_statement
    {
    }
    | IF '(' expression ')' statement	%prec LOWER_THAN_ELSE
    {
    }
    | IF '(' expression ')' statement ELSE statement
    {
    }
    | FOR '(' forexpressionlist ';' expression ';' forexpressionlist ')' statement
    {
    }
    | DO statement WHILE '(' expression ')' ';'
    {
    }
    | WHILE '(' expression ')' statement
    {
    }
    ;

declaration
    : typename IDENTIFIER
    {
    }
    | typename IDENTIFIER '=' expression
    {
    }
    ;

forexpressionlist
    : /* empty */
    {
    }
    | nextforexpressionlist
    {
    }
    ;

nextforexpressionlist
    : expression
    {
    }
    | expression ',' nextforexpressionlist
    {
    }
    ;

funcexpressionlist
    : /* empty */
    {
    }
    | nextfuncexpressionlist
    {
    }
    ;

nextfuncexpressionlist
    : expression
    {
    }
    | expression ',' nextfuncexpressionlist
    {
    }
    ;

listexpressionlist
    : /* empty */
    {
    }
    | nextlistexpressionlist
    {
    }
    ;

nextlistexpressionlist
    : expression
    {
    }
    | expression ',' nextlistexpressionlist
    {
    }
    ;

expression
    : unaryexpression
    {
    }
    | lvalue '=' expression
    {
    }
    | lvalue ADD_ASSIGN expression
    {
    }
    | lvalue SUB_ASSIGN expression
    {
    }
    | lvalue MUL_ASSIGN expression
    {
    }
    | lvalue DIV_ASSIGN expression
    {
    }
    | lvalue MOD_ASSIGN expression
    {
    }
    | expression EQ expression
    {
    }
    | expression NEQ expression
    {
    }
    | expression LEQ expression
    {
    }
    | expression GEQ expression
    {
    }
    | expression '<' expression
    {
    }
    | expression '>' expression
    {
    }
    | expression '+' expression
    {
    }
    | expression '-' expression
    {
    }
    | expression '*' expression
    {
    }
    | expression '/' expression
    {
    }
    | expression '%' expression
    {
    }
    | expression '&' expression
    {
    }
    | expression '|' expression
    {
    }
    | expression '^' expression
    {
    }
    | expression BOOLEAN_AND expression
    {
    }
    | expression BOOLEAN_OR expression
    {
    }
    | expression SHIFT_LEFT expression
    {
    }
    | expression SHIFT_RIGHT expression
    {
    }
    ;

unaryexpression
    : '-' expression
    {
    }
    | '!' expression
    {
    }
    | '~' expression
    {
    }
    | INC_OP lvalue
    {
    }
    | DEC_OP lvalue
    {
    }
    | typecast
    {
    }
    | unarypostfixexpression
    {
    }
    | '(' expression ')'
    {
    }
    ;

typecast
    : '(' typename ')' lvalue
    {
    }
    | '(' typename ')' constant
    {
    }
    | '(' typename ')' unarypostfixexpression
    {
    }
    | '(' typename ')' '(' expression ')'
    {
    }
    ;

unarypostfixexpression
    : vector_initializer
    {
    }
    | quaternion_initializer
    {
    }
    | list_initializer
    {
    }
    | lvalue
    {
    }
    | lvalue INC_OP
    {
    }
    | lvalue DEC_OP
    {
    }
    | IDENTIFIER '(' funcexpressionlist ')'
    {
    }
    | PRINT '(' expression ')'
    {
    }
    | constant
    {
    }
    ;

vector_initializer
    : '<' expression ',' expression ',' expression '>'	%prec INITIALIZER
    {
    }
    | ZERO_VECTOR
    {
    }
    ;

quaternion_initializer
    : '<' expression ',' expression ',' expression ',' expression '>' %prec INITIALIZER
    {
    }
    | ZERO_ROTATION
    {
    }
    ;

list_initializer
    : '[' listexpressionlist ']' %prec INITIALIZER
    {
    }
    ;

lvalue
    : IDENTIFIER
    {
    }
    | IDENTIFIER PERIOD IDENTIFIER
    {
    }
    ;

% %

Values of Constants (not official)
Constant | Type | Value | Comment
TRUE | integer | 1
FALSE | integer | 0
STATUS_PHYSICS | integer | 1 | 0x1
STATUS_ROTATE_X | integer | 2 | 0x2
STATUS_ROTATE_Y | integer | 4 | 0x4
STATUS_ROTATE_Z | integer | 8 | 0x8
STATUS_PHANTOM | integer | 16 | 0x10
STATUS_SANDBOX | integer | 32 | 0x20
STATUS_BLOCK_GRAB | integer | 64 | 0x40
STATUS_DIE_AT_EDGE | integer | 128 | 0x80
STATUS_RETURN_AT_EDGE | integer | 256 | 0x100
AGENT | integer | 1 | 0x1
ACTIVE | integer | 2 | 0x2
PASSIVE | integer | 4 | 0x4
SCRIPTED | integer | 8 | 0x8
CONTROL_FWD | integer | 1 | 0x1
CONTROL_BACK | integer | 2 | 0x2
CONTROL_LEFT | integer | 4 | 0x4
CONTROL_RIGHT | integer | 8 | 0x8
CONTROL_UP | integer | 16 | 0x10
CONTROL_DOWN | integer | 32 | 0x20
CONTROL_ROT_LEFT | integer | 256 | 0x100
CONTROL_ROT_RIGHT | integer | 512 | 0x200
CONTROL_LBUTTON | integer | 268435456 | 0x10000000
CONTROL_ML_LBUTTON | integer | 1073741824 | 0x40000000
PERMISSION_DEBIT | integer | 2 | 0x2
PERMISSION_TAKE_CONTROLS | integer | 4 | 0x4
PERMISSION_REMAP_CONTROLS | integer | 8 | 0x8
PERMISSION_TRIGGER_ANIMATION | integer | 16 | 0x10
PERMISSION_ATTACH | integer | 32 | 0x20
PERMISSION_RELEASE_OWNERSHIP | integer | 64 | 0x40
PERMISSION_CHANGE_LINKS | integer | 128 | 0x80
PERMISSION_CHANGE_JOINTS | integer | 256 | 0x100
PERMISSION_CHANGE_PERMISSIONS | integer | 512 | 0x200
PERMISSION_TRACK_CAMERA | integer | 1024 | 0x400
AGENT_FLYING | integer | 1 | 0x1
AGENT_ATTACHMENTS | integer | 2 | 0x2
AGENT_SCRIPTED | integer | 4 | 0x4
AGENT_MOUSELOOK | integer | 8 | 0x8
AGENT_SITTING | integer | 16 | 0x10
AGENT_ON_OBJECT | integer | 32 | 0x20
AGENT_AWAY | integer | 64 | 0x40
AGENT_WALKING | integer | 128 | 0x80
AGENT_IN_AIR | integer | 256 | 0x100
AGENT_TYPING | integer | 512 | 0x200
AGENT_CROUCHING | integer | 1024 | 0x400
AGENT_BUSY | integer | 2048 | 0x800
AGENT_ALWAYS_RUN | integer | 4096 | 0x1000
PSYS_PART_INTERP_COLOR_MASK | integer | 1 | 0x1
PSYS_PART_INTERP_SCALE_MASK | integer | 2 | 0x2
PSYS_PART_BOUNCE_MASK | integer | 4 | 0x4
PSYS_PART_WIND_MASK | integer | 8 | 0x8
PSYS_PART_FOLLOW_SRC_MASK | integer | 16 | 0x10
PSYS_PART_FOLLOW_VELOCITY_MASK | integer | 32 | 0x20
PSYS_PART_TARGET_POS_MASK | integer | 64 | 0x40
PSYS_PART_TARGET_LINEAR_MASK | integer | 128 | 0x80
PSYS_PART_EMISSIVE_MASK | integer | 256 | 0x100
PSYS_PART_FLAGS | integer | 0
PSYS_PART_START_COLOR | integer | 1
PSYS_PART_START_ALPHA | integer | 2
PSYS_PART_END_COLOR | integer | 3
PSYS_PART_END_ALPHA | integer | 4
PSYS_PART_START_SCALE | integer | 5
PSYS_PART_END_SCALE | integer | 6
PSYS_PART_MAX_AGE | integer | 7
PSYS_SRC_ACCEL | integer | 8
PSYS_SRC_PATTERN | integer | 9
PSYS_SRC_INNERANGLE | integer | 10
PSYS_SRC_OUTERANGLE | integer | 11
PSYS_SRC_TEXTURE | integer | 12
PSYS_SRC_BURST_RATE | integer | 13
PSYS_SRC_BURST_PART_COUNT | integer | 15
PSYS_SRC_BURST_RADIUS | integer | 16
PSYS_SRC_BURST_SPEED_MIN | integer | 17
PSYS_SRC_BURST_SPEED_MAX | integer | 18
PSYS_SRC_MAX_AGE | integer | 19
PSYS_SRC_TARGET_KEY | integer | 20
PSYS_SRC_OMEGA | integer | 21
PSYS_SRC_ANGLE_BEGIN | integer | 22
PSYS_SRC_ANGLE_END | integer | 23
PSYS_SRC_PATTERN_DROP | integer | 1 | 0x1
PSYS_SRC_PATTERN_EXPLODE | integer | 2 | 0x2
PSYS_SRC_PATTERN_ANGLE | integer | 4 | 0x4
PSYS_SRC_PATTERN_ANGLE_CONE | integer | 8 | 0x8
PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY | integer | 16 | 0x10
VEHICLE_TYPE_NONE | integer | 0
VEHICLE_TYPE_SLED | integer | 1
VEHICLE_TYPE_CAR | integer | 2
VEHICLE_TYPE_BOAT | integer | 3
VEHICLE_TYPE_AIRPLANE | integer | 4
VEHICLE_TYPE_BALLOON | integer | 5
VEHICLE_LINEAR_FRICTION_TIMESCALE | integer | 16
VEHICLE_ANGULAR_FRICTION_TIMESCALE | integer | 17
VEHICLE_LINEAR_MOTOR_DIRECTION | integer | 18
VEHICLE_LINEAR_MOTOR_OFFSET | integer | 20
VEHICLE_ANGULAR_MOTOR_DIRECTION | integer | 19
VEHICLE_HOVER_HEIGHT | integer | 24
VEHICLE_HOVER_EFFICIENCY | integer | 25
VEHICLE_HOVER_TIMESCALE | integer | 26
VEHICLE_BUOYANCY | integer | 27
VEHICLE_LINEAR_DEFLECTION_EFFICIENCY | integer | 28
VEHICLE_LINEAR_DEFLECTION_TIMESCALE | integer | 29
VEHICLE_LINEAR_MOTOR_TIMESCALE | integer | 30
VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE | integer | 31
VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY | integer | 32
VEHICLE_ANGULAR_DEFLECTION_TIMESCALE | integer | 33
VEHICLE_ANGULAR_MOTOR_TIMESCALE | integer | 34
VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE | integer | 35
VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY | integer | 36
VEHICLE_VERTICAL_ATTRACTION_TIMESCALE | integer | 37
VEHICLE_BANKING_EFFICIENCY | integer | 38
VEHICLE_BANKING_MIX | integer | 39
VEHICLE_BANKING_TIMESCALE | integer | 40
VEHICLE_REFERENCE_FRAME | integer | 44
VEHICLE_FLAG_NO_DEFLECTION_UP | integer | 1 | 0x1
VEHICLE_FLAG_LIMIT_ROLL_ONLY | integer | 2 | 0x2
VEHICLE_FLAG_HOVER_WATER_ONLY | integer | 4 | 0x4
VEHICLE_FLAG_HOVER_TERRAIN_ONLY | integer | 8 | 0x8
VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT | integer | 16 | 0x10
VEHICLE_FLAG_HOVER_UP_ONLY | integer | 32 | 0x20
VEHICLE_FLAG_LIMIT_MOTOR_UP | integer | 64 | 0x40
VEHICLE_FLAG_MOUSELOOK_STEER | integer | 128 | 0x80
VEHICLE_FLAG_MOUSELOOK_BANK | integer | 256 | 0x100
VEHICLE_FLAG_CAMERA_DECOUPLED | integer | 512 | 0x200
INVENTORY_ALL | integer | -1
INVENTORY_NONE | integer | -1
INVENTORY_TEXTURE | integer | 0
INVENTORY_SOUND | integer | 1
INVENTORY_LANDMARK | integer | 3
INVENTORY_CLOTHING | integer | 5
INVENTORY_OBJECT | integer | 6
INVENTORY_NOTECARD | integer | 7
INVENTORY_SCRIPT | integer | 10
INVENTORY_BODYPART | integer | 13
INVENTORY_ANIMATION | integer | 20
INVENTORY_GESTURE | integer | 21
ATTACH_CHEST | integer | 1
ATTACH_HEAD | integer | 2
ATTACH_LSHOULDER | integer | 3
ATTACH_RSHOULDER | integer | 4
ATTACH_LHAND | integer | 5
ATTACH_RHAND | integer | 6
ATTACH_LFOOT | integer | 7
ATTACH_RFOOT | integer | 8
ATTACH_BACK | integer | 9
ATTACH_PELVIS | integer | 10
ATTACH_MOUTH | integer | 11
ATTACH_CHIN | integer | 12
ATTACH_LEAR | integer | 13
ATTACH_REAR | integer | 14
ATTACH_LEYE | integer | 15
ATTACH_REYE | integer | 16
ATTACH_NOSE | integer | 17
ATTACH_RUARM | integer | 18
ATTACH_RLARM | integer | 19
ATTACH_LUARM | integer | 20
ATTACH_LLARM | integer | 21
ATTACH_RHIP | integer | 22
ATTACH_RULEG | integer | 23
ATTACH_RLLEG | integer | 24
ATTACH_LHIP | integer | 25
ATTACH_LULEG | integer | 26
ATTACH_LLLEG | integer | 27
ATTACH_BELLY | integer | 28
ATTACH_RPEC | integer | 29
ATTACH_LPEC | integer | 30
LAND_LEVEL | integer | 0
LAND_RAISE | integer | 1
LAND_LOWER | integer | 2
LAND_SMOOTH | integer | 3
LAND_NOISE | integer | 4
LAND_REVERT | integer | 5
LAND_SMALL_BRUSH | integer | 1
LAND_MEDIUM_BRUSH | integer | 2
LAND_LARGE_BRUSH | integer | 3
DATA_ONLINE | integer | 1
DATA_NAME | integer | 2
DATA_BORN | integer | 3
DATA_RATING | integer | 4
DATA_SIM_POS | integer | 5
DATA_SIM_STATUS | integer | 6
DATA_SIM_RATING | integer | 7
ANIM_ON | integer | 1 | 0x1
LOOP | integer | 2 | 0x2
REVERSE | integer | 4 | 0x4
PING_PONG | integer | 8 | 0x8
SMOOTH | integer | 16 | 0x10
ROTATE | integer | 32 | 0x20
SCALE | integer | 64 | 0x40
ALL_SIDES | integer | -1
LINK_SET | integer | -1
LINK_ROOT | integer | 1
LINK_ALL_OTHERS | integer | -2
LINK_ALL_CHILDREN | integer | -3
LINK_THIS | integer | -4
CHANGED_INVENTORY | integer | 1 | 0x1
CHANGED_COLOR | integer | 2 | 0x2
CHANGED_SHAPE | integer | 4 | 0x4
CHANGED_SCALE | integer | 8 | 0x8
CHANGED_TEXTURE | integer | 16 | 0x10
CHANGED_LINK | integer | 32 | 0x20
CHANGED_ALLOWED_DROP | integer | 64 | 0x40
CHANGED_OWNER | integer | 128 | 0x80
TYPE_INVALID | integer | 0
TYPE_INTEGER | integer | 1
TYPE_FLOAT | integer | 2
TYPE_STRING | integer | 3
TYPE_KEY | integer | 4
TYPE_VECTOR | integer | 5
TYPE_ROTATION | integer | 6
REMOTE_DATA_CHANNEL | integer | 1
REMOTE_DATA_REQUEST | integer | 2
REMOTE_DATA_REPLY | integer | 3
PRIM_TYPE | integer | 1 | Value changed to 9
PRIM_MATERIAL | integer | 2
PRIM_PHYSICS | integer | 3
PRIM_TEMP_ON_REZ | integer | 4
PRIM_PHANTOM | integer | 5
PRIM_POSITION | integer | 6
PRIM_SIZE | integer | 7
PRIM_ROTATION | integer | 8
PRIM_TYPE | integer | 9
PRIM_TEXTURE | integer | 17
PRIM_COLOR | integer | 18
PRIM_BUMP_SHINY | integer | 19
PRIM_FULLBRIGHT | integer | 20
PRIM_FLEXIBLE | integer | 21
PRIM_TEXGEN | integer | 22
PRIM_TEXGEN_DEFAULT | integer | 0
PRIM_TEXGEN_PLANAR | integer | 1
PRIM_TYPE_BOX | integer | 0
PRIM_TYPE_CYLINDER | integer | 1
PRIM_TYPE_PRISM | integer | 2
PRIM_TYPE_SPHERE | integer | 3
PRIM_TYPE_TORUS | integer | 4
PRIM_TYPE_TUBE | integer | 5
PRIM_TYPE_RING | integer | 6
PRIM_HOLE_DEFAULT | integer | 0 | 0x0
PRIM_HOLE_CIRCLE | integer | 16 | 0x10
PRIM_HOLE_SQUARE | integer | 32 | 0x20
PRIM_HOLE_TRIANGLE | integer | 48 | 0x30
PRIM_MATERIAL_STONE | integer | 0
PRIM_MATERIAL_METAL | integer | 1
PRIM_MATERIAL_GLASS | integer | 2
PRIM_MATERIAL_WOOD | integer | 3
PRIM_MATERIAL_FLESH | integer | 4
PRIM_MATERIAL_PLASTIC | integer | 5
PRIM_MATERIAL_RUBBER | integer | 6
PRIM_MATERIAL_LIGHT | integer | 7
PRIM_SHINY_NONE | integer | 0
PRIM_SHINY_LOW | integer | 1
PRIM_SHINY_MEDIUM | integer | 2
PRIM_SHINY_HIGH | integer | 3
PRIM_BUMP_NONE | integer | 0
PRIM_BUMP_BRIGHT | integer | 1
PRIM_BUMP_DARK | integer | 2
PRIM_BUMP_WOOD | integer | 3
PRIM_BUMP_BARK | integer | 4
PRIM_BUMP_BRICKS | integer | 5
PRIM_BUMP_CHECKER | integer | 6
PRIM_BUMP_CONCRETE | integer | 7
PRIM_BUMP_TILE | integer | 8
PRIM_BUMP_STONE | integer | 9
PRIM_BUMP_DISKS | integer | 10
PRIM_BUMP_GRAVEL | integer | 11
PRIM_BUMP_BLOBS | integer | 12
PRIM_BUMP_SIDING | integer | 13
PRIM_BUMP_LARGETILE | integer | 14
PRIM_BUMP_STUCCO | integer | 15
PRIM_BUMP_SUCTION | integer | 16
PRIM_BUMP_WEAVE | integer | 17
MASK_BASE | integer | 0
MASK_OWNER | integer | 1
MASK_GROUP | integer | 2
MASK_EVERYONE | integer | 3
MASK_NEXT | integer | 4
PERM_TRANSFER | integer | 8192 | 0x2000
PERM_MODIFY | integer | 16384 | 0x4000
PERM_COPY | integer | 32768 | 0x8000
PERM_MOVE | integer | 524288 | 0x80000
PERM_ALL | integer | 2147483647 | 0x7FFFFFFF
PARCEL_MEDIA_COMMAND_STOP | integer | 0
PARCEL_MEDIA_COMMAND_PAUSE | integer | 1
PARCEL_MEDIA_COMMAND_PLAY | integer | 2
PARCEL_MEDIA_COMMAND_LOOP | integer | 3
PARCEL_MEDIA_COMMAND_TEXTURE | integer | 4
PARCEL_MEDIA_COMMAND_URL | integer | 5
PARCEL_MEDIA_COMMAND_TIME | integer | 6
PARCEL_MEDIA_COMMAND_AGENT | integer | 7
PARCEL_MEDIA_COMMAND_UNLOAD | integer | 8
PARCEL_MEDIA_COMMAND_AUTO_ALIGN | integer | 9
PAY_HIDE | integer | -1
PAY_DEFAULT | integer | -2
NULL_KEY | string | "00000000-0000-0000-0000-000000000000"
EOF | string | "\n\n\n"
PI | float | 3.14159274f | 3.14159265358979323846264338327950
TWO_PI | float | 6.28318548f | 6.28318530717958647692528676655900
PI_BY_TWO | float | 1.57079637f | 1.57079632679489661923132169163975
DEG_TO_RAD | float | 0.01745329238f | (PI / 180)
RAD_TO_DEG | float | 57.29578f | (180 / PI)
SQRT2 | float | 1.414213538f | 1.4142135623730950488016887242097
ZERO_VECTOR | vector | <0.0f, 0.0f, 0.0f>
ZERO_ROTATION | rotation | <0.0f, 0.0f, 0.0f, 1.0f>


Liaisons

Liaisons are the Lindens' in-world support reps; the GMs (gamemaster) of SL, if SL was a Game to Master.

Liaisons might also be thought of as a combination of babysitter and tech support rep. Liaisons are not the best resource to go to if you're after scripting help, as that's not their primary area of expertise. If you need scripting help, you're already at the right site.

Liaisons have a limited range of support tools: they can freeze (prevent movement and chat) users, suspend accounts, view some sim statistics, etc. They cannot fix server problems, but they can contact the grid monkey.

LindenLab | Lindens | User


Library

A library is a collection of functions and structures, accessed by another program or script to extend the functionality of the calling program or script. The most important feature of any library is the API that allows controls and access to it. Most typically, libraries writen in LSL use link messages as the method for implementing their APIs, though some use chat. The script library contains a number of libraries writen in LSL. The Exchange features the specifications for a few APIs and protocols.

SL uses many libraries; here are a few:

  • APR
  • Cg
  • cURL
  • expat
  • FMOD
  • FreeType
  • Havok
  • JPEG Library 6b
  • Kakadu/JPEG2000
  • Mesa 3-D graphics library
  • OpenSSL/SSLeay
  • OGG/Vorbis
  • xmlrpc-epi
  • zlib

API | Communications | Script Library | Exchange


LibraryAirLockDoor

AirLockDoor Script

by
Mitzpatrick Fitzsimmons

//__________________________________________________  ___________________

//Air-Lock Door Script by Mitzpatrick Fitzsimmons
//********************** DESCRIPTION *************************
// This door script differs from most, as it uses the
// llSetPrimativeParams function to simulate an open
// or closed door. It can be used with any Prim Type,
// however, the script will need to be modified slightly.
// I find the best effect is with BOX or CYLINDER, as it
// reminds me of a Star-Trek type operation.
// The benefits with this type of door is that no rotation
// factors need to be calculated (as there is no real movement)
// but it might not be the best solution for a "HOUSE-TYPE" door.
// I use this on my space-ship, and it looks pretty cool IMHO.
// Note that the prim you use will need to be rotated 90 on the X
// or Y axis for a door that you would walk through, or if you plan
// to jump/fly through you can leave it as is.

// This script is set to the owners chat commands on channel 1
// but can easily be modified to a touch responce or other method.

// Feel free to modify this script as you see fit, and NO you
// dont need to give me any credit, OR leave this 'EXTRA-LONG'
// DESCRIPTION in it....heheh :)

//---------------------------------------------------------------------------------- GLOBAL VARIABLES

// I find it is easier to copy the UUID of your sounds and then set them as Globals rather than to have
// mulitple copies stuffed inside your prims... just a thought. You dont need to use sounds, but it's more fun.

 key DOOR_OPEN_SOUND="  ";        //Paste the UUID of your favorite door sound between the quotes
 key DOOR_CLOSE_SOUND="  ";        //Paste the UUID of your favorite door sound between the quotes

//---------------------------------------------------------------------------------- FUNCTIONS

OPEN_DOOR()  {
    llPlaySound(DOOR_OPEN_SOUND, 1);
    llOwnerSay("Door open!");
    // Here is where the door open effect comes into play. the setting is 0.60 (just above half) play around with it to suit your needs.

    llSetPrimitiveParams([PRIM_TYPE, PRIM_TYPE_CYLINDER, 0, <0.0, 1.0, 0.0>, 0.60, <0.0, 0.0, 0.0>, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>]);
}

CLOSE_DOOR()  {
    llPlaySound(DOOR_CLOSE_SOUND, 1);
    llOwnerSay("Door closed!");
    llSetPrimitiveParams([PRIM_TYPE, PRIM_TYPE_CYLINDER, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>]);
}

//---------------------------------------------------------------------------------- MEAT & POTATOES

default
{
    state_entry()  {
        llListen(1,"",llGetOwner(),"");     // Listens for the OWNER on channel 1
        llOwnerSay("Activated");        // Just something I use to know the door is working. You can leave this out if you want.
    }

    listen(integer channel, string name, key id, string message)  {

        if(id==llGetOwner())  {        // WE WILL ONLY LISTEN TO THE OWNER
            if( message    ==  "open door1" )      {    // I number doors so I can call on just one specific door at a time
            OPEN_DOOR();
            }

            if( message    == "open all" )      {    // For multiple doors to open simutaneously
            OPEN_DOOR();
            }

        if( message    == "close door1" )      {
            CLOSE_DOOR();
            }

            if( message    == "close all" )      {
            CLOSE_DOOR();
            }

        }else
        {
            llSay(0, "You are not the OWNER." );    // In case someone else trys to open your door
        }
    }
}


LibraryAttachInvis

AttachInvis

Some objects, particulary objects whose only purpose is to hold scripts, don't want to be seen when attached.

Still, it's hard to find invisible things on the floor.

UpdateVis(integer isAttached) {
    if (isAttached) {
        llSetLinkAlpha(LINK_SET, 0.0, ALL_SIDES);
    } else {
        llSetLinkAlpha(LINK_SET, 1.0, ALL_SIDES);
    }
}

default {
    state_entry() { UpdateVis(llGetAttached()); }
    attach( key k ) { UpdateVis( k != NULL_KEY ); }
}
ScriptLibrary | Dolyn's Scripts


LibraryCardReader

CardReader

Load a notecard into a list.

Lists are not always the best option for this. However, if the notecard is small, this will save a lot of pain. Hopefully the example can save some confusion over reading card data.

It's probably better the use a finished reading state

string gReadCardName; // notecard name for readCard state
integer gReadCardLine;  // global counter for card reader
list gReadCardData = []; //loaded by call
integer gCardRead = FALSE; //use a read completed state and this can be done away with

DumpData(list srcList) {
    integer i;
    integer j = llGetListLength( srcList );
    for( i=0; i<j; i++ ) {
        string item = llList2String(srcList, i);
        llOwnerSay((string)i + ": " + item);
    }
}

default {
    state_entry() {
        if (!gCardRead) {
            llOwnerSay("Touch to Test");
        } else {
            DumpData(gReadCardData);
        }
    }

    touch_start(integer total_number) {
        gReadCardName = "notes1";
        state readCard;
    }

}

state readCard {
    state_entry() {
        gReadCardData = [];
        gReadCardLine = 0;
        llGetNotecardLine(gReadCardName, gReadCardLine);
    }

    dataserver(key query_id, string data) {
    // we are in our isolated state here, we don't need to track the query_id
        if (data != EOF) {
            gReadCardData += data;
            gReadCardLine++;
            llGetNotecardLine(gReadCardName, gReadCardLine);
        } else {
            gCardRead = TRUE;
            state default;
        }
    }
}
ScriptLibrary | Dolyn's Scripts


LibraryCBAgentTracker

// AgentTracker

// A specialized sensor that lets modules know if
// a specific agent (defined in trackAgent()) goes farther then
// X amount of distance (defined in trackAgent()) from the point
// it was at when trackAgent() was called.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Triggers an agentOutOfRange(key agentKey) method call if agent
// moves out of range.

// Methods:
// trackAgent(key agentKey, float range)
// Starts tracking the specified agent. Ignores subsequent calls
// unless stopTrackAgent() is called.

// stopTrackAgent(key agentKey)
// Stops tracking the specified agent.
// A call to trackAgent will start another tracking session.

float SENSOR_INTERVAL = 1.0;

vector startingPosition = ZERO_VECTOR;
float movementRange = 0;
key targetAgentKey = NULL_KEY;

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_agentOutOfRange(key agentKey) {
    callMethod(0, "agentOutOfRange", [agentKey]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "trackAgent") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // trackAgent(key agentKey, float range)
            key     agentKey    = (key)     llList2String(paramList, 0);
            float   range       = (float)   llList2String(paramList, 1);

            targetAgentKey = agentKey;
            movementRange = range;

            // Find the agents initial position:
            llSensor("", targetAgentKey, AGENT, 96, TWO_PI);
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }

    sensor(integer numDetected) {
        startingPosition = llDetectedPos(0);
        state tracking;
    }

    no_sensor() {
        trigger_agentOutOfRange(targetAgentKey);
    }
}

state tracking {
    state_entry() {
        llSensorRepeat("", targetAgentKey, AGENT,
            llVecDist(llGetPos(), startingPosition) + movementRange, TWO_PI, SENSOR_INTERVAL);
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "stopTrackAgent") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // stopTrackAgent(key agentKey)
            key agentKey = (key) llList2String(paramList, 0);

            if (agentKey == targetAgentKey || agentKey == NULL_KEY)
                state default;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }

    sensor(integer d) {
        float distanceMoved = llVecDist(startingPosition, llDetectedPos(0));
        if (distanceMoved > movementRange) {
            trigger_agentOutOfRange(targetAgentKey);
            state default;
        }
    }

    no_sensor() {
        trigger_agentOutOfRange(targetAgentKey);
        state default;
    }

    state_exit() {
        llSensorRemove();
    }
}

LibraryContentBrowser


LibraryCBButtonAbstractionLayer

// ButtonAbstractionLayer
// This script is responsible for monitoring touchable linked prims ("buttons")
// It also implements the ability to toggle button availability
// via its enableButton and disableButton link_message events.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Library functions:

// Replaces the list elements in dest with elements in src, starting at start.
// For example, if dest was [A, B, C, D], src was [E, F] and start was 1,
// the list returned would be [A, E, F, D].
// If llGetListLength(src) + start > llGetListLength(dest), the returned list length
// will be greater then dest list's length.

string NULL = "";
list replaceListSlice(list dest, list src, integer start) {
    if (llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
        integer len;
        for(len = llGetListLength(dest); len < start; len++) {
            dest += NULL;
        }
    }
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

integer wildcardPatternMatches(string str, string pattern) {
    string firstChar = llGetSubString(pattern,  0,  0);
    string lastChar  = llGetSubString(pattern, -1, -1);

    if (firstChar == "%") {
        if (lastChar == "%") {
            pattern = llGetSubString(pattern, 1, -2);
            return llSubStringIndex(str, pattern) != -1;
        } else {
            pattern = llDeleteSubString(pattern, 0, 0);
            return llSubStringIndex(str, pattern) == llStringLength(str) - llStringLength(pattern);
        }
    } else if (lastChar == "%") {
        pattern = llGetSubString(pattern, 0, -2);
        return llSubStringIndex(str, pattern) == 0;
    } else {
        return str == pattern;
    }
}

list    enabledButtonNamePatterns = [];
integer numPatterns               = 0;

integer isEnabled(string buttonName) {
    integer patternIndex = 0;

    for (patternIndex = 0; patternIndex < numPatterns; patternIndex++) {
        string pattern = llList2String(enabledButtonNamePatterns, patternIndex);
        if (wildcardPatternMatches(buttonName, pattern)) {
            return TRUE;
        }
    }
    return FALSE;
}

// ============ llDetected* Related ============
integer DETECTED_KEY         = 1002;
integer DETECTED_LINK_NUMBER = 1003;
integer DETECTED_NAME        = 1004;
integer DETECTED_POS         = 1006;
integer DETECTED_ROT         = 1007;

list collectDetectedData(integer toucher) {
    return [
        DETECTED_KEY,           llDetectedKey(toucher),
        DETECTED_LINK_NUMBER,   llDetectedLinkNumber(toucher),
        DETECTED_NAME,          llDetectedName(toucher),
        DETECTED_POS,           llDetectedPos(toucher),
        DETECTED_ROT,           llDetectedRot(toucher)
    ];
}
// =============================================

list curFilter = [];

// This function must be executed in a touch_start event.
// it compares values in the curFilter list with values
// from the llDetected* functions, to see if the toucher is not
// filtered.
integer isTouchAllowed(integer toucher) {
    if (curFilter == []) {
        return TRUE;
    }

    integer filterIndex;

    filterIndex = llListFindList(curFilter, [(string) DETECTED_KEY]);
    if (filterIndex != -1) {
        if (llList2String(curFilter, filterIndex + 1) != (string) llDetectedKey(toucher))
            return FALSE;
    }

    filterIndex = llListFindList(curFilter, [(string) DETECTED_NAME]);
    if (filterIndex != -1) {
        if (llList2String(curFilter, filterIndex + 1) != (string) llDetectedName(toucher))
            return FALSE;
    }

    return TRUE;
}

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_buttonPressed(string buttonName, list detectedData) {
    callMethod(0, "buttonPressed", [buttonName, dumpList2String(detectedData)]);
}

trigger_buttonReleased(string buttonName, list detectedData) {
    callMethod(0, "buttonReleased", [buttonName, dumpList2String(detectedData)]);
}

trigger_reregisterButtons() {
    callMethod(0, "reregisterButtons", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_reregisterButtons();
        llPassTouches(TRUE);
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "addButtonListener") {
            list paramList = parseStringKeepNulls(parameters);
            // Method name:
            // addButtonListener(string buttonNamePattern)
            string buttonNamePattern = llList2String(paramList, 0);

            if (llListFindList(enabledButtonNamePatterns, [buttonNamePattern]) == -1) {
                enabledButtonNamePatterns += buttonNamePattern;
                numPatterns               = llGetListLength(enabledButtonNamePatterns);
            }

        } else if (methodName == "removeButtonListener") {
            list paramList = parseStringKeepNulls(parameters);
            // Method name:
            // removeButtonListener(string buttonNamePattern)
            string buttonNamePattern = llList2String(paramList, 0);
            integer patternIndex            = llListFindList(enabledButtonNamePatterns, [buttonNamePattern]);

            if (patternIndex != -1) {
                enabledButtonNamePatterns = llDeleteSubList(enabledButtonNamePatterns, patternIndex, patternIndex);
                numPatterns               = llGetListLength(enabledButtonNamePatterns);
            }
        } else if (methodName == "touchFilter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // touchFilter(list rules)
            // Rules is a list of DETECTED_* values that we
            // use to filter touch_start events.
            list rules = parseStringKeepNulls(llList2String(paramList, 0));
            curFilter = rules;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }

    touch_start(integer total_number) {
        // In case of griefers who spam touch a button to try and make the terminal
        // ignore the user, search through the list of touchers until the script finds
        // one that the touchfilter will accept.
        integer i;
        for(i = 0; i < total_number; i++) {
            if (isTouchAllowed(i)) {
                integer linkNumber = llDetectedLinkNumber(i);
                string linkName = llGetLinkName(linkNumber);

                if (isEnabled(linkName)) {
                    trigger_buttonPressed(linkName, collectDetectedData(i));
                }
                return;
            }
        }
    }

    touch_end(integer total_number) {
        integer i;
        for(i = 0; i < total_number; i++) {
            if (isTouchAllowed(i)) {
                integer linkNumber = llDetectedLinkNumber(i);
                string linkName = llGetLinkName(linkNumber);

                if (isEnabled(linkName)) {
                    trigger_buttonReleased(linkName, collectDetectedData(i));
                }
                return;
            }
        }
    }
}

LibraryContentBrowser


LibraryCBInboundDataParser

// InboundDataParser
// This script is responsible for parsing data sent from the server
// via inbound XMLRPC. It invokes received* events to convey this
// data to other modules in the object.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

list replaceListSlice(list dest, list src, integer start) {
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

list list2ListStrided(list src, integer start, integer end, integer stride) {
    return llList2ListStrided(llList2List(src, start, end), 0, end, stride);
}

list chopString(string src, integer elementLength) {
    list stringParts = [];
    integer stringIndex = 0;
    integer length = llStringLength(src);
    for (stringIndex = 0; stringIndex < length; stringIndex += elementLength) {
        stringParts += llGetSubString(src, stringIndex, stringIndex + elementLength - 1);
    }
    return stringParts;
}

list wordWrap(string src, integer lineMaxLength) {
    if (lineMaxLength < 2)
        lineMaxLength = 2;
    list lines;
    list words = llParseString2List(src, [" "], []);

    string curLine = "";
    integer wordIndex;
    // I use llGetListLength in the iteration because the length of words
    // may increase.
    // Quick speedup tip: Make the llGetListLength call update a variable
    // when the list is mutated.
    for (wordIndex = 0; wordIndex < llGetListLength(words); wordIndex++) {
        string  word       = llList2String(words, wordIndex);
        integer wordLength = llStringLength(word);
        if (wordLength > lineMaxLength) {
            // Get the chunk of the word that will fit on the screen
            // minus one character, for the dash.
            string truncated = llGetSubString(word,    0, lineMaxLength - 2) + "-";
            string rest      = llDeleteSubString(word, 0, lineMaxLength - 2);
            // Add them to the list after the current index, we want them
            // to be handled like normal words.
            words = llListInsertList(words, [truncated], wordIndex + 1);
            words = llListInsertList(words, [rest], wordIndex + 2);
        } else {
            integer curLineLength = llStringLength(curLine);
            integer lenAfterAdd   = curLineLength + wordLength;
            if (lenAfterAdd > lineMaxLength) {
                lines += curLine;
                curLine = "";
                words = llListInsertList(words, [word], wordIndex + 1);
            } else if (lenAfterAdd == lineMaxLength) {
                lines += curLine + word;
                curLine = "";
            } else {
                curLine += word;
                curLine += " ";
            }
        }
    }
    if (curLine != "")
        lines += curLine;
    return lines;
}

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

trigger_getMenuTextAreaSize() {
    callMethod(0, "getMenuTextAreaSize", []);
}

// Events triggered by script:
trigger_receivedMenuCategory(string menu, string categoryTitle, integer categoryID) {
    callMethod(0, "receivedMenuCategory", [menu, categoryTitle, categoryID]);
}

trigger_receivedMenuContent(string menu, string contentName, integer contentID) {
    callMethod(0, "receivedMenuContent", [menu, contentName, contentID]);
}

trigger_receivedMenuKeywordSearch(string menu, string displayText) {
    callMethod(0, "receivedMenuKeywordSearch", [menu, displayText]);
}

trigger_receivedMenuText(string menu, string text) {
    callMethod(0, "receivedMenuText", [menu, text]);
}

trigger_receivedMenuSummary(string menu, string summaryTitle, integer summaryID) {
    callMethod(0, "receivedMenuSummary", [menu, summaryTitle, summaryID]);
}

trigger_receivedMenuBegin(string menuName, string menuTitle) {
    callMethod(0, "receivedMenuBegin", [menuName, menuTitle]);
}

trigger_receivedMenuEnd(string menuName) {
    callMethod(0, "receivedMenuEnd", [menuName]);
}

trigger_receivedTerminalReset() {
    callMethod(0, "receivedTerminalReset", []);
}

trigger_receivedServerError(string details) {
    callMethod(0, "receivedServerError", [details]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// ====================== XMLRPC request constants: ====================== \\
    // Sent by the client to the server when the server polls
    // and the client has no data to send.
    integer NO_REQUESTS     = 5000;

    // Sent by the server to the client when a poll occurs.
    integer SERVER_POLL     = 6000;

    // Sent by client to server requesting that the server stop polling.
    integer STOP_POLLING    = 6001;

    // Sent by server to client that tells client that the RPC string contains multiple command seperated
    // by COMMAND_SEPERATOR.
    integer MULTI_COMMAND   = 6002;

    // Sent by server to client requesting that the client restart.
    integer TERMINAL_RESET  = 6003;

    // Sent by the client to the server (or external entity) requesting a
    // piece of content:
    integer GET_CATEGORY        = 7001; // String value: catagory requested
    integer GET_NOTECARD        = 7002; // String value: Notecard name requested.
    integer GET_PREV            = 7003; // String value: ""
    integer GET_NEXT            = 7004; // String value: ""
    integer GET_KEYWORD_SEARCH  = 7005; // String value: Keyword requested.
    integer GET_SUMMARY         = 7007;
    integer GET_BUTTON          = 7008;

    // Sent by the server to the client, between MENU_BEGIN and MENU_END
    // requesting that it display text.
    integer SET_CATEGORY        = 8001; // String value: Catagory name to display.
    integer SET_NOTECARD        = 8002; // String value: Notecard name to display.
    integer SET_PREV            = 8003; // String value ignored
    integer SET_NEXT            = 8004; // String value ignored.
    integer SET_KEYWORD_SEARCH  = 8005; // String value ignored.
    integer SET_TEXT            = 8006; // String value: any text.
    integer SET_SUMMARY         = 8007;
    integer SET_BUTTON          = 8008;

    // Sent by the server to the client specifying the start
    // and end of a menu.
    integer MENU_BEGIN  = 9001; // String value: Menu title
    integer MENU_END    = 9002;
// ====================== /XMLRPC request constants ====================== \\

// For use with the MULTI_COMMAND RPC integer.
string COMMAND_SEPERATOR = "]^[";

// Notecard title parameter seperator:
    string NOTECARD_SEPERATOR = " || ";

// Category name and key seperator:
    string CATEGORY_SEPERATOR = "]_[";

// For wordwrap:
integer charsPerLine;

string curMenuName = "";
triggerAppropriateEventFor(integer commandConstant, string commandData) {
    if (commandConstant == TERMINAL_RESET) {
        trigger_receivedTerminalReset();
    } else if (commandConstant == MENU_BEGIN) {
        list parsedCommandData = llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string menuTitle = llList2String(parsedCommandData, 0);
        string menuName  = llList2String(parsedCommandData, 1);
        // TODO: Differentiate between menu names and titles.
        trigger_receivedMenuBegin(menuName, menuTitle);
        curMenuName = menuName;
    } else if (commandConstant == MENU_END) {
        list parsedCommandData = llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        //string menuTitle = llList2String(parsedCommandData, 0);
        string menuName  = llList2String(parsedCommandData, 1);
        trigger_receivedMenuEnd(menuName);
    } else if (commandConstant == SET_CATEGORY) {
        list    parsedCommandData =           llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string  categoryName      =           llList2String(parsedCommandData, 0);
        integer categoryID        = (integer) llList2String(parsedCommandData, 1);
        trigger_receivedMenuCategory(curMenuName, categoryName, categoryID);
    } else if (commandConstant == SET_NOTECARD) {
        list    parsedCommandData =           llParseStringKeepNulls(commandData, [NOTECARD_SEPERATOR], []);
        string  contentName       =           llList2String(parsedCommandData, 0);
        integer contentID         = (integer) llList2String(parsedCommandData, 1);
        trigger_receivedMenuContent(curMenuName, contentName, contentID);
    } else if (commandConstant == SET_KEYWORD_SEARCH) {
        list    parsedCommandData = llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string  displayText       = llList2String(parsedCommandData, 0);
        trigger_receivedMenuKeywordSearch(curMenuName, displayText);
    } else if (commandConstant == SET_TEXT) {
        if (llStringLength(commandData) > charsPerLine) {
            // Note, this is basically the same as the wordwrap function above
            // but to make everything appear to run faster, I unrolled it and
            // stuck it here.
            integer lineMaxLength = charsPerLine;
            list words = llParseString2List(commandData, [" "], []);

            string curLine = "";
            integer wordIndex;
            // I use llGetListLength in the iteration because the length of words
            // may increase.
            // Quick speedup tip: Make the llGetListLength call update a variable
            // when the list is mutated.
            for (wordIndex = 0; wordIndex < llGetListLength(words); wordIndex++) {
                string  word       = llList2String(words, wordIndex);
                integer wordLength = llStringLength(word);
                if (wordLength > lineMaxLength) {
                    // Get the chunk of the word that will fit on the screen
                    // minus one character, for the dash.
                    string truncated = llGetSubString(word,    0, lineMaxLength - 2) + "-";
                    string rest      = llDeleteSubString(word, 0, lineMaxLength - 2);
                    // Add them to the list after the current index, we want them
                    // to be handled like normal words.
                    words = llListInsertList(words, [truncated], wordIndex + 1);
                    words = llListInsertList(words, [rest], wordIndex + 2);
                } else {
                    integer curLineLength = llStringLength(curLine);
                    integer lenAfterAdd   = curLineLength + wordLength;
                    if (lenAfterAdd > lineMaxLength) {
                        trigger_receivedMenuText(curMenuName, curLine);
                        curLine = "";
                        words = llListInsertList(words, [word], wordIndex + 1);
                    } else if (lenAfterAdd == lineMaxLength) {
                        trigger_receivedMenuText(curMenuName, curLine + word);
                        curLine = "";
                    } else {
                        curLine += word;
                        curLine += " ";
                    }
                }
            }
            if (curLine != "")
                trigger_receivedMenuText(curMenuName, curLine);
        } else {
            trigger_receivedMenuText(curMenuName, commandData);
        }
    } else if (commandConstant == SET_SUMMARY) {
        list    parsedCommandData =           llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string  summaryName       =           llList2String(parsedCommandData, 0);
        integer summaryID         = (integer) llList2String(parsedCommandData, 1);
        trigger_receivedMenuSummary(curMenuName, summaryName, summaryID);
    } else if (commandConstant == MULTI_COMMAND) {
        list commandsInData = llParseString2List(commandData, [COMMAND_SEPERATOR], []);
        list intDatas = list2ListStrided(commandsInData, 0, -1, 2);
        list strDatas = list2ListStrided(commandsInData, 1, -1, 2);

        integer i;
        integer numCommands = llGetListLength(intDatas);
        for(i = 0; i < numCommands; i++) {
            integer command = (integer) llList2String(intDatas, i);
            string  data    =           llList2String(strDatas, i);
            triggerAppropriateEventFor(command, data);
        }
    }
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_getMenuTextAreaSize();
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "getMenuTextAreaSize_ret") {
            list paramList = stringToList(parameters);
            // Method signature:
            // getMenuTextAreaSize_ret(integer width, integer height)
            integer width  = (integer) llList2String(paramList, 0);
            integer height = (integer) llList2String(paramList, 1);
            charsPerLine = width;
            state main;
        }
    }
}
state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "receivedRpcData") {
            list paramList = stringToList(parameters);
            // Method signature:
            // receivedRpcData(integer iData, string sData)
            integer iData = (integer)   llList2String(paramList, 0);
            string  sData =             llList2String(paramList, 1);
            // Work is done in triggerAppropriateEventFor because I can
            // use recursion there. Recursion is necessesary for the MULTI_COMMAND command.
            // The only way I can recurse the link_message event is by triggering
            // another link_message, further clogging the limited script2script message
            // space.
            triggerAppropriateEventFor(iData, sData);
        } else if (methodName == "receivedEmailData") {
            list paramList = stringToList(parameters);
            // Method signature:
            // receivedEmailData(integer timestamp, string sender, string subject, string message)
            integer timestamp   = (integer) llList2String(paramList, 0);
            string  sender      =           llList2String(paramList, 1);
            string  subject     =           llList2String(paramList, 2);
            string  message     =           llList2String(paramList, 3);

            if (subject == "ERROR") {
                trigger_receivedServerError(message);
            }
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(num, methodName, [TRUE]);
        }
    }
}

LibraryContentBrowser


LibraryCBKeywordSearchHandler

// KeywordSearchManager
// This module handles keyword searches made by the user.
// A keyword search is started by calling keywordSearch() with
// a paremeter equal to the key of the agent making the search.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// ====================== XMLRPC request constants: ====================== \\
    // Sent by the client to the server when the server polls
    // and the client has no data to send.
    integer NO_REQUESTS     = 5000;

    // Sent by the server to the client when a poll occurs.
    integer SERVER_POLL     = 6000;

    // Sent by client to server requesting that the server stop polling.
    integer STOP_POLLING    = 6001;

    // Sent by the client to the server (or external entity) requesting a
    // piece of content:
    integer GET_CATEGORY        = 7001; // String value: catagory requested
    integer GET_NOTECARD         = 7002; // String value: Notecard name requested.
    integer GET_PREV            = 7003; // String value: ""
    integer GET_NEXT            = 7004; // String value: ""
    integer GET_KEYWORD_SEARCH  = 7005; // String value: Keyword requested.

    // Sent by the server to the client, between MENU_BEGIN and MENU_END
    // requesting that it display text.
    integer SET_CATEGORY        = 8001; // String value: Catagory name to display.
    integer SET_NOTECARD         = 8002; // String value: Notecard name to display.
    integer SET_PREV            = 8003; // String value ignored
    integer SET_NEXT            = 8004; // String value ignored.
    integer SET_KEYWORD_SEARCH  = 8005; // String value ignored.
    integer SET_TEXT            = 8006; // String value: any text.

    // Sent by the server to the client specifying the start
    // and end of a menu.
    integer MENU_BEGIN  = 9001; // String value: Menu title
    integer MENU_END    = 9002;
// ====================== /XMLRPC request constants ====================== \\

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_addChatHandle(string moduleName, integer channel) {
    callMethod(0, "addChatHandle", [moduleName, channel]);
}

trigger_removeChatHandle(string moduleName, integer channel) {
    callMethod(0, "removeChatHandle", [moduleName, channel]);
}

trigger_sendRpcData(integer iData, string sData) {
    callMethod(0, "sendRpcData", [iData, sData]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

key searcherKey = NULL_KEY;
string this;
default {
    state_entry() {
        this = llGetScriptName();
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "keywordSearch") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // keywordSearch(key searcher)
            key searcher = (key) llList2String(paramList, 0);

            searcherKey = searcher;
            state input;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

state input {
    state_entry() {
        llSay(0, "Please state your search request.");
        trigger_addChatHandle(llGetScriptName(), 0);
    }

    link_message(integer sender_num, integer num, string parameters, key methodName) {
        if (methodName == "receivedChatData") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // receivedChatData(string moduleName, integer channel, string name, key id, string message)
            string  moduleName =           llList2String(paramList, 0);
            integer channel    = (integer) llList2String(paramList, 1);
            string  name       =           llList2String(paramList, 2);
            key     id         = (key)     llList2String(paramList, 3);
            string  message    =           llList2String(paramList, 4);

            if (moduleName == llGetScriptName()) {
                if (id == searcherKey) {
                    trigger_sendRpcData(GET_KEYWORD_SEARCH, message);
                    llSay(0, "Searching for \"" + message + "\"");
                    state default;
                }
            }
        } else if (methodName == "setUser") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setUser(key userId)

            // Since we really dont want to be here when the user
            // changes, switch back to default.
            state default;
        }
    }

    state_exit() {
        trigger_removeChatHandle(llGetScriptName(), 0);
    }
}

LibraryContentBrowser


LibraryCBLogoutButtonView

// LogoutButtonView
// Responsible for maintaining the appearence of the logout button
// and fowarding/interpreting buttonPressed events for it.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Upon getting its buttonPressed method triggered,
// this component first identifys what line corresponds with the button.
// If a line does not correspond with the button, it doesnt do anything.
// If it does, it triggeres a lineButtonPressed(integer lineNum) event.

// Method calling:
// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_addButtonListener(string buttonNamePattern) {
    callMethod(0, "addButtonListener", [buttonNamePattern]);
}

trigger_removeButtonListener(string buttonNamePattern) {
    callMethod(0, "removeButtonListener", [buttonNamePattern]);
}

trigger_logoutButtonPressed() {
    callMethod(0, "logoutButtonPressed", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// ============ llDetected* Related ============
integer DETECTED_KEY         = 1002;
integer DETECTED_LINK_NUMBER = 1003;
integer DETECTED_NAME        = 1004;
integer DETECTED_POS         = 1006;
integer DETECTED_ROT         = 1007;

list getDetectedValue(list detectedData, integer detectedConstant) {
    integer index = llListFindList(detectedData, [(string) detectedConstant]);
    if (index != -1) {
        return llList2List(detectedData, index + 1, index + 1);
    }
    return [];
}

buttonInit() {
    trigger_addButtonListener(LOGOUT_BUTTON_NAME);
}

// Name of the logout button
string LOGOUT_BUTTON_NAME = "logout";

// Color button changes to when its pressed.
vector    COLOR_PRESSED  = <0.5, 0.5, 1>;

// Color button changes to when its released, the button
// stays this color after the press->release process.
vector    COLOR_RELEASED = <1, 1, 1>;

string this;
default {
    state_entry() {
        buttonInit();
        this = llGetScriptName();
        state running;
    }
}

state running {
    state_entry() {
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "buttonPressed") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // buttonPressed(string buttonName, list detectedData)
            // Triggered when a registered button is pressed.

            string buttonName   = llList2String(paramList, 0);
            list   detectedData = parseStringKeepNulls(llList2String(paramList, 1));

            // If the button released is a line button:
            if (buttonName == LOGOUT_BUTTON_NAME) {
                integer buttonLinkNum = (integer) ((string) getDetectedValue(detectedData, DETECTED_LINK_NUMBER));
                trigger_logoutButtonPressed();
                llSetLinkColor(buttonLinkNum, COLOR_PRESSED, ALL_SIDES);
            }
        } else if (methodName == "buttonReleased") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // buttonReleased(string buttonName, list detectedData)
            // Triggered when a registered button is released.

            string buttonName   = llList2String(paramList, 0);
            list   detectedData = parseStringKeepNulls(llList2String(paramList, 1));

            if (buttonName == LOGOUT_BUTTON_NAME) {
                integer buttonLinkNum = (integer) ((string) getDetectedValue(detectedData, DETECTED_LINK_NUMBER));
                llSetLinkColor(buttonLinkNum, COLOR_RELEASED, ALL_SIDES);
            }
        } else if (methodName == "reregisterButtons") {
            // Method signature:
            // reregisterButtons()
            buttonInit();
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBMenuColorScrolling

// MenuColorScrolling
// Responsible for maintaining the MenuTextArea's colors onscreen
// stores setMenuLineColor data and  writes it to the screen
// in responce to scrollUp and scrollDown events

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Number of lines to scroll down for each press of a scroll button.
integer LINES_PER_SCROLL = 12;

// Library functions:

// replaceListSlice()
// Replaces the list elements in dest with elements in src, starting at start.
// For example, if dest was [A, B, C, D], src was [E, F] and start was 1,
// the list returned would be [A, E, F, D].
// If llGetListLength(src) + start > llGetListLength(dest), the returned list length
// will be greater then dest list's length.

string NULL = "";
list replaceListSlice(list dest, list src, integer start) {
    if (llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
        integer len;
        for(len = llGetListLength(dest); len < start; len++) {
            dest += NULL;
        }
    }
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_setDisplayLineColor(integer line, vector color) {
    callMethod(0, "setDisplayLineColor", [line, color]);
}

integer getMenuTextAreaPositionRetVal;
trigger_getMenuTextAreaPosition() {
    getMenuTextAreaPositionRetVal = (integer) llFrand(28342);
    callMethod(getMenuTextAreaPositionRetVal, "getMenuTextAreaPosition", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}
// Global variables manipulated by method calls (link messages):
// Stores data that is passed with setMenuLineText. The line number passed
// to setMenuLineText corresponds with the index of the text in this list.
list textColors = [];

// The index in textStrings that is displayed on the topmost line of the screen.
integer indexAtTop = 0;

integer firstMenuLine;
integer lastMenuLine;
integer menuLineTotal;

string screenWriter;

// A boolean telling if the scroller should ignore writes to the screen
// or not. Since the scroller calls the same method other components would
// to write to the screen, it should ignore itself.
//integer ignoreScreenWrites = FALSE;
string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_getMenuTextAreaPosition();
    }
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "getMenuTextAreaPosition_ret" && num == getMenuTextAreaPositionRetVal) {
            list paramList = parseStringKeepNulls(parameters);
            // Return value format:
            // getMenuTextAreaPosition_ret(integer startline, integer endLine)
            integer startLine = (integer) llList2String(paramList, 0);
            integer endLine   = (integer) llList2String(paramList, 1);
            firstMenuLine = startLine;
            lastMenuLine  = endLine;
            menuLineTotal = (lastMenuLine - firstMenuLine) + 1;
            state main;
        }
    }
}

state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "setMenuLineColor") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setMenuLineColor(string moduleName, integer lineNumber, vector color)
            string  moduleName =           llList2String(paramList, 0);
            integer lineNumber = (integer) llList2String(paramList, 1);
            vector  color      = (vector)  llList2String(paramList, 2);
            if (moduleName != screenWriter)
                return;
            if (lineNumber >= firstMenuLine + indexAtTop && lineNumber <= lastMenuLine + indexAtTop)
                trigger_setDisplayLineColor(lineNumber - indexAtTop, color);

            // Note: Was ignorable.
            textColors = replaceListSlice(textColors, [color], lineNumber);
        } else if (methodName == "menuScrollUp") {
            // Method signature:
            // menuScrollUp()
            indexAtTop -= LINES_PER_SCROLL;

            // If we've reached the first index of the text data:
            if (indexAtTop <= 0) {
                indexAtTop = 0;
            }

            // Notify the other modules of a menu screen-position change:
            //trigger_setStartLineOffset(indexAtTop);
            // Set the screen to the scrolled values:
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                trigger_setDisplayLineColor(screenLine, llList2Vector(textColors, textIndex));
            }
        } else if (methodName == "menuScrollDown") {
            // Method signature:
            // menuScrollDown()
            indexAtTop += LINES_PER_SCROLL;

            // Make sure indexAtTop is valid:
            integer textLineCount = llGetListLength(textColors);
            // If there's not enough text data to fill the screen when we've scrolled down:
            if (textLineCount - indexAtTop < menuLineTotal) {
                // Adjust so that the last text data element is on the last line
                // of the screen.
                indexAtTop = textLineCount - menuLineTotal;
            }

            //trigger_setStartLineOffset(indexAtTop);
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                trigger_setDisplayLineColor(screenLine, llList2Vector(textColors, textIndex));
            }
        } else if (methodName == "cls") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // cls(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName != screenWriter)
                return;
            // Clear all menu data:
            indexAtTop  = 0;
            textColors = [];
        } else if (methodName == "setScreenWriter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setScreenWriter(string moduleName)
            string moduleName = llList2String(paramList, 0);
            screenWriter = moduleName;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBMenuDataController

// MenuDataController
// Responsable for interpreting the content, summary and category
// data sent to it and displaying it on the MenuTextArea
// it also is responsable for performing the actions that occur
// when a line button is pressed that corresponds to menu data.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

string NULL = "";
list replaceListSlice(list dest, list src, integer start) {
    if (llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
        integer len;
        for(len = llGetListLength(dest); len < start; len++) {
            dest += NULL;
        }
    }
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

// For displaying incoming data:
trigger_setScreenWriter(string moduleName) {
    callMethod(0, "setScreenWriter", [moduleName]);
}

trigger_setMenuLineText(string moduleName, integer line, string text) {
    callMethod(0, "setMenuLineText", [moduleName, line, text]);
}

trigger_setMenuLineColor(string moduleName, integer line, vector color) {
    callMethod(0, "setMenuLineColor", [moduleName, line, color]);
}

trigger_cls(string moduleName) {
    callMethod(0, "cls", [moduleName]);
}

trigger_enableMenuLineButton(string moduleName, integer lineNumber) {
    callMethod(0, "enableMenuLineButton", [moduleName, lineNumber]);
}

trigger_disableAllMenuLineButtons(string moduleName) {
    callMethod(0, "disableAllMenuLineButtons", [moduleName]);
}

trigger_setButtonWriter(string moduleName) {
    callMethod(0, "setButtonWriter", [moduleName]);
}

// For reacting to user's request:
trigger_requestMenuCategory(integer id) {
    callMethod(0, "requestMenuCategory", [id]);
}

trigger_requestMenuSummary(integer id) {
    callMethod(0, "requestMenuSummary", [id]);
}

trigger_keywordSearch(key searcher) {
    callMethod(0, "keywordSearch", [searcher]);
}

trigger_sendRepositoryContent(key destination, integer id) {
    callMethod(0, "sendRepositoryContent", [destination, id]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

integer DETECTED_GROUP  = 1001;
integer DETECTED_KEY    = 1002;
integer DETECTED_NAME   = 1004;
integer DETECTED_POS    = 1006;
integer DETECTED_ROT    = 1007;

// Asssists with getting values from buttonPressed event's detected data.
string getDetectedValue(list detectedData, integer detectedConstant) {
    integer index = llListFindList(detectedData, [(string) detectedConstant]);
    if (index != -1) {
        return llList2String(detectedData, index + 1);
    }
    return "";
}

integer TYPE_DISABLED = 0;
integer TYPE_CATEGORY = 1;
integer TYPE_SUMMARY  = 2;
integer TYPE_CONTENT  = 3;
integer TYPE_SEARCH   = 4;

// Maps the line number of the button to
// the button's function (what the button
// will trigger when it is pressed.)
list buttonTypes;
// Contains any parameters used for the button's
// function, each index in this list corresponds
// to an index in the buttonTypes list, and contains
// a dumped list of parameters.
list buttonParams;
// Contains the next available line on the screen
// to write data to.
integer nextLine = 0;
// Clears any data pertaining to the currently displayed
// menu.
clearMenuData() {
    buttonTypes  = [];
    buttonParams = [];
    nextLine = 0;
}

writeLine(string text, vector color, integer buttonType, list buttonParamList) {
    if (buttonType != TYPE_DISABLED) {
        trigger_enableMenuLineButton(this, nextLine);
    }
    trigger_setMenuLineColor(this, nextLine, color);
    trigger_setMenuLineText(this, nextLine, text);
    buttonTypes  = replaceListSlice(buttonTypes, [buttonType], nextLine);
    buttonParams = replaceListSlice(buttonParams, [dumpList2String(buttonParamList)], nextLine);
    nextLine++;
}

// For readability:
integer LIST_INDEX_NOT_FOUND = -1;
string this;
default {
    state_entry() {
        this = llGetScriptName();
        state main;
    }
}
state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "receivedMenuCategory") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // receivedMenuCategory(string menu, string categoryName, integer categoryID)
            string menu         =           llList2String(paramList, 0);
            string categoryName =           llList2String(paramList, 1);
            integer categoryID  = (integer) llList2String(paramList, 2);
            writeLine("<- " + categoryName, <0, 1, 0>, TYPE_CATEGORY, [categoryID]);
        } else if (methodName == "receivedMenuContent") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature
            // receivedMenuContent(string menu, string contentTitle, integer contentID)
            string  menu         =           llList2String(paramList, 0);
            string  contentTitle =           llList2String(paramList, 1);
            integer contentID    = (integer) llList2String(paramList, 2);
            writeLine("<- \"" + contentTitle + "\"", <0, 0, 1>, TYPE_CONTENT, [contentID]);
        } else if (methodName == "receivedMenuSummary") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // receivedMenuSummary(string menu, string summaryName, integer summaryID)
            string  menu        =           llList2String(paramList, 0);
            string  summaryName =           llList2String(paramList, 1);
            integer summaryID   = (integer) llList2String(paramList, 2);
            writeLine("<- " + summaryName, <1, 0, 0>, TYPE_SUMMARY, [summaryID]);
        } else if (methodName == "receivedMenuText") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // receivedMenuText(string menu, string text)
            string menu = llList2String(paramList, 0);
            string text = llList2String(paramList, 1);
            writeLine(text, <1, 1, 1>, TYPE_DISABLED, []);
        } else if (methodName == "receivedMenuKeywordSearch") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // receivedMenuKeywordSearch(string menu, string displayText)
            string displayText = llList2String(paramList, 1);
            writeLine("<- " + displayText, <1, 1, 1>, TYPE_SEARCH, []);
        } else if (methodName == "receivedMenuBegin") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // receivedMenuBegin(string menuName, string menuTitle)
            string menuName  = llList2String(paramList, 0);
            string menuTitle = llList2String(paramList, 1);
            trigger_setScreenWriter(this);
            trigger_setButtonWriter(this);
            trigger_cls(this);
            trigger_disableAllMenuLineButtons(this);
            clearMenuData();
            if (menuTitle != "") {
                writeLine(menuTitle, <1, 1, 1>, TYPE_DISABLED, []);
            }
        } else if (methodName == "menuLineButtonPressed") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // lineButtonPressed(integer lineNumber, list detectedData)
            integer lineNumber   = (integer) llList2String(paramList, 0);
            list    detectedData = parseStringKeepNulls(llList2String(paramList, 1));
            key pusher = (key) getDetectedValue(detectedData, DETECTED_KEY);

            integer buttonType = llList2Integer(buttonTypes, lineNumber);
            list buttonParams  = parseStringKeepNulls(llList2String(buttonParams, lineNumber));
            if (buttonType == TYPE_CATEGORY) {
                integer id = (integer) llList2String(buttonParams, 0);
                trigger_requestMenuCategory(id);
            } else if (buttonType == TYPE_SUMMARY) {
                integer id = (integer) llList2String(buttonParams, 0);
                trigger_requestMenuSummary(id);
            } else if (buttonType == TYPE_CONTENT) {
                integer id = (integer) llList2String(buttonParams, 0);
                trigger_sendRepositoryContent(pusher, id);
            } else if (buttonType == TYPE_SEARCH) {
                trigger_keywordSearch(pusher);
            }
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBMenuLineButtonScrolling

// MenuLineButtonScrolling
// Responsible for ensuring that scrolling the screen still
// keeps corresponding buttons and corresponding lines together.
// stores enableMenuLineButton and disableMenuLineButton data
// and enables/disables  line buttons
// in responce to scrollUp and scrollDown events

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Number of lines to scroll down for each press of a scroll button.
integer LINES_PER_SCROLL = 12;

// Library functions:

// replaceListSlice()
// Replaces the list elements in dest with elements in src, starting at start.
// For example, if dest was [A, B, C, D], src was [E, F] and start was 1,
// the list returned would be [A, E, F, D].
// If llGetListLength(src) + start > llGetListLength(dest), the returned list length
// will be greater then dest list's length.

string NULL = "";
list replaceListSlice(list dest, list src, integer start) {
    if (llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
        integer len;
        for(len = llGetListLength(dest); len < start; len++) {
            dest += NULL;
        }
    }
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_menuLineButtonPressed(integer line, list detectedData) {
    callMethod(0, "menuLineButtonPressed", [line, dumpList2String(detectedData)]);
}

trigger_enableLineButton(integer line) {
    callMethod(0, "enableLineButton", [line]);
}

trigger_disableLineButton(integer line) {
    callMethod(0, "disableLineButton", [line]);
}

trigger_enableAllLineButtons() {
    callMethod(0, "enableAllLineButtons", []);
}

trigger_disableAllLineButtons() {
    callMethod(0, "disableAllLineButtons", []);
}

integer getMenuTextAreaPositionRetVal;
trigger_getMenuTextAreaPosition() {
    getMenuTextAreaPositionRetVal = (integer) llFrand(13831);
    callMethod(getMenuTextAreaPositionRetVal, "getMenuTextAreaPosition", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// Global variables manipulated by method calls (link messages):

// Stores data about the status of the buttons corresponding to lines.
// since each line has a button associated with it, the scroll module
// needs to store if the buttons are enabled or not.
// It does this as efficiantly as possible, by storing buttons statuses
// as bitfields in the textButtons list; the first index (0) in textButtons
// stores the bitfield containing info for the first 32 buttons.
list textButtons = [];
integer buttonCount;

integer isLineButtonEnabled(integer line) {
    // Since there are 32 line-button statuses stored in each index,
    // divide the line number by 32, and round the result.
    integer bitfieldIndex = line / 32;
    // Get the bitfield at the index.
    integer bitfield      = llList2Integer(textButtons, bitfieldIndex);
    // Get the position of the line-button bit in the bitfield.
    integer bitPosition   = line - (bitfieldIndex * 32);
    // Get the value of the bit.
    integer bit           = llFloor(llPow(2, bitPosition));
    return (bitfield & bit) == bit;
}

disableLineButton(integer line) {
    // See isLineButtonEnabled for more comments about this process.
    integer bitfieldIndex = line / 32;
    integer bitfield      = llList2Integer(textButtons, bitfieldIndex);
    integer bitPosition   = line - (bitfieldIndex * 32);
    integer bit           = llFloor(llPow(2, bitPosition));

    // AND the masked-out bit to the bitfield.
    bitfield = bitfield & ~bit;
    // Store the updated bitfield back into the list.
    textButtons = replaceListSlice(textButtons, [bitfield], bitfieldIndex);
}

enableLineButton(integer line) {
    // See isLineButtonEnabled for more comments about this process.
    integer bitfieldIndex = line / 32;
    integer bitfield      = llList2Integer(textButtons, bitfieldIndex);
    integer bitPosition   = line - (bitfieldIndex * 32);
    integer bit           = llFloor(llPow(2, bitPosition));

    // OR the bit in the bitfield.
    bitfield = bitfield | bit;
    // Store the updated bitfield back into the list.
    textButtons = replaceListSlice(textButtons, [bitfield], bitfieldIndex);
}

// The index in textStrings that is displayed on the topmost line of the screen.
integer indexAtTop = 0;

integer firstMenuLine;
integer lastMenuLine;
integer menuLineTotal;

string buttonWriter;
string screenWriter;
// A boolean telling if the scroller should ignore writes to the screen
// or not. Since the scroller calls the same method other components would
// to write to the screen, it should ignore itself.
//integer ignoreScreenWrites = FALSE;

string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_getMenuTextAreaPosition();
    }
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "getMenuTextAreaPosition_ret" && num == getMenuTextAreaPositionRetVal) {
            list paramList = parseStringKeepNulls(parameters);
            // Return value format:
            // getMenuTextAreaPosition_ret(integer startline, integer endLine)
            integer startLine = (integer) llList2String(paramList, 0);
            integer endLine   = (integer) llList2String(paramList, 1);
            firstMenuLine = startLine;
            lastMenuLine  = endLine;
            menuLineTotal = (lastMenuLine - firstMenuLine) + 1;
            state main;
        }
    }
}

state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "menuScrollUp") {
            indexAtTop -= LINES_PER_SCROLL;

            // If we've reached the first index of the text data:
            if (indexAtTop <= 0) {
                indexAtTop = 0;
            }

            // Notify the other modules of a menu screen-position change:
            //trigger_setStartLineOffset(indexAtTop);
            // Set the screen to the scrolled values:
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                if (isLineButtonEnabled(textIndex)) {
                    trigger_enableLineButton(screenLine);
                } else {
                    trigger_disableLineButton(screenLine);
                }
            }
        } else if (methodName == "menuScrollDown") {
            indexAtTop += LINES_PER_SCROLL;

            // Make sure indexAtTop is valid:
            // If there's not enough text data to fill the screen when we've scrolled down:
            if (buttonCount - indexAtTop < menuLineTotal) {
                // Adjust so that the last text data element is on the last line
                // of the screen.
                indexAtTop = buttonCount - menuLineTotal;
            }

            //trigger_setStartLineOffset(indexAtTop);
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                if (isLineButtonEnabled(textIndex)) {
                    trigger_enableLineButton(screenLine);
                } else {
                    trigger_disableLineButton(screenLine);
                }
            }
        } else if (methodName == "lineButtonPressed") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // lineButtonPressed(integer lineNumber, list detectedData)
            integer lineNumber   = (integer) llList2String(paramList, 0);
            list    detectedData = parseStringKeepNulls(llList2String(paramList, 1));
            trigger_menuLineButtonPressed(lineNumber + indexAtTop, detectedData);
        } else if (methodName == "enableMenuLineButton") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // enableMenuLineButton(string moduleName, integer lineNumber)
            string  moduleName =           llList2String(paramList, 0);
            integer lineNumber = (integer) llList2String(paramList, 1);
            if (moduleName != buttonWriter)
                return;
            enableLineButton(lineNumber);

            if (lineNumber >= firstMenuLine + indexAtTop && lineNumber <= lastMenuLine + indexAtTop) {
                trigger_enableLineButton(lineNumber - indexAtTop);
            }
        } else if (methodName == "enableAllMenuLineButtons") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // enableAllMenuLineButtons(string moduleName)
            // Note: Was ignorable.
            string moduleName = llList2String(paramList, 0);
            if (moduleName != buttonWriter)
                return;

            integer i;
            integer numBitfields = llGetListLength(textButtons);
            for (i = 0; i < numBitfields; i++) {
                // Replace the bitfield at the index with -1, this will effectively
                // make all bits 1.
                textButtons = replaceListSlice(textButtons, [-1], i);
            }
            trigger_enableAllLineButtons();
        } else if (methodName == "disableMenuLineButton") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // disableLineButton(string moduleName, integer lineNumber)
            string  moduleName =           llList2String(paramList, 0);
            integer lineNumber = (integer) llList2String(paramList, 1);
            if (moduleName != buttonWriter)
                return;

            // Note: Was ignorable.
            disableLineButton(lineNumber);

            if (lineNumber >= firstMenuLine + indexAtTop && lineNumber <= lastMenuLine + indexAtTop) {
                trigger_disableLineButton(lineNumber - indexAtTop);
            }
        } else if (methodName == "disableAllMenuLineButtons") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // disableAllMenuLineButtons(string moduleName)
            // Note: Was ignorable.
            // should I set indexAtTop to 0?
            string moduleName = llList2String(paramList, 0);
            if (moduleName != buttonWriter)
                return;
            indexAtTop = 0;
            buttonCount = 0;
            textButtons = [];
            trigger_disableAllLineButtons();
        } else if (methodName == "setMenuLineText") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setMenuLineText(string moduleName, integer line, string text)
            string  moduleName =           llList2String(paramList, 0);
            integer line       = (integer) llList2String(paramList, 1);
            string  text       =           llList2String(paramList, 2);
            if (moduleName != screenWriter)
                return;
            if (buttonCount - 1 < line)
                buttonCount = line + 1;
        } else if (methodName == "setButtonWriter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setScreenWriter(string moduleName)
            string moduleName = llList2String(paramList, 0);
            buttonWriter = moduleName;
        } else if (methodName == "setScreenWriter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setScreenWriter(string moduleName)
            string moduleName = llList2String(paramList, 0);
            screenWriter = moduleName;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBMenuLineButtonView

// MenuLineButtonView
// Responsble for the appearence of the buttons and interpreting
// buttonPressed events for buttons corresponding to lines onscreen.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Upon getting its buttonPressed method triggered,
// this component first identifys what line corresponds with the button.
// If a line does not correspond with the button, it doesnt do anything.
// If it does, it triggeres a lineButtonPressed(integer lineNum) event.

// Library functions:
integer strStartsWith(string str, string prefix) {
    return llSubStringIndex(str, prefix) == 0;
}

string removePrefixFrom(string str, string prefix) {
    if (!strStartsWith(str, prefix)) return str;

    integer prefixLen = llStringLength(prefix);
    return llDeleteSubString(str, 0, prefixLen - 1);
}

// Replaces the list elements in dest with elements in src, starting at start.
// For example, if dest was [A, B, C, D], src was [E, F] and start was 1,
// the list returned would be [A, E, F, D].
// If llGetListLength(src) + start > llGetListLength(dest), the returned list length
// will be greater then dest list's length.

string NULL = " ";
list replaceListSlice(list dest, list src, integer start) {
    if (llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
        integer len;
        for(len = llGetListLength(dest); len < start; len++) {
            dest += NULL;
        }
    }
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

// Method calling:
// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_getMenuTextAreaPosition() {
    callMethod(0, "getMenuTextAreaPosition", []);
}

trigger_addButtonListener(string buttonNamePattern) {
    callMethod(0, "addButtonListener", [buttonNamePattern]);
}

trigger_removeButtonListener(string buttonNamePattern) {
    callMethod(0, "removeButtonListener", [buttonNamePattern]);
}

trigger_lineButtonPressed(integer lineNum, list detectedData) {
    callMethod(0, "lineButtonPressed", [lineNum, dumpList2String(detectedData)]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// Global variables (interCaps) and constants (ALL_CAPS):
// Prefix of the name of buttons that correspond to lines.
string     LINE_BUTTON_NAME_PREFIX = "Line:";

// Related to LINE_BUTTON_NAME_PREFIX
// However, this is a specially formatted identifyer
// for the ButtonAbstractionLayer. % means wildcard.
// so Line:% means "match any string that starts with 'Line:'"
string     LINE_BUTTON_IDENTIFYER     = "Line:%";

// Color button changes to when its pressed.
vector    COLOR_PRESSED  = <1, 0, 0>;

// Color button changes to when its released, the button
// stays this color after the press->release process.
vector    COLOR_RELEASED = <0, 1, 0>;

// Color button changes to when its disabled,
// the button is COLOR_RELEASED when its enabled.
vector    COLOR_DISABLED = <0.16078, 0.12941, 0.14118>;

// The first line on the screen that's a menu button
integer firstButtonLine;

// The last line on the screen that's a menu button
integer lastButtonLine;

// A bitfield, llPow(2, lineNum) == bit for button.
integer enabledLines     = -1;

// Black-boxes the math involved with determining
// if a line-button is enabled or not.
integer isLineButtonEnabled(integer lineNum) {
    integer bit = (integer) llPow(2, lineNum);
    return ((enabledLines & bit) == bit);
}

// A list of the button's link numbers, the index of the link number
// in this list corresponds to the line the button is for.
list buttonLinkNumbers = [];

refreshLinkNumbers() {
    integer linkNumber = 1;
    buttonLinkNumbers  = [];
    while(llGetLinkKey(linkNumber) != NULL_KEY) {
        string name = llGetLinkName(linkNumber);
        if (strStartsWith(name, LINE_BUTTON_NAME_PREFIX)) {
            integer lineNumber = (integer) removePrefixFrom(name, LINE_BUTTON_NAME_PREFIX);
            buttonLinkNumbers = replaceListSlice(buttonLinkNumbers, [linkNumber], lineNumber);
        }
        linkNumber++;
    }
}
enableLineButton(integer lineNum) {
    // Only enable it if its already disabled.
    if (!isLineButtonEnabled(lineNum)) {
        // If there are no lines enabled, the button listener is
        // probobly disabled as well.
        if (enabledLines == 0) {
            trigger_addButtonListener(LINE_BUTTON_IDENTIFYER);
        }
        enabledLines = enabledLines | (integer) llPow(2, lineNum);
        integer linkNum = llList2Integer(buttonLinkNumbers, lineNum);
        llSetLinkColor(linkNum, COLOR_RELEASED, ALL_SIDES);
    }
}

disableLineButton(integer lineNum) {
    // Only disable it if its already enabled.
    if (isLineButtonEnabled(lineNum)) {
        enabledLines = enabledLines & ~(integer) llPow(2, lineNum);
        // If there are no enabled lines:
        if (enabledLines == 0) {
            trigger_removeButtonListener(LINE_BUTTON_IDENTIFYER);
        }
        integer linkNum = llList2Integer(buttonLinkNumbers, lineNum);
        llSetLinkColor(linkNum, COLOR_DISABLED, ALL_SIDES);
    }
}

disableAll() {
    integer buttonNumber;
    for(buttonNumber = firstButtonLine; buttonNumber <= lastButtonLine; buttonNumber++) {
        disableLineButton(buttonNumber);
    }
}

enableAll() {
    integer buttonNumber;
    for(buttonNumber = firstButtonLine; buttonNumber <= lastButtonLine; buttonNumber++) {
        enableLineButton(buttonNumber);
    }
}

// ============ llDetected* Related ============
integer DETECTED_GROUP       = 1001;
integer DETECTED_KEY         = 1002;
integer DETECTED_LINK_NUMBER = 1003;
integer DETECTED_NAME        = 1004;
integer DETECTED_POS         = 1006;
integer DETECTED_ROT         = 1007;

list getDetectedValue(list detectedData, integer detectedConstant) {
    integer index = llListFindList(detectedData, [(string) detectedConstant]);
    if (index != -1) {
        return llList2List(detectedData, index + 1, index + 1);
    }
    return [];
}
// =============================================

buttonInit() {
    //llSay(0, "LineButtonLayer starting up...");
    refreshLinkNumbers();
    trigger_addButtonListener(LINE_BUTTON_IDENTIFYER);
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        buttonInit();
        trigger_getMenuTextAreaPosition();
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "getMenuTextAreaPosition_ret") {
            list paramList = parseStringKeepNulls(parameters);
            // Return value format:
            // getMenuTextAreaSize_ret(integer startline, integer endLine)
            integer startLine = (integer) llList2String(paramList, 0);
            integer endLine   = (integer) llList2String(paramList, 1);
            firstButtonLine = startLine;
            lastButtonLine = endLine;
            disableAll();
            state running;
        }
    }
}

state running {
    state_entry() {
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "buttonPressed") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // buttonPressed(string buttonName, list detectedData)
            // Triggered when a registered button is pressed.

            string buttonName   = llList2String(paramList, 0);
            list   detectedData = parseStringKeepNulls(llList2String(paramList, 1));

            // If the button released is a line button:
            if (strStartsWith(buttonName, LINE_BUTTON_NAME_PREFIX)) {
                // The rest of the string is the number of the line the button corresponds to.
                integer lineNum = (integer) removePrefixFrom(buttonName, LINE_BUTTON_NAME_PREFIX);
                if (isLineButtonEnabled(lineNum)) {
                    trigger_lineButtonPressed(lineNum, detectedData);
                    integer linkNum = (integer) llList2String(getDetectedValue(detectedData, DETECTED_LINK_NUMBER), 0);
                    llSetLinkColor(linkNum, COLOR_PRESSED, ALL_SIDES);
                }
            }
        } else if (methodName == "buttonReleased") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // buttonReleased(string buttonName, list detectedData)
            // Triggered when a registered button is released.

            string buttonName   = llList2String(paramList, 0);
            list   detectedData = parseStringKeepNulls(llList2String(paramList, 1));

            // If the button released is a line button:
            if (strStartsWith(buttonName, LINE_BUTTON_NAME_PREFIX)) {
                // The rest of the string is the number of the line the button corresponds to.
                integer lineNum = (integer) removePrefixFrom(buttonName, LINE_BUTTON_NAME_PREFIX);
                if (isLineButtonEnabled(lineNum)) {
                    integer linkNum = (integer) llList2String(getDetectedValue(detectedData, DETECTED_LINK_NUMBER), 0);
                    llSetLinkColor(linkNum, COLOR_RELEASED, ALL_SIDES);
                }
            }
        } else if (methodName == "disableLineButton") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // disableLineButton(integer lineNum)
            integer lineNum = (integer) llList2String(paramList, 0);
            disableLineButton(lineNum);
        } else if (methodName == "disableAllLineButtons") {
            // Method signature:
            // disableAllLineButtons()
            disableAll();
        } else if (methodName == "enableLineButton") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // enableLineButton(integer lineNum)
            integer lineNum = (integer) llList2String(paramList, 0);
            enableLineButton(lineNum);
        } else if (methodName == "enableAllLineButtons") {
            // Method signature:
            // enableAllLineButtons()
            enableAll();
        } else if (methodName == "reregisterButtons") {
            // Method signature:
            // reregisterButtons()
            buttonInit();
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }

    changed(integer change) {
        if (change & CHANGED_LINK) {
            refreshLinkNumbers();
        }
    }
}

LibraryContentBrowser


LibraryCBMenuScrollButtonView

// Responabile for how scroll buttons look to the user.
// triggers menuScrollUp and menuScrollDown when
// scroll up button or scroll down button is pressed.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

// This method is triggered to register the scroll buttons with the Button Abstraction Layer
// so buttonPressed events are called when prims matching the buttonNamePattern
// are touched.
trigger_addButtonListener(string buttonNamePattern) {
    callMethod(0, "addButtonListener", [buttonNamePattern]);
}
integer linkName2Number(string linkName) {
    integer linkNumber;
    for (linkNumber = 1; llGetLinkKey(linkNumber) != NULL_KEY; linkNumber++) {
        if (llGetLinkName(linkNumber) == linkName) {
            return linkNumber;
        }
    }
    return -1;
}

// This method is triggered to deregister the scroll buttons, so buttonPressed
// is no longer triggered when prims are touched whose names match the pattern
// specified by buttonNamePattern.
// NOTE: buttonNamePattern must be identical to the buttonNamePattern passed to
//        addButtonListener().
trigger_removeButtonListener(string buttonNamePattern) {
    callMethod(0, "removeButtonListener", [buttonNamePattern]);
}

trigger_menuScrollUp() {
    callMethod(0, "menuScrollUp", []);
}

trigger_menuScrollDown() {
    callMethod(0, "menuScrollDown", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// Color button changes to when its pressed.
vector    COLOR_PRESSED  = <0.2, 0.2, 1>;

// Color button changes to when its released, the button
// stays this color after the press->release process.
vector    COLOR_RELEASED = <0.88235, 0.54902, 0.54902>;

// Color button changes to when its disabled,
// the button is COLOR_RELEASED when its enabled.
vector    COLOR_DISABLED = <0.16078, 0.12941, 0.14118>;

string BUTTON_SCROLL_UP_NAME   = "Scroll:Up";
string BUTTON_SCROLL_DOWN_NAME = "Scroll:Down";

integer scrollUpLinkNumber;
integer scrollDownLinkNumber;

integer scrollUpEnabled   = TRUE;
integer scrollDownEnabled = TRUE;

disableScrollUp() {
    if (scrollUpEnabled) {
        scrollUpEnabled = FALSE;
        llSetLinkColor(scrollUpLinkNumber, COLOR_DISABLED, ALL_SIDES);
    }
}

enableScrollUp() {
    if (!scrollUpEnabled) {
        scrollUpEnabled = TRUE;
        llSetLinkColor(scrollUpLinkNumber, COLOR_RELEASED, ALL_SIDES);
    }
}

disableScrollDown() {
    if (scrollDownEnabled) {
        scrollDownEnabled = FALSE;
        llSetLinkColor(scrollDownLinkNumber, COLOR_DISABLED, ALL_SIDES);
    }
}

enableScrollDown() {
    if (!scrollDownEnabled) {
        scrollDownEnabled = TRUE;
        llSetLinkColor(scrollDownLinkNumber, COLOR_RELEASED, ALL_SIDES);
    }
}

initScrollButtons() {
    trigger_addButtonListener(BUTTON_SCROLL_UP_NAME);
    trigger_addButtonListener(BUTTON_SCROLL_DOWN_NAME);
    scrollUpLinkNumber   = linkName2Number(BUTTON_SCROLL_UP_NAME);
    scrollDownLinkNumber = linkName2Number(BUTTON_SCROLL_DOWN_NAME);
    disableScrollUp();
    disableScrollDown();
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        initScrollButtons();
        state main;
    }
}

state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "buttonPressed") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // buttonPressed(string buttonName, list detectedData)
            string buttonName   = llList2String(paramList, 0);
            list   detectedData = parseStringKeepNulls(llList2String(paramList, 1));
            if (buttonName == BUTTON_SCROLL_UP_NAME && scrollUpEnabled) {
                trigger_menuScrollUp();
                llSetLinkColor(scrollUpLinkNumber, COLOR_PRESSED, ALL_SIDES);
            } else if (buttonName == BUTTON_SCROLL_DOWN_NAME && scrollDownEnabled) {
                trigger_menuScrollDown();
                llSetLinkColor(scrollDownLinkNumber, COLOR_PRESSED, ALL_SIDES);
            }
        } else if (methodName == "buttonReleased") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // buttonReleased(string buttonName, list detectedData)
            string buttonName   = llList2String(paramList, 0);
            list   detectedData = parseStringKeepNulls(llList2String(paramList, 1));
            if (buttonName == BUTTON_SCROLL_UP_NAME && scrollUpEnabled) {
                llSetLinkColor(scrollUpLinkNumber, COLOR_RELEASED, ALL_SIDES);
            } else if (buttonName == BUTTON_SCROLL_DOWN_NAME && scrollDownEnabled) {
                llSetLinkColor(scrollDownLinkNumber, COLOR_RELEASED, ALL_SIDES);
            }
        } else if (methodName == "disableMenuScrollUp") {
            disableScrollUp();
        } else if (methodName == "disableMenuScrollDown") {
            disableScrollDown();
        } else if (methodName == "enableMenuScrollUp") {
            enableScrollUp();
        } else if (methodName == "enableMenuScrollDown") {
            enableScrollDown();
        } else if (methodName == "reregisterButtons") {
            // Method signature:
            // reregisterButtons()
            // Called when the ButtonAbstractionLayer is reset.
            state default;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBMenuTextArea

// Maintains a virtual "window" onscreen that menu content is writeen to.
// Most of its functionality has been deligated out to MenuColorScrolling,
// MenuTextScrolling and MenuLineButtonScrolling for memory-related reasons.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_setDisplayLineText(integer line, string text) {
    callMethod(0, "setDisplayLineText", [line, text]);
}

trigger_setDisplayLineColor(integer line, vector color) {
    callMethod(0, "setDisplayLineColor", [line, color]);
}

integer getScreenSizeRetVal;
trigger_getScreenSize() {
    getScreenSizeRetVal = (integer) llFrand(172421);
    callMethod(getScreenSizeRetVal, "getScreenSize", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

integer END_LINE   = 11;
integer START_LINE = 0;
integer menuWidth;

string screenWriter;
string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_getScreenSize();
    }

    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "getScreenSize_ret" && callId == getScreenSizeRetVal) {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // getScreenSize_ret(integer width, integer height)
            integer width  = (integer) llList2String(paramList, 0);
            integer height = (integer) llList2String(paramList, 1);
            menuWidth = width;
            state main;
        }
    }
}

state main {
    link_message(integer sender, integer callId, string parameters, key methodName) {
        //list paramList = parseStringKeepNulls(parameters);
        if (methodName == "cls") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature :
            // cls()
            string moduleName = llList2String(paramList, 0);
            if (moduleName != screenWriter)
                return;
            integer line;
            for (line = START_LINE; line <= END_LINE; line++) {
                trigger_setDisplayLineText(line, "");
            }
        } else if (methodName == "getMenuTextAreaSize") {
            // Method signature:
            // <int, int> getSize()
            returnValue(methodName, callId, [menuWidth, END_LINE - START_LINE + 1]);
        } else if (methodName == "getMenuTextAreaPosition") {
            // Method signature:
            // <int, int> getMenuTextAreaPosition
            returnValue(methodName, callId, [START_LINE, END_LINE]);
        } else if (methodName == "setScreenWriter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setScreenWriter(string moduleName)
            string moduleName = llList2String(paramList, 0);
            screenWriter = moduleName;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

See:
LibraryCBMenuTextScrolling
LibraryCBMenuColorScrolling
LibraryCBMenuLineButtonScrolling

LibraryContentBrowser


LibraryCBMenuTextScrolling

// MenuTextScrolling
// Responsible for maintaining the MenuTextArea's text lines onscreen
// stores setMenuLineText data and  writes it to the screen
// in responce to scrollUp and scrollDown events

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Number of lines to scroll down for each press of a scroll button.
integer LINES_PER_SCROLL = 12;

// Library functions:

// replaceListSlice()
// Replaces the list elements in dest with elements in src, starting at start.
// For example, if dest was [A, B, C, D], src was [E, F] and start was 1,
// the list returned would be [A, E, F, D].
// If llGetListLength(src) + start > llGetListLength(dest), the returned list length
// will be greater then dest list's length.

string NULL = "";
list replaceListSlice(list dest, list src, integer start) {
    if (llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
        integer len;
        for(len = llGetListLength(dest); len < start; len++) {
            dest += NULL;
        }
    }
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_setDisplayLineText(integer line, string text) {
    callMethod(0, "setDisplayLineText", [line, text]);
}

integer getMenuTextAreaPositionRetVal;
trigger_getMenuTextAreaPosition() {
    getMenuTextAreaPositionRetVal = (integer) llFrand(172374);
    callMethod(getMenuTextAreaPositionRetVal, "getMenuTextAreaPosition", []);
}

// Scroll button stuff:
trigger_disableMenuScrollUp() {
    callMethod(0, "disableMenuScrollUp", []);
}

trigger_enableMenuScrollUp() {
    callMethod(0, "enableMenuScrollUp", []);
}

trigger_disableMenuScrollDown() {
    callMethod(0, "disableMenuScrollDown", []);
}

trigger_enableMenuScrollDown() {
    callMethod(0, "enableMenuScrollDown", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// Global variables manipulated by method calls (link messages):
// Stores data that is passed with setMenuLineText. The line number passed
// to setMenuLineText corresponds with the index of the text in this list.
list textStrings = [];

// The index in textStrings that is displayed on the topmost line of the screen.
integer indexAtTop = 0;

integer firstMenuLine;
integer lastMenuLine;
integer menuLineTotal;

string screenWriter;

// A boolean telling if the scroller should ignore writes to the screen
// or not. Since the scroller calls the same method other components would
// to write to the screen, it should ignore itself.
//integer ignoreScreenWrites = FALSE;
string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_getMenuTextAreaPosition();
    }
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "getMenuTextAreaPosition_ret" && num == getMenuTextAreaPositionRetVal) {
            list paramList = parseStringKeepNulls(parameters);
            // Return value format:
            // getMenuTextAreaPosition_ret(integer startline, integer endLine)
            integer startLine = (integer) llList2String(paramList, 0);
            integer endLine   = (integer) llList2String(paramList, 1);
            firstMenuLine = startLine;
            lastMenuLine  = endLine;
            menuLineTotal = (lastMenuLine - firstMenuLine) + 1;
            state main;
        }
    }
}

state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "setMenuLineText") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setMenuLineText(string moduleName, integer lineNumber, string text)
            string  moduleName =           llList2String(paramList, 0);
            integer lineNumber = (integer) llList2String(paramList, 1);
            string  text       =           llList2String(paramList, 2);
            if (moduleName != screenWriter)
                return;
            if (lineNumber >= firstMenuLine + indexAtTop && lineNumber <= lastMenuLine + indexAtTop)
                trigger_setDisplayLineText(lineNumber - indexAtTop, text);

            if (text != "") {
                if (llGetFreeMemory() < 500) {
                    llSay(0, "Warning: Scroll memory is criticaly low.");
                    //ignoreScreenWrites = TRUE;
                    return;
                }
                textStrings = replaceListSlice(textStrings, [text], lineNumber);
                if (lineNumber >= menuLineTotal) {
                    // Enable scroll-down.
                    trigger_enableMenuScrollDown();
                }
            }

        } else if (methodName == "menuScrollUp") {
            // Method signature:
            // menuScrollUp()
            indexAtTop -= LINES_PER_SCROLL;
            trigger_enableMenuScrollDown();

            // If we've reached the first index of the text data:
            if (indexAtTop <= 0) {
                indexAtTop = 0;
                // Disable scrolling up:
                trigger_disableMenuScrollUp();
            }

            // Notify the other modules of a menu screen-position change:
            //trigger_setStartLineOffset(indexAtTop);
            // Set the screen to the scrolled values:
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                trigger_setDisplayLineText(screenLine, llList2String(textStrings, textIndex));
            }
        } else if (methodName == "menuScrollDown") {
            // Method signature:
            // menuScrollDown()
            indexAtTop += LINES_PER_SCROLL;
            trigger_enableMenuScrollUp();

            // Make sure indexAtTop is valid:
            integer textLineCount = llGetListLength(textStrings);
            // If there's not enough text data to fill the screen when we've scrolled down:
            if (textLineCount - indexAtTop < menuLineTotal) {
                // Adjust so that the last text data element is on the last line
                // of the screen.
                indexAtTop = textLineCount - menuLineTotal;
                trigger_disableMenuScrollDown();
            } else if (textLineCount - indexAtTop == menuLineTotal) {
                // Else, if there is enough text data to fill the screen
                // but none to fill the next scroll down.
                trigger_disableMenuScrollDown();
            }

            //trigger_setStartLineOffset(indexAtTop);
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                trigger_setDisplayLineText(screenLine, llList2String(textStrings, textIndex));
            }
        } else if (methodName == "cls") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // cls(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == screenWriter) {
                // Clear all menu data:
                indexAtTop  = 0;
                textStrings = [];
                // Disable all scroll buttons:
                trigger_disableMenuScrollUp();
                trigger_disableMenuScrollDown();
            }
        } else if (methodName == "setScreenWriter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setScreenWriter(string moduleName)
            string moduleName = llList2String(paramList, 0);
            screenWriter = moduleName;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBNotecardDisplayer

// Displays a notecard's contents onscreen.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

vector LINE_COLOR = <0,1,0>; // Green

integer strStartsWith(string str, string prefix) {
    return llSubStringIndex(str, prefix) == 0;
}

string removePrefixFrom(string str, string prefix) {
    if (!strStartsWith(str, prefix)) return str;

    integer prefixLen = llStringLength(prefix);
    return llDeleteSubString(str, 0, prefixLen - 1);
}

string notecardName = "";
integer curLine = 0;
key curReq = NULL_KEY;

// It doesnt really matter what these values are.
// Just as long as they're not = to any formatting values.
//string FORMAT_COLOR             = "color";
//string FORMAT_PREFIX            = "pre";
//string FORMAT_SUFFIX            = "suf";
//string FORMAT_PARSE_LIST_INDEX  = "indx";

//list LINE_FORMATTING = [
//    FORMAT_COLOR, <0, 1, 0>
//];

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_displayStatusMessage(string message) {
    callMethod(0, "displayStatusMessage", [message]);
}

//trigger_formatAndDisplay(integer line, string text, list formatting) {
//    callMethod("formatAndDisplay", [line, text, encodeList(formatting)]);
//}
trigger_setScreenWriter(string moduleName) {
    callMethod(0, "setScreenWriter", [moduleName]);
}
trigger_setMenuLineText(string moduleName, integer line, string text) {
    callMethod(0, "setMenuLineText", [moduleName, line, text]);
}

trigger_setMenuLineColor(string moduleName, integer line, vector color) {
    callMethod(0, "setMenuLineColor", [moduleName, line, color]);
}

trigger_cls(string moduleName) {
    callMethod(0, "cls", [moduleName]);
}

trigger_enableMenuLineButton(string moduleName, integer lineNumber) {
    callMethod(0, "enableMenuLineButton", [moduleName, lineNumber]);
}

trigger_disableMenuLineButton(string moduleName, integer lineNumber) {
    callMethod(0, "disableMenuLineButton", [moduleName, lineNumber]);
}

trigger_disableAllMenuLineButtons(string moduleName) {
    callMethod(0, "disableAllMenuLineButtons", [moduleName]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        state main;
    }
}
state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "displayNotecard") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // displayNotecard(string notecardName)
            notecardName = llList2String(paramList, 0);
            state readingNotecard;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

state readingNotecard {
    state_entry() {
        trigger_setScreenWriter(this);
        trigger_displayStatusMessage("Reading notecard...");
        trigger_disableAllMenuLineButtons(this);
        trigger_cls(this);
        curLine = 0;
        curReq = llGetNotecardLine(notecardName, curLine);
    }

    dataserver(key req, string data) {
        if (req == curReq) {
            curReq = NULL_KEY;
            if (data != EOF) {
                if (strStartsWith(data, "#")) {
                    data = removePrefixFrom(data, "#");
                    trigger_enableMenuLineButton(this, curLine);
                }

                trigger_setMenuLineText(this, curLine, data);
                trigger_setMenuLineColor(this, curLine, LINE_COLOR);
                curReq = llGetNotecardLine(notecardName, ++curLine);
            } else {
                trigger_displayStatusMessage("Reading notecard...Done.");
                state default;
            }
        }
    }
}

LibraryContentBrowser


LibraryCBOutboundRequestRelay

// Provides an interface for communicating requests to the server.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// ====================== XMLRPC request constants: ====================== \\
    // Sent by the client to the server when the server polls
    // and the client has no data to send.
    integer NO_REQUESTS     = 5000;

    // Sent by the server to the client when a poll occurs.
    integer SERVER_POLL     = 6000;

    // Sent by client to server requesting that the server stop polling.
    integer STOP_POLLING    = 6001;

    // Sent by server to client that tells client that the RPC string contains multiple command seperated
    // by COMMAND_SEPERATOR.
    integer MULTI_COMMAND   = 6002;

    // Sent by server to client requesting that the client restart.
    integer TERMINAL_RESET  = 6003;

    // Sent by the client to the server (or external entity) requesting a
    // piece of content:
    integer GET_CATEGORY        = 7001; // String value: catagory requested
    integer GET_NOTECARD        = 7002; // String value: Notecard name requested.
    integer GET_PREV            = 7003; // String value: ""
    integer GET_NEXT            = 7004; // String value: ""
    integer GET_KEYWORD_SEARCH  = 7005; // String value: Keyword requested.
    integer GET_SUMMARY         = 7007;
    integer GET_BUTTON          = 7008;

    // Sent by the server to the client, between MENU_BEGIN and MENU_END
    // requesting that it display text.
    integer SET_CATEGORY        = 8001; // String value: Catagory name to display.
    integer SET_NOTECARD        = 8002; // String value: Notecard name to display.
    integer SET_PREV            = 8003; // String value ignored
    integer SET_NEXT            = 8004; // String value ignored.
    integer SET_KEYWORD_SEARCH  = 8005; // String value ignored.
    integer SET_TEXT            = 8006; // String value: any text.
    integer SET_SUMMARY         = 8007;
    integer SET_BUTTON          = 8008;

    // Sent by the server to the client specifying the start
    // and end of a menu.
    integer MENU_BEGIN  = 9001; // String value: Menu title
    integer MENU_END    = 9002;
// ====================== /XMLRPC request constants ====================== \\

string CATEGORY_SEPERATOR = "]_[";

string SERVER_EMAIL_ADDRESS = "slpl@zerocubed.com";
string STARTUP_MESSAGE_COMMAND = "Register";
string SHUTDOWN_MESSAGE_COMMAND = "Shutdown";
string MESSAGE_SUBJECT = "SL Library Terminal";
string PROTOCOL_VERSION = "1.0";

string TYPE_INBOUND = "INBOUND";
string TYPE_OUTBOUND = "OUTBOUND";

// The number of scripts that have RPC channels.
integer RPC_SCRIPT_COUNT = 2;

list rpcChannelKeys;
list rpcChannelTypes;
// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_displayStatusMessage(string msg) {
    callMethod(0, "displayStatusMessage", [msg]);
}

trigger_sendRpcData(integer iData, string sData) {
    callMethod(0, "sendRpcData", [iData, sData]);
}

trigger_sendRpcControlData(integer iData, string sData) {
    callMethod(0, "sendRpcControlData", [iData, sData]);
}

integer getOutboundChannelReturnVal;
trigger_getOutboundChannel() {
    getOutboundChannelReturnVal = (integer) llFrand(1824214);
    callMethod(getOutboundChannelReturnVal, "getOutboundChannel", []);
}

integer getInboundChannelReturnVal;
trigger_getInboundChannel() {
    getInboundChannelReturnVal = (integer) llFrand(29481);
    callMethod(getInboundChannelReturnVal, "getInboundChannel", []);
}

integer sendEmailDataReturnVal;
trigger_sendEmailData(string address, string subject, string message) {
    sendEmailDataReturnVal = (integer) llFrand(119333);
    callMethod(sendEmailDataReturnVal, "sendEmailData", [address, subject, message]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

sendServerEmail(string command, list commandParams) {
    commandParams = [PROTOCOL_VERSION, command] + commandParams;
    trigger_sendEmailData(SERVER_EMAIL_ADDRESS, MESSAGE_SUBJECT, llDumpList2String(commandParams, " "));
}

integer trySend() {
    // If we have keys from all rpc scripts:
    if (llGetListLength(rpcChannelKeys) == RPC_SCRIPT_COUNT) {
        // This email is in the format:
        // <version> <terminalKey> OUTBOUND <rpcKey> INBOUND <rpcKey>
        list emailParams = [
            "TerminalUUID",
            llGetKey()];

        integer i;
        for(i = 0; i < RPC_SCRIPT_COUNT; i++) {
            emailParams += llList2String(rpcChannelTypes, i);
            emailParams += llList2String(rpcChannelKeys, i);
        }
        sendServerEmail(STARTUP_MESSAGE_COMMAND, emailParams);
        returnValue("connectionStartup", startupCommandCallId, [TRUE]);
        return TRUE;
    }
    return FALSE;
}

integer startupCommandCallId;
string this;
default {
    state_entry() {
        this = llGetScriptName();
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "connectionStartup") {
            // Method signature:
            // integer connectionStartup()
            startupCommandCallId = num;
            state starting;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

state starting {
    state_entry() {
        trigger_getInboundChannel();
        trigger_getOutboundChannel();
        rpcChannelKeys = rpcChannelTypes = [];
    }

    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "getInboundChannel_ret" && callId == getInboundChannelReturnVal) {
            list paramList = parseStringKeepNulls(parameters);
            // Method sig:
            // getInboundChannel_ret(key channel)
            key rpcChan = (key) llList2String(paramList, 0);
            rpcChannelKeys += rpcChan;
            rpcChannelTypes += TYPE_INBOUND;
            if(trySend())
                state started;
        } else if (methodName == "getOutboundChannel_ret" && callId == getOutboundChannelReturnVal) {
            list paramList = parseStringKeepNulls(parameters);
            // Method sig:
            // getOutboundCHannel_ret(key channel)
            key rpcChan = (key) llList2String(paramList, 0);
            rpcChannelKeys += rpcChan;
            rpcChannelTypes += TYPE_OUTBOUND;
            if (trySend())
                state started;
        }
    }
}

state started {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "requestMenuCategory") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // requestMenuCategory(integer ID)
            integer id = (integer) llList2String(paramList, 0);
            trigger_sendRpcData(GET_CATEGORY, CATEGORY_SEPERATOR + (string) id);
            trigger_displayStatusMessage("Sent data to server, waiting for reply...");
        } else if (methodName == "requestMenuSummary") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // requestMenuSummary(integer ID)
            integer id = (integer) llList2String(paramList, 0);
            trigger_sendRpcData(GET_SUMMARY, CATEGORY_SEPERATOR + (string) id);
            trigger_displayStatusMessage("Sent data to server, waiting for reply...");
        } else if (methodName == "requestMenuKeywordSearch") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // requestKeywordSearch(string keywords)
            string keywords = llList2String(paramList, 0);
            trigger_sendRpcData(GET_KEYWORD_SEARCH, keywords);
            trigger_displayStatusMessage("Sent data to server, waiting for reply...");
        } else if (methodName == "connectionShutdown") {
            // Method signature:
            // requestPollingShutdown()
            list emailParams = [
                "TerminalUUID",
                llGetKey()];
            sendServerEmail(SHUTDOWN_MESSAGE_COMMAND, emailParams);
            state default;
        } else if (methodName == "connectionStartup") {
            // Method signature:
            // integer connectionStartup()
            startupCommandCallId = num;
            state starting;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBRepositoryInterface

// Provides an interface for communicating with the inventory repository.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

integer REPOSITORY_CHANNEL = 19283;

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

trigger_pong(string moduleName) {
    integer callId = (integer) llFrand(127239);
    callMethod(callId, "pong", [moduleName]);
}

integer connectToRemoteReceiverReturnVal;
trigger_connectToRemoteReceiver(key uuid) {
    integer callId = (integer) llFrand(182374);
    connectToRemoteReceiverReturnVal = callId;
    callMethod(callId, "connectToRemoteReceiver", [uuid]);
}

integer callRemoteMethodReturnVal;
trigger_callRemoteMethod(key receiver, integer returnsValue, integer methodCallId, string methodName, list paramList) {
    integer callId = (integer) llFrand(182435);
    callRemoteMethodReturnVal = callId;
    callMethod(callId, "callRemoteMethod", [receiver, returnsValue, methodCallId, methodName, listToString(paramList)]);
}

trigger_displayStatusMessage(string msg) {
    callMethod(0, "displayStatusMessage", [msg]);
}

integer INFO = 0;
integer WARNING = 1;
integer ERROR = 2;
trigger_log(integer type, string message) {
    callMethod(0, "log", [type, message]);
}

integer requestContentReturnVal;
remote_requestContent(key destination, integer id) {
    requestContentReturnVal = (integer) llFrand(182341);
    trigger_callRemoteMethod(repositoryUuid, TRUE, requestContentReturnVal, "requestContent", [destination, id]);
}

string REPOSITORY_NAME = "SLPL_REPOSITORY";

key repositoryUuid;
integer repositoryConnected = FALSE;
string this;
default {
    state_entry() {
        this = llGetScriptName();
        llSensorRepeat(REPOSITORY_NAME, NULL_KEY, ACTIVE|PASSIVE, 96, TWO_PI, 5);
        trigger_log(INFO, "Searching for repository...");
        llSetTimerEvent(30);
        llResetTime();
    }

    sensor(integer det) {
        repositoryUuid = llDetectedKey(0);
        trigger_connectToRemoteReceiver(repositoryUuid);
        llSensorRemove();
    }

    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "connectToRemoteReceiver_ret"
                && callId == connectToRemoteReceiverReturnVal) {
            list paramList = stringToList(parameters);
            // Method signature:
            // connectToRemoteReceiver_ret(integer connected)
            integer connected = (integer) llList2String(paramList, 0);
            if (connected) {
                trigger_log(INFO, "Connected to repository " + (string) repositoryUuid);
            }
            repositoryConnected = connected;
            state main;
        } else if (methodName == "moduleReset") {
            list paramList = stringToList(parameters);
            string moduleName = llList2String(paramList, 0);
            if (moduleName == "OutboundRMI") {
                trigger_connectToRemoteReceiver(repositoryUuid);
            }
        }
    }

    timer() {
        integer elapsed = (integer) llGetTime();
        if (elapsed > 300) {
            trigger_log(ERROR, "Repository not located after " + (string) elapsed
                + " seconds, aborting search.");
            state unconnected;
        } else {
            trigger_log(WARNING, "Repository not located after " + (string) elapsed
                + " seconds.");
        }
    }
}

state unconnected {
    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "sendRepositoryContent") {
            list paramList = stringToList(parameters);
            // Method signature:
            // sendRepositoryContent(key destination, integer id)
            key     destination  = (key) llList2String(paramList, 0);
            integer id           = (integer)  llList2String(paramList, 1);
            trigger_log(ERROR, "Can't retrieve content #" + (string)id
                + " from repository. Repository not found.");
            state default;
        }
    }
}

state main {
    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "sendRepositoryContent") {
            list paramList = stringToList(parameters);
            // Method signature:
            // sendRepositoryContent(key destination, integer id)
            key     destination  = (key) llList2String(paramList, 0);
            integer id           = (integer)  llList2String(paramList, 1);
            remote_requestContent(destination, id);
            trigger_log(INFO, "Requesting content#" + (string)id + " from repository...");
        } else if (methodName == "requestContent_ret"
                && callId == requestContentReturnVal) {
            list paramList = stringToList(parameters);
            // Method sig:
            // requestContent_ret(integer successful)
            integer successful = (integer) llList2String(paramList, 0);

            if (successful) {
                trigger_log(INFO, "Request for content successful.");
            } else {
                trigger_log(ERROR, "Request for content failed.");
            }
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(callId, methodName, [TRUE]);
        }
    }
}
LibraryContentBrowser


LibraryCBStartupShutdownHandler

// Startup&ShutdownHandler
// Responsible for executing commands that perform startup initilization, respond to fatal
// error conditions, and shutdown the terminal when requested to do so.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Responsible for executing high-level commands
// to initilize the terminal, for notifying other
// components of a user change, and for shutting down
// the terminal when the user logs off.
// This script also monitors a button named IO_BUTTON
// that will turn the terminal on.

integer MAIN_MENU_CATEGORY_ID = 0;
float POLL_START_TIMEOUT = 0;//30.0;

// Meters user is allowed to move before auto logout.
float USER_MOVE_RANGE = 4.0;

// Current user's key, NULL_KEY if no one is using terminal.
key curUser = NULL_KEY;

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

trigger_displayStatusMessage(string message) {
    callMethod(0, "displayStatusMessage", [message]);
}

trigger_setUser(key newUser) {
    callMethod(0, "setUser", [newUser]);
}

trigger_trackAgent(key agentKey, float movementRange) {
    callMethod(0, "trackAgent", [agentKey, movementRange]);
}

trigger_stopTrackAgent(key agentKey) {
    callMethod(0, "stopTrackAgent", [agentKey]);
}

trigger_disableMenuLineButton(string moduleName, integer buttonNumber) {
    callMethod(0, "disableMenuLineButton", [moduleName, buttonNumber]);
}

trigger_setButtonWriter(string moduleName) {
    callMethod(0, "setButtonWriter", [moduleName]);
}

trigger_touchFilter(list rules) {
    callMethod(0, "touchFilter", [listToString(rules)]);
}

trigger_displayNotecard(string noteName) {
    callMethod(0, "displayNotecard", [noteName]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

trigger_requestMenuCategory(integer id) {
    callMethod(0, "requestMenuCategory", [id]);
}

integer connectionStartupReturnVal;
trigger_connectionStartup() {
    connectionStartupReturnVal = (integer) llFrand(182842);
    callMethod(connectionStartupReturnVal, "connectionStartup", []);
}

trigger_connectionShutdown() {
    callMethod(0, "connectionShutdown", []);
}

m_moduleReset(string moduleName) {
    callMethod(0, "moduleReset", [moduleName]);
}

//========================

integer DETECTED_GROUP  = 1001;
integer DETECTED_KEY    = 1002;
integer DETECTED_NAME   = 1004;
integer DETECTED_POS    = 1006;
integer DETECTED_ROT    = 1007;

list getDetectedValue(list detectedData, integer detectedConstant) {
    integer index = llListFindList(detectedData, [(string) detectedConstant]);
    if (index != -1) {
        return llList2List(detectedData, index + 1, index + 1);
    }
    return [];
}

// Performed when user logs on.
setUser(key user) {
    if (user == NULL_KEY) {
        trigger_touchFilter([]);
        trigger_setUser(NULL_KEY);
        trigger_stopTrackAgent(NULL_KEY);
    } else {
        trigger_setUser(user);
        trigger_touchFilter([DETECTED_KEY, user]);
        trigger_trackAgent(user, USER_MOVE_RANGE);
    }
}

// Performed when user logs off.
shutdown() {
    trigger_displayStatusMessage("Shutting down...");
    setUser(NULL_KEY);
    trigger_connectionShutdown();
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        m_moduleReset(this);
        setUser(NULL_KEY);
        trigger_displayNotecard(llGetInventoryName(INVENTORY_NOTECARD, 0));
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "menuLineButtonPressed") {
            list paramList = stringToList(parameters);
            // Method signature:
            // lineButtonPressed(integer lineNumber, list detectedData)
            integer lineNumber = (integer)      llList2String(paramList, 0);
            list    detectedData = stringToList(llList2String(paramList, 1));
            trigger_setButtonWriter(this);
            trigger_disableMenuLineButton(this, lineNumber);

            key toucherKey = llList2String(getDetectedValue(detectedData, DETECTED_KEY), 0);
            trigger_displayStatusMessage("Set user to " + (string) toucherKey);
            curUser = toucherKey;

            trigger_displayStatusMessage("Connecting to server...");
            trigger_connectionStartup();
        } else if (methodName == "connectionStartup_ret" && num == connectionStartupReturnVal) {
            list paramList = stringToList(parameters);
            // Method signature:
            // connectionStartup_ret(integer successful)
            integer successful = (integer) llList2String(paramList, 0);
            if (successful) {
                trigger_displayStatusMessage("Connected to server. Requesting main menu...");
                setUser(curUser);
                trigger_requestMenuCategory(MAIN_MENU_CATEGORY_ID);
                state started;
            } else {
                trigger_displayStatusMessage("Unable to connect to server, please try again later.");
            }
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(num, methodName, [TRUE]);
        }
    }
}

state started {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "agentOutOfRange") {
            list paramList = stringToList(parameters);
            // Method signature:
            // agentOutOfRange(key agent)
            key agent = (key) llList2String(paramList, 0);

            if (agent == curUser) {
                trigger_displayStatusMessage("User out of range, terminal resetting...");
                state default;
            }
        } else if (methodName == "receivedTerminalReset") {
            // Method signature:
            // receivedTerminalReset()
            trigger_displayStatusMessage("Server error, terminal resetting...");
            state default;
        } else if (methodName == "receivedServerError") {
            // Method signature:
            // receivedServerError(string details)
            trigger_displayStatusMessage("Server error, terminal resetting...");
            state default;
        } else if (methodName == "logoutButtonPressed") {
            // Method signature:
            // logoutButtonPressed()
            trigger_displayStatusMessage("Logging off...");
            state default;
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(num, methodName, [TRUE]);
        }
    }
    state_exit() {
        shutdown();
    }
}

LibraryContentBrowser


LibraryCBStatusMonitor

// StatusMonitor
// Monitors system status via intercepting link message method calls,
// and displays the proper status messages.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// ====================== XMLRPC request constants: ====================== \\
    // Sent by the client to the server when the server polls
    // and the client has no data to send.
    integer NO_REQUESTS     = 5000;

    // Sent by the server to the client when a poll occurs.
    integer SERVER_POLL     = 6000;

    // Sent by client to server requesting that the server stop polling.
    integer STOP_POLLING    = 6001;

    // Sent by server to client that tells client that the RPC string contains multiple command seperated
    // by COMMAND_SEPERATOR.
    integer MULTI_COMMAND = 6002;

    // Sent by server to client requesting that the client restart.
    integer TERMINAL_RESET = 6003;

    // Sent by the client to the server (or external entity) requesting a
    // piece of content:
    integer GET_CATEGORY        = 7001; // String value: catagory requested
    integer GET_NOTECARD         = 7002; // String value: Notecard name requested.
    integer GET_PREV            = 7003; // String value: ""
    integer GET_NEXT            = 7004; // String value: ""
    integer GET_KEYWORD_SEARCH  = 7005; // String value: Keyword requested.

    // Sent by the server to the client, between MENU_BEGIN and MENU_END
    // requesting that it display text.
    integer SET_CATEGORY        = 8001; // String value: Catagory name to display.
    integer SET_NOTECARD         = 8002; // String value: Notecard name to display.
    integer SET_PREV            = 8003; // String value ignored
    integer SET_NEXT            = 8004; // String value ignored.
    integer SET_KEYWORD_SEARCH  = 8005; // String value ignored.
    integer SET_TEXT            = 8006; // String value: any text.

    // Sent by the server to the client specifying the start
    // and end of a menu.
    integer MENU_BEGIN  = 9001; // String value: Menu title
    integer MENU_END    = 9002;
// ====================== /XMLRPC request constants ====================== \\

// For use with the MULTI_COMMAND RPC integer.
string COMMAND_SEPERATOR = "]^[";

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

string lastMessage = "";
trigger_displayStatusMessage(string msg) {
    if (msg != lastMessage) {
        callMethod(0, "displayStatusMessage", [msg]);
        lastMessage = msg;
    }
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

string idleString = "";
string this;
default {
    state_entry() {
        this = llGetScriptName();
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "sendRpcData") {
            trigger_displayStatusMessage("Sent data to server, waiting for reply...");
        } else if (methodName == "receivedRpcData") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // receivedRpcData(integer iData, string sData)
            integer iData = (integer)   llList2String(paramList, 0);
            string  sData =             llList2String(paramList, 1);

            if (iData == MENU_END) {
                trigger_displayStatusMessage("Data received, displaying...");
                idleString = "Display complete.";
                state waitingForIdle;
            } else if (iData == MULTI_COMMAND) {
                list parsedCommands = llParseString2List(sData, [COMMAND_SEPERATOR], []);

                if (llListFindList(parsedCommands, [(string) MENU_END]) != -1) {
                    trigger_displayStatusMessage("Data received, displaying...");
                    idleString = "Display complete.";
                    state waitingForIdle;
                }
            } else {
                trigger_displayStatusMessage("Recieving data...");
            }
        } else if (methodName == "menuScrollUp" || methodName == "menuScrollDown") {
            trigger_displayStatusMessage("Scrolling...");
            idleString = "Scroll complete.";
            state waitingForIdle;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

state waitingForIdle {
    state_entry() {
        llSetTimerEvent(2);
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        llSetTimerEvent(2);
    }

    timer() {
        if (idleString != "")
            trigger_displayStatusMessage(idleString);
        llSetTimerEvent(0);
        state default;
    }
}

LibraryContentBrowser


LibraryCBStatusTextArea

// The virtual "window" where the StatusMonitor "writes" to.

// Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_setDisplayLineText(integer line, string text) {
    callMethod(0, "setDisplayLineText", [line, text]);
}

trigger_setDisplayLineColor(integer line, vector color) {
    callMethod(0, "setDisplayLineColor", [line, color]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

integer LINE = 12;
integer statusBarEnabled = TRUE;
string this;
default {
    state_entry() {
        this = llGetScriptName();
    }

    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "displayStatusMessage") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // displayStatusMsg(string message)
            string message = llList2String(paramList, 0);
            trigger_setDisplayLineText(LINE, message);
            trigger_setDisplayLineColor(LINE, <1, 1, 1>);
        } else if (methodName == "setStatusDisplay") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setStatusDisplay(boolean show)
            integer show = (integer) llList2String(paramList, 0);
            statusBarEnabled = show;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryCBXyTextDriver

// XyTextDriver
// Provides an interface for writing to the xytext screen.

// Portions Copyright (C) 2005-2006  Francisco V. Saldana
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// Francisco V. Saldana can be contacted using his email account
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// This script is responsible for taking text sent to it via its
// link_message methodName setLineText and commanding the xytext to
// set the text on the specified line. It also implements a setLineColor
// link_message method, that allows for other components to set the color
// of a line. The controller encapsulates all the intracasies of setting up
// and giving commands to the XyText.
// Its basically a heavily modified version of the main setup script
// used in Xylor's notecard reading screen, so props to him for a good
// portion of the stuff in here (that's also why some of it is inconsistant
// with the message protocol used by the other modules, I didn't feel like
// like modifying it *too* much ^_^).

integer NUMBER_OF_ROWS  = 13;
integer CELLS_IN_ROW    = 10;
integer CHARS_PER_CELL  = 6;

integer SET_CELL_INFO           = 204004;
integer SET_CELL_COLOR_INFO     = 205001;
integer REQUEST_LINK_NUMBERS    = 7000;
integer RETURN_TOP_LEFT_CELL    = 7001;

integer CELL_CHANNEL_BASE       = 9000000;
integer COLOR_CHANNEL_BASE      = 8000000;

setCharPrimInfo(integer textChannel, integer charPosition) {
    llMessageLinked(LINK_SET, SET_CELL_INFO, llList2CSV([textChannel, charPosition]), "");
}

initializeCells(integer top_left_cell) {
    // Set up message channels for all XyText cells.

    // Set up each cell, row by row.
    integer CellChannel     = CELL_CHANNEL_BASE;
    integer ColorChannel    = COLOR_CHANNEL_BASE;
    integer CellCharPos     = 0;
    integer CharsPerRow     = CELLS_IN_ROW * CHARS_PER_CELL;
    integer LineNum         = 0;
    integer LastCell        = top_left_cell + NUMBER_OF_ROWS * CELLS_IN_ROW - 1;

    integer i;
    for (i = top_left_cell; i <= LastCell; i++) {
        // Set the info for this cell.
        llMessageLinked(i, SET_CELL_INFO, llList2CSV([CellChannel, CellCharPos]), "");
        llMessageLinked(i, SET_CELL_COLOR_INFO, (string) ColorChannel, "");
        // Move to the next cell.
        CellCharPos += CHARS_PER_CELL;
        // Have we reached the end of the row?
        if (CellCharPos >= CharsPerRow) {
            // Yes, go to the next row.
            CellChannel++;
            ColorChannel++;
            LineNum++;
            CellCharPos = 0;
        }
    }
}

integer getTrueLineNum(integer lineNum) {
    if (lineNum < 0) {
        lineNum += NUMBER_OF_ROWS;
    }
    return lineNum;
}

setLineText(integer line, string text) {
    line = getTrueLineNum(line);

    if (text != "") {
        linesWithText = linesWithText | (integer) llPow(2, line);
    } else {
        linesWithText = linesWithText & ~(integer) llPow(2, line);
    }

    integer lineChannel = CELL_CHANNEL_BASE + line;
    llMessageLinked(LINK_SET, lineChannel, text, "");
}

setLineColor(integer line, vector color) {
    line = getTrueLineNum(line);

    integer lineChannel = COLOR_CHANNEL_BASE + line;

    llMessageLinked(LINK_SET, lineChannel, (string) color, "");
}

integer linesWithText = -1;
integer lineHasText(integer lineNum) {
    lineNum = getTrueLineNum(lineNum);
    integer bit = (integer) llPow(2, lineNum);
    return ((bit & linesWithText) == bit);
}

cls() {
    integer i;
    for(i = 0; i < NUMBER_OF_ROWS; i++) {
        if (lineHasText(i)) {
            setLineText(i, "");
        }
    }
    linesWithText = 0;
}

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer,
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        llMessageLinked(LINK_SET, REQUEST_LINK_NUMBERS, "", "");
    }
    link_message(integer sender, integer channel, string parameters, key methodName) {
        if (channel == RETURN_TOP_LEFT_CELL) {
            // This conditional one is for setting up the XyText, it is
            // A call specific to it.

            // Use the sender's link as the top left cell, and initialize the grid.
            llSay(0, "Initializing cells...");
            initializeCells(sender);
            cls();
            state main;
        }
    }
}
state main {
    state_entry() {
        llSay(0, "Cells initilized.");
    }

    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "setDisplayLineText") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setLineText(integer line, string text)
            integer line        = (integer) llList2String(paramList, 0);
            string  text        =           llList2String(paramList, 1);
            setLineText(line, text);
        } else if (methodName == "setDisplayLineColor") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setLineColor(integer line, vector color)
            integer line        = (integer) llList2String(paramList, 0);
            vector  color       = (vector)  llList2String(paramList, 1);
            setLineColor(line, color);
        } else if (methodName == "resetScreen") {
            // Method signature:
            // resetScreen();
            cls();
        } else if (methodName == "getScreenSize") {
            // Method signature:
            // [int, int] getScreenSize()
            // Return value: getScreenSize_ret(integer width, integer height)
            returnValue(methodName, callId, [CELLS_IN_ROW * CHARS_PER_CELL, NUMBER_OF_ROWS]);
        } else if (methodName == "screenTest") {
            // Method signature:
            // screenTest()
            integer i;
            integer j;
            integer charsPerLine = CELLS_IN_ROW * CHARS_PER_CELL;
            for (i = 0; i < NUMBER_OF_ROWS; ++i) {
                string lineText;
                for (j = 0; j < charsPerLine; j += llStringLength((string)i)) {
                    lineText += (string) i;
                }
                setLineText(i, lineText);
            }
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}

LibraryContentBrowser


LibraryChatbots

Chatbots

Chatbots (also known as chatterbots) attempt to simulate a reasonable conversation between you and its' programming. These bots are not perfect and can result in some odd conversations. A few chatbot scripts are available for you to use within SecondLife.

Linden AIML - Inspired by Richard Wallace's AIML used in the ALICE.

ELIZA - Simulates the first chatbot from 1966 scanning for keywords and rephrasing sentences to parody a Rogerian Therapist.

ScriptLibrary


LibraryComandToAgent

Comand to Agent

*This script is a very useful tab that I have used frequently for my items in-game. Its great for comanding items to other people, which can be useful at times.*

NOTE- This can also be used with nonphys objects, and follows anyone that you comand it to within a 96 meter radius

//////////// Comand to Agent ////////////
///Created by Joker Opus, June 21 2006.///
//If I made any mistakes, please correct them, do not ruin the script//

default
{
         state_entry()
         {
             llListen(1,"",llGetOwner(),""); //This obviously shows that it listens to the owner on channel 1
         }
         on_rez(integer start_param)
         {
             llResetScript(); // This allows it to listen to the next owner. because if it doesnt reset, it will continue to listen to the maker.
         }
         listen(integer channel, string name, key id, string msg)
         {

            if (msg == name)
             {
              llSensorRepeat(name, NULL_KEY, AGENT,96, PI_BY_TWO, 0.001) // This is what calls the sensor, and it will make it scan for the agent
             }
             else if (msg == id)
             {
             //Incase the target is called as a key
              llSensorRepeat(name, NULL_KEY, AGENT,96, PI_BY_TWO, 0.001)

              llSetPos(llDetectedName(0));
              }
              else if (msg == "stop")
              {
              llSensorRemove(); //If you want to stop the target following
              }
         }
         sensor(integer total_number)
         {
             llSetPos(llDetectedName(0));
             llSetPos(llDetectedKey(0)); //Incase the agent calls a key
         }
         no_sensor()
         {
             llInstantMessage(llGetOwner, "Could not find an agent in that range.");
         }

}

I DID make this outside of SL, so..
Please correct the issues i made in this script


LibraryContentBrowser

NOTE: This page is currently under construction.

A few people have noticed my LibraryEmailModule and LibraryChatCodec script "modules." About a year ago Jade Lily created the SecondLife Public Library, and asked me to create a browsing system for their text and media content. During that period, I was experimenting with applying Object-Oriented Programming's principles to SecondLife scripts, to see if the organizational capabilities of the paradigm could be applied in such a context.

The result: script modules. The monolithic script I knew a project of this size would grow into pushed me to split scripts up not based on need, but so that each script was an self-contained entity that exposed an interface other scripts could use to communicate with it. Some modules, like the XyTextDriver and ChatCodec exposed a simple, highly-usable interface over a complex algorithm or process.

The Content Browser

The Content Browser object looks very much like an ATM, a large text screen with buttons on the left side corresponding to lines of text.

Unless otherwise indicated, these scripts are covered by the GNU General Public License version 2.

LibraryCBStartupShutdownHandler
LibraryCBInboundDataParser
LibraryCBRepositoryInterface
LibraryCBAgentTracker
LibraryCBKeywordSearchHandler
LibraryCBMenuDataController
LibraryCBNotecardDisplayer
LibraryCBOutboundRequestRelay
LibraryCBStatusMonitor
LibraryCBStatusTextArea
LibraryCBXyTextDriver
LibraryCBButtonAbstractionLayer
LibraryCBLogoutButtonView
LibraryCBMenuLineButtonView
LibraryCBMenuScrollButtonView
LibraryCBMenuTextArea
LibraryCBMenuColorScrolling
LibraryCBMenuTextScrolling
LibraryCBMenuLineButtonScrolling


LibraryDolynFoley

Dolyn's Scripts

Please feel free to use any of this code as you like. Feel free to IM me in SL. I don't do commissions, but I'm happy to help if I can.

Code

Name | Description
AttachInvis | Some objects, particulary objects whose only purpose is to hold scripts, don't want to be seen when attached.
CardReader | Load a notecard into a list.
GetBinaryString | In bitwise there are many helpful examples that dump out the bits for viewing. I couldn't find a function for this, so here one is.
InventoryLoader | Code to easily get a box full of toys into your inventory.
PrimLister | List the number and name all the prims in a complex object.

Formatting

A note on formatting style. All my examples are in the
One True Brace Style. That is, K&R style, as opposed to that other (Allman) style that most of the examples you find are in. ( Yes, I do feel the need to expunge all trace of Allman style from code where I find it. It's not compulsive, really, it's just so I can read the thing. )

While many years of professional programming has caused me to favor this style, others like different things. It doesn't really matter what style you choose; most programmers tend to use a hybrid if they play with a language long enough. However, be consistent! Consistent code is easier to debug and will make syntax errors much easier to spot.

ScriptLibrary


LibraryDualTimers

I get the question a lot: "How do I run 2 timers at the same time?".

The short answer is "You don't". There are in fact 3 ways to do it:
1) Start a timer running in a remote script and have it communicate back with linked messages.
2) If you have 2 timers with a "greatest common factor", run the script for that time and use counters/switches.
3) Use a repeating sensor that's looking for something that doesn't exist.

#1 is relatively straight forward to set up, and is also the least efficient method in terms of both complexity an execution time, but is a more "pure" solution.

for #2. You can do something like the following:
//Dual Timer Triggers by Redux
//This only works with integer timers.  This can be done with float timers as well, but you would need a GCF with a decimal
//This could be more efficient if you calculated the greatest common factor and set the timer for that.

integer timerCounter = 0;  //a place holder
integer shorterTimer = 3;  //the number of seconds to wait until triggering the first (shorter) timer
integer longerTimer = 7; //the number of seconds to wait until triggering the second (longer) timer
integer bestTime; //optimized timer
default
{
    state_entry()
    {
        //finds the largest number that both are divisible by for the timer
        bestTime = longerTimer / 2;
        while( (shorterTimer % bestTime) || (longerTimer % bestTime))
        {
            --bestTime;
        }

        llSetTimerEvent(bestTime);
    }

    timer()
    {
      ++timerCounter;

      if(!(timerCounter * bestTime % shorterTimer))
      {
         llOwnerSay("Shorter Timer trigger");
      }
      if(!(timerCounter * bestTime % longerTimer))
      {
         llOwnerSay("Trigger Timer 2");
      }
    }
}

IMPORTANT NOTE: This script will trigger erratically ONCE after about 4,000 years have passed, when the integer maxes out at 2 billion and resets to negative 2 billion. If this concerns you, you can add the following to the if statement in the timer():
if(!(timerCounter * bestTime % (shorterTimer * longerTimer)))
{
   counter = 0;
}

For the third method, if you have 2 non-integer timers that you cant easily resolve to a greatest common factor, you can use a sensor repeat as a second timer. This also can make it easier to keep track of your timers, and is more pleasing to the eye (albeit WAY laggier, and sort of a hack, still better than the linked messages). This assumes you wont be needing sensors in this state.

//sensor repeat dual timer hack by Redux
float timer1 = 1.79; //some period of time for timer1
float timer2 = 4.33; //some period of time for timer2
key nonExistentPrim = "04662fa1-4a6b-2193-06c9-b52564609606"; //the key of a prim I just deleted.

default
{
    state_entry()
    {
        llSetTimerEvent(timer1);
        llSensorRepeat("", nonExistentPrim, AGENT, 1, 1, timer2);
    }

    timer()
    {
        llOwnerSay("Timer 1");
    }

    no_sensor()
    {
        llOwnerSay("Timer 2");
    }
}

These can probably be optimized further, but are a good starting point.


LibraryFPPAddingFunctions

Overview

Picking a random action.
Using the movement script

sample random actions

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPBrain1

Overview

The Brain script's function is to decide what to do and then tell the other scripts what to do. To start with, the basic bBrain script will do just one thing, pick a random location to move to. Later on we will add more functions.

Functions

Pick a random location within x meters of the pet's current location.
Tell the movement script where to go.
Wait for a link message from the movement script.
Handle being rezzed in a new location

User defined functions

There are two functions that are going to be used more than once, sending a "MOVETO" message and selecting a new location to move to. So we will put these into user defined functions.

move_to
The move_to function is going to take three parameters, target, speed and range, and send the data in a link message as discussed in the link message section.

move_to(vector new_target,float speed,float range)
{
    list command=["MOVETO",new_target,speed,range];
    llMessageLinked( LINK_SET, company_channel, llDumpList2String(command, "-=-"), "" );
}

select_new_location
The select_new_location function is going to take a float as a parameter and return a vector. The float, max_dist, is the maximum distance from the object's current location that the new location should be. The vector it returns is a random location no more than max_dist away from the current position. The new location will be no higher or lower than half of max_dist.
To keep things simple for this example, the max_dist will be used for the x and y axis separately. We will be improving this scripts later on.
You can put what you want in this function. Its function is to return the next location. You could put code here to randomly pick from a set of landmarks. Or find the nearest object called tree and move towards it.

vector select_new_location(float max_dist)
{
  vector current_pos = llGetPos();
  vector offset;
    //Create a vector to hold the offset in.
  offset.x = llFrand(2* max_dist) - max_dist);
    //Set the x value to a number between -max_dist and +max_dist
  offset.y = llFrand(2* max_dist) - max_dist);
    //Set the y value to a number between -max_dist and +max_dist
  offset.z = llFrand(max_dist) - (max_dist/2));
    //Set the z value to a number between -max_dist/2 and +max_dist/2
  return current_pos + offset;
    //Return the new location to move to.  This is the current position plus the offset we just created.
}

States and Events

This simple brain script only has one behaviour, and so we only need one state, the default state. Within the default state, three events are needed. The state_entry to start the pet off, the link_message event to 'listen' for the movement script saying that it has reached the location and the on_rez event to cater for when the pet is rezzed.

state_entry event

The state_entry event occurs when a script first enters a specific state. As this state_entry is in the default state, this is the first code that is triggered when the Brain script is first run. So what do we want to do when the script first runs? We want the brain script to pick a random location to move to and then tell the movement script to go there. With our two user defined functions this is a pretty simple bit of code.
state_entry

{
    vector new_target = select_new_location(patrol_range);
    move_to(new_target,speed,range);
}

Do we want the state_entry event to do anything else? Not at the moment. This will tell the movement script to start the object on the way to the new location. All the brain script has to do is wait for the movement script to tell it when it's there. Which is what the

Link_Message event

does. The linkmessage event is going to be triggered whenever a script is sent to the prim that this script is in. When that message is received this event is going to check to see if its from one of your scripts, by checking the company_channel, and then test to see if its an "ATPOS" message from the movement script. If it is what does the brains need to do? Pick a new random location to move to and tell the movement script.
link_message(integer sender,integer num,string str,key id)
{
    //Is this from a known script
    if(num == company_channel)
    {
        //It is!  Lets unpack the data
         list data = llParseStringKeepNulls( str, ["-=-"], [] );
         string Cmd = llList2String( data, 0 );
    //Is it an ATPOS message?
         if( Cmd == "ATPOS" )
        {
            //It is!  Pick a new location
             vector next_pos = select_new_position(patrol_range);
            //And tell the movement script to go there
             move_to_position(next_pos);
         }
    }
}
Once the pet is in motion again, all the brain script has to do is wait for the next ATPOS message.

on_rez event

One last situation we have to deal with. When the pet is rezzed, we have to stop whatever motion it had when we rezzed it and start it off again.

on_rez(integer param)
{
    vector new_target = select_new_location(patrol_range);
    move_to(new_target,speed,range);
}

Putting it all together

integer company_channel = 250;
float range = 0.5;
float speed = 1;
float patrol_range = 3;

vector select_new_location(float max_dist)
{
  vector current_pos = llGetPos();
  vector offset;
    //Create a vector to hold the offset in.
  offset.x = (llFrand(2* max_dist) - max_dist);
    //Set the x value to a number between -max_dist and +max_dist
  offset.y = (llFrand(2* max_dist) - max_dist);
    //Set the y value to a number between -max_dist and +max_dist
  offset.z = (llFrand(max_dist) - (max_dist/2));
    //Set the z value to a number between -max_dist/2 and +max_dist/2 (might need to change, it always seems to want to move down) (DM)
  return current_pos + offset;
    //Return the new location to move to.  This is the current position plus the offset we just created.
}
move_to(vector new_target,float speed,float range)
{
    list command=["MOVETO",new_target,speed,range];
    llMessageLinked( LINK_SET, company_channel, llDumpList2String(command, "-=-"), "" );
}
default
{
    state_entry()
    {
        vector new_target = select_new_location(patrol_range);
        move_to(new_target,speed,range);
    }
    link_message(integer sender,integer num,string str,key id)
    {
       //Is this from a known script
       if(num == company_channel)
        {
          //It is!  Lets unpack the data
          list data = llParseStringKeepNulls( str, ["-=-"], [] );
          string Cmd = llList2String( data, 0 );
          //Is it an ATPOS message?
          if( Cmd == "ATPOS" )
            {
            //It is!  Pick a new location
             vector next_pos = select_new_location(patrol_range);
            //And tell the movement script to go there
             move_to(next_pos, speed, range);
         }
    }
}
on_rez(integer param)
{
    vector new_target = select_new_location(patrol_range);
    move_to(new_target,speed,range);
}
}

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPBrain2

Problems with the current version
The select_new_position function created in the basic Brain script section suffres from a number of problems.
~Sometimes the position chosen is below the ground. ~
~Sometimes the postion may be below sea level, which we may not want.
~The chosen location may be inside another object or avatar ~
~There may be an object, or avatar, in the pet's path to the target position.

Requirements

current select_new_position

above ground
dealing with the sea

giving up
There are times when your pet is going to get stuck. It might not be able to reach the target because of the surroundings or the target may actually be inside another object.

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPClass1

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPClass2

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPClass3

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPClass4

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPFAQ

Add your questions here

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPIntroduction

Introduction

The objective is to create a small flying object that moves around in a realistic way. For this project, that means as if it were alive, and obeying the rules of physics. Everyone probably has a good idea of what that means, but it can be hard to describe exactly what we mean. In order to simulate something, you have to identify the behaviours that you want to simulate.

Let's take a look at a bird

It moves forward, in the direction it's facing.

Lots of flying objects have this behaviour. The thrust that moves them always pushes forward.

They take time to turn

And its often dependant on speed. Most flying creatures and vehicles cannot instantly change direction, they have a turning circle.

It stays approximately in the same x and y orientation.

Sure they may roll in turns and stick their nose in the air to go up, but most living things prefer to have gravity pointing down, most machines work better when gravity is always in the same direction. If you want to play with pitch and rolls, the vehicle code may be what you need. For this project, keeping the pet upraight is going to be good enough.

It moves further in the x-y plane.

Most flying things, perhaps with the exception of rockets tend to move more in a horizontal direction than in the vertical plane.

Why not use llMoveToTarget?
llMoveToTarget fails the realism test. It moves the object in a direct straight line to the target. We want our object to move forward and turning. llMoveToTarget moves to a new location in so many seconds, we want our object to move to a target at a certain speed. Plus living things rarely move to an exact location, they go somewhere that is near enough where they want.

Requirements

Choose random location based on its current location
We want our pet to stay in the rough area that it is rezzed in. So any location it moves to should be relative to its current position.
Move realistically
You know, like a bird.
Return to its owner if it strays too far
We dont want our pet wandering off. So the requirement is for it to stay near its owner and react to the owner moving.
Have a highly re-usable movement script
Having a good, reliable physical movement script has no end of uses. So we want to design it so that it can be resused.
Be expandable
The object of this example is to provide the very basic design for a flying creature. Since everyone will create different things, the design must be easy to expand.

Model Limitations

The methods used in this example add a few limitations to the model. This example works best for small, light objects like small birds, droids and other irritating flying things.

Number of Primitives
Because the pet is going to be a physical object, there is a limit on the number of primitives the object can be made of. The object for the pet cannot have more than 32 prims. This is one more than for a vehicle. This is because the pet doesn't have a driver.
However, this project needs one prim as part of the object avoidance system. So you're back to using 31 prims.
Axis Orientation
The object must be designed to move in the direction of its x axis and its top should be in the direction of the z axis. The movement code will be pushing the object in this direction. If your model isn't lined up correctly, it will fly wonky. To check your pet's axis orientation, edit it and make sure the "local axis" check box is selected. The arrows should now show the object's local orientation.
However, if you have real problems with this, the prim saved for the object avoidance can be as the parent prim to line the model up correctly.
Maximum Weight
To make the pet actually hover, the buoyancy function is going to be used. A buoyant object cannot float if it weighs over 90 kg. This is because the buoyancy uses up energy. For this project, all you need to know is that your object cannot weigh more than 90kg. Use the llGetMass() function to find the mass of your pet. If you have real problems getting the weight down, try hollowing your prims. The type of material doesn't affect the mass.

Overall design

LSL has a Finite State Machine architecture. FSMs are great for game AI and autonomous agents (Which is what our pet is). FSMs work best if each FSM (Or script) performs a single discrete function. We are going to take advantage of this by splitting the pet's code into three, each performing a specific function:
Brain script
This script will decide what to do. It will decide whether to perform an random action or move to an new location. Once it has decided what to do, it will tell the other scripts to do it. It will keep track of where the owner is, and if the pet strays too far, tell the movement script to move towards the owner.
Movement Script
The movement script will wait for an order to move and then move the pet to that location. Once it has reached the location, it will tell the other scripts that it has happened. All the physics functions will be in here. It will have simple object avoidance.
Random script
Anything we occasionally want the pet to do, like say something, breath fire, say hallo to someone will be coded in this script. It will wait for the brain to tell it to do something, pick a random action and when it has finished, tell the brain script that it has finished. If it needs to move the pet, it will tell the movement script where to move to. This script will be very different for each pet.

One of the objectives is to create re-usable code. By making the movement script separate you can just drop into a new project and start working on your control script. Likewise, the brain script is going to have a lot of code that is going to be common across a range of projects. Plus, although it is small at the moment, it can grow as you design more functions.
By using three scripts, an entirely new pet can be created by dropping the brain and movement script into a new model, and then creating a new random events script.

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPLinkMessages

As we have several scripts, we need to use link messages to pass data from one to another. The function llMessageLinked is used to send a link message from one script to another. When a prim receives a link mesage, every script in that prim will have its link_message event triggered.

Linknum
For the purposes of this example, Linknum will be set to LINK_SET. This means that every link message will be sent to every prim. This example is so small that any performance issues over doing it this way are insignificant.

num
All our data is going to be packed into the string variable, so num can be redundant. But in this example I'm going to use it as a method of identifiying link messages from YOUR scripts.. This has a couple of advantages.
Ability to mix your scripts with other peoples scripts
No error checking when used with other peoples scripts.
Possible public interface.

To do this, pick a number, call it your Company Channel. Use this value for num in all your link messages. In the link events, always check to see if the company channel is correct. If it is, you know that it is a message from one of your scripts, and the data will be in a expected format. Now, if you swap scripts with someone else doing the same thing with num, your scripts will not clash with each other as they wil have different nums. If you publish your link message formats and company channel, then others can buy your scripts, and use them via the link messages. This allows scripts to act like computer chips from different manufacturers all wired together.

Data marshalling
The message containing the information on where to move is going to contain a mixture of data types. More than can be fit into one link message, and containing data types that the link message cannot handle. So we need a method of marshalling the data into a link message.

And there just happens to be a perfect example of how to do this on this wiki, ExampleArgumentMultiplexing

So for a "move to " message, we will probably want to send the following information.
A string contain the message type
A vector containing the target location
A float containing a speed
A float containing a range

Dropping all that in the multiplexing example's code:

integer company_channel = 250;//The channel number your scripts will use
    float speed = 2; //How fast the object is to move
    vector new_target  = <100,23,45>;//Where the object should move to
    float range = 0.5;//How near to the target the object should get.
    list command=["MOVETO",new_target,speed,range];
    llMessageLinked( LINK_SET, company_channel, llDumpList2String(command, "-=-"), "" );

Data unmarshalling

In a similar way, this is how we get our data out. Note where the company channel is being checked.

link_message(integer sender_num, integer num, string str, key id)
{
     list Arguments = llParseStringKeepNulls( str, ["-=-"], [] );
     string Command = llList2String( Arguments, 0 );
    if(num == company_channel)
    {
         if( Command == "MOVETO" )
         {
              vector new_target = (vector)llList2String( Arguments, 1 );
              float speed = (float)llList2String( Arguments, 2 );
              float range = (float)llList2String( Arguments, 3 );
              ...
         }
         else ...
    }
}

This is the method that will be used throughout this example.

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPMovement1

Overview

The basic movement script is going to have one behaviour. It receives a MOVETO link message it will start the object moving at SPEED and then steer the object to within RANGE of the NEW_TARGET. When it is within range of the new position, it's going to stop the object and then send an ATPOS link message.

How it works
We are going to set a constant force on the pet using the llSetForce function. This force is going to constantly push the pet in the direction of its x axis.
At the same time, we are going to set up a rotational force to turn the pet in the direction of the target location. llRotLookAt will do that.
As the pet turns, the forward force is going to turn as well so the pet always moves forward. But because of inertia, it will still be actually moving in its previous direction. To counteract this slide, we will apply an impulse of force, using llApplyImpulse, in the opposite direction to the pet's motion. This will simulate friction, cancelling out all the pet's motion, except the forward motion which is being replenished by the constant force.
Getting all these forces working correctly is a balancing act, the movement script will need to be tuned before it produces the movement you like.

Force = Mass x Acceleration
In a lot of the movement code you will see that I always multiply the acceleration I want by the mass of the object. As in
llSetForce(<0,0,0> * llGetMass(),TRUE);

This is because the acceleration of an object depends on both the force used and the object's mass, as Acceleration = Force / Mass. If you push an object that has a mass of 1kg with a force of 1N, it will accelerate at 1 meter per second2. If you double the mass and keep the force the same, the acceleration will be only 0.5 m/s2.
If you want to bring a moving physical object to a complete stop, hit it with an impulse in the opposite direction to which it's moving. To get the exact impulse needed, multiply the object's mass by its velocity.
llApplyImpulse(-llGetVel() * llGetMass(),FALSE);
    //Note the FALSE at the end.  llGetVel() returns the global velocity, not the local velocity.
    //This means the friction impulse must be in global terms.

Of course if the object still has a force being applied to it, it will start moving again. And don't forget that the wind can push your physical object around.
One very useful side effect of multiplying all accelerations by the mass is that the action of the code becomes independent of the mass of the object. So if your script is set to give an acceleration of 2 meters per second2 and you put in a new model that has twice the mass, it should still accelerate at the same rate because the force being applied will be double what it was before.

States and Events

The basic movement script only needs one state

User Defined Functions

getrottopointaxist()
This script is going to use llRotLookAt() to turn the pet. But llRotLookAt tries to point the z axis at the target. We want the x axis to be the pointer. Luckly, someone on the Wiki has already solved this problem. The example function here povides a solution.

all_stop
There are going to be times when we want to bring a physical object to a complete stop, without turning off physics itself.
all_stop()
{
    //Turn off linear and rotational forces
    llSetForceAndTorque(<0,0,0>,<0,0,0>,TRUE);
    //Stop any movement by applying an impulse of force in the opposite direction
    llApplyImpulse(-llGetVel() * llGetMass(),FALSE);
    // Do the same for any rotational velocity
    llApplyRotationalImpulse(-llGetTorque() * llGetMass(),FALSE);
}

Events

state_entry
Set all the physical parameters requried, including setting the object to physical. The state_entry event also starts the timer event. This should be relatively slow. I find 0.5 a good interval, although I have used slower. If you think you need this to be faster, consider increasing your friction instead, it will have the same effect.
state_entry()
{
    llSetTimerEvent(0.5);
    //Every half a second, call the timer event to apply some friction.
    llSetBuoyancy(1);
    //Setting the buoyancy to 1 cancels out the gravity.  The object will now float.
    llSetStatus(STATUS_ROTATE_X,FALSE);
    llSetStatus(STATUS_ROTATE_Y,FALSE);
    //The two lines above stop the object from rotating in anything but the z axis.
    //It can spin but cannot pitch or roll.  This keeps our pet upright
    llSetStatus(STATUS_PHANTOM,TRUE);
    //Make the object phantom.  Dynamic objects are processing heavy, especially if they are colliding with a lot of other objects.
    //After the brain has been improved and some object avoidance added, this line can be removed.
    //But if your intending to have several pets flying around at the same time, it might be best to leave them as phantom

    llSetStatus(STATUS_PHYSICS,TRUE);
    //Finally, set the Physics flag on, so the object is now subject to forces, inertia and all the other dynamic features.
}

link_message
When a link message is received, the link message event needs to check that it is a MOVETO message. If it is:
Remove any existing Targets that have been set up.
Set up a new Target.
Set up the forward force
Set up the rotational force to turn the pet
link_message(integer sender,integer num, string data,key id)
  {
    if(num == company_channel)
    {
      list data = llParseStringKeepNulls( str, ["-=-"], [] );
      string Cmd = llList2String(data,0);
      if( Cmd == "MOVETO" )
      {
    //Extract the new data from the link message's string
        new_target = (vector)llList2String(data,1);
        float speed = (float)llList2String(data,2);
        float range = (float)llList2String(data,3);
        llTargetRemove(target_id);
    //Remove any old targets
        target_id = llTarget(new_target,range);
    //Set up a new target at location new_target and range range.
        llSetForce(<speed,0,0> * llGetMass(),TRUE);
    //Set up a force equal to the the object's mass multiplied by the variable speed.
    //Set the force so it always pushes in the direction of the x axis
        llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
    //Set up a rotational force towards new_target.
     }
  }

llRotLookAt is a pain to use. First you have to use it with getrottopointaxisat to make it useful. Second, the two parameters rot_str and rot_dmp are very tricky to use. ADD WORKING VALUES HERE

not_at_target
Turn and adjust z position . z force set up as gobal variable
not_at_target()
{
   llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
  vector currentpos = llGetPos();
  if(currentpos.z > new_target.z)
  {
    //little push downwards
    llApplyImpulse(<0,0,-z_force> * llGetMass(),FALSE);
  }
  else
  {
    //little upwards
    llApplyImpulse(<0,0,z_force> * llGetMass(),FALSE);
  }
}

at_target

When the pet has reached the target location, the movement script needs to stop the pet, and then send a link message telling the Brain script that it has arrived.
at_target()
{
    all_stop();
    llMessageLinked( LINK_SET, company_channel, "ATPOS", "" );
}

timer
Add Friction to counteract inertia. The timer goes off relatively slowly. The float friction is the fraction of the objects velocity that is going to be removed. So make sure it's less than 1. This is a type of negative feedback loop, we are feeding back a negative fraction of the old velocity to create a new velocity.
timer()
    {
    float friction = 0.6;
    //friction will be a global variable in the complete script.
        //Apply some friction
        llApplyImpulse(-llGetVel() * llGetMass() * friction,FALSE);
    }

on_rez
Handle the rez situation
on_rez(integer param)
{
    llTargetRemove(target_id);
    all_stop():
}

Putting it all together

//Global Constants.  Change these values to tune your movement script.
//These values are set up for a 5kg object.
float heartbeat = 0.5;
//How often the friction impulse should be applied

float rot_str = 2;

float rot_dmp = 0.5;

float friction = 0.6;
//  What fraction of the object's current velocity should be removed each heartbeat

float z_force = 0.2; //check this one
//The amount of force  used to push the pet up or down.
integer company_channel;
//Your unique ID.  Used to recognise the correct link message.

//Global Variables:
float speed;
float range;
vector new_target;
integer target_id;

//User defined fucntions
all_stop()
{
    //Turn off linear and rotational forces
    llSetForceAndTorque(<0,0,0>,<0,0,0>,TRUE);
    //Stop any movement by applying an impulse of force in the opposite direction
    llApplyImpulse(-llGetVel() * llGetMass(),FALSE);
    // Do the same for any rotational velocity
    llApplyRotationalImpulse(-llGetTorque() * llGetMass(),FALSE);
}
//Code from Wiki
// AXIS_* constants, represent the unit vector 1 unit on the specified axis.
vector AXIS_UP = <0,0,1>;
vector AXIS_LEFT = <0,1,0>;
vector AXIS_FWD = <1,0,0>;

// getRotToPointAxisAt()
// Gets the rotation to point the specified axis at the specified position.
// @param axis The axis to point. Easiest to just use an AXIS_* constant.
// @param target The target, in region-local coordinates, to point the axis at.
// @return The rotation necessary to point axis at target.
// Created by Ope Rand, modifyed by Christopher Omega
rotation getRotToPointAxisAt(vector axis, vector target) {
    return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}
//end of code from Wiki

default
{
    state_entry()
    {
        llSetTimerEvent(heartbeat);
        //Every half a second, call the timer event to apply some friction.
        llSetBuoyancy(1);
        //Setting the buoyancy to 1 cancels out the gravity.  The object will now float.
        llSetStatus(STATUS_ROTATE_X,FALSE);
        llSetStatus(STATUS_ROTATE_Y,FALSE);
        //The two lines above stop the object from rotating in anything but the z axis.
        llSetStatus(STATUS_PHANTOM,TRUE);
        //Make the object phantom.
        llSetStatus(STATUS_PHYSICS,TRUE);
        //Finally, set the Physics flag on, so the object is now subject to forces, inertia and all the other dynamic features.
    }
    link_message(integer sender,integer num, string str,key id)
    {
        if(num == company_channel)
        {
            list data = llParseStringKeepNulls( str, ["-=-"], [] );
            string Cmd = llList2String(data,0);
            if( Cmd == "MOVETO" )
            {
                //Extract the new data from the link message's string
                new_target = (vector)llList2String(data,1);
                speed = (float)llList2String(data,2);
                range = (float)llList2String(data,3);
                llTargetRemove(target_id);
                //Remove any old targets
                target_id = llTarget(new_target,range);
                //Set up a new target at location new_target and range range.
                llSetForce(<speed,0,0> * llGetMass(),TRUE);
                //Set up a force equal to the the object's mass multiplied by the variable speed.
                //Set the force so it always pushes in the direction of the x axis
                llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
                //Set up a rotational force towards new_target.
            }
        }
    }
    not_at_target()
    {
        llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
        vector currentpos = llGetPos();
        if(currentpos.z > new_target.z)
        {
            //little push downwards
            llApplyImpulse(<0,0,-z_force> * llGetMass(),FALSE);
        }
        else
        {
            //little upwards
            llApplyImpulse(<0,0,z_force> * llGetMass(),FALSE);
        }
    }
at_target(integer tnum, vector target_pos, vector our_location)
{
    all_stop();
    llMessageLinked( LINK_SET, company_channel, "ATPOS", "" );
}
timer()
    {
        //Apply some friction
        llApplyImpulse(-llGetVel() * llGetMass() * friction,FALSE);
    }
on_rez(integer param)
{
    llTargetRemove(target_id);
    all_stop();
}
}

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPMovement2

Basic Collision avoidance

The 32 prim

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPMovementTuning

Make sure your Brain script is working first.

change select_new_pos to always return a vector that is about 3-5 meters directly behind the pet. Set it off and watch what happens.

If it turns on the spot and then moves in a straight line towards the target, decrease rot_str, and probably rot_dmp.

If it turns, but not enough to get around to the target, increase rot_str, or decrease the speed.

If it sideslips too much, increase the friction.

If it waggles left and right when its pointing at the target, increase rot_dmp.

If it misses or orbits the target, increase the target range.

Variables that impact on how the script moves

Brain

speed:
Changes how fast the pet moves. Some of the forward speed is "scrubbed" off by friction. Increasing the speed will increase the turning circle.
range:
How near to the target the pt has to get before it decides it has reahed its destination. Too small a target range and your pet will never reach it, valuse around 0.5 are good.

Movement

rot_str:
rot_dmp;
Friction:
Increasing the friction will slow the pet down and reduce the amount it sideslips.
z_force:

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPParticles

New script

particle engine

link messages
Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPReturn2Owner

Finding the owner

Changing to a new behaviour

Back to the Create a flying pet home page
Back to the Script Library


LibraryFPPVoice

Back to the Create a flying pet home page
Back to the Script Library


LibraryGeneralMenuEngineExample1

This is an example for using the General Menu Engine script.

It is a simple menu system that lets you change parameters of an object.
You get a "main menu" with two submenus, one for changing color (allows selection of Red/Green/Blue) the other for changing size (Normal/double)

The script gets a linkmessage with the MENUID and BUTTONTEXT, eg. "MCOLOR|Red", and it reacts to this.

Instructions:
* Create the "menudefs" notecard, put it in the object
* Put the MenuEngine script into the object (it will read the menudefs upon start/reset, takes a few seconds)
* Put the "Menu Demo" script in the object
* To test: touch the object to access functions and dialogs!

The "menudefs" notecard:
MENU DEFAULT
TEXT Change which parameter of the prim?
TOMENU MCOLOR Color
TOMENU MSIZE Size

MENU MCOLOR
TEXT What color should it be?
OPTION Red
OPTION Green
OPTION Blue

MENU MSIZE
TEXT What size should the prim be?
OPTION Normal
OPTION Double

The Menu demo script:
// Menu demo program

//---Menu Library interface-------------
integer LM_DOMENU   = 10001;
integer LM_OPTIONDATA = 10002;
integer LM_MENULOADED = 10003;
integer LM_RESETMENUSYSTEM = 10004;
integer LM_READSTRING = 10005;
integer LM_READSTRINGDATA = 10006;

resetMenu() {
    llMessageLinked(llGetLinkNumber(),LM_RESETMENUSYSTEM,"",NULL_KEY);
}
doMenu( key user, string menuname ) {
    llMessageLinked(llGetLinkNumber(),LM_DOMENU,menuname,user);
}
readString( key user, string var, string prompt ) {
    llMessageLinked(llGetLinkNumber(),LM_READSTRING,var+" "+prompt,user);
}
//---Menu Library interface End----------

default
{
    state_entry()
    {
    }

    link_message(integer sender_num, integer num, string str, key id) {
        if (num==LM_OPTIONDATA) {
            // menu result? let's process it!

            if (str=="MCOLOR|Red") llSetColor(<1,0,0>,ALL_SIDES);
            if (str=="MCOLOR|Green") llSetColor(<0,1,0>,ALL_SIDES);
            if (str=="MCOLOR|Blue") llSetColor(<0,0,1>,ALL_SIDES);

            if (str=="MSIZE|Normal") llSetScale(<0.5,0.5,0.5>);
            if (str=="MSIZE|Double") llSetScale(<1,1,1>);
        }
        if (num==LM_MENULOADED) llSay(0,"Menu ready");
    }

    touch_start(integer total_number)
    {
        doMenu(llDetectedKey(0),"DEFAULT");
    }
}


LibraryGetBinaryString

GetBinaryString

In bitwise there are many helpful examples that dump out the bits for viewing. I couldn't find a function for this, so here one is.

// Note, we're poping these bits off the top so our string will be in the right order.
string GetBinaryString(integer val) {
    string retVal = "";

    integer counter = 0xffffffff;
    while(counter) {
        if (val & 0x80000000) {
            retVal += "1";
        } else {
            retVal += "0";
        }
        val = val << 1;
        counter = counter << 1;
    }
    return retVal;
}

ScriptLibrary | Dolyn's Scripts


LibraryGoGame2

Continued from Go Game first page...

You should now have a GoButton, a GoJoinButton, a GoSensorGrid and a GoTileGrid. Create a prim, drop an instance of each of those objects into its inventory, then finally put in the following two scripts which contain the main game logic, and you should have yourself a working Go board.

First off is the GoGameLogicScript which contains most of the actual game logic for handling captures and the like.

// GoGameLogicScript
// Jonathan Shaftoe
// Contains most of the game logic code for the Go game. It's big and it's nasty. It uses recursion
// to do group capture detection, so can be slow.
// For usage, see GoGameMainScript

integer gGameSize = 9; // size of the game board, can be 9, 13 or 19

integer gBlackScore; // current scores (captured stones)
integer gWhiteScore;

integer gDeadWhite; // count of dead stones in endgame
integer gDeadBlack;

string gGameStatePrev; // Previous game state, used to check for Ko
string gGameState; // Game state - string representing the current state of the game board.
string gCurrentGame; // Another copy of the game state, used to store current state when working out
                    // captures.

list gGroup = []; // list of indexes of stones in (possibly captured) group.
integer gGroupLiberties; // How many liberties the currently being checked group has
integer gLibertyX; // coordinates of found liberty. We only care if there are no liberties, one liberty
integer gLibertyY; // or more than one (number if more than one doesn't matter), so we store the first
                   // found liberty here, then subsequently found liberties we check if they're the same
                   // as this one, and if not then we know we have more than one.
integer gSearchColour; // Are we looking for black or white stones

integer gTurn; // Whose turn is it, black or white
integer gLastWasPass;  // So we know if we get two passes in a row
integer gTurnPreEndgame;  // For turn handling during endgame

integer gGotAtari; // If we've found at least one group with only one liberty left after a stone
                   // is played, then we have an atari

list gToDelete; // list of indexes of stones to be removed due to capture (can be more than one group)

integer gGroupType; // type of group check being done, to avoid having to pass it through recursive calls.
// 0 - suicide check, 1 - normal turn check, 2 - endgame dead group marking, 3 - endgame scoring.
float gSize=4.0; // global scale factor.

set_player(integer turn, integer endgame, integer send_message) {
    gTurn = turn;
    if (send_message == 1) {
        llMessageLinked(LINK_ROOT, 101, (string)gTurn, (key)((string)endgame));
    }
}

integer get_board_state(integer x, integer y) {
    integer index = x + 1 + ((y + 1) * (gGameSize + 2));
    string num = llGetSubString(gGameState, index, index);
    return (integer)num;
}

set_board_state(integer x, integer y, integer newstate) {
    integer index = x + 1 + ((y + 1) * (gGameSize + 2));
    string before = llGetSubString(gGameState, 0, index - 1);
    string after = llGetSubString(gGameState, index + 1, llStringLength(gGameState) - 1);
    gGameState = before + (string)newstate + after;
}

// Sets gameboard size and initialises gameState. Note 3s used around edge to make
// group detection easier (no boundary conditions, 3 is neither 1 (black) or 2 (white))
set_size(integer size) {
    gGameSize = size;
    if (gGameSize == 9) {
        gGameState = "33333333333" +
                     "30000000003" +
                     "30000000003" +
                     "30000000003" +
                     "30000000003" +
                     "30000000003" +
                     "30000000003" +
                     "30000000003" +
                     "30000000003" +
                     "30000000003" +
                     "33333333333";
    } else if (gGameSize == 13) {
        gGameState = "333333333333333" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "300000000000003" +
                     "333333333333333";
    } else if (gGameSize == 19) {
        gGameState = "333333333333333333333" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "300000000000000000003" +
                     "333333333333333333333";
    }
}

// functions below for doing recursive group detection. This is NOT efficient.
// on a 19x19 board, you can run out of script execution stack space if there are big
// groups. Could be improved.

recurse_check_util(integer x, integer y, integer dir) {
    integer neighbour = get_board_state(x, y);
    if (neighbour == gSearchColour) {
        recurse_check(x, y, dir);
    } else if (neighbour == 0 && gGroupType != 2) {
        if (x != gLibertyX || y != gLibertyY) {
            gLibertyX = x;
            gLibertyY = y;
            gGroupLiberties++;
        }
    } else if ((neighbour == 2 || neighbour == 1) && gGroupType == 3) {
        if (gGroupLiberties != neighbour) {
            gGroupLiberties += neighbour;
        }
    }
}

recurse_check(integer x, integer y, integer dir) {
    if (gGroupType == 1 && gGroupLiberties >= 2) {
        return;
    }
    if (gGroupType == 0 && gGroupLiberties == 1) {
        return;
    }
    if (llListFindList(gGroup, [x + (y * gGameSize)]) != -1) {
        return;
    }
    gGroup = gGroup + [x + (y * gGameSize)];
    if (dir != 0) {
        recurse_check_util(x - 1, y, 1);
    }
    if (dir != 1) {
        recurse_check_util(x + 1, y, 0);
    }
    if (dir != 2) {
        recurse_check_util(x, y - 1, 3);
    }
    if (dir != 3) {
        recurse_check_util(x, y + 1, 2);
    }
}

integer check_captures(integer x, integer y, integer dir, integer type) {
    integer groupSize = 0;
    gGroup = [];
    gGroupLiberties = 0;
    gLibertyX = -1;
    gLibertyY = -1;
    gGroupType = type;
    recurse_check(x, y, dir);

// if type == 0 then we're checking for a suicide play.
    if (type == 3 || gGroupLiberties == 0) {
        groupSize = llGetListLength(gGroup);
    }
    if (type == 1) { // finding captured groups in normal gameplay
        if (gGroupLiberties == 1) {
            gGotAtari = 1;
        } else if (gGroupLiberties == 0) {
            integer i = 0;
            while (i < groupSize) {
                integer index = llList2Integer(gGroup, i);
                integer remx = index % gGameSize;
                integer remy = index / gGameSize;
                set_board_state(remx, remy, 0);
                i++;
            }
            gToDelete = gToDelete + gGroup;
        }
    } else if (type == 2) {  // marking dead groups in endgame
        integer i = 0;
        while (i < groupSize) {
            integer index = llList2Integer(gGroup, i);
            integer remx = index % gGameSize;
            integer remy = index / gGameSize;
            set_board_state(remx, remy, 0);
            i++;
            gToDelete = gToDelete + gGroup;
        }
    } else if (type == 3) { // counting territory space in endgame - gGroupLiberties used to store colour of neighbouring stones to work out territory owner. If both colours are found then they're added so result will be 3 (or more)
        integer i = 0;
        while (i < groupSize) {
            integer index = llList2Integer(gGroup, i);
            integer remx = index % gGameSize;
            integer remy = index / gGameSize;
            set_board_state(remx, remy, 3);
            i++;
        }
        if (gGroupLiberties == 1) {
            gBlackScore += groupSize;
        } else if (gGroupLiberties == 2) {
            gWhiteScore += groupSize;
        }
    }

    return groupSize;
}

default {
    state_entry() {
        gBlackScore = 0;
        gWhiteScore = 0;
        gTurn = 0;
        gLastWasPass = FALSE;
        state playing;
    }
}

// Normal state during game play.
state playing {
    state_entry() {
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 100) {
            set_size((integer)str);
            gSize = (float)((string)id);
        } else if (num == 4) {
            list params = llParseString2List(str, [","], []);
            integer x = llList2Integer(params, 0);
            integer y = llList2Integer(params, 1);
            if (get_board_state(x, y) != 0) {
                llWhisper(0, "Cannot play there, already occupied.");
                llMessageLinked(LINK_ALL_CHILDREN, 21, "", "");
            } else {
                gCurrentGame = gGameState;
                set_board_state(x, y, gTurn + 1);
                gSearchColour = (1 - gTurn) + 1;
                integer scorechange = 0;
                gGotAtari = 0;
                gToDelete = [];
                if (get_board_state(x + 1, y) == gSearchColour) {
                    scorechange += check_captures(x + 1, y, 0, 1);
                }
                if (get_board_state(x - 1, y) == gSearchColour) {
                    scorechange += check_captures(x - 1, y, 1, 1);
                }
                if (get_board_state(x, y + 1) == gSearchColour) {
                    scorechange += check_captures(x, y + 1, 2, 1);
                }
                if (get_board_state(x, y - 1) == gSearchColour) {
                    scorechange += check_captures(x, y - 1, 3, 1);
                }
                if (scorechange > 0 && gGameState == gGameStatePrev) {
                    llWhisper(0, "Cannot play there due to ko");
                    gGameState = gCurrentGame;
                    llMessageLinked(LINK_ALL_CHILDREN, 21, "", "");
                    return;
                }
                if (scorechange == 0) {
                    gSearchColour = gTurn + 1;
                    integer checkSuicide = check_captures(x, y, -1, 0);
                    if (checkSuicide > 0) {
                        llWhisper(0, "Cannot play there, suicide play.");
                        gGameState = gCurrentGame;
                        llMessageLinked(LINK_ALL_CHILDREN, 21, "", "");
                        return;
                    }
                }
                llMessageLinked(LINK_ROOT, 500, (string)x, (string)y);
                if (gGotAtari == 1) {
                    if (gTurn == 0) {
                        llWhisper(0, "Black says 'atari'");
                    } else {
                        llWhisper(0, "White says 'atari'");
                    }
                }
                gGameStatePrev = gCurrentGame;
                if (scorechange > 0) {
                    // let's actually do the removing now
                    integer i = 0;
                    integer delete_index;
                    integer remx;
                    integer remy;
                    while (i < scorechange) {
                        delete_index = llList2Integer(gToDelete, i);
                        remx = delete_index % gGameSize;
                        remy = delete_index / gGameSize;
                        llMessageLinked(LINK_ALL_CHILDREN, 201, (string)remx + "," + (string)remy + ",0", "");
                        i++;
                    }
                    llMessageLinked(LINK_ALL_CHILDREN, 202, "", "");
                    string piece;
                    if (scorechange == 1) {
                        piece = "piece";
                    } else {
                        piece = "pieces";
                    }
                    if (gTurn == 0) {
                        gBlackScore += scorechange;
                        llWhisper(0, "Black captured " + (string)scorechange + " " + piece);
                    } else {
                        gWhiteScore += scorechange;
                        llWhisper(0, "White captured " + (string)scorechange + " " + piece);
                    }
                    llMessageLinked(LINK_ROOT, 400, (string)scorechange, (string)gTurn);
                }
                gLastWasPass = FALSE;
                set_player(1 - gTurn, 0, 1);
                llMessageLinked(LINK_ALL_CHILDREN, 201, (string)x + "," + (string)y + "," + (string)(2 - gTurn), "");
            }
        } else if (num == 10) {
            if (gTurn == 0) {
                llWhisper(0, "Black passes");
            } else {
                llWhisper(0, "White passes");
            }
            gTurn = 1 - gTurn;
            gGameStatePrev = gGameState;
            if (gLastWasPass == TRUE) {
                llWhisper(0, "Consecutive passes, entering endgame.");
                state endgame;
            }
            gLastWasPass = TRUE;
            set_player(gTurn, 0, 1);
        } else if (num == 999) {
            state default;
        }
    }
}

// State during endgame, when players must mark any dead groups belonging to the other player.

state endgame {
    state_entry() {
        llMessageLinked(LINK_ROOT, 102, "", "");
        gLastWasPass = FALSE;
        gTurnPreEndgame = gTurn;
        gCurrentGame = gGameState;
        gDeadBlack = 0;
        gDeadWhite = 0;
        set_player(gTurn, 1, 1);
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 103) {
            set_player((integer)str, (integer)((string)id), 0);
        } else if (num == 4) {
            list params = llParseString2List(str, [","], []);
            integer x = llList2Integer(params, 0);
            integer y = llList2Integer(params, 1);
            if (get_board_state(x, y) != (1 - gTurn) + 1) {
                llWhisper(0, "Invalid selection. Select your opponent's groups which are dead.");
                llMessageLinked(LINK_ALL_CHILDREN, 21, "", "");
            } else {
                gSearchColour = (1 - gTurn) + 1;
                gToDelete = [];
                integer scorechange = check_captures(x, y, -1, 2);
                integer i = 0;
                integer delete_index;
                integer remx;
                integer remy;
                while (i < scorechange) {
                    delete_index = llList2Integer(gToDelete, i);
                    remx = delete_index % gGameSize;
                    remy = delete_index / gGameSize;
                    llMessageLinked(LINK_ALL_CHILDREN, 201, (string)remx + "," + (string)remy + ",0", "1");
                    i++;
                }
                llMessageLinked(LINK_ALL_CHILDREN, 202, "", "");
                if (gSearchColour == 1) {
                    gDeadBlack += scorechange;
                } else {
                    gDeadWhite += scorechange;
                }
                set_player(gTurn, 1, 1);
            }
        } else if (num == 104) {
            if (str == "It's fine") {
                llMessageLinked(LINK_ALL_CHILDREN, 203, "", "");
                if (gTurn == gTurnPreEndgame) {
                    state scoring;
                } else {
                    set_player(gTurn, 1, 1);
                }
            } else if (str == "Dispute") {
                llWhisper(0, "Dead groups disputed, resuming play.");
                gTurn = gTurnPreEndgame;
                set_player(gTurn, 0, 1);
                gGameState = gCurrentGame;
                llMessageLinked(LINK_ALL_CHILDREN, 15, "", "");
                state playing;
            }
        } else if (num == 999) {
            state default;
        }
    }
}

// Actually working out the final score.

state scoring {
    state_entry() {
        llMessageLinked(LINK_SET, 105, "", "");
        llWhisper(0, "Calculating final scores. Please be patient ...\nWarning - large open areas on a large board can cause this to error due to lack of memory.");
        llSetText("Calculating final scores ...", <0, 0, 1>, 1.0);
        integer x;
        integer y;
        gSearchColour = 0;
        for (x = 0; x < gGameSize; x++) {
            for (y = 0; y < gGameSize; y++) {
                if (get_board_state(x, y) == 0) {
                    check_captures(x, y, -1, 3);
                }
            }
        }
        gBlackScore += gDeadWhite;
        gWhiteScore += gDeadBlack;
        llWhisper(0, "Final score, black: " + (string)gBlackScore + "  and white: " + (string)gWhiteScore);
        llSetText("Final score, black: " + (string)gBlackScore + "  and white: " + (string)gWhiteScore, <0, 0, 1>, 1.0);
        llMessageLinked(LINK_ROOT, 106, "", "");
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 999) {
            state default;
        }
    }
}

And now for the GoGameMainScript, which does all the gluing together of all the other scripts, and handles players joining/leaving the game and the like.

// GoGameMainScript
// Jonathan Shaftoe
// This is the main controlling script which sorts out rezzing all the objects and moving
// them around and sending messages to the other bits and pieces. It's big and it's messy.
// To use, create a single prim, drop into it a previously created GoButton, GoJoinButton,
// GoSensorGrid and GoTileGrid, then drop in the GoGaneLogicScript, then finally drop in this
// script. If you want the info button to give out a notecard, create one called Go Info and
// drop that into the inventory of the prim too (actually, it'll probably error if you don't
// do this, so.. )

// Channel used for listens, used for dialogs.
integer CHANNEL = 85483;
// Size of go board, 9, 13 or 19. This is just what it initialises to, can be changed
// during game setup
integer gGameSize = 9;

// Store the current listener, so we can remove it.
integer gListener;
// This is a general scaling multiplier, allowing you either to create a small dinky Go Board, or a
// large one. The maximum size that will work is 9.0, as beyond that linking of things fails. I've
// not experimented with the smallest workable size.
float SIZE = 3.0;

// Keys and names of the players playing each colour
key gBlackPlayer;
key gWhitePlayer;
string gBlackName;
string gWhiteName;
// Current player, whose turn it is.
key gPlayer;

// How many sensors we have, and the size of grid being used.
integer gXSensors;
integer gYSensors;

// Current scores
integer gBlackScore = 0;
integer gWhiteScore = 0;

// Used for counting rezzes, so we know when everything has been rezzedd
integer gCountRez;

// Whose turn it is, 0 is black, 1 is white.
integer gTurn = 0;

// How long since last turn (for reset)
string gLastTurnDate;
float gLastTurnTime;

// for the alpha part of coords, so we know what to call each square in messages. Note
// absence of I to avoid confusion with 1.
string COORDS = "ABCDEFGHJKLMNOPQRST";

set_player(integer turn, integer endgame, integer send_message) {
    vector colour;
    integer score = 0;
    string name;
    gTurn = turn;
    if (gTurn == 0) {
        gPlayer = gBlackPlayer;
        score = gBlackScore;
        colour = <0, 0, 0>;
        name = gBlackName;
    } else {
        gPlayer = gWhitePlayer;
        score = gWhiteScore;
        colour = <1, 1, 1>;
        name = gWhiteName;
    }
    llMessageLinked(LINK_ALL_CHILDREN, 0, "", gPlayer);
    if (endgame == 0) {
        llSetText(name + "'s turn (prisoners: " + (string)score + ").", colour, 1.0);
    } else if (endgame == 1) {
        llSetText(name + "'s turn to remove dead groups (prisoners: " + (string)score + ").", colour, 1.0);
        llInstantMessage(gPlayer, "Select opponent's dead groups then click done.");
    }
    if (send_message == 1) {
        llMessageLinked(LINK_ROOT, 103, (string)gTurn, (string)endgame);
    }
    gLastTurnDate = llGetDate();
    gLastTurnTime = llGetGMTclock();
}

set_size(integer size) {
    gGameSize = size;
    if (gGameSize == 9) {
        gXSensors = 3;
        gYSensors = 3;
        llSetTexture("a9878863-732f-09af-b908-09070ea9b213", 0);
    } else if (gGameSize == 13) {
        gXSensors = 4;
        gYSensors = 4;
        llSetTexture("a0973434-028b-3323-b572-8347095e6c3c", 0);
    } else if (gGameSize == 19) {
        gXSensors = 4;
        gYSensors = 5;
        llSetTexture("9575b5cf-72ec-14bf-e400-320f9008bbd9", 0);
    }
    llSetScale(<(gGameSize + 1.0) * SIZE / gGameSize, (gGameSize + 1.0) * SIZE / gGameSize, 0.01 * SIZE>);
    llMessageLinked(LINK_ALL_CHILDREN, 3, (string)gGameSize + "," + (string)gXSensors + "," + (string)gYSensors + "," + (string)SIZE, "");
    llMessageLinked(LINK_ROOT, 100, (string)gGameSize, (string)SIZE);
}

// State we start in, but don't stay in long. Need permission to link to do set-up.
default {
    state_entry() {
        llSetPrimitiveParams([
            PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0,
                  ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
            PRIM_SIZE, <1 * SIZE, 1 * SIZE, 0.01 * SIZE>,
            PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>,  <0, 0, 0>, 0.0,
            PRIM_COLOR, ALL_SIDES, <0.8, 0.6, 0.5>, 1.0
        ]);
        llSetObjectName("Jonathan's Go Board");
        llSetObjectDesc("Jonathan's Go Board");
        llSitTarget(ZERO_VECTOR, ZERO_ROTATION);
        llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
    }

    run_time_permissions(integer perms) {
        llBreakAllLinks();
        state initialising;
    }
}

// Initialisation state, rezzing all the buttons we need
state initialising {
    state_entry() {
        gCountRez = 7;
        llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * .45, SIZE * .02>, ZERO_VECTOR, llGetRot(), 1);
        llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * .25, SIZE * .02>, ZERO_VECTOR, llGetRot(), 2);
        llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * -.25, SIZE * .02>, ZERO_VECTOR, llGetRot(), 4);
        llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * -.45, SIZE * .02>, ZERO_VECTOR, llGetRot(), 3);
        llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), 0, SIZE * .02>, ZERO_VECTOR, llGetRot(), 5);
        llRezObject("GoJoinButton", llGetPos() + <0, SIZE * 0.5, 0.1 * SIZE>, ZERO_VECTOR, llGetRot(), 1);
        llRezObject("GoJoinButton", llGetPos() + <0, SIZE * -0.5, 0.1 * SIZE>, ZERO_VECTOR, llGetRot(), 2);
    }

    object_rez(key id) {
        llCreateLink(id, TRUE);
        gCountRez--;
        if (gCountRez == 0) {
            llMessageLinked(LINK_ALL_CHILDREN, 1, (string)SIZE, "");
            state setup_sensors;
        }
    }
}

// Initialization state 2, seting up the sensor and tile grids.
state setup_sensors {
    state_entry() {
        gCountRez = 2;

        llRezObject("GoSensorGrid", llGetPos() + <0, 0, 0.005 * SIZE + .01>, ZERO_VECTOR, llGetRot(), 0);
        llRezObject("GoTileGrid", llGetPos() + <0, 0, 0.005 * SIZE + 0.1>, ZERO_VECTOR, llGetRot(), 0);
    }

    object_rez(key id) {
        llCreateLink(id, TRUE);
        gCountRez--;
        if (gCountRez == 0) {
            set_size(9);
            state awaiting_start;
        }
    }
}

// All initialised, waiting for players to join the game, plus can change game board size.

state awaiting_start {
    state_entry() {
        llSetTouchText("Game Size");
        llSetText("Go Game - awaiting players", <0, 0, 0>, 1.0);
    }

    state_exit() {
        llSetTimerEvent(0);
    }

    touch_start(integer num) {
        llDialog(llDetectedKey(0), "Set size of Go game board.\nCurrently set to: " + (string)gGameSize + "x" + (string)gGameSize, ["9x9", "13x13", "19x19"], CHANNEL);
        llListenRemove(gListener);
        gListener = llListen(CHANNEL, "", llDetectedKey(0), "");
        // Make sure we timeout the listen, in case they ignore the dialog
        llSetTimerEvent(60 * 2);
    }

    listen(integer channel, string name, key id, string message) {
        if (message == "9x9") {
          set_size(9);
        } else if (message == "13x13") {
          set_size(13);
        } else if (message == "19x19") {
          set_size(19);
        }
        llWhisper(0, "Go board set to size: " + (string)gGameSize + "x" + (string)gGameSize);
        llListenRemove(gListener);
        llSetTimerEvent(0);
    }

    timer() {
        llListenRemove(gListener);
        llSetTimerEvent(0);
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 1) {
            gBlackPlayer = id;
            gBlackName = llKey2Name(id);
            llWhisper(0, gBlackName + " will play black.");
        } else if (num == 2) {
            gWhitePlayer = id;
            gWhiteName = llKey2Name(id);
            llWhisper(0, gWhiteName + " will play white.");
        } else if (num == 12) {
            llWhisper(0, llKey2Name(id) + " resets the Go board.");
            state resetting;
        } else if (num == 14) {
            llGiveInventory(id, "Go Info");
        }
        if (num == 1 || num == 2) {
            if (gBlackPlayer != "" && gWhitePlayer != "") {
                llWhisper(0, "Game started.");
                set_player(0, 0, 1);
                state playing;
            }
        }
    }
}

// Game has started, two players are playing.
state playing {
    state_entry() {
        llSetTouchText("Undo zoom");
    }

    touch_end(integer num) {
        if (llDetectedKey(0) == gPlayer) {
            llMessageLinked(LINK_ALL_CHILDREN, 0, "", gPlayer);
        }
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 101) {
            set_player((integer)str, (integer)((string)id), 0);
        } else if (num == 400) {
            integer turn = (integer)((string)id);
            integer score = (integer)str;
            if (turn == 0) {
                gBlackScore += score;
            } else {
                gWhiteScore += score;
            }
        } else if (num == 500) {
            integer x = (integer)str;
            integer y = (integer)((string)id);
            llPlaySound("a46a5924-5679-6483-233a-5b0b165ec477", 1.0);
            string player;
            if (gTurn == 0) {
                player = "Black";
            } else {
                player = "White";
            }
            string coord = llGetSubString(COORDS, x, x);
            llWhisper(0, player + " played at " + coord + ", " + (string)(y + 1));
        } else if (num == 102) {
            state endgame;
        } else if (num == 12) {
            string nowDate = llGetDate();
            float nowTime = llGetGMTclock();
            if (nowDate == gLastTurnDate && ((nowTime - gLastTurnTime) < 600)) {
                llInstantMessage(id, "You cannot reset the Go board, an active game is in progress. If you are playing and wanting to reset, resign first to end a game. If a game has been abandoned, then the board will be resettable if 10 minutes pass with no turn played.");
            } else {
                llDialog(id, "Are you sure you want to reset the board? The game in progress will be lost.", ["Reset", "Cancel"], CHANNEL);
                llListenRemove(gListener);
                gListener = llListen(CHANNEL, "", id, "");
                llSetTimerEvent(60 * 4);
            }
        } else if (num == 13) {
            if (id == gWhitePlayer || id == gBlackPlayer) {
                llDialog(id, "Are you sure you want to resign the game?", ["Resign", "Cancel"], CHANNEL);
                llListenRemove(gListener);
                gListener = llListen(CHANNEL, "", id, "");
                llSetTimerEvent(60 * 4);
            }
        } else if (num == 14) {
            llWhisper(0, "Game in progress between " + gBlackName + " (Black: current prisoners " + (string)gBlackScore + ") and " + gWhiteName + " (White, current prisoners: " + (string)gWhiteScore + ")");
            llGiveInventory(id, "Go Info");
        }
    }

    listen(integer channel, string name, key id, string message) {
        llListenRemove(gListener);
        llSetTimerEvent(0);
        if (message == "Resign") {
            string resignerName;
            string winnerName;
            if (id == gBlackPlayer) {
                resignerName = "Black";
                winnerName = "White";
            } else {
                resignerName = "White";
                winnerName = "Black";
            }
            llWhisper(0, resignerName + " has resigned. " + winnerName + " wins.");
            llSetText(resignerName + " resigns. " + winnerName + " wins.", <0, 0, 1>, 1.0);
            state gameover;
        } else if (message == "Reset") {
            llWhisper(0, name + " resets the Go board.");
            state resetting;
        }
    }

    timer() {
        llListenRemove(gListener);
        llSetTimerEvent(0);
    }

    state_exit() {
        llSetTimerEvent(0);
    }
}

// Two passes have occured, we're in endgame, players must mark oppositions dead groups (and then
// agree with what their opponent has marked as dead )

state endgame {
    state_entry() {
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 11) {
            set_player(1 - gTurn, 2, 1);
            llDialog(gPlayer, "Are you happy with the groups your opponent has marked as dead?\nIf you dispute, play will resume.", ["It's fine", "Dispute"], CHANNEL);
            llListenRemove(gListener);
            gListener = llListen(CHANNEL, "", gPlayer, "");
            // Make sure we timeout the listen, in case they ignore the dialog
            llSetTimerEvent(60 * 4);
        } else if (num == 105) {
            state scoring;
        } else if (num == 101) {
            set_player((integer)str, (integer)((string)id), 0);
        } else if (num == 12) {
            string nowDate = llGetDate();
            float nowTime = llGetGMTclock();
            if (nowDate == gLastTurnDate && ((nowTime - gLastTurnTime) < 600)) {
                llInstantMessage(id, "You cannot reset the Go board, an active game is in progress. If you are playing and wanting to reset, resign first to end a game. If a game has been abandoned, then the board will be resettable if 10 minutes pass with no turn played.");
            } else {
                llDialog(id, "Are you sure you want to reset the board? The game in progress will be lost.", ["Reset", "Cancel"], CHANNEL);
                llListenRemove(gListener);
                gListener = llListen(CHANNEL, "", id, "");
                llSetTimerEvent(60 * 4);
            }
        } else if (num == 14) {
            llWhisper(0, "Game in progress between " + gBlackName + " (Black: current prisoners " + (string)gBlackScore + ") and " + gWhiteName + " (White, current prisoners: " + (string)gWhiteScore + ") - players in endgame");
            llGiveInventory(id, "Go Info");
        }
    }

    listen(integer channel, string name, key id, string message) {
        llListenRemove(gListener);
        llSetTimerEvent(0);
        llMessageLinked(LINK_ROOT, 104, message, "");
        if (message == "Dispute") {
            state playing;
        } else if (message == "Reset") {
            llWhisper(0, name + " resets the Go board.");
            state resetting;
        }
    }

    touch_end(integer num) {
        if (llDetectedKey(0) == gPlayer) {
            llMessageLinked(LINK_ALL_CHILDREN, 0, "", gPlayer);
        }
    }

    timer() {
        llListenRemove(gListener);
        llSetTimerEvent(0);
    }

    state_exit() {
        llSetTimerEvent(0);
    }
}

// End game finished, waiting for GoGameLogic to work out final score.
state scoring {
    state_entry() {
    }

    link_message(integer sender, integer num, string str, key id) {
        if (num == 106) {
            state gameover;
        } else if (num == 14) {
            llWhisper(0, "Game in progress between " + gBlackName + " (Black: current prisoners " + (string)gBlackScore + ") and " + gWhiteName + " (White, current prisoners: " + (string)gWhiteScore + ") - game scoring.");
            llGiveInventory(id, "Go Info");
        }
    }
}

// Game finished and final score displayed
state gameover {
    state_entry() {
    }
    link_message(integer sender, integer num, string str, key id) {
        if (num == 12) {
            state resetting;
        } else if (num == 14) {
            llGiveInventory(id, "Go Info");
        }
    }
}

// Reset board ready to start new game.
state resetting {
    state_entry() {
        llMessageLinked(LINK_SET, 999, "", "");
        set_size(gGameSize);
        gBlackPlayer = "";
        gWhitePlayer = "";
        gBlackScore = 0;
        gWhiteScore = 0;
        state awaiting_start;
    }
}

Here is the text of the Go Info notecard given out by the info button. This should be saved as a notecard called Go Info and dropped in the inventory of the go game prim.

Hello and welcome to Jonathan's Go game.

Go is an ancient game from Asia with simple rules but complex gameplay. Two players, white and black, take turns to place a stone on a board. If a group of one colour is completely surrounded by a group of another, it is removed from the board. Note that stones are placed on the junctions of lines, not in the spaces between them, and that diagonals do not count. Once both players agree that there is no further advantage in playing any further stones, the one with the greatest score wins.

A full description of the rules of Go is beyond the scope of a notecard. There are many websites with descriptions and tutorials, for example:

  http://www.britgo.org/

There are two types of scoring in common use,  Japanese or Territory scoring, and Chinese or Area scoring. I've implemented Territory scoring. In practice there is little difference between the two.

Though the basic rules are implemented, there are some unusual circumstances which aren't covered, for example triple ko or seki. In the unlikely event of these occuring, you'll have to sort out how to handle it between yourselves.

I have designed the board to be hopefully as intuitive to use as possible. Before starting a game, the size of the game board can be set by clicking anywhere on the board and selecting from the three commonly used sizes on offer. A 9x9 game can be played relatively quickly, but loses the deeper levels of strategy involved in a larger game. It is a good choice for beginners. A 19x19 game is a proper full sized game, and can take upwards of two hours to complete. 13x13 is a midpoint between the two.

Join a game by clicking one of the two join buttons. Once two players have joined, the game will start, and only those two players will be able to use the board until the game is finished, or the board is reset. The board can only be reset if no move has been played for a certain amount of time (currently set to 10 minutes) - this prevents boards being left in a used state.

Place a stone on the board by clicking first on the coloured translucent square over the area you want to play in, then on the actual square over the point you want to play. This two step process is to conserve the prim usage of the board. If after the first click you change your mind, you can 'unzoom' by clicking anywhere else on the board. If you try to play somewhere illegal, due to ko or suicide for example, the board will tell you.

Once both players have passed consecutively, the board enters end-game. During this stage, each player in turn is invited to select which of his or her opponent's stones are 'dead' - that is stones whose capture is inevitable and unpreventable, generally all groups lacking two distinct 'eyes' (holes inside the group). Selecting a stone will remove all of the stones in that connected group. Once each player has signified that they have completed doing this, by pressing 'Done', the other is asked if they agree with the stones removed. If not, they can dispute the endgame and play resumes. It is essential to remove dead groups, as the automatic scoring cannot otherwise work.

Once both players have selected their opponent's dead groups and had their selection confirmed, final scoring is calculated. This can take a while, depending on the size of board and size of territory to be counted. Unfortunately due to the limits of LSL in unusual circumtances, for example on a large board with a large area of territory, this can cause an out of memory error in the script. Please do not enter endgame scoring on a sparsely populated board, use the 'resign' button instead.

Enjoy!

Jonathan Shaftoe

And finally, here's some documentation I did for myself on the many link messages used. If you're going to try to figure out how it all works or change the code, this will probably be invaluable!

// Messages
// 0. From MainScript to Button, Sensor - sets current player, black or white, and resets Sensor to zoom1
// 1. From MainScript, to JoinButton, Button. Sets SIZE
// 2. From MainScript, to ?. Sets SIZE and gGameSize (depricated?)
// 1 & 2 From JoinButton to MainScript - sets who's playing black/white
// 3. From MainScript, to Tile, Sensor, sets gGameSize, gX/YSensors and SIZE, sent with set_size
// 4. From Sensor to Logic, turn attempt made, coordinates passed.
// 5. From Sensor to Sensor. coordinates of zoom from zoom1 to zoom2.
// 10. From Button, to Logic, player pressed 'pass'
// 11. From Button to Main, player pressed 'done'
// 12. From Button to Main, player pressed 'reset'
// 13. From Button to Main, player pressed 'resign'
// 14. From Button to Main, player pressed 'info'
// 15. From Logic, to Tile, endgame disputed, reset tile to previous state
// 20. From Sensor to Sensor, go to waiting state, move attempted
// 21. From Logic to Sensor, go back from waiting state to zoom2, attempt to move failed.
// 100. From Main to Logic, sets gGameSize and SIZE, sent with every set_size
// 101. From Logic, to Main, sets whose turn and whether we're in endgame.
// 102. From Logic to Main, entering endgame after two passes
// 103. From Main to Logic, sets current player/endgame state
// 104. From Main to Logic, whether player disputes endgame/dead groups or not
// 105. From Logic to Main and Sensors, doing scoring (so sensors hide)
// 106. From Logic to Main, from scoring to gameover
// 201. From Logic to Tile, display piece, x, y, colour (1, 2) passed (displays immediately) or 0 to remove (doesn't display until ..).
// 202. From Logic to Tile, display result of removes from 201 messages.
// 203. From Logic to Tile, clears backup states (for endgame disputes) as agreed.
// 301. From Tile to TileFace, do actual display for given state.
// 400. From Logic to Main, send new captures done by given player for score update
// 500. From Logic to Main, turn played, so do message and do sound
// 999. From Main, to JoinButton, Sensors, Tile, Logic - game reset.

// Main sends: 0, 1, 2, 3, 100, 103, 104, 999
// Main receives: 1, 2, 11, 12, 13, 14, 101, 102, 105, 106, 400, 500

// TileFace sends: none
// TileFace receives: 301

// Tile sends: 301
// Tile receives: 3, 15, 201, 202, 203, 999

// Logic sends: 15, 21, 101, 102, 105, 106, 201, 202, 203, 400, 500
// Logic receives: 4, 10, 100, 103, 104, 999

// Button sends: 10, 11, 12, 13, 14
// Button receives: 0, 1

// Sensor sends: 4, 5, 20
// Sensor receives: 0, 3, 5, 20, 21, 105, 999

// JoinButton sends: 1, 2
// JoinButton receives: 1, 999


LibraryHSL2RGB

vector HSL_2_RGB( vector hsl )
{
  // hsl H S L
 vector rgb;
 float var1, var2;

  if ( S == 0 )                       //HSL values = 0 ? 1
  {
     rgb.x = hsl.y;                  //RGB results
     rgb.y = hsl.y;
     rgb.z = hsl.y;
  }
  else
  {
     if ( L < 0.5 ) var_2 = L * ( 1 + S );
     else           var_2 = ( L + S ) - ( S * L );

     var_1 = 2 * L - var_2;

     R = 255 * Hue_2_RGB( var_1, var_2, H + ( 1 / 3 ) );
     G = 255 * Hue_2_RGB( var_1, var_2, H );
     B = 255 * Hue_2_RGB( var_1, var_2, H - ( 1 / 3 ) )  ;
  }
  return rgb;
}

float Hue_2_RGB( vector hue v1, v2, vH )             //Function Hue_2_RGB
{
    // hue v1, v2, vH

   if ( vH < 0 ) vH += 1
   if ( vH > 1 ) vH -= 1
   if ( ( 6 * vH ) < 1 ) return ( v1 + ( v2 - v1 ) * 6 * vH )
   if ( ( 2 * vH ) < 1 ) return ( v2 )
   if ( ( 3 * vH ) < 2 ) return ( v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - vH ) * 6 )
   return ( v1 )
}


LibraryInventoryLoader

InventoryLoader

Code to easily get a box full of toys into your inventory.

Just drag this one into the container and watch the counter. Note that it will kill the object text as it gives you the on going status, but for most things this is not a problem.

Sometimes you don't get all the items. That's not the fault of the script; permissions are, by their nature, inconvenient.

string scriptName;
string objName;
list inventory;

LoadInventory() {
    string  text = objName + " is unpacking...\n";
    integer fails = 0;

    integer i;
    integer j = llGetInventoryNumber(INVENTORY_ALL);
    integer checkCount = (j/10) + 1;
    for (i = 0; i < j; i++) {
        string name = llGetInventoryName(INVENTORY_ALL, i);
        if (name!=scriptName) {
            if(llGetInventoryPermMask(name, MASK_NEXT) & PERM_COPY) {
               inventory += name;
            } else {
                fails++;
                llOwnerSay("Cannot give asset \""+name+"\", owner lacks copy permission");
            }
        }
        if (i==0 || (i % checkCount)==0) {
            integer pct = (integer)( ((i + 1.0) / j) * 100 );
            llSetText(text + (string)pct + "%", <1, 1, 1>, 1.0);
        }
    }
    if (fails==0) {
        llSetText(objName + " Unpacking Complete\n"
            +"All " + (string)(j - 1) + " sent.", <0, 1, 0>, 1.0);
    } else {
        llSetText(objName + " Unpacking Complete\n"
            + (string)(j - 1 - fails) + " of " + (string)(j - 1) + " sent.",
            <1, 0, 0>, 1.0);
    }
}

default {
    state_entry() {
        scriptName = llGetScriptName();
        objName = llGetObjectName();
        LoadInventory();

        llGiveInventoryList(llGetOwner(), objName, inventory);
        llRemoveInventory(scriptName);
    }
}
ScriptLibrary | Dolyn's Scripts


LibraryInventoryMenuModule

Table of Contents
  • About
  • Formating
    • Request
      • Mask Formating
    • Return
  • Code
    • Example
    • Core Tansport Functions
      • dump
      • parse
      • inventory_menu
    • Inventory Menu Module
  • Links

About

The module was developed to quickly allow for added inventory functionality. This module takes care of the dirty handling of the dialog boxes without requiring the scripter to do more then interpret the returned data.

Formating

The request and return data are specialy formated. There are two functions used to send and interpret the data dump and parse.

Request
integer InventoryMenuComm;
inventory_menu(key user, string message, float timeout, integer type,
        integer pos, integer mask, integer return_channel, list answer) {
    llMessageLinked(llGetLinkNumber(), InventoryMenuComm, dump([return_channel] + answer, "|"),
        dump([user, message, timeout, type, pos, mask], "|"));
}

Variable | Type | Description
InventoryMenuComm | integer | Channel that this module listens on, usualy a global variable
return_channel | integer | Channel that the return will come back on.
answer | list | This will be returned in the string paramater, when/if results come back
user | key | User to display the dialog box for
message | string | Message to prepend into the dialog boxes
timeout | float | Length of time the dialog box has to live before the script removes all information about the box
type | integer | Any INVENTORY_* flag, used to set which type of inventory the user is to pick
pos | integer | Given an inventory number will make the first dialog box show this inventory item
mask | float | Configuration mask for error handling and setup
ToP

Bit | Mask Bits | Return | Description
0 | 0x001 | -1 | No inventory of specified type
1 | 0x002 | -2 | Target agent not found
2 | 0x004 | -3 | Target agent has left the sim
3 | 0x008 | -4 | Dialog box times out
4 | 0x010 | -5 | Queue being cleared
5 | 0x020 | -6 | Bad return channel
8 | 0x100 | -5 via bit 4 | Clears the queue then creates the dialog box
9 | 0x200 | -5 via bit 4 | Same as bit 4 but only for boxes for the current user
ToP
Return
llMessageLinked(
    link,
    retchan,
    dump(answer,"|"),
    dump([value, name, type, type_count, user_key, user_name, time_left], "|")
);
ToP

Code

Core Tansport Functions
Paste these directly into your code for quick setup.
dump | Used to take a list and convert it to a string, the seperator used is unique to the data (if possible)
parse | Parses the string from dump and quickdump back into a list
inventory_menu | Generate a valid session request

string dump(list a, string b) {
    string c = (string)a;
    if(1+llSubStringIndex(c,b) || llStringLength(b)!=1)
    {
        b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ";
        integer d = -llStringLength(b);
        while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
            ++d;
        b = llGetSubString(b,d,d);
    }
    return b + llDumpList2String(a, b);
}

list parse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [b],[]);
}

inventory_menu(key user, string message, float timeout, integer type,
        integer pos, integer mask, integer return_channel, list answer) {
    llMessageLinked(llGetLinkNumber(), InventoryMenuComm, dump([return_channel] + answer, "|"),
        dump([user, message, timeout, type, pos, mask], "|"));
}

integer InventoryMenuComm = 13;
ToP

Example
string dump(list a, string b) {
    string c = (string)a;
    if(1+llSubStringIndex(c,b) || llStringLength(b)!=1)
    {
        b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ";
        integer d = -llStringLength(b);
        while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
            ++d;
        b = llGetSubString(b,d,d);
    }
    return b + llDumpList2String(a, b);
}

list parse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [b],[]);
}

inventory_menu(key user, string message, float timeout, integer type,
        integer pos, integer mask, integer return_channel, list answer) {
    llMessageLinked(llGetLinkNumber(), InventoryMenuComm, dump([return_channel] + answer, "|"),
        dump([user, message, timeout, type, pos, mask], "|"));
}

integer InventoryMenuComm = 13;

default
{
    touch_start(integer a)
    {
        inventory_menu(llDetectedKey(0), "Choose something for me to give you:", 300, INVENTORY_ALL, 0, 0xFF, 1, []);
        llSay(0,"Choose something for me to give you");
    }
    link_message(integer a, integer b, string c, key d)
    {
        if(b == 1)
        {
            list e = parse(d);
            integer num = llList2Integer(d,0);
            if(num >= 0)
            {
                string name = llList2String(e,1);
                if(llGetInventoryPermMask(name, MASK_OWNER)) //change to use llGetInventoryType when added
                    llGiveInventory(name);
                else
                    llSay(0,"Object doesn't exist");
            }
            else
            {//an error has occured
                if(num == -1)
                    llSay(0,"No inventory to give of specified type");
                else if(num == -2)
                    llSay(0,"Agent could not be found in the sim");
                else if(num == -3)
                    llSay(0,"Agent has left the sim");
                else if(num == -4)
                    llSay(0,"Request timed out");
                else if(num == -5)
                    llSay(0,"Request removed, queue cleared");
            }
        }
    }
}
ToP

Inventory Menu Module
//////////////////////////////////////////////////////////////////////////////////////
//
//    Inventory Menu Module
//    Version 0.6 Release
//    Copyright (C) 2004-2005  Strife Onizuka
//    http://home.comcast.net/~mailerdaemon/
//    http://secondlife.com/badgeo/wakka.php?wakka=LibraryInventoryMenuModule
//
//    This library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Lesser General Public License
//    as published by the Free Software Foundation; either
//    version 2.1 of the License, or (at your option) any later version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with this library; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//////////////////////////////////////////////////////////////////////////////////////
//{ Defaults: Change these if you want

//com is the second integer returned from link_message that it must match
integer InventoryMenuComm = 13;

//defaulttimeout is value used when a dialog box time to live is below the min time
//(mintime is hardcoded to 5 seconds)
integer defaulttimeout = 120;

//how often to check the for timeouts.
float ticksize = 5.0;

//number of buttons in a dialog box, unlikely to ever change but here anyway.
integer Buttons = 12;
//}
//////////////////////////////////////////////////////////////////////////////////////
//Don't change anything else unless you *really* need to.

//{mask values
//////////////////////////////////////////////////////////////////////////////////////
//
//0    0x001    -1              No inventory of specified type
//1    0x002    -2              Target agent not found
//2    0x004    -3              Target agent has left the sim
//3    0x008    -4              Dialog box times out
//4    0x010    -5              Queue being cleared
//5    0x020    -6              Bad return channel
//8    0x100    -5 via bit 4    Clears the queue then creates the dialog box
//9    0x200    -5 via bit 4    Same as bit 4 but only for boxes for the current user
//
//////////////////////////////////////////////////////////////////////////////////////
//}
//{global variables
list chans;     //listen channel
list handles;   //listen handle
list times;     //ttl

list masks;     //error/debug mask
list prims;     //prim to message
list intchans;  //integer channel to message on

list users;     //user keys
list answers;   //return list predefined
list types;     //inventory flag.

list messages;  //dialog box text
list buttons;   //buttons
list names;     //name of selected values
list values;    //inventory number

integer ticks;
//}
//{ msging
msglink(integer link, integer chan, string ans, list ret){
    if(chan != InventoryMenuComm)
        llMessageLinked(link, chan, ans, dump(ret, "|"));
}

inventory_menu(key user, string message, float timeout, integer type,
        integer pos, integer mask, integer retchan, list ret) {
    llMessageLinked(llGetLinkNumber(), InventoryMenuComm, dump([retchan] + ret, "|"),
        dump([user, message, timeout, type, pos, mask], "|"));
}
//}
//{ parse, dupm and quickdump
string quickdump(list a, string b){  return b + llDumpList2String(a, b); }

string dump(list a, string b) {
    string c = (string)a;
    if(1+llSubStringIndex(c,b) || llStringLength(b)!=1)
    {
        b += "|\\/?!@#$%^&*()_=:;~{}[],\n\" qQxXzZ";
        integer d = -llStringLength(b);
        while(1+llSubStringIndex(c,llGetSubString(b,d,d)) && d)
            d++;
        b = llGetSubString(b,d,d);
    }
    return b + llDumpList2String(a, b);
}

list parse(string a) {
    string b = llGetSubString(a,0,0);//save memory
    return llParseStringKeepNulls(llDeleteSubString(a,0,0), [b],[]);
}
//}
//{list cleanup
clear() {
    integer a = llGetListLength(handles);
    while(a--)
    {
        llListenRemove(llList2Integer(handles,a));
        if(llList2Integer(masks,a)&0x010)
        {
            integer b = llList2Integer(types,a);
            msglink(llList2Integer(prims,a), llList2Integer(intchans,a),
                    llList2String(answers,a), [-5, "", b, llGetInventoryNumber(b),
                    llList2Key(users,a), "", -1]);
        }
    }
    chans = handles = users = masks = intchans = prims = answers =
            types = messages = buttons = names = values = times = [];
    ticks = 0;
    llSetTimerEvent(0);
}

cleanup() {
    if(times == [])
    {
        chans = handles = users = masks = intchans = prims = answers =
                types = messages = buttons = names = values = times;
        llSetTimerEvent(0);
        ticks = 0;
    }
}

remove(integer a) {
    llListenRemove(llList2Integer(handles,  a));
    handles     = llDeleteSubList(handles,  a,a);
    times       = llDeleteSubList(times,    a,a);
    chans       = llDeleteSubList(chans,    a,a);
    users       = llDeleteSubList(users,    a,a);
    answers     = llDeleteSubList(answers,  a,a);
    prims       = llDeleteSubList(prims,    a,a);
    masks       = llDeleteSubList(masks,    a,a);
    intchans    = llDeleteSubList(intchans, a,a);
    types       = llDeleteSubList(types,    a,a);
    messages    = llDeleteSubList(messages, a,a);
    buttons     = llDeleteSubList(buttons,  a,a);
    names       = llDeleteSubList(names,    a,a);
    values      = llDeleteSubList(values,   a,a);
}
//}

generate(integer a, integer pos) {
    integer curType = llList2Integer(types, a);
    integer assets = llGetInventoryNumber(curType);
    if(assets == 1) {
        msglink(llList2Integer(prims,a), llList2Integer(intchans,a),
            llList2String(answers,a), [0, llGetInventoryName(curType,0), curType,
            assets, llList2Key(users,a), "",
            (llList2Float(times,a) - ticks) * ticksize]);
        remove(a);
    }
    else if(assets > 1) {
        if(pos > assets)    pos = assets;
        //{variables
        list text;
        list buts;
        list nam;
        list val;
        integer b;
        string d;
        string e;
        integer h;
        //}
        integer entries = Buttons;
        integer offset = (Buttons - 2) * ((pos - 1) / (Buttons - 2));
        if(assets <= Buttons)
            offset = 0;
        else if (offset && assets > Buttons) {
            offset++;
            val     += "";
            nam     += "-";
            buts    += "<Prev>";
            --entries;
        }
        if(assets - offset > entries)
            --entries;
        for(;(h=(b+offset))<assets && b<entries;++b) {
            val     += h;
            nam     += (d = llGetInventoryName(curType, h));
            text    += (e = (llGetSubString("ABCDEFGHJKLMN",b,b) + ": " + d));
            buts    += llGetSubString(e,0,15);
        }
        if(entries + (offset!=0) + 1 == Buttons) {
            val     += "";
            nam     += "+";
            buts    += "<Next>";
        }

        integer cat     =    -255 - (integer)llFrand(0x7FFFFF00);
        while(1+llListFindList(chans,[cat]))
            --cat;
        llListenRemove(llList2Integer(handles, a));
        key user = llList2Key(users,a);
        values  = llListReplaceList(values, [dump(val,"^")],a,a);
        names   = llListReplaceList(names,  [dump(nam,"^")],a,a);
        buttons = llListReplaceList(buttons,[dump(buts,"^")],a,a);
        chans   = llListReplaceList(chans,  [cat], a,a);
        handles = llListReplaceList(handles,[llListen(cat,"",user,"")], a,a);

        llDialog(user, llList2String(messages,a)+dump(text, "\n"),
                    buts, llList2Integer(chans,a));
    }
    else if(llList2Integer(masks,a) & 0x001) {
        msglink(llList2Integer(prims,a), llList2Integer(intchans,a),
            llList2String(answers,a),
            [-1, "", curType, assets, llList2Key(users,a), "", -1]);
        remove(a);
    }
}

default {
    state_entry() {
        llOwnerSay("Inventory Menu Module, Version 0.6, " +
            "Released Under the GNU Lesser General Public License");
        llOwnerSay("Copyright (C) 2004-2005 Strife Onizuka");
        if(ticksize < 2.5)
            ticksize = 2.5;
    }
    on_rez(integer a) {
        clear();
    }
    link_message(integer a, integer b, string c, key d) {
        if(b==InventoryMenuComm)
        {
            b = llSubStringIndex(llDeleteSubString(c,0,0), llGetSubString(c,0,0));
            list    e = parse(d);        //user, message, timeout, type, pos, mask
            key     user    =    llList2String(e,0);
            integer type    =    (integer)llList2String(e,3);
            integer mask    =    (integer)llList2String(e,5);
            integer chan    =    (integer)llDeleteSubString(c, b + 1, 0);
            if(user == "" || user == NULL_KEY) user = llGetOwner(); //lazy check

            //{cleanup
            if(mask & 0x100)
                clear();
            else if(mask & 0x200)
            {//clean out other box's that went to this user.
                while(1 + (b = llListFindList(users, [user])))
                {
                    if(llList2Integer(masks,b)&0x010)
                    {
                        integer f = llList2Integer(types, b);
                        msglink(llList2Integer(prims,b), llList2Integer(intchans,b),
                            llList2String(answers,b), [-5, "", f,
                            llGetInventoryNumber(f), llList2Key(users,b), "", -1]);
                    }
                    remove(b);
                }
            }
            cleanup();
            //}

            //{Fatal Errors
            if(!llGetInventoryNumber(type))
            {
                if(mask & 0x001)
                    msglink(a, chan, llDeleteSubString(c,0,b),
                            [-1, "", type, llGetInventoryNumber(type), user, "", -1]);
                return;
            }
            else if(llGetAgentSize(user) == ZERO_VECTOR)
            {// target not in the sim
                if(mask & 0x002)
                    msglink(a, chan, llDeleteSubString(c,0,b),
                            [-2, "", type, llGetInventoryNumber(type), user, "", -1]);
                return;
            }//}
            else
            {//all fatal errors handled.
                integer h       =    (integer)llFrand(0x7FFFFF00);
                float   timeout =    (float)llList2String(e,2);

                if(timeout < 5.0 || timeout > 7200)
                    timeout = defaulttimeout;
                while(1+llListFindList(handles,[h]))
                    --h;

                chans       +=  0;
                handles     +=  h;
                times       +=  (ticks + (timeout / ticksize));

                masks       +=  mask;
                prims       +=  a;
                intchans    +=  chan;

                users       +=  user;
                answers     +=  llDeleteSubString(c,0,b);
                types       +=  type;

                messages    +=  llList2String(e,1);
                buttons     +=  "";
                names       +=  "";
                values      +=  "";
                llSetTimerEvent(ticksize);
                generate(-1, (integer)llList2String(e,4));
            }
        }
    }
    listen(integer a, string b, key c, string d) {
        a=llListFindList(chans,[a]);
        if(a+1 && llList2Key(users,a) == c)
        {//it's one of our listens
            integer f = llListFindList(parse(llList2String(buttons,a)),[d]);
            if(f+1)
            {//we matched a button
                string g = llList2String(parse(llList2String(values, a)),f);
                string h = llList2String(parse(llList2String(names, a)),f);
                if(g!="")
                {
                    integer e = llList2Integer(types,a);
                    msglink(llList2Integer(prims,a), llList2Integer(intchans,a),
                            llList2String(answers,a), [g, h, e,
                            llGetInventoryNumber(e), c, b,
                            (llList2Float(times,a) - ticks) * ticksize]);
                    remove(a);
                    cleanup();
                    return;
                }
                else if(h=="+")
                    generate(a,(integer)llList2String(parse(
                            llList2String(values, a)), f - 1) + Buttons - 2);
                else if(h=="-")
                    generate(a,(integer)llList2String(parse(
                            llList2String(values, a)), f + 1) - Buttons + 2);
                else
                    llOwnerSay("Catastrophic failure");
            }
        }
    }
    timer() {
        ++ticks;
        integer a;
        float c;
        key d;
        integer e;
        integer f;
        integer g;
        while(a<llGetListLength(times))
        {
            e = llList2Integer(masks,a);
            c = llList2Float(times,a);
            g = 0;
            if(llGetAgentSize(d = llList2Key(users,a)) == <0.0,0.0,0.0>)
            {
                g = 1;
                if(e&0x004)
                {
                    f = llList2Integer(types,a);
                    msglink(llList2Integer(prims,a), llList2Integer(intchans,a),
                            llList2String(answers,a), [-3, "", f,
                            llGetInventoryNumber(f),
                            d, "", (ticks - c) * ticksize]);
                    jump skip;
                }
            }
            if(c <= ticks)
            {
                g = 1;
                if(e&0x008)
                {
                    f = llList2Integer(types,a);
                    msglink(llList2Integer(prims,a), llList2Integer(intchans,a),
                            llList2String(answers,a), [-4, "", f,
                            llGetInventoryNumber(f),
                            d, "", (ticks - c) * ticksize]);
                }
            }
            @skip;
            if(g)
                remove(a);
            else
                ++a;
        }
        cleanup();
    }
}
ToP

Links

ScriptLibrary
LibraryDialogModule


LibraryLindenAIML

LindenAIML is inspired by Richard Wallace's AIML (artificial intelligence markup language). AIML utilizes the fact that humans, in conversation, often return to the same patterns of speech again and again, varying the words that determine the sentences reference, while keeping the mechanical structures largely similar. AIML replaces these variable portions of utterances with wildcards and attempts to match patterns in speech based on the invariant portions of utterances. For more information, read about ALICE.
True AIML is an XML compliant language, and resembles HTML more than anything else. LindenAIML, while not XML compliant at this writing, imports most of the more commonly used features of AIML while supplementing it with features created specifically for the Second Life platform.

The basic code for the LindenAIML interpreter is as follows:

//The following code was generated by Luciftias Neurocam and Azrael Baphomet.
//It is freely distributable under the terms of the GNU public license.
//Please report significant mods to either Azrael or Luciftias.  Thank you.

string gName="filename";
key gQueryID;
list AIMList;
list REPLYList;
string data;
string newmessage;
integer gLine;
integer handle;
integer touched=0;

integer myParse(string message)
{
     integer begin; //index of message string marking beginning of matched pattern
    integer begin2;
    integer begina; //as above
    integer chat_channel=0;
    integer check_line; //no longer implemented
    integer end; //length of matched pattern, so ending index actually begin+end-1
    integer end_tag;
    integer end2;
    integer end3;
    integer enda;
    integer error_cond=0; //use later
    integer i=0; //loop (line) number
    integer ind;
    integer ind2;
    integer j;
    integer lenaimlist; //length of AIMList
    integer lenclust;
    integer poundindex;
    integer reply_index=-1;
    integer s_ind;
    integer starindex; //beginning index of *ed expression in message
    list cl; //individual words in clusters parsed to individual list entries
    list cl_a;
    list cl_b;
    list cl2; //individual words in cluster2 blah blah blah
    list cluster_a;
    list cluster_b;
    list cluster2;//cluster of post * words
    list clusters; //first cluster of pre * words
    list headmsglist;
    list msglist; //string message as parsed to list, separated by " "
    list new_reply_list;
    list newmsglist;
    list poundlist;
    list prestarlist;
    list starlist;
    list startopoundlist; //grabs text between star and pound
    list tailmsglist;
    list temp_reply_list;
    list unparsed_clusters;
    string last_message; //not used yet
    string newmsg;
    string parsed_reply;
    string poundstring;
    string resp_line;
    string starstring; //string corresponding to *ed characters
    string string1;
    string tempmessage="";
    string unparsed_clusters_string;
    string unparsed_reply;

    //begin actual program
    integer punc_index=llStringLength(message);
    punc_index--;
    if(llGetSubString(message,punc_index,punc_index)=="." || llGetSubString(message,punc_index,punc_index)=="!" || llGetSubString(message,punc_index,punc_index)=="?")
    {
        message=llDeleteSubString(message,punc_index,punc_index);
    }
    lenaimlist=llGetListLength(AIMList);
    reply_index=-1;
    while(reply_index==-1 && i< lenaimlist)//for(i=0;i<lenaimlist;i++)
    {
        clusters=[];
        cluster2=[];
        cl=[];
        cl2=[];
        //override loop:  if this string occurs anywhere in input, ignore all other parsing and respond with template following override
        if(llSubStringIndex(llList2String(AIMList,i),"<override>")!=-1)
        {
            //strip out <override> and </override>

            ind=llSubStringIndex(llList2String(AIMList,i),"<override>");
            string1=llDeleteSubString(llList2String(AIMList,i),ind,ind+9);
            ind2=llSubStringIndex(string1,"</override>");
            string override_string=llDeleteSubString(string1,ind2,ind2+10);//this now represents string to match to message
            if(llSubStringIndex(message,override_string)!=-1)
            {

                reply_index=i;
            }
        }

        //find clusters/cluster2 in AIMList entry
        if(llSubStringIndex(llList2String(AIMList,i),"<pattern>")!=-1)
        {
            //strip out <pattern> and </pattern> from every line
            ind=llSubStringIndex(llList2String(AIMList,i),"<pattern>");
            string1=llDeleteSubString(llList2String(AIMList,i),ind,ind+8);
            ind2=llSubStringIndex(string1,"</pattern>");
            string unparsed_clusters_string=llDeleteSubString(string1,ind2,ind2+9);//this now represents string to match to message
            if(llSubStringIndex(unparsed_clusters_string,"*")!=-1)// && llSubStringIndex(unparsed_clusters_string,"#")==-1) //parse only lines using wildcard this way.  non-wildcard parse follows
            {
                unparsed_clusters=llParseString2List(unparsed_clusters_string,["*"],[]); //break into 2 clusters, before * and after *

                lenclust=llGetListLength(unparsed_clusters);
                //unparsed_clusters=llDeleteSubList(unparsed_clusters,lenclust,lenclust);//what does this do?
                clusters=llList2List(unparsed_clusters,0,0); //Before * cluster
                cluster2=llList2List(unparsed_clusters,1,1);//after * cluster
                if(llSubStringIndex(unparsed_clusters_string,"*")==0)
                {
                    cluster2=clusters;
                    clusters=[];
                }
                cl=llParseString2List( (string) clusters,[" "],[]); //breaks out individual words in clusters
                cl2=llParseString2List( (string) cluster2,[" "],[]); //breaks out individual words in cluster2
                msglist=llParseString2List(message,[" "],[]); //breaks up input message into list of individual words.
                if(cl2==[])
                    cl2=["asdfasfads"]; //insert unmatcheable value into cl2
                if(cl==[])
                    cl=["hgsjgh"];

                //Case of cluster *
                if(llListFindList(msglist,cl)!=-1 && llListFindList(msglist,cl2)==-1)
                {

                    begin =llListFindList(msglist,cl); //find cl in msglist
                    end=llGetListLength(cl);
                    end--;
                    tailmsglist=llDeleteSubList(msglist,begin,begin+end); //return only * and post * words

                    starindex=llListFindList(msglist,tailmsglist);  //locates *
                    end2=llGetListLength(msglist);
                    end2--;
                    //if(end>=0 && begin>=0 && starindex >=0 && end2>=0) //so no negative indexes
                    //    newmsg=llDumpList2String(llListReplaceList(msglist,["*"],starindex,starindex+end2)," ");  //replaces phrase with * in message
                    //what to replace * with in reply
                    starlist=llList2List(msglist,starindex,starindex+end2);

                    //check similarity of matched pattern with message string
                    if(llListReplaceList(msglist,["*"],starindex,starindex+end2)==cl+"*")
                    {
                        reply_index=i;
                        starstring=llDumpList2String(starlist," ");
                    }
                }

                //Case of * cluster2
                if(llListFindList(msglist,cl)==-1 && llListFindList(msglist,cl2)!=-1)
                {
                    begin=llListFindList(msglist,cl2);
                    end=llGetListLength(cl2);
                    end--;
                    headmsglist=llDeleteSubList(msglist,begin, begin+end);
                    starindex=llListFindList(msglist,headmsglist);
                    end2=llGetListLength(headmsglist);
                    end2--;
                    //if(end>=0 && begin >=0 && starindex>=0 && end2>=0)
                    //    new
                    starlist=llList2List(msglist, starindex, starindex+end2);
                    starstring=llDumpList2String(starlist," ");
                    if(llListReplaceList(msglist,["*"],starindex,starindex+end2)=="*"+cl2)
                    {
                        reply_index=i;
                        starstring=llDumpList2String(starlist," ");

                    }
                }

                //Case of clusters * cluster2
                if(llListFindList(msglist,cl)!=-1 && llListFindList(msglist,cl2)!=-1)
                {
                    //first delete clusters from message

                    begin=llListFindList(msglist, cl);
                    end=llGetListLength(cl);
                    end--;
                    headmsglist=llDeleteSubList(msglist,begin,begin+end);

                    //then delete cluster2 from tailmsglist
                    begina =llListFindList(headmsglist, cl2);
                    enda=llGetListLength(cl2);
                    enda--;
                    starlist=llDeleteSubList(headmsglist,begina,begina+enda);
                    starindex=llListFindList(msglist,starlist);
                    end2=llGetListLength(starlist);
                    end2--;
                    // starstring=llDumpList2String(starlist," ");
                    if(llListReplaceList(msglist,["*"],starindex,starindex+end2)==cl+"*"+cl2 )
                    {
                        reply_index=i;
                        starstring=llDumpList2String(starlist," ");
                    }
                }
                //case of cluster * cluster_a # cluster_b
                if(llSubStringIndex(unparsed_clusters_string,"#")!=-1)
                {
                    ////first delete clusters from message

                    begin=llListFindList(msglist, cl);
                    end=llGetListLength(cl);
                    end--;
                    headmsglist=llDeleteSubList(msglist,begin,begin+end);
                    prestarlist=llList2List(msglist,begin,begin+end);
                    //parse cluster2 into cluster_a and cluster_b
                    startopoundlist=llParseString2List((string) cluster2, ["#"],[]);
                    cluster_a=llList2List(startopoundlist,0,0);
                    list cl_a=llParseString2List( (string) cluster_a,[" "],[]);
                    //then delete cl_a from tailmsglist

                    begina =llListFindList(headmsglist, cl_a);
                    enda=llGetListLength(cl_a);
                    enda--;

                     //replace starlist in msglist first;
                     integer endb=llGetListLength(headmsglist);
                     starlist=llDeleteSubList(headmsglist,begina,endb--);
                     starindex=llListFindList(msglist,starlist);
                     end2=llGetListLength(starlist);
                    end2--;
                    //newmsglist=llListReplaceList(tailmsglist,["*"],starindex,starindex+end2);
                     if(llGetListLength(startopoundlist)>1)
                    {
                        cluster_b=llList2List(startopoundlist,1,1);
                        cl_b=llParseString2List( (string) cluster_b,[" "],[]);
                    //find index of cl_b in msglist
                    integer clbstart=llListFindList(headmsglist,llParseString2List((string) llList2List(startopoundlist,1,1),[" "],[]));                    //delete all but this
                    integer lenmsglist=llGetListLength(headmsglist);
                    poundlist=llDeleteSubList(headmsglist,clbstart,lenmsglist--);
                    enda=llGetListLength(llParseString2List( (string) llList2List(startopoundlist,0,0),[" "],[]));
                    poundlist=llDeleteSubList(poundlist,0,enda--) ;
                    }

                    //integer endb=llGetListLength(headmsglist);
                    // starlist=llDeleteSubList(headmsglist,begina,endb--);

                    starindex=llListFindList(msglist,starlist);

                    //
                    integer starlength=llGetListLength(starlist);
                    starlength--;
                    newmsglist=llListReplaceList(msglist,["*"],starindex,starindex+starlength--);
                    integer poundindex=llListFindList(newmsglist,poundlist);
                    integer poundlength=llGetListLength(poundlist);
                    poundlength--;
                    newmsglist=llListReplaceList(newmsglist,["#"],poundindex,poundindex+poundlength--);
                    if(newmsglist==cl+["*"]+cl_a+["#"]+cl_b)
                    {
                        reply_index=i;
                        starstring=llDumpList2String(starlist," ");
                        poundstring=llDumpList2String(poundlist," ");
                    }

                }
            }
            else
            {
                if(unparsed_clusters_string==message)
                    reply_index=i;
            }
        }

        i++;

    }

    if(reply_index!=-1) //if a match exists
    {
        if(llSubStringIndex(llList2String(AIMList,reply_index+1),"<channel>")!=-1)
        {
            ind=llSubStringIndex(llList2String(AIMList,reply_index+1),"<channel>");
            string1=llDeleteSubString(llList2String(AIMList,reply_index+1),ind,ind+8);
            ind2=llSubStringIndex(string1,"</channel>");
            string chat_channel_string=llDeleteSubString(string1,ind2,ind2+9);
            chat_channel=(integer) chat_channel_string;

        }
        unparsed_reply=llList2String(REPLYList, reply_index);
        end_tag=llSubStringIndex(unparsed_reply,"</template>");
        end_tag--;
        parsed_reply=llGetSubString(unparsed_reply,10,end_tag--); //from end of <template> to </template>
        if(llSubStringIndex(parsed_reply, "<star/>")==-1)
        {
            if(llSubStringIndex(parsed_reply,"<srai")==-1)
            {
                llSay(chat_channel,parsed_reply);

                reply_index=0;
            }
            else
            {
                error_cond=-1;
                integer lenp=llStringLength(parsed_reply);
                integer termin=lenp--;
                termin--;termin--;termin--;termin--;termin--;termin--;termin--;termin--;
                // integer starindex2=llSubStringIndex(parsed_reply,"<star/>");
                // starindex2--;
                //if(starindex2!=-1)
                //{
                //    parsed_reply=llGetSubString(parsed_reply,6,starindex) +" " +starstring;
                //}
                //else
                //{

                newmessage=llGetSubString(parsed_reply,6,termin);
                // }
            }
        }
        else // if <star/> expression exists, insert antecedant
        {
            //if <pound/> expression exists
            if(llSubStringIndex(parsed_reply,"<poun")!=-1)
            {
                temp_reply_list=llParseString2List(parsed_reply,[" "],[]);
                s_ind=llListFindList(temp_reply_list,["<pound/>"]);
                new_reply_list=llListReplaceList(temp_reply_list,[poundstring],s_ind,s_ind);
                parsed_reply=llDumpList2String(new_reply_list," ");
                llSay(0,(string)new_reply_list);

            }
            //else new_reply_list=llParseString2List(parsed_reply,[" "],[]);
            if(llSubStringIndex(parsed_reply,"<srai")==-1)
            {
                temp_reply_list=llParseString2List(parsed_reply,[" "],[]);
                integer s_ind2=llListFindList(temp_reply_list,["<star/>"]);
                temp_reply_list=llListReplaceList(temp_reply_list,[starstring],s_ind2,s_ind2);

                parsed_reply=llDumpList2String(temp_reply_list," ");
                llSay(chat_channel,parsed_reply);

                reply_index=0;
            }
            else
            {
                //strip out <srai> tags
                integer srai_ind1=llSubStringIndex(parsed_reply,"<srai>");
                integer srai_ind2=llSubStringIndex(parsed_reply,"</srai>");
                //parsed_reply=llDeleteSubString(parsed_reply,srai_ind1,srai_ind1+5);

                parsed_reply=llGetSubString(parsed_reply,srai_ind1+6,srai_ind2-=1);
                //if pound expression exists
                if(llSubStringIndex(parsed_reply,"<poun")!=-1)
                {
                    error_cond=-1;
                    integer lenp=llStringLength(parsed_reply);
                    integer termin=lenp--;
                    integer poundindex2=llSubStringIndex(parsed_reply,"<pound/>");

                    poundindex2--;
                    if(poundindex2!=-1)
                    {
                        parsed_reply=llGetSubString(parsed_reply,6,poundindex2) +poundstring;
                        parsed_reply=llDeleteSubString(parsed_reply,poundindex2,poundindex2+8);
                        parsed_reply=llInsertString(parsed_reply,poundindex,poundstring);
                        //use llDeleteSubString followed by llInsertString
                    }
                    else
                    {
                        parsed_reply=llGetSubString(parsed_reply,6,termin);

                    }

                }
                error_cond=-1;

                integer lenp=llStringLength(parsed_reply);
                integer termin=lenp--;
                integer starindex2=llSubStringIndex(parsed_reply,"<star/>");
                starindex2--;
                if(starindex2!=-1)
                {
                   parsed_reply=llGetSubString(parsed_reply,6,starindex2) +starstring;
                   parsed_reply=llDeleteSubString(parsed_reply,starindex2,starindex2+8);
                   parsed_reply=llInsertString(parsed_reply,starindex,starstring);
                        //use llDeleteSubString followed by llInsertString

                }
                else
                {
                    newmessage=llGetSubString(parsed_reply,6,termin);

                }

            }

        }
    }
    else
    {
        llSay(0,"I'm afraid I don't understand what you said...yet");
        //email message to some address dedicated to receiving unmatched patterns.  Uncomment line after inserting correct address
      //llEmail(address@whatever.com, "LindenAIML: New Template Needed", "no match for: " +message); // send email to self
    }
    return error_cond;
}

default
{
    state_entry()
    {
        llSetText("LindenAIML Concierge", <1,1,1>, 1.0);
        gQueryID=llGetNotecardLine(gName,gLine);//request first line
        gLine++; //increase line count
    }

    dataserver(key query_id,string data)
    {
        if(query_id==gQueryID)
        {
            if(data!=EOF)
            {
                if( llGetSubString(data,0,3)=="<pat" || llGetSubString(data,0,3)=="<ove" || llGetSubString(data,0,3)=="<cha")
                {
                    if(gLine==0) //for now ignore all but pattern or template lines
                    {
                        AIMList=(list) [data];
                    }
                    else
                    {
                        AIMList=AIMList + [data];
                    }
                }
                if(llGetSubString(data,0,3)=="<tem"  || llGetSubString(data,0,3)=="<cha"  )
                {
                    if(gLine==0) //for now ignore all but pattern or template lines
                    {
                        REPLYList=(list) [data];
                    }
                    else
                    {
                        REPLYList=REPLYList + [data];

                    }

                }
            }
            gQueryID=llGetNotecardLine(gName,gLine); //request next line
            gLine++;
        }
    }

    touch_start(integer total_number)
    {

        if(touched==0)
        {
            handle=llListen(0,"",llGetOwner(),"");
            llSay(0,"AIML on");
            llGiveInventory(llDetectedKey(0), "LindenAIML");
            llGiveInventory(llDetectedKey(0), "LAIMLDocs");
            touched++;
        }
        else
        {
            llListenRemove(handle);
            llSay(0,"AIML off");
            touched=0;
        }

    }

    listen(integer channel, string name, key id, string msg)
    {

        integer error_cond=myParse(msg);
        if(error_cond==-1)
        {
            //message=newmessage;

            myParse(newmessage);
        }
    }
}

In addition to the LindenAIML interpreter code above, one also needs a ?dialogue template? written in
LindenAIML designed to coordinate responses to queries made on Second Life chat.
The text below is intended to be incorporated as a notecard in an object containing the interpreter. This file, referred to as "filename" in the above code, contains the AIML tagged text to be used as a script for the interpreter. The syntax of LindenAIML is strict, as will be outlined below.
<category>
<pattern>How are you *</pattern>
<template><star/> is fine</template>
</category>
<category>
<pattern>Are you well * bot</pattern>
<template>This <star/> bot is great</template>
</category>
<category>
<pattern>Who is *</pattern>
<template>I don't know, who is <star/></template>
</category>
<category>
<override>fish</override>
<template>I hate fish</template>
</category>
<category>
<pattern>Are there too many * in this sim</pattern>
<template>I don't know much about <star/></template>
</category>
<category>
<pattern>Are you OK * bot</pattern>
<template><srai>Are you well <star/> bot</srai></template>
</category>
<category>
<pattern>Reply on channel 1</pattern>
<template>OK</template>
<channel>1</channel>
</category>
<pattern>The * in spain falls mainly # the plain</pattern>
<template>That confounded <star/> falls mainly <pound/> that blasted plain</template>
</category>

A brief explication of the tags. As of this writing, the <category> tag is not used. In the future it will contain flags about topic and such like. But right now it's simply included to keep the LindenAIML file looking somewhat familiar to AIML users.

The <pattern> tags contain patterns to match user utterances.
The <template> tags contain hypothetical replies to the patterns.
The <channel> tag (not included in every entry contained in <category></category> pair) allows the user to determin what channel the reply will occur on. So the bot can issue commands to other devices listening on those channels.

<override> represents a special case of <pattern>. In the example, if the user says anything that contains the word "fish", the reply will be that in the <template></template> tags immediately following the <override></override> pair.

<srai></srai> is the "syntactic reduction" tagpair. If a pattern produces a template with syntactic reduction tags enclosing the response, that responses is fed to the algorithm as a new user message to parse. The upshot of this: Patterns followed by <srai> tags are regarded as equivalent to the text contained in the <srai> tagpair, which should be defined elsewhere in the file (and by convention, previously in the file). Too many of these can slow operation considerably. Be warned.

the <star/> tag is an isolated tag and represents the insertion point for phrases denoted by the wildcard "*" in the pattern to match. So If I say: "I hate you bot", and the pattern to match is "I * you bot", the template reply might be "Well, I <star/> you too.", resulting in a reply: "Well, I hate you too."

A second wildcard has been implemented, using the ?#? character. As with ?*?, the ?#? character will commonly replace the <pound/> tag in the reply string. As of this writing
the second wildcard only processes patterns of the format "string1 * string2 # string3". Updates to LindenAIML will also allow the processing of patterns such as "* string #".

Beware punctuation following <star/> or <pound/> tags. I have not, as yet, integrated that kind of punctuation into the parsing procedures. However, end of utterance punctuation is stripped from the user message at this point, so it is not necessary to include punctuation at the end of patterns.

It is important to note that LindenAIML is sensitive to the format of the AIML file. No extra spaces between tags, and line-format should be as above. For example, if you put a space between the <pattern> tag and the first word of the actual pattern, you run the risk of confusing the interpreter. Also, tags must be lower case at this point. All tagsets except the <category></category> must be on the same line of text. In future versions, we hope to make LindenAIML more flexible sytlistically.

-Azrael Baphomet and Luciftias Neurocam


LibraryPathfinder

Pathfinder beta v0.9

IMPORTANT: This script is still under development. It has been tested, and worked to find a path from plum to darkwood, but that's the only testing that's been done since it started working. I will remove this note once more testing is completed. In theory, it should all work. :)

This script takes a destination sim and outputs a path to get from the starting sim to the target sim. You can also specify specific sims to avoid, such as if you find that it's down when you get there.

The script uses a database for storing the locations of the sims. I use a perl script (here) to convert a map from SLMaps.com. The output from the current map (2005-03-10) is here.

Script Interaction

All comunication with this script is via llMessageLinked(integer linknum, integer num, string str, key id).
All coordinates sent to this script should follow the coordinate system on SLMaps.com.

To start a search send 142 as num, the destination in str as "xTarget, yTarget". If you want the script to avoid any locations, send them in id as (key)"xAvoid1, yAvoid1, xAvoid2, yAvoid2, etc...". If you send in uneven number of CSVs, the script just ignores them all.

If a search is requested while one is already in progress, pathfinder will message it back to whatever link sent the request, using 100 for num. You could either have the controlling script recheck every few seconds, if you want to let the current search complete, or you could just reset the script using llResetOtherScript.

When completed, pathfinder will message the results with 115 for num, and the path will be a set of steps in a strided CSV list. So for example, if you're trying to get from Kissling (999, 1003) to Darkwood (996, 1003) the output would be "0, -1, -1, 0, -1, 0, -1, 0, 0, 1". I'll post a better explination and hopefully a script to actually move using this when I get a chance (hopefully soon).

The Pathfinder script

//Pathfinder script
//Created by Psyfire Tigereye
//Beta version 0.9

//For more info on pathfinding, check out
// http://www.policyalmanac.org/games/aStarTutorial.htm

////// A note about nodes //////
//A node is a rotation with the following format: <x, y, g, p>
//x is the x coordinate of the node, y is the y coordinate.
//x and y both use llGetRegionCorner() / 256 values.
//g is the G cost of the node, i.e. how many steps it took to get there from startNode.
//p is the parent node. It is the index in closedNodes that it's parent is located at.
//A p value of -1 is used for the first node, to indicate that it's where you started.

//Customization
//The name of the note card w/ the sim data.
string simDataCard = "simData";

//The com channel of the link message
integer searchStartMsg = 142;
integer readyMsg = 143;
integer badPath = 144;
integer searchDone= 145;

//Global variables
list openNodes;
list closedNodes;
key mapSizeReq;
key simDataReq;
string simData;
integer nextLine;
vector dest;
integer reqSource;
integer currentNodeIndex;
integer xStart;
integer yStart;
integer xMax;
integer yMax;
integer yStep;

//Functions

//Returns the H cost (estimated distance to the target).
integer hCost(rotation testNode) {
    integer retv = llAbs((integer)testNode.x - (integer)dest.x)
        + llAbs((integer)testNode.y - (integer)dest.y);
    return retv;
}

//Finds the node on the open list with the lowest F cost (distance traveled + H)
integer lowestF() {
    integer listLength = llGetListLength(openNodes);
    rotation testNode = llList2Rot(openNodes, 0);
    integer i; integer lowest = (integer)testNode.z + hCost(testNode);
    integer retv = 0; integer testF;
    for (i = 1; i < listLength; ++i) {
        testNode = llList2Rot(openNodes, i);
        testF = (integer)testNode.z + hCost(testNode);
        if (testF <= lowest) {
            lowest = testF;
            retv = i;
        }
    }
    return retv;
}

//Checks if there's a node at the specific x and y coordinates.
integer isSim(float x, float y) {
    if ((x < xStart || x > xMax) || (y < yStart || y > yStart + yStep)) {
        return FALSE;
    }
    x -= xStart; y -= yStart;
    integer index = (integer)(y * yStep + x);
    //Entry will be 1 if it's a sim, or 0 if not, so this give TRUE or FALSE.
    return (integer)llGetSubString(simData, index, index);
}

//What to do when there's no way to the target.
noPath(integer why) {
    list reasons = ["Bad target", "Bad start", "No path"];
    llMessageLinked(reqSource, badPath, llList2String(reasons, why), NULL_KEY);
}

//Returns a list of the sims N, S, E, and W of the sim at x, y.
list borderLands(float x, float y) {
    list borders = [x, y - 1.0];
    borders += [x, y + 1.0];
    borders += [x - 1.0, y];
    borders += [x + 1.0, y];
    integer i;
    list retList;
    for (i = 0; i < 8; i += 2) {
        if (isSim(llList2Integer(borders, i), llList2Integer(borders, i + 1))) {
            retList += llList2List(borders, i, i + 1);
        }
    }
    return retList;
}

//Returns a value based on if the sim is on a list already or not.
integer checkNode(integer x, integer y) {
    if (x == dest.x && y == dest.y) {
        return -3;
    }
    integer i;
    rotation tempNode;
    integer len = llGetListLength(closedNodes);
    for (i = 0; i < len; ++i) {
        tempNode = llList2Rot(closedNodes, i);
        if (x == tempNode.x && y == tempNode.y) {
            return -1;
        }
    }
    len = llGetListLength(openNodes);
    for (i = 0; i < len; ++i) {
        tempNode = llList2Rot(closedNodes, i);
        if (x == tempNode.x && y == tempNode.y) {
            return i;
        }
    }
    return -2;
}

//The actual search process.
search() {
    integer found = FALSE;
    integer lowestOpen;
    rotation currentNode;
    rotation tempNode;
    list neighbors;
    integer status;
    integer len; integer i;
    integer newX; integer newY;
    while (!found) {
        lowestOpen = lowestF();
        currentNode = llList2Rot(openNodes, lowestOpen);
        closedNodes += currentNode;
        openNodes = llDeleteSubList(openNodes, lowestOpen, lowestOpen);
        currentNodeIndex = llGetListLength(closedNodes) - 1;
        neighbors = borderLands(currentNode.x, currentNode.y);
        len = llGetListLength(neighbors);
        if (len == 0 && llGetListLength(openNodes) == 0) {
            noPath(2);
            return;
        }
        for (i = 0; i < len; i += 2) {
            newX = llList2Integer(neighbors, i);
            newY = llList2Integer(neighbors, i + 1);
            status = checkNode(newX, newY);
            if (status == -2 && isSim(newX, newY)) {
                openNodes += [<newX, newY, currentNode.z + 1, currentNodeIndex>];
            }
            else if (status == -3) {
                closedNodes += [<newX, newY, 0, currentNodeIndex>];
                found = TRUE;
            }
            else if (status >= 0) {
                tempNode = llList2Rot(openNodes, status);
                if (tempNode.z <= currentNode.z) {
                    tempNode.z = currentNode.z + 1;
                    tempNode.s = currentNodeIndex;
                    openNodes = llListInsertList(llDeleteSubList(openNodes,
                        status, status), [tempNode], status);
                }
            }
        }
    }
    //Finish code
    list reversePath;
    rotation next = llList2Rot(closedNodes, - 1);
    while (next.s >= 0) {
        reversePath += next;
        next = llList2Rot(closedNodes, (integer)next.s);
    }
    reversePath += next;
    len = llGetListLength(reversePath);
    rotation fromNode = llList2Rot(reversePath, len);
    rotation toNode;
    list finalPath;
    for (i = len - 1; i >= 0; --i) {
        toNode = llList2Rot(reversePath, i);
        finalPath += [(integer)(toNode.x - fromNode.x),
            (integer)(toNode.y - fromNode.y)];
        fromNode = toNode;
    }
    llMessageLinked(reqSource, searchDone, llList2CSV(finalPath), NULL_KEY);
}

//States
default {
    state_entry() {
        mapSizeReq = llGetNotecardLine(simDataCard, 1);
    }

    dataserver(key queryid, string data) {
        if (queryid == mapSizeReq) {
            list splitData = llCSV2List(data);
            xStart = (integer)llList2String(splitData, 0);
            xMax = (integer)llList2String(splitData, 1);
            yStart = (integer)llList2String(splitData, 2);
            yMax = (integer)llList2String(splitData, 3);
            yStep = xMax - xStart + 1;
            nextLine = 2;
            simDataReq = llGetNotecardLine(simDataCard, nextLine);
        }
        if (queryid == simDataReq) {
            if (data != EOF) {
                simData += data;
                ++nextLine;
                simDataReq = llGetNotecardLine(simDataCard, nextLine);
            }
            else {
                llMessageLinked(LINK_SET, readyMsg, "Ready", NULL_KEY);
            }
        }
    }

    link_message(integer sender_num, integer num, string str, key id) {
        if (num == searchStartMsg) {
            //Clear old search values
            closedNodes = [];
            //Get ready for this search
            reqSource = sender_num;
            list destInfo = llCSV2List(str);
            dest = <(float)llList2String(destInfo, 0),
                (float)llList2String(destInfo, 1), 0>;
            vector here = llGetRegionCorner();
            here.x /= 256; here.y /= 256;
            //Check if the start or destination are islands, or non-existant
            if (!isSim(dest.x, dest.y) ||
                    llGetListLength(borderLands(dest.x, dest.y)) == 0) {
                noPath(0);
                return;
            }
            else if (!isSim(here.x, here.y) ||
                    llGetListLength(borderLands(here.x, here.y)) == 0) {
                noPath(1);
                return;
            }
            openNodes = [<here.x, here.y, 0, -1>];
            list avoid = llCSV2List((string)id);
            integer i;
            integer len = llGetListLength(avoid);
            for (i = 0; i < len; i += 2) {
                closedNodes += [<(float)llList2String(avoid, i),
                    (float)llList2String(avoid, i + 1), 0, 0>];
            }
            search();
        }
    }
}


LibraryPathfinderPerl

To run this, save the latest map from SLMaps.com as "map.htm" in the same folder, then run this script using Perl. The output will be a file named "simData.txt" which you can open and copy to a notecard named simData in game for the pathfinder.

#!/usr/bin/perl -w

open MAP, "map.htm" or warn "can't open map.htm\n", <STDIN>, die;
@allLines = <MAP>;
close MAP;

$allLines[0] =~ /date (.{10}) /;
open SAVEFILE, ">simData.txt";
print SAVEFILE "### Pathfinder database ### Last updated $1 ###\n";

for ($i = 0; $i < @allLines; ++$i) {
    if ($allLines[$i] =~ /<tr /) {
        if (@xRow) {
            push @map, ([@xRow]);
            @xRow = ();
        }
    }
    if ($allLines[$i] =~ /<td /) {
        if ($allLines[$i] =~ m!<td.*?>(.*?)</td>!) {
            push @xRow, ($1 eq "" ? "0" : $1);
        }
        else {
            $allLines[$i + 1] =~ m!secondlife://!;
            $i += 2;
            push @xRow, ("1");
        }
    }
}

print SAVEFILE "$map[0][3], $map[0][-4], $map[-2][0], $map[2][0]\n";
for ($y = @map - 2; $y > 1; --$y) {
    for ($x = 3; $x < @{ $map[$y] } - 3; ++$x) {
        $toSplit .= $map[$y][$x];
    }
}
while($toSplit =~ s/(.{255})//) {
    $output .= "$1\n";
}
$output .= $toSplit;
print SAVEFILE $output;

Pathfinder script | Output as of 2005-03-10


LibraryPlanePontoon

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.0 License.

A workaround recently used to detect water & start a splash particle system for plane pontoons on landing:
// Script created by SiRiS Asturias.
// All rights reserved. (c)2005 (Released as OpenSource 12/06/2005)
// Free for use as long as this header stays intact & follows the below guidelines.

// This work is licensed under the:
// Creative Commons Attribution-NonCommercial-ShareAlike License.
// To view the full copy of this license, please visit:
// http://creativecommons.org/licenses/by-nc-sa/2.0/

integer a; // Idle switch
default
{
    state_entry()
    {
        llSetHoverHeight(0.15, TRUE, 0.5); // Make plane hover
        llSetTimerEvent(0.2); //Land Timer
    }
    timer()
    {
        vector pos = llGetPos() - <0.0,0.0,0.2>; //Current position minus offset
        float water = llWater(<0.0,0.0,0.0>); // Water height in the current Sim
        if (a == 1) // If idle
        {
            if (pos.z > water) // Check to see if we are off the water
            {
                llOwnerSay("In the air");
                llSetTimerEvent(0.2); //Took off, start landing timer again
                a = 0; //Set state back to airborne
            }
        }
        else if (pos.z <= water) // Landed on water, trigger particles and wait to leave
        {
                llOwnerSay("Water Landing");
                Water(); // Start splash particle system
                llSetTimerEvent(5.0); // Idle grounded timer
                a = 1; //Set state to water landing
        }
    }
}
This way the only thing it does each timer event is a water to current height comparison on the Z vector.
Could easily be modded to add only calling the llWater function per Sim change for even more efficiency per timer event.
Enjoy! SiRiSAsturias


LibraryPrimLister

PrimLister

List the number and name all the child prims in a complex object.

Some LSL functions use the prim number. Sometimes, the prim you think is 2 is actually 3. This script is an easy way check that.

DumpData() {
    integer i;
    integer j = llGetNumberOfPrims();
    for( i=1; i<=j; ++i ) {
        llOwnerSay((string)i + ": " + llGetLinkName(i));
    }
}

default {
    state_entry() {
        string scriptName = llGetScriptName();
        llOwnerSay(scriptName);
        llOwnerSay( llGetObjectName() );
        DumpData();
        llRemoveInventory(scriptName);
    }
}
ScriptLibrary | Dolyn's Scripts


LibraryReduxOneLineDoor

If you're in a laggy sim (aren't we all?), you probably want doors that don't take 30 seconds to open. There are a ton of fancy doors, but no basic stripped down doors! Such a shame. This is the absolute most stripped down door possible. The whole operation is only one line of code! A call to llGetFreeMemory() returns 16078 on this one. A blank script with a memory call is 16172. That means that the entire door is less than 100 bytes of code!

This is rather a moot issue since the full 16k is allocated even if you don't use it, but less code does mean a faster script. -BW
Brevity is the soul of code. -Redux

It's not fancy, but if you want something simple, then this is for you.

For attached rotation: To link this door to another prim or set of prims: rotate to 0, 90, 0, change the size to <2, 2, .1> and path cut it from 0.375 to 0.875.
For rotating around a remote axis: If you path cut it a little more and make it hollow, you can create the illusion of rotating around a remote axis. It will take some creative texturing, but try using a hollow of 65 with a path cut 0.500 to 0.750

//a door in only 1 line of code!
//Ultra compact rotating door by Redux Dengaku.  Lagless, small, and quick.  Pass it around!

integer direction = 1;
default {
  touch_start(integer total_number) {
     llSetRot(llGetRot() * <0, 0, (direction = -direction) * 0.70711, 0.70711>); //the line!
  }
}

if you want one that opens in 2 steps instead of 1 step, you can use this:

//Ultra compact rotating door by Redux Dengaku.  Lagless, small, and quick.  Pass it around!
integer direction = 1;
default {
  touch_start(integer total_number) {
     llSetRot(llGetRot() * <0, 0, (direction = -direction) * 0.38268, 0.92388>);
     llSetRot(llGetRot() * <0, 0, direction * 0.38268, 0.92388>);
  }
}

That adds another 81 bytes, bringing the grand total to 175 bytes. This will still work when linked. Enjoy!


LibrarySiRiSInABox

I recently was able to obtain a copy of SiRiS Asturias brain from Sourceforge and get it running in a prim. This script can be placed in ANY prim, and it will be able to answer ANY scripting question you ask it.

SiRiS's Brain is completely public domain. Look at it, modify it, sell it in an item, whatever. But I'll be annoyed if you scam noobs into buying it.
//SiRiS in a Box, by Redux Dengaku 1/19/07
default
{
    state_entry()
    {
        llListen(0, "", llGetOwner(), "");  //Begin listening
        llOwnerSay("This magical box contains the brain of SiRiS.  Ask it any LSL related question and ye shall recieve wisdom!");
    }

    on_rez(integer num)
    {
        llResetScript();
    }

    listen(integer chan, string name, key id, string msg)
    {
        if(llFrand(4) < 1)  //After so many questions, SiRiS needs sleep
        {
            llOwnerSay("I... I'm so sleepy!");
            llSleep(llFrand(43200));
        }

        string command = llList2String(llParseString2List(msg, [" "], []), 0);  //Obtain the first word of the string
        if(llListFindList(["how", "what", "where", "can", "does"], [llToLower(command)]) + 1) //Check the first word against a list of question words
        {
            llOwnerSay("RTFW!");  //Answer the question
            llLoadURL(id, "RTFW?", "http://lslwiki.com");  //Redirect to the appropriate URL
        }
    }
}

If you fail to get a response from your SiRiS box, it probably needs sleep, so you may need to wait for a response.


LibrarySynchronousDescReturn

To receive a return value synchronously, a module declares these two functions:
// ========== For synchronous return values ==========
list parsePacket(string src) {
    return [
        llGetSubString(src, 0, 3), // Hex return ID.
        llGetSubString(src, 4, 5), // Hex packet number.
        llGetSubString(src, 6, 7), // Hex packet count.
        llDeleteSubString(src, 0, 7) // Message data.
    ];
}
string RECEIVED_MSG = "RECEIVED";
list getReturnValue(integer returnId, list defaultVal, float timeout) {
    string builtValue;
    list parsedDesc;
    string desc;
    float startTime = llGetTime();
    // Wait for the returner script to post my return ID
    // to the description.
    do {
        desc = llGetObjectDesc();
        parsedDesc = parsePacket(desc);
    } while ((integer)("0x"+llList2String(parsedDesc, 0)) != returnId && llGetTime() - startTime < timeout && desc != "RESET");
    if (desc == "RESET" || llGetTime() - startTime >= timeout)
        return defaultVal;
    integer count = (integer)("0x"+llList2String(parsedDesc, 2));
    integer lastReceived = -1;
    do {
        string data = llList2String(parsedDesc, 3);
        if ((integer)("0x"+llList2String(parsedDesc, 0)) == returnId && data != RECEIVED_MSG) {
            if (lastReceived + 1 == (integer)("0x"+llList2String(parsedDesc, 1))) {
                // Received next packet.
                builtValue += data;
                ++lastReceived;
            }
            string metadata = llGetSubString((string)parsedDesc, 0, 7);
            llSetObjectDesc(metadata + RECEIVED_MSG);
        }
        desc = llGetObjectDesc();
        parsedDesc = parsePacket(desc);
    } while (lastReceived + 1 != count && desc != "RESET");
    if (desc == "RESET")
        return defaultVal;
    return stringToList(builtValue);
}
// =============================================

The method stub has to change a bit too:
integer m_doSomething() {
    integer retID = (integer)llFrand(187431); // MUST BE UNIQUE
    callMethod(0, "returnListen", [retID]); // MUST BE CALLED BEFORE METHOD CALL.
    callMethod(retID, "doSomething", []);
    return retID;
}

When calling the method, you need to wrap it in a getReturnValue call.

default {
    state_entry() {
        list returnValue = getReturnValue(m_doSomething(), ["argh"], 120);
        if (llList2String(returnValue, 0) == "argh") {
            llOwnerSay("Synchro return failed.");
        } else {
            llOwnerSay("Return value: " + (string)returnValue);
        }
    }
}
getReturnValue blocks until it receives the return value or it times out. If it times out, it returns the default value passed to it. The callee's code doesn't need to change at all, it still acts as though its returning a value asyncronously. You do, however, need the SynchronousDescReturn module in the object's contents.

The SyncronousDescReturn module:
// Allows modules to call the blocking getReturnValue function
// when calling a method that "returns" a value
// (calls a corresponding <methodName>_ret method).
// This module uses the object's description field for data transfer
// but makes a best-effort attempt to preserve the user-specified
// description. As it cannot 100% guarentee this,
// it is recommended to not use the description field
// descriptively.
// See the Synchronous Return Values notecard for details
// about implementing the required structure in the caller.

// How long (in seconds) to wait for a module
// to acknowledge the return packet was received.
float RETURN_TIMEOUT = 2.5;
// What the receiveing module posts to the description
// when it receives a packet.
string RECEIVED_MSG = "RECEIVED";
// Maximum number of characters in the description.
integer MAX_DESC = 127;

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20;
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================
m_moduleReset(string moduleName) {
    callMethod(0, "moduleReset", [moduleName]);
}

string intToHex(integer x, integer len) {
    string hexc="0123456789ABCDEF";
    integer x0 = x & 0xF;
    string res = llGetSubString(hexc,x0,x0);
    x = (x >> 4) & 0x0FFFFFFF; //otherwise we get infinite loop on negatives.
    while (x != 0) {
        x0 = x & 0xF;
        res = llGetSubString(hexc,x0,x0) + res;
        x = x >> 4;
    }
    for (len -= llStringLength(res); len > 0; --len)
        res = "0" + res;
    return res;
}

list parsePacket(string src) {
    return [
        llGetSubString(src, 0, 3), // Hex return ID.
        llGetSubString(src, 4, 5), // Hex packet number.
        llGetSubString(src, 6, 7), // Hex packet count.
        llDeleteSubString(src, 0, 7) // Message data.
    ];
}

sendPacket(integer returnIndex, integer packetNum) {
    integer dataLength = MAX_DESC - 8; // 4-char ID + 2-char num + 2-char count.
    integer startIndex = packetNum * dataLength;
    string returnValue = llList2String(returnValues, returnIndex);
    string packetData = llGetSubString(returnValue, startIndex, startIndex + dataLength - 1);
    integer packetCount = llCeil(llStringLength(returnValue) / (float)dataLength);
    packet = llList2String(returnIds, returnIndex)
        + intToHex(packetNum, 2)
        + intToHex(packetCount, 2)
        + packetData;
    llSetObjectDesc(packet);
    //llOwnerSay("Sent packet " + (string)packetNum+": "+ packet);
    lastPacketSent = llListReplaceList(lastPacketSent, [packetNum], returnIndex, returnIndex);
    llResetTime();
}

deleteReturnData(integer index) {
    lastPacketSent = llDeleteSubList(lastPacketSent, index, index);
    returnValues   = llDeleteSubList(returnValues,   index, index);
    returnIds        = llDeleteSubList(returnIds,    index, index);
    if (llGetListLength(returnIds) == 0) {
        llSetTimerEvent(0);
        llSetObjectDesc(originalDesc);
    }
}

addReturnData(integer id, string data) {
    string hexId = intToHex(id, 4);
    if (llListFindList(returnIds, [hexId]) != -1)
        return; // TODO: Make this smarter.
    returnIds      += hexId;
    returnValues   += data;
    lastPacketSent += -1;
    llSetTimerEvent(0.1);
}

list waitingReturns;
list returnValues;
list returnIds;
list lastPacketSent;
string originalDesc;
string packet;

string this;
default {
    state_entry() {
        this = llGetScriptName();
        m_moduleReset(this);
        originalDesc = llGetObjectDesc();
        llSetObjectDesc("RESET");
        llSleep(5);
        if (originalDesc != "RESET") {
            llSetObjectDesc(originalDesc);
        } else {
            llSetObjectDesc("");
        }
    }
    link_message(integer sender, integer call, string parameters, key methodName) {
        if (methodName == "returnListen") {
            list paramList = stringToList(parameters);
            // Method sig:
            // returnListen(integer id)
            // Called before a method returning a value is called - indicates
            // that the calling module wants to receive the data syncronously.
            integer id = llList2Integer(paramList, 0);
            if (llListFindList(waitingReturns, [id]) == -1)
                waitingReturns += id;
        } else if (llDeleteSubString(methodName, 0, -5) == "_ret") {
            integer index = llListFindList(waitingReturns, [call]);
            if (index != -1) {
                //llOwnerSay("Heard registered return value.");
                originalDesc = llGetObjectDesc();
                waitingReturns = llDeleteSubList(waitingReturns, index, index);
                addReturnData(call, parameters);
            }
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(call, methodName, [TRUE]);
        }
    }
    timer() {
        string desc = llGetObjectDesc();
        list parsedDesc = parsePacket(desc);
        if (llList2Integer(lastPacketSent, 0) == -1 || desc == originalDesc) {
            // We haven't sent anything yet.
            sendPacket(0, 0);
        } else if (llList2String(parsedDesc, 3) == RECEIVED_MSG) {
            // See the parseDesc and sendPacket functions
            // for the format of the description.
            string returnId = llList2String(parsedDesc, 0);
            integer returnIndex = llListFindList(returnIds, [returnId]);
            integer sentPacket = llList2Integer(lastPacketSent, returnIndex);
            if ((integer)("0x"+llList2String(parsedDesc, 1)) == sentPacket) {
                llResetTime();
                // I might want to compute the number of packets the message
                // has myself, just so I can trust the value.
                // Right now Ill just use the val passed back to me.
                if (sentPacket + 1 == (integer)("0x"+llList2String(parsedDesc, 2))) {
                    // They received the last packet.
                    deleteReturnData(returnIndex);
                } else {
                    // Send the next packet.
                    sendPacket(returnIndex, sentPacket + 1);
                    //llOwnerSay("Sent packet " + (string)(sentPacket+1) + " of return " + returnId);
                }
            } else {
                //llOwnerSay("Resending packet " + (string)sentPacket + " of return " + returnId);
                // Stale return message, resend the packet.
                sendPacket(returnIndex, sentPacket);
            }
        } else if (desc != packet) {
            // Someone has set the description to something else
            // while the return operation was going on
            llSetObjectDesc(packet);
            llResetTime();
        } else {
            // We're waiting for the receiver to take
            // the data and post a RECEIVED_MSG.
            if (llGetTime() > RETURN_TIMEOUT) {
                deleteReturnData(0);
            }
        }
    }
}

How it Works

The concept is simple really. SynchronousDescReturn accepts call IDs via its returnListen method. It then listens to all return value method calls in a kind of "promiscuous" fashon. When one of these return calls matches a registered call ID, then it stores the value returned. It then broadcasts this value and the value's ID in the object's description. While all this is happening, every module that's expecting a synchronous return value is blocking in getReturnValue, continuously reading the object's description, waiting for the SynchronousDescReturn to post a return value with the ID it called the method with. Return values are sent in packets, due to the short 128 character limit put on the object's description. The packet format is similar to that used by the ExchangePacketChat protocol. Every time a packet is received by the calling module, the calling module posts a RECEIVED packet to the description.

Why this is evil

1. Its extremely easy to create a deadlock scenerio using synchro returns.
Take this example:
Module A calls Module B's method getFoo, and goes into getReturnValue, waiting for getFoo to return.
In Module B's getFoo, Module B calls Module A's getBar, and goes into getReturnValue, waiting for getBar to return.
Without timeouts, this would result in deadlock - neither module would respond to the other's request. This is why timeouts are good.
This is something inherant in the way the synchro returns work - each module is a seperate thread, so single-threaded stack push and pop emulation is pretty much impossible.
To avoid this you have to be careful where you use synchro returns.

2. Polling is hard on the sim.
Each module blocking in getReturnValue is requesting the object's description as fast as it can. This can potentially be a lot of calls to llGetObjectDesc. Simulator performance may take a significant hit when many modules are waiting for a return value.

3. Any failure in the system could result in an infinite loop.
If the SynchronousDescReturn" does not register the call ID before the callee returns the value, then the caller could potentially be left waiting for the Synchro module to post something that it never will. This is why timeouts are good.

4. It is slower then an async return.
Async returns will always be faster then synchro primarily because async only involves the caller and callee. In my testing, a simple synchro return call took about 3 seconds to complete, whereas that same call using async took 1.
Depending upon the number of times you need to call the method, an async return might be better even if it appears to increase the code's complexity.

LibraryContentBrowser


LibraryWarpPosFix

As long as warpPos remains broken, there needs to be a fix. I've tried posting the temporary fix on the warpPos page, but it keeps getting torn down with egotistical comments like "THIS IS NOT THE SAME FUNCTION" or "YOU ARE SULLYING THE BEAUTY OF WARPPOS!". Meanwhile people still don't have working teleporters, and don't know how to fix them.

For whatever reason the people who run this site like to make things difficult and suppressing knowledge, so this page may not stay up for very long. If you value free sharing of knowledge, copy this code and put it elsewhere, because it sure isn't valued here. People are trying to hide the code from you and force you to suffer in the darkness.

P.S., I'm not writing code for lslwiki anymore. Next time something breaks, I'm posting the fix on rpgstats or directly on my website. Wiki's are about sharing information and making it available, not about suppressing it so you can waggle your balls about how good an idea you had 2 years ago is.

//1.13.3 (2) fix by Redux
//Optimized by SiRiS & Talarus
warpPos(vector d)  //This function is not warpPos
{
    if (d.z > 768) {d.z = 768;}
    integer s = (integer)(llVecMag(d-llGetPos())/10)+1; //The number of jumps necessary
    do{
        llSetPrimitiveParams([PRIM_POSITION, d]);
    } while (--s);
}


LibraryXyTextFonts

The following script-fu script can be used with The Gimp to create textures for use with single texture versions of "XyText".

(define (script-fu-xytext-font inText inFont inFontSize inTextColor inImageSize inAdjust inMaxWidth)
    (let*
        (
            ; define our local variables
            ; create a new image:
            (theImageWidth  1)
            (theImageHeight 1)
            (theMaxWidth)
            (theImageWidthModTen)
            (theImageHeightModTen)
            (theXPos 0)
            (theYPos 0)
            (theImage)
            (theLayer)
            (theBackground)
            (theTextWidth)
            (theTextHeight)
            (theChar)
            (theText)     ;a declaration for the text
                          ;we create later
        )
        (while (> inImageSize 0) (set! inImageSize (- inImageSize 1))
            (set! theImageWidth (* theImageWidth 2))
            (set! theImageHeight (* theImageHeight 2))
        )
        (set! theImageWidthModTen (- theImageWidth (* (/ theImageWidth 10) 10)))
        (set! theImageHeightModTen (- theImageHeight (* (/ theImageHeight 10) 10)))
        (set! inFontSize (/ (* theImageHeight inFontSize) 100))
        (set! theMaxWidth (/ (* theImageHeight inMaxWidth) 100))

        (gimp-context-set-foreground inTextColor)
        (gimp-context-set-background inTextColor)

        (set! theImage (car
            (gimp-image-new
                theImageWidth
                theImageHeight
                RGB
            )
        ))
        (set! theBackground (car
            (gimp-layer-new
                theImage
                theImageWidth
                theImageHeight
                RGBA-IMAGE
                "Background"
                0.5
                NORMAL
            )
        ))
        (gimp-image-add-layer theImage theBackground 0)
        (gimp-drawable-fill theBackground BACKGROUND-FILL)
        (set! theLayer (car
            (gimp-layer-new
                theImage
                theImageWidth
                theImageHeight
                RGBA-IMAGE
                "layer 1"
                100
                NORMAL
            )
        ))
        (gimp-image-add-layer theImage theLayer -1)
        (gimp-selection-none theImage)
        (gimp-edit-clear theLayer)
        (while (and (< theYPos 10) (> (string-length inText) 0))
            (set! theChar (substring inText 0 1))
            (set! theText (car (gimp-text-fontname
                theImage -1
                0 0
                theChar
                0
                TRUE
                inFontSize PIXELS
                inFont
            )))
            (if (>= theText 0) (begin
                (gimp-layer-translate theText
                    (- (/ theImageWidth 2) (/ (car (gimp-drawable-width theText)) 2))
                    (- (/ theImageHeight 2) (/ (car (gimp-drawable-height theText)) 2))
                )
                (if (> (car (gimp-drawable-width theText)) theMaxWidth)
                    (gimp-layer-scale theText
                        theMaxWidth
                        (car (gimp-drawable-height theText))
                        1
                    )
                )
                (gimp-layer-translate theText
                    0
                    (/ (* theImageHeight inAdjust) 100)
                )
                (gimp-layer-resize-to-image-size theText)
                (gimp-layer-resize theText
                    (- theImageWidth theImageWidthModTen)
                    (- theImageWidth theImageHeightModTen)
                    (/ theImageWidthModTen 2)
                    (/ theImageHeightModTen 2)
                )

                (gimp-layer-scale theText
                    (/ theImageWidth 10)
                    (/ theImageHeight 10)
                    0
                )
                (gimp-layer-translate theText
                    (- (/ (* theImageWidth theXPos) 10) (/ theImageWidthModTen 2))
                    (- (/ (* theImageHeight theYPos) 10) (/ theImageHeightModTen 2))
                )
                (gimp-image-merge-down theImage theText CLIP-TO-BOTTOM-LAYER)
            ))
            (set! inText (substring inText 1 (string-length inText)))
            (set! theXPos (+ theXPos 1))
            (if (>= theXPos 10) (begin
                (set! theXPos 0)
                (set! theYPos (+ theYPos 1))
            ))
        )

    (gimp-image-merge-visible-layers theImage CLIP-TO-BOTTOM-LAYER)
    (gimp-display-new theImage)
))

        (script-fu-register
          "script-fu-xytext-font"                        ;func name
          "XYText Font"                                  ;menu label
          "Creates a texture for single letter\
            XYText font scripts."                     ;description
          "Cadell DeCuir"                             ;author
          "Copyright 2007 Cadell DeCuir"             ;copyright notice
          "June 14, 2007"                          ;date created
          ""                     ;image type that the script works on
          SF-STRING      "Char Map"         " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"   ;a string variable
          SF-FONT        "Font"         "Courier"    ;a font variable
          SF-ADJUSTMENT  "Font size (Percent)"     '(80 50 200 1 10 0 0)
                                                      ;a spin-button
          SF-COLOR       "Color"        '(255 255 255)     ;color variable
          SF-ADJUSTMENT  "Image Size (Powers of 2)" '(9 6 10 1 1 0 0)
          SF-ADJUSTMENT  "Font Shift Vertical (Percent)"	'(-10 -50 50 1 10 0 0)
          SF-ADJUSTMENT  "Font Max Width (Percent)" '(50 10 100 1 10 0 0)
        )
        (script-fu-menu-register "script-fu-xytext-font" "<Toolbox>/Xtns/Script-Fu/Second Life")


LIFO

LIFO stands for "Last In, First Out". It is a way of ordering data in a structure for processing that ensures the last piece of data added is the first processed. A stack is a LIFO.

LSL | FIFO | stack


light

<a href= http:dvjzw.fizwig.com/viagra-generic-sildenafil.html >caverta generic viagra</a> [url=http:dvjzw.fizwig.com/viagra-generic-sildenafil.html]caverta generic viagra[/url] <a href= http:dvjzw.fizwig.com/qcq1lkq.html >buy cheapest viagra</a> [url=http:dvjzw.fizwig.com/qcq1lkq.html]buy cheapest viagra[/url] <a href= http:dvjzw.fizwig.com/vxii6j.html >buy online phentermine valium viagra xanax</a> [url=http:dvjzw.fizwig.com/vxii6j.html]buy online phentermine valium viagra xanax[/url]


Limits

LSL and SL have many limits (or maximums).

Name | Limit
agents/avatars in a simulator | ~41-60 (server hardware-dependent)
friends in Friends list | ?
groups a user can join | 15
parcelable land | 65536m2
linked object | 256 prims
notecard | 65536 characters
objects in simulator | 15000
physical object (i.e. vehicle) | 31 prims
script delay | varies per function


Linden

The user surname used by employees of Linden Lab, Lindens: developers and liaisons.

Also short for LindenDollar.

Linden Lab | Lindens | Linden Dollar | Liaisons | User


LindenAIML

Moved to LibraryLindenAIML


LindenBuck

See LindenDollar.


Linden Dollar

A Linden dollar (L$, "LindenBucks", or "Lindens") is the monetary currency used in Second Life.

As of March 17, 2006, the current exchange rate is approximately L$281 per USD$1.00, or about USD$3.50 for L$1,000. Because of the state of the economy, the exchange rate is constantly changing. See the LindeX currency exchange for current market rates.

Functions:
llGetPermissions | Determine whether the permission PERMISSION_DEBIT has been granted.
llGiveMoney | Pay L$ from the owner's account to another user. (Requires PERMISSION_DEBIT be granted.)
llRequestPermissions | Request the permission PERMISSION_DEBIT.
llSetPayPrice | Set the values of the "Quick Pay" buttons in the "Pay" dialog box.

Event:
When an object is paid money, the money() event is triggered.

money | Linden


Linden Lab

Linden Lab, LLC is the developer of Second Life. Lindens are Linden Lab employees.

For alternative viewpoints on it, please see other viewpoints.


LindenLabOther

Linden Lab: Alternative Viewpoints

What follows is a more complete perspective of the entity known as Linden Lab.

  • Linden Lab was founded in the 12th Century to defend the the Kingdom of Jerusalem following the First Crusade. In 1307, King Philip IV ordered a coordinated arrest of what were believed to have been all Lindens then operating within France. -CatherineOmega

  • Linden Lab was one of the many boy bands to pop up under the influence of the Beatles. Philip was the cute one, Andrew was the nice one, James was the smart one, and Cory was the rebel. -BuhBuhCuh

  • Linden Lab was created in the 50's by a mandate from "Tailgunner" Joe McCarthy as an adjunct to the HUAC. Originally tasked with "removing communist influnces from the nation's electro-mechanical infrastructure" its mandate has grown over the years to include responsibility for 99% of all chalk production in the continental United States and enforcement of the "skill testing question" regulations of contests. -WednesdayGrimm

LindenLab | Humor


Lindens

"Lindens" is the colloquial term used in reference to the collective group of users who are employees of Linden Lab, LLC, the company that owns, operates, and develops Second Life. Lindens always use an account whose surname is "Linden." A user with this last name is an authority figure. Although there are a variety of roles filled by Linden Lab employees (developers, database admins, system admins, artists, etc.), most users interact with Linden Lab employees who work in a special "community representative" role, the liaison.

LSL-Knowledgeable Lindens

For bug discussion or questions about what makes LSL tick, contact these Lindens.

Name | Specialization
Andrew | programmer (physics engine, server-side)
Babbage | script runtime programmer
Content | stunt scripter?
Cory | Lead Developer
Haney | scripting
Kelly | general, particles
Runitai | occlusion
Steve | general (lighting, LOD)
Yadweb | flexible prims
Source: Linden Profiles, http://forums.secondlife.com/showpost.php?p=475&postcount=7, and other forum posts.
Linden Lab | Second Life | User


LindenScriptingLanguage

See LSL.


LindenVehicleTutorial

Note: What follows is the unedited vehicle section of the Linden-provided LSL manual. Please do NOT edit this page to improve upon it, as this is meant to preserve precisely what the Lindens wrote. Go here for the version of these pages that can, should be, and is actively maintained.

Note to the reader, some of the information in the "Constants" section is contraditory to "Vehicles", the information in "Vehicles" is more accurate then that of "Constants"

11. Vehicles
11.1. Overview
11.2. Warnings
11.3. Definitions
11.4. Setting the Vehicle Type
11.5. Linear and Angular Deflection
11.6. Moving and Steering the Vehicle
11.7. The Linear Motor
11.8. The Angular Motor
11.9. Using the Camera to Steer
11.10. The Vertical Attractor
11.11. Banking
11.12. Friction Timescales
11.13. Buoyancy
11.14. Hover
11.15. Reference Frame
C. Constants
C.24. Vehicle Parameters
C.25. Vehicle Flags
C.26. Vehicle Types

Chapter 11. Vehicles

Custom Vehicles can be constructed and controlled using the LSL. This chapter will cover the basics of how vehicles work, the terms used when describing vehicles, and a more thorough examination of the api available.

There are several ways to make scripted objects move themselves around. One way is to turn the object into a "vehicle". This feature is versatile enough to make things that slide, hover, fly, and float. Some of the behaviors that can be enabled are:

  • deflection of linear and angular velocity to preferred axis of motion

  • asymmetric linear and angular friction

  • hovering over terrain/water or at a global height

  • banking on turns

  • linear and angular motor for push and turning

11.1. Overview

Each scripted object can have one vehicle behavior that is configurable through the llSetVehicleType, llSetVehicleFloatParam, llSetVehicleVectorParam, llSetVehicleRotationParam, llSetVehicleFlags, and llRemoveVehicleFlags library calls.

These script calls are described in more detail below, but the important thing to notice here is that the vehicle behavior has several parameters that can be adjusted to change how the vehicle handles. Depending on the values chosen the vehicle can veer like a boat in water, or ride like a sled on rails.

Setting the vehicle flags allow you to make exceptions to some default behaviors. Some of these flags only have an effect when certain behaviors are enabled. For example, the VEHICLE_FLAG_HOVER_WATER_ONLY will make the vehicle ignore the height of the terrain, however it only makes a difference if the vehicle is hovering.

11.2. Warnings

Vehicles are a work in progress and will likely experience changes in future versions of Second Life. Some of the details of vehicle behavior may be changed as necessary to ensure stability and user safety. In particular, many of the limits and defaults described in the appendices will probably change and should not be relied upon in the long term.

It is not recommended that you mix vehicle behavior with some of the other script calls that provide impulse and forces to the object, especially llSetBuoyancy, llSetForce, llSetTorque, and llSetHoverHeight.

While the following methods probably don't cause any instabilities, their behavior may conflict with vehicles and cause undesired and/or inconsistent results, so use llLookAt, llRotLookAt, llMoveToTarget, and llTargetOmega at your own risk.

If you think you have found a bug relating to how vehicle's work, one way to submit the problem is to give a copy of the vehicle and script to Andrew Linden with comments or a notecard describing the problem. Please name all submissions "Bugged Vehicle XX" where XX are your Second Life initials. The vehicle and script will be examined at the earliest convenience.

11.3. Definitions

The terms "roll", "pitch", and "yaw" are often used to describe the modes of rotations that can happen to a airplane or boat. They correspond to rotations about the local x-, y-, and z-axis respectively.

        z-axis  .
      yaw-axis /|\
                |     __. y-axis
   ._        ___|      /| pitch-axis
  _||\       \\ |\.   /
  \|| \_______\_|__\_/_______
   | _ _   o o o o o o o    |\_  ______\ x-axis
   // ./_______,----,__________)       / roll-axis
  /_,/        //  ./
             /__,/

The right-hand-rule, often introduced in beginning physics courses, is used to define the direction of positive rotation about any axis. As an example of how to use the right hand rule, consider a positive rotation about the roll axis. To help visualize how such a rotation would move the airplane, place your right thumb parallel to the plane's roll-axis such that the thumb points in the positive x-direction, then curl the four fingers into a fist. Your fingers will be pointing in the direction that the plane will spin.

    .-.--.--.--.              __
   / /  /  /  _ \            /  \
  (-(- (- (- (   | _________|______\ axis of
   \.\._\._\._)  |          |      / rotation
    |           \:__,---.  \|/
    |                    |  + positive
     \           .,_.___.'    rotation
      \_ ^ `.__,/
      |      /
      |      |

Many of the parameters that control a vehicle's behavior are of the form: VEHICLE_BEHAVIOR_TIMESCALE. A behavior's "timescale" can usually be understood as the time for the behavior to push, twist, or otherwise affect the vehicle such that the difference between what it is doing, and what it is supposed to be doing, has been reduced to 1/e of what it was, where "e" is the natural exponent (approximately 2.718281828). In other words, it is the timescale for exponential decay toward full compliance to the desired behavior. When you want the vehicle to be very responsive use a short timescale of one second or less, and if you want to disable a behavior then set the timescale to a very large number like 300 (5 minutes) or more. Note, for stability reasons, there is usually a limit to how small a timescale is allowed to be, and is usually on the order of a tenth of a second. Setting a timescale to zero is safe and is always equivalent to setting it to its minimum. Any feature with a timescale can be effectively disabled by setting the timescale so large that it would take them all day to have any effect.

11.4. Setting the Vehicle Type

Before any vehicle parameters can be set the vehicle behavior must first be enabled. It is enabled by calling llSetVehicleType with any VEHICLE_TYPE_*, except VEHICLE_TYPE_NONE which will disable the vehicle. See the vehicle type constants section for currently available types. More types will be available soon.

Setting the vehicle type is necessary for enabling the vehicle behavior and sets all of the parameters to its default values. For each vehicle type listed we provide the corresponding equivalent code in long format. Is is important to realize that the defaults are not the optimal settings for any of these vehicle types and that they will definitely be changed in the future. Do not rely on these values to be constant until specified.

Should you want to make a unique or experimental vehicle you will still have to enable the vehicle behavior with one of the default types first, after which you will be able to change any of the parameters or flags within the allowed ranges.

Setting the vehicle type does not automatically take controls or otherwise move the object. However should you enable the vehicle behavior while the object is free to move and parked on a hill then it may start to slide away.

We're looking for new and better default vehicle types. If you think you've found a set of parameters that make a better car, boat, or any other default type of vehicle then you may submit your proposed list of settings to Andrew Linden via a script or notecard.

11.5. Linear and Angular Deflection

A common feature of real vehicles is their tendency to move along "preferred axes of motion". That is, due to their wheels, wings, shape, or method of propulsion they tend to push or redirect themselves along axes that are static in the vehicle's local frame. This general feature defines a class of vehicles and included in this category a common dart is a "vehicle": it has fins in the back such that if it were to tumble in the air it would eventually align itself to move point-forward -- we'll call this alignment effect angular deflection.

A wheeled craft exhibits a different effect: when a skateboard is pushed in some direction it will tend to redirect the resultant motion along that which it is free to roll -- we'll call this effect linear deflection.

So a typical Second Life vehicle is an object that exhibits linear and/or angular deflection along the "preferential axes of motion". The default preferential axes of motion are the local x- (at), y- (left), and z- (up) axes of the local frame of the vehicle's root primitive. The deflection behaviors relate to the x-axis (at): linear deflection will tend to rotate its velocity until it points along it's positive local x-axis while the angular deflection will tend to reorient the vehicle such that it's x-axis points in the direction that it is moving. The other axes are relevant to vehicle behaviors that are described later, such as the vertical attractor which tries to keep a vehicle's local z-axis pointed toward the world z-axis (up). The vehicle axes can be rotated relative to the object's actual local axes by using the VEHICLE_REFERENCE_FRAME parameter, however that is an advanced feature and is covered in detail in a later section of these documents.

Depending on the vehicle it might be desirable to have lots of linear and/or angular deflection or not. The speed of the deflections are controlled by setting the relevant parameters using the llSetVehicleFloatParam script call. Each variety of deflection has a "timescale" parameter that determines how quickly a full deflection happens. Basically the timescale it the time coefficient for exponential decay toward full deflection. So, a vehicle that deflects quickly should have a small timescale. For instance, a typical dart might have a angular deflection timescale of a couple of seconds but a linear deflection of several seconds; it will tend to reorient itself before it changes direction. To set the deflection timescales of a dart you might use the lines below:

llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 2.0);
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 6.0);

Each variety of deflection has an "efficiency" parameter that is a slider between 0.0 and 1.0. Unlike the other efficiency parameters of other vehicle behaviors, the deflection efficiencies do not slide between "bouncy" and "damped", but instead slide from "no deflection whatsoever" (0.0) to "maximum deflection" (1.0). That is, they behave much like the deflection timescales, however they are normalized to the range between 0.0 and 1.0.

11.6. Moving and Steering the Vehicle

Once enabled, a vehicle can be pushed and rotated by external forces and/or from script calls such as llApplyImpulse, however linear and angular motors have been built in to make motion smoother and easier to control. Their directions can be set using the llSetVehicleVectorParam call. For example, to make the vehicle try to move at 5 meters/second along its local x-axis (the default look-at direction) you would put the following line in your script:

llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <5, 0, 0>);

The motor strength is not the full story, since you can also control how fast the motor engages (VEHICLE_LINEAR_MOTOR_TIMESCALE) and there is a parameter that causes the motor's effectiveness to decay over time (VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE).

Steering the vehicle involves setting the VEHICLE_ANGULAR_MOTOR_DIRECTION and related parameters. It is also possible to set some flags that allow the angular motor slave to your camera view when in mouselook.

For more details about the vehicle motors read the sections on the linear and angular motors below.

11.7. The Linear Motor

The parameters that control the linear motor are:

  • VEHICLE_LINEAR_MOTOR_DIRECTION

    A vector. It is the velocity (meters/sec) that the vehicle will try to attain. It points in the vehicle's local frame, and has a maximum length of 40.

  • VEHICLE_LINEAR_MOTOR_OFFSET

    A vector. It is the offset point from the vehicle's center of mass at which the linear motor's impulse is applied. This allows the linear motor to also cause rotational torque. It is in the vehicle's local frame and its maximum length is 100 meters! No need to worry about stability -- if the vehicle starts to spin too fast (greater than about 4*PI radians per second) then angular velocity damping will kick in. The reason the offset is allowed to be so large is so that it can compete with the other vehicle behaviors such as angular deflection and the vertical attractor. Some of the other vehicle behaviors may drastically reduce the effective torque from the linear motor offset, in which case a longer leverage arm may help.

  • VEHICLE_LINEAR_MOTOR_TIMESCALE

    A float. Determines how long it takes for the motor to push the vehicle to full speed. Its minimum value is approximately 0.06 seconds.

  • VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE

    A float. The effectiveness of the motor will exponentially decay over this timescale, but the effectiveness will be reset whenever the motor's value is explicitly set. The maximum value of this decay timescale is 120 seconds, and this timescale is always in effect.

The flags that affect the linear motor are:

  • VEHICLE_FLAG_LIMIT_MOTOR_UP

    Useful for "ground vehicles". Setting this flag will clamp the z-component of the linear motor (in world frame) to prevent it from defeating gravity.

Setting the motor speed is not enough to enable all interesting vehicles. For example, some will want a car that immediately gets up to the speed they want, while others will want a boat that slowly climbs up to its maximum velocity. To control this effect the VEHICLE_LINEAR_MOTOR_TIMESCALE parameter can be used. Basically the "timescale" of a motor is the time constant for the vehicle to exponentially accelerate toward its full speed.

What would happen if you were to accidentally set the vehicle's linear velocity to maximum possible speed and then let go? It would run away and never stop, right? Not necessarily: an automatic "motor decay" has been built in such that all motors will gradually decrease their effectiveness after being set.

Each time the linear motor's vector is set its "grip" immediately starts to decay exponentially with a timescale determined by the VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, such that after enough time the motor ceases to have any effect. This decay timescale serves two purposes. First, since it cannot be set longer than 120 seconds, and is always enabled it guarantees that a vehicle will not push itself about forever in the absence of active control (from keyboard commands or some logic loop in the script). Second, it can be used to push some vehicles around using a simple impulse model. That is, rather than setting the motor "on" or "off" depending on whether a particular key is pressed "down" or "up" the decay timescale can be set short and the motor can be set "on" whenever the key transitions from "up" to "down" and allowed to automatically decay.

Since the motor's effectiveness is reset whenever the motor's vector is set, then setting it to a vector of length zero is different from allowing it to decay completely. The first case will cause the vehicle to try to reach zero velocity, while the second will leave the motor impotent.

The two motor timescales have very similar names, but have different effects, so try not to get them confused. VEHICLE_LINEAR_MOTOR_TIMESCALE is the time for motor to "win", and VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE is the time for the motor's "effectiveness" to decay toward zero. If you set one when you think you are changing the other you will have frustrating results. Also, if the motor's decay timescale is shorter than the regular timescale, then the effective magnitude of the motor vector will be diminished.

11.8. The Angular Motor

The parameters that control the angular motor are:

  • VEHICLE_ANGULAR_MOTOR_DIRECTION

    A vector. It is the angular velocity (radians/sec) that the vehicle will try to rotate. It points in the vehicle's local frame, and has a maximum value of 4*PI (two revolutions per second).

  • VEHICLE_ANGULAR_MOTOR_TIMESCALE

    A float. Determines how long it takes for the motor to spin the vehicle to full speed. Its minimum value is approximately 0.06 seconds.

  • VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE

    A float. The effectiveness of the motor will exponentially decay over this timescale, but the effectiveness will be reset whenever the motor's value is explicitly set. The maximum value of this decay timescale is 120 seconds, and this timescale is always in effect.

Like the linear motor the angular motor can be set explicitly, and has magnitude/direction, a timescale, and a decay timescale.

When it comes to actually steering a vehicle there are several ways to do it. One way would be for the script to grab keyboard input and to explicitly turn the motor on/off based on which keys are pressed. When steering this way you probably don't want it to turn very far or for very long. One way to do it using the angular motor would be to leave the decay timescale long, enable a significant amount of angular friction (to quickly slow the vehicle down when the motor is turned off) then set the angular motor to a large vector on a key press, and set it to zero when the key is released. That has the effect of making the vehicle unresponsive to external collisions, due to the angular friction.

Another way to do it is to set the VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE to a short value and push the vehicle about with a more impulsive method that sets the motor fast on a key press down (and optionally setting the motor to zero on a key up) relying on the automatic exponential decay of the motor's effectiveness rather than a constant angular friction.

Finally, it may be possible to discard the angular motor entirely and use the VEHICLE_LINEAR_MOTOR_OFFSET. Whenever the offset has a component that is perpendicular to the direction of the linear motor the vehicle will rotate as it travels. Note, with the incorrect values for offset and strength the linear motor effect can easily cause the vehicle to tumble and spin uncontrollably, so experiement with small offsets first!.

Setting the angular motor to zero magnitude is different from allowing it to decay. When the motor completely decays it no longer affects the motion of the vehicle, however setting it to zero will reset the "grip" of the vehicle and will make the vehicle try to achieve zero angular velocity.

Many real vehicles bank (roll about their forward axis) to effect a turn, such as motorcycles and airplanes. To make it easier to build banking vehicles there is banking behavior available which can be controlled by setting other parameters and is described in more detail here.

It is also possible to make a vehicle turn in response to changing the camera view (right now this only works in mouselook).

11.9. Using the Camera to Steer

The vehicle can be instructed to rotate its forward axis to point in the same direction as the camera view. This is achieved by setting some flags that change how the VEHICLE_ANGULAR_MOTOR_DIRECTION is interpreted. When used properly this feature has the advantage of being able to provide simple and stable steering that is resilient to bad render frame rates on the client.

The flags that affect the angular motor are:

  • VEHICLE_FLAG_MOUSELOOK_STEER

    Steer the vehicle using the mouse. Use this flag to make the angular motor try to make the vehicle turn such that its local x-axis points in the same direction as the client-side camera.

  • VEHICLE_FLAG_MOUSELOOK_BANK

    Same as above, but relies on banking. It remaps left-right motions of the client camera (also known as "yaw") to rotations about the vehicle's local x-axis (also known as "roll").

  • VEHICLE_FLAG_CAMERA_DECOUPLED

    Makes mouselook camera rotate independently of the vehicle. By default the client mouselook camera will rotate about with the vehicle, however when this flag is set the camera direction is independent of the vehicle's rotation.

When using the VEHICLE_FLAG_MOUSELOOK_STEER (or VEHICLE_FLAG_MOUSELOOK_BANK) the meaning of the VEHICLE_ANGULAR_MOTOR_DIRECTION parameter subtly changes. Instead of representing the "angular velocity" of the motor the components of the parameter scale the "measured angular velocity" (as determined by the rotation between the client's camera view direction and the forward-axis of the vehicle) to compute the "final angular velocity". That is, suppose you set the angular motor to <0, 0, 5>, then moved the camera view to be PI/4 radians to the left of the vehicle's forward axis, and down PI/8 toward the ground. The measured angular velocity would be <0, -PI/8, PI/4> radians/second, but the final velocity would be <0, 0, 5*PI/4>... the vehicle will turn left, but will not dip its nose down. Thus, by setting a component of the VEHICLE_ANGULAR_MOTOR_DIRECTION to zero, one can negate the pitch or yaw response of the motor, or even scale one to be much more responsive than the other.

The VEHICLE_ANGULAR_MOTOR_TIMESCALE still has an effect when using mouselook control, and scales the global responsiveness of the angular motor. The VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, on the other hand, is ignored when using mouselook controls.

11.10. The Vertical Attractor

Some vehicles, like boats, should always keep their up-side up. This can be done by enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency, and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an efficiency of 1.0 will cause the spring to reach it's equilibrium with exponential decay.

llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 4.0);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5);

The vertical attractor is disabled by setting its timescale to anything larger than 300 seconds.

Note that by default the vertical attractor will prevent the vehicle from diving and climbing. So, if you wanted to make a airplane you would probably want to unlock the attractor around the pitch axis by setting the VEHICLE_FLAG_LIMIT_ROLL_ONLY bit:

llSetVehicleFlags(VEHICLE_FLAG_LIMIT_ROLL_ONLY);

11.11. Banking

The vertical attractor feature must be enabled in order for the banking behavior to function. The way banking works is this: a rotation around the vehicle's roll-axis will produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude of the yaw effect will be proportional to the VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's velocity along it's preferred axis of motion.

The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it's positive then any positive rotation (by the right-hand rule) about the roll-axis will effect a (negative) torque around the yaw-axis, making it turn to the right -- that is the vehicle will lean into the turn, which is how real airplanes and motorcycle's work. Negating the banking coefficient will make it so that the vehicle leans to the outside of the turn (not very "physical" but might allow interesting vehicles so why not?).

The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making banking vehicles do what you want rather than what the laws of physics allow. For example, consider a real motorcycle... it must be moving forward in order for it to turn while banking, however video-game motorcycles are often configured to turn in place when at a dead stop -- because they're often easier to control that way using the limited interface of the keyboard or game controller. The VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic banking by functioning as a slider between a banking that is correspondingly totally static (0.0) and totally dynamic (1.0). By "static" we mean that the banking effect depends only on the vehicle's rotation about it's roll-axis compared to "dynamic" where the banking is also proportional to it's velocity along it's roll-axis. Finding the best value of the "mixture" will probably require trial and error.

The time it takes for the banking behavior to defeat a pre-existing angular velocity about the world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to bank quickly then give it a banking timescale of about a second or less, otherwise you can make a sluggish vehicle by giving it a timescale of several seconds.

11.12. Friction Timescales

VEHICLE_LINEAR_FRICTION_TIMESCALE is a vector parameter that defines the timescales for the vehicle to come to a complete stop along the three local axes of the vehicle's reference frame. The timescale along each axis is independent of the others. For example, a sliding ground car would probably have very little friction along its x- and z-axes (so it can easily slide forward and fall down) while there would usually significant friction along its y-axis:

llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1000, 1000, 3>);

Remember that a longer timescale corresponds to a weaker friction, hence to effectively disable all linear friction you would set all of the timescales to large values.

Setting the linear friction as a scalar is allowed, and has the effect of setting all of the timescales to the same value. Both code snippets below are equivalent, and both make friction negligible:

// set all linear friction timescales to 1000
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1000, 1000, 1000>);

// same as above, but fewer characters
llSetVehicleFloatParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, 1000);

VEHICLE_ANGULAR_FRICTION_TIMESCALE is also a vector parameter that defines the timescales for the vehicle to stop rotating about the x-, y-, and z-axes, and are set and disabled in the same way as the linear friction.

11.13. Buoyancy

The vehicle has a built-in buoyancy feature that is independent of the llSetBuoyancy call. It is recommended that the two buoyancies do not mix! To make a vehicle buoyant, set the VEHICLE_BUOYANCY parameter to something between -1.0 (extra gravity) to 1.0 (full anti-gravity).

The buoyancy behavior is independent of hover, however in order for hover to work without a large offset of the VEHICLE_HOVER_HEIGHT, the VEHICLE_BUOYANCY should be set to 1.0.

It is not recommended that you mix vehicle buoyancy with the llSetBuoyancy script call. It would probably cause the object to fly up into space.

11.14. Hover

The hover behavior is enabled by setting the VEHICLE_HOVER_TIMESCALE to a value less than 300 seconds; larger timescales totally disable it. Most vehicles will work best with short hover timescales of a few seconds or less. The shorter the timescale, the faster the vehicle will slave to is target height. Note, that if the values of VEHICLE_LINEAR_FRICTION_TIMESCALE may affect the speed of the hover.

Hover is independent of buoyancy, however the VEHICLE_BUOYANCY should be set to 1.0, otherwise the vehicle will not lift itself off of the ground until the VEHICLE_HOVER_HEIGHT is made large enough to counter the acceleration of gravity, and the vehicle will never float all the way to its target height.

The VEHICLE_HOVER_EFFICIENCY can be thought of as a slider between bouncy (0.0) and smoothed (1.0). When in the bouncy range the vehicle will tend to hover a little lower than its target height and the VEHICLE_HOVER_TIMESCALE will be approximately the oscillation period of the bounce (the real period will tend to be a little longer than the timescale).

For performance reasons, until improvements are made to the Second Life physics engine the vehicles can only hover over the terrain and water, so they will not be able to hover above objects made out of primitives, such as bridges and houses. By default the hover behavior will float over terrain and water, however this can be changed by setting some flags:

If you wanted to make a boat you should set the VEHICLE_HOVER_WATER_ONLY flag, or if you wanted to drive a hover tank under water you would use the VEHICLE_HOVER_TERRAIN_ONLY flag instead. Finally, if you wanted to make a submarine or a balloon you would use the VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT. Note that the flags are independent of each other and that setting two contradictory flags will have undefined behavior. The flags are set using the script call llSetVehicleFlags().

The VEHICLE_HOVER_HEIGHT determines how high the vehicle will hover over the terrain and/or water, or the global height, and has a maximum value of 100 meters. Note that for hovering purposes the "center" of the vehicle is its "center of mass" which is not always obvious to the untrained eye, and it changes when avatar's sit on the vehicle.

11.15. Reference Frame

The vehicle relies on the x- (at), y- (left), and z- (up) axes in order to figure out which way it prefers to move and which end is up. By default these axes are identical to the local axes of the root primitive of the object, however this means that the vehicle's root primitive must, by default, be oriented to agree with the designed at, left, and up axes of the vehicle. But, what if the vehicle object was already pre-built with the root primitive in some non-trivial orientation relative to where the vehicle as a whole should move? This is where the VEHICLE_REFERENCE_FRAME parameter becomes useful; the vehicle's axes can be arbitrarily reoriented by setting this parameter.

As an example, suppose you had built a rocket out of a big cylinder, a cone for the nose, and some stretched cut boxes for the fins, then linked them all together with the cylinder as the root primitive. Ideally the rocket would move nose-first, however the cylinder's axis of symmetry is its local z-axis while the default "at-axis" of the vehicle, the axis it will want to deflect to forward under angular deflection, is the local x-axis and points out from the curved surface of the cylinder. The script code below will rotate the vehicle's axes such that the local z-axis becomes the "at-axis" and the local negative x-axis becomes the "up-axis":

// rotate the vehicle frame -PI/2 about the local y-axis (left-axis)
rotation rot = llEuler2Rot(<0, PI/2, 0>);
llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, rot);

Another example of how the reference frame parameter could be used is to consider flying craft that uses the vertical attractor for stability during flying but wants to use VTOL (vertical takeoff and landing). During flight the craft's dorsal axis should point up, but during landing its nose-axis should be up. To land the vehicle: while the vertical attractor is in effect, rotate the existing VEHICLE_REFERENCE_FRAME by +PI/2 about the left-axis, then the vehicle will pitch up such that it's nose points toward the sky. The vehicle could be allowed to fall to the landing pad under friction, or a decreasing hover effect.

C.24. Vehicle Parameters

Parameters

VEHICLE_LINEAR_FRICTION_TIMESCALE

A vector of timescales for exponential decay of the vehicle's linear velocity along its preferred axes of motion (at, left, up). Range = [0.07, inf) seconds for each element of the vector.

VEHICLE_ANGULAR_FRICTION_TIMESCALE

A vector of timescales for exponential decay of the vehicle's angular velocity about its preferred axes of motion (at, left, up). Range = [0.07, inf) seconds for each element of the vector.

VEHICLE_LINEAR_MOTOR_DIRECTION

The direction and magnitude (in preferred frame) of the vehicle's linear motor. The vehicle will accelerate (or decelerate if necessary) to match its velocity to its motor. Range of magnitude = [0, 30] meters/second.

VEHICLE_LINEAR_MOTOR_OFFSET

The offset point from the vehicle's center of mass at which the linear motor's impulse is applied. This allows the linear motor to also cause rotational torque. Range of magnitude = [0, 100] meters.

VEHICLE_LINEAR_MOTOR_TIMESCALE

The timescale for exponential approach to full linear motor velocity.

VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE

The timescale for exponential decay of the linear motor's magnitude.

VEHICLE_ANGULAR_MOTOR_DIRECTION

The direction and magnitude (in preferred frame) of the vehicle's angular motor.The vehicle will accelerate (or decelerate if necessary) to match its velocity to its motor.

VEHICLE_ANGULAR_MOTOR_TIMESCALE

The timescale for exponential approach to full angular motor velocity.

VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE

The timescale for exponential decay of the angular motor's magnitude.

VEHICLE_HOVER_HEIGHT

The height (above the terrain or water, or global) at which the vehicle will try to hover.

VEHICLE_HOVER_EFFICIENCY

A slider between minimum (0.0 = bouncy) and maximum (1.0 = fast as possible) damped motion of the hover behavior.

VEHICLE_HOVER_TIMESCALE

The period of bounce (or timescale of exponential approach, depending on the hover efficiency) for the vehicle to hover to the proper height.

VEHICLE_BUOYANCY

A slider between minimum (0.0) and maximum anti-gravity (1.0).

VEHICLE_LINEAR_DEFLECTION_EFFICIENCY

A slider between minimum (0.0) and maximum (1.0) deflection of linear velocity. That is, it's a simple scalar for modulating the strength of linear deflection.

VEHICLE_LINEAR_DEFLECTION_TIMESCALE

The timescale for exponential success of linear deflection. It is another way to specify how much time it takes for the vehicle's linear velocity to be redirected to it's preferred axis of motion.

VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY

A slider between minimum (0.0) and maximum (1.0) deflection of angular orientation. That is, it's a simple scalar for modulating the strength of angular deflection such that the vehicle's preferred axis of motion points toward it's real velocity.

VEHICLE_ANGULAR_DEFLECTION_TIMESCALE

The timescale for exponential success of angular deflection. It's another way to specify the strength of the vehicle's tendency to reorient itself so that it's preferred axis of motion agrees with it's true velocity.

VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY

A slider between minimum (0.0 = wobbly) and maximum (1.0 = firm as possible) stability of the vehicle to keep itself upright.

VEHICLE_VERTICAL_ATTRACTION_TIMESCALE

The period of wobble, or timescale for exponential approach, of the vehicle to rotate such that it's preferred "up" axis is oriented along the world's "up" axis.

VEHICLE_BANKING_EFFICIENCY

A slider between anti (-1.0), none (0.0), and maximum (1.0) banking strength.

VEHICLE_BANKING_MIX

A slider between static (0.0) and dynamic (1.0) banking. "Static" means the banking scales only with the angle of roll, whereas "dynamic" is a term that also scales with the vehicle's linear speed.

VEHICLE_BANKING_TIMESCALE

The timescale for banking to exponentially approach it's maximum effect. This is another way to scale the strength of the banking effect, however it affects the term that is proportional to the difference between what the banking behavior is trying to do, and what the vehicle is actually doing.

VEHICLE_REFERENCE_FRAME

A rotation of the vehicle's preferred axes of motion and orientation (at, left, up) with respect to the vehicle's local frame (x, y, z).

C.25. Vehicle Flags

Flags

VEHICLE_FLAG_NO_DEFLECTION_UP

This flag prevents linear deflection parallel to world z-axis. This is useful for preventing ground vehicles with large linear deflection, like bumper cars, from climbing their linear deflection into the sky.

VEHICLE_FLAG_LIMIT_ROLL_ONLY

For vehicles with vertical attractor that want to be able to climb/dive, for instance, airplanes that want to use the banking feature.

VEHICLE_FLAG_HOVER_WATER_ONLY

Ignore terrain height when hovering.

VEHICLE_FLAG_HOVER_TERRAIN_ONLY

Ignore water height when hovering.

VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT

Hover at global height instead of height above ground or water.

VEHICLE_FLAG_HOVER_UP_ONLY

Hover doesn't push down. Use this flag for hovering vehicles that should be able to jump above their hover height.

VEHICLE_FLAG_LIMIT_MOTOR_UP

Prevents ground vehicles from motoring into the sky. This flag has a subtle effect when used with conjunction with banking: the strength of the banking will decay when the vehicle no longer experiences collisions. The decay timescale is the same as VEHICLE_BANKING_TIMESCALE . This is to help prevent ground vehicles from steering when they are in mid jump.

VEHICLE_FLAG_MOUSELOOK_STEER

Steer the vehicle using the mouse. Use this flag to make the angular motor try to make the vehicle turn such that its local x-axis points in the same direction as the client-side camera.

VEHICLE_FLAG_MOUSELOOK_BANK

Same as above, but relies on banking. It remaps left-right motions of the client camera (also known as "yaw") to rotations about the vehicle's local x-axis.

VEHICLE_FLAG_CAMERA_DECOUPLED

Makes mouselook camera rotate independently of the vehicle. By default the client mouselook camera will rotate about with the vehicle, however when this flag is set the camera direction is independent of the vehicle's rotation.

C.26. Vehicle Types

Types

VEHICLE_TYPE_SLED

Simple vehicle that bumps along the ground, and likes to move along it's local x-axis.

// most friction for left-right, least for up-down
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <30, 1, 1000> );

// no angular friction
llSetVehicleVectorParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, <1000, 1000, 1000> );

// no linear motor
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 120 );

// no angular motor
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 120 );

// no hover (but with timescale of 10 sec if enabled)
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 10 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 10 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 0 );

// maximum linear deflection with timescale of 1 second
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 1 );

// no angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 10 );

// no vertical attractor (doesn't mind flipping over)
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 1000 );

// no banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 1 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 10 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT
                      | VEHICLE_FLAG_HOVER_UP_ONLY );

// set these flags (the limit_roll flag will have no effect
// until banking is enabled, if ever)
llSetVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                   | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                   | VEHICLE_FLAG_LIMIT_MOTOR_UP );

VEHICLE_TYPE_CAR

Another vehicle that bounces along the ground but needs the motors to be driven from external controls or timer events.

// most friction for left-right, least for up-down
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <100, 2, 1000> );

// no angular friction
llSetVehicleVectorParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, <1000, 1000, 1000> );

// linear motor wins after about a second, decays after about a minute
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 1 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor wins after a second, decays in less time than that
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 1 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.8 );

// no hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 0 );

// maximum linear deflection with timescale of 2 seconds
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 2 );

// no angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 10 );

// critically damped vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 10 );

// weak negative critically damped banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, -0.2 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 1 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 1 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);

// set these flags
llSetVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                   | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                   | VEHICLE_FLAG_HOVER_UP_ONLY
                   | VEHICLE_FLAG_LIMIT_MOTOR_UP );

VEHICLE_TYPE_BOAT

Hovers over water with lots of friction and some angular deflection.

// least for forward-back, most friction for up-down
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <10, 3, 2> );

// uniform angular friction (setting it as a scalar rather than a vector)
llSetVehicleFloatParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, 10 );

// linear motor wins after about five seconds, decays after about a minute
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor wins after four seconds, decays in same amount of time
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 4 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 4 );

// hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 2.0 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 1 );

// halfway linear deflection with timescale of 3 seconds
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 3 );

// angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 5 );

// somewhat bouncy vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 5 );

// weak negative damped banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, -0.3 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 0.8 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 1 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);

// set these flags
llSetVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                   | VEHICLE_FLAG_HOVER_WATER_ONLY
                   | VEHICLE_FLAG_HOVER_UP_ONLY
                   | VEHICLE_FLAG_LIMIT_MOTOR_UP );

VEHICLE_TYPE_AIRPLANE

Uses linear deflection for lift, no hover, and banking to turn.

// very little friction along forward-back axis
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <200, 10, 5> );

// uniform angular friction
llSetVehicleFloatParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, 20 );

// linear motor
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 2 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 4 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 8 );

// no hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 0 );

// linear deflection
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 0.5 );

// angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 1.0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 2.0 );

// vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.9 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 2 );

// banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 0.7 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 2 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                      | VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT
                      | VEHICLE_FLAG_HOVER_UP_ONLY
                      | VEHICLE_FLAG_LIMIT_MOTOR_UP );

// set these flags
llSetVehicleFlags( VEHICLE_FLAG_LIMIT_ROLL_ONLY );

VEHICLE_TYPE_BALLOON

Hover, and friction, but no deflection.

// uniform linear friction
llSetVehicleFloatParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, 5 );

// uniform angular friction
llSetVehicleFloatParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, 10 );

// linear motor
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 6 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 10 );

// hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 5 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0.8 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 10 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 1 );

// no linear deflection
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 5 );

// no angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 5 );

// no vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 1000 );

// no banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 0.7 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 5 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove all flags
llRemoveVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                      | VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT
                      | VEHICLE_FLAG_HOVER_UP_ONLY
                      | VEHICLE_FLAG_LIMIT_MOTOR_UP );


Lindogram

Lindogram (abbreviated Lg) is a name, made up by one of us non-Lindens, for the units of mass returned by llGetMass and llGetObjectMass. The official SL documentation does not specify the units returned by these functions, and Wiki articles often assume that the returned values represent kilograms. But if the values were kilograms, SL matter would be about 100 times less dense than its RL conterpart.

Given the use of Standard International units elsewhere in SL, one has to wonder if the programmer who first implemented llGetMass intended to use kilograms as the unit, but made a mistake when converting the density of water (1 gram per cubic centimeter) to kilograms per cubic meter. Once done, changing the units would break every script that used physics, so it would have been less disruptive to just not specify the units.

So geocentrism is okay, but SL matter being a hundred times less dense than real matter isn't? --gabe

Not exactly geocentrism as we're not its center as such, but anyhow:P The sun thing is just an odd fact of life, the mass thing is a more direct confusing thing when you interpret or want to understand what you are doing. My take, anyway.

Yes, if you are scripting something physical and you want to apply any of your RL intuition, it's important not to think of the mass units as kilograms. It would probably be easiest if the Lindens simply said that the units returned by llGetMass are 100 kg.

Physics | Mass


Line

A line of code is one line of text in a script's source code.

To not run a line of code, comment it out.

Glossary


linkmessage

Link Messages

Link messages are a fast and efficient means to communicate between two or more scripts contained within a prim or object.

Link messages are sent by the llMessageLinked function and handled by the link_message event. Link messages are a much faster and more secure form of communication than chat, but only work within linked sets of objects (or within a single object). They don't work between separate unlinked objects, even if the two are intersecting, nor between attachments attached to different points on the same avatar.

Function: llMessageLinked
Event: link_message
Example: See ExampleLinkMessage.

Link | Communications


LinkMessages

LinkMessage


Lisp

Lisp is the second-oldest programming language still in use today. There's a lot that could be said about Lisp, much of it potentially inflammatory. The most important thing about Lisp is that the programs are written in exactly the same way as the data structures: the code is data. Everything else flows from this.

John Pallister has added this page because he would like to use Lisp to develop metaprogramming tools for LSL, and then develop novel and intriguing structures within SL. Unfortunately, finding the time to work on this is proving difficult. But such is life.

Why do we have this page? I've got a thousand projects and i haven't created pages for them, could you remove this page? For this page to be legitamized it needs to tied into the wiki more tightly then just being an old scripting language - BW


Literal

A literal is any value in a program that is actually typed out in its source code.

For example:
"A literal"
The above is a literal string, but a variable containing the same value is not.

LSL | value | variable


LiveJournal

LiveJournal is a free journaling (or, as some like to call it, blogging) service.

There is a (not terribly active) SecondLife LiveJournal community.


LL

LL is the abbreviation of Linden Lab ("Lab" not "Labs").

Linden Lab | Linden | Lindens


llDetectedSex

THIS IS NOT A REAL FUNCTION (ya, we wish it was)

integer llDetectedSex(integer num)

Returns the sex for the agent "num" detected.
0 for male.
1 for female.

If the object is not an AGENT, llDetectedSex will still return a 1 or a 0. Prims aparently are not unisex.
This begs the question of where do prims come from? Is deleting them genocide?
The Mother Prim? Maybe the deleted prims will goes back to the Great Prim? :P
SAVE THE PRIMS!

Q: Would a massive cleanup of temp_on_rez objects be considered "primocide"?
A: I don't think so, i believe that it is justified since they were marked for death since birth; they were destined to die. But the better question: "Is it right to sentance a prim to a short, temp_on_rez, life?" -- BW

Caused some controversy at its introduction. Some made the reasonable argument that this function should have been llDetectedGender, and that it didn't say much about sex at all. Some took exception to the notion that men were "FALSE." Others predictably considered the function's introduction a confirmation of endemic shallowness. Still others demanded a corresponding llDetectedSexualPreference be created.

May be implemented by 2.10.9b but not yet implemented as of 1.8.3

btw jk, I was just playing with the sandbox and aparently created this page

Actually, Woudln't it make more since to use 0 for female and 1 for male, They look more anatomically correct - ChadStatosky
That would require LSL to make more sense than it does - Ice
I'm just noticing how with this current setup (female > male)... I suppose this is some sort of statement? - AakanaarLaSalle
I think all it says, for bits, that women turn them on. - BW"
"I suspect it's a feminist thing. OMG DON'T SAY MALE > FEMALE. IT'S MUCH BETTER TO SAY FEMALE > MALE"
Functions - Humor
(This page is a joke)


llGodLikeRezObject

llGodLikeRezObject(key inventory, vector pos)

If you don't already know how to use this, you can't use it.

This function, if you have the god bit set (only Lindens :-P) allows you to rez an object by key (specified by the inventory argument) at region location: pos.

If you're not a Linden, you want llRezObject or llRezAtRoot.

Functions


llHTTPRequestThrottle1

The new figure is now: 1 per second per object per sim!

http://blog.secondlife.com/2006/09/01/second-life-1121-release-on-september-6/

Posted here by Kelly Linden:

llHTTPRequest is throttled in the following manner:
More than 20 requests per 100 seconds, across all objects owned by the same owner in the same sim will trigger the throttle.

The objects will become unthrottled quicker if ALL requests stop, and take a little longer if requests continue.

If a request is throttled, llHTTPRequest will return a NULL key. This means you can know immediately if a request happened or not.

Examples:
If you only own 1 object in a sim that is making requests:
* sending 1 request every 5 seconds (or longer) will never get throttled.
* sending bursts of 20 requests (or fewer) every 100 seconds (or longer) will never get throttled.

If you own 5 objects in a sim that are making requests:
* The total number of requests from all 5 objects must not pass the throttle
* each object sending 1 request every 25 seconds will not hit the throttle.
* each object busting 4 requests (or fewer) every 100 seconds (or longer) will not hit the throttle.

Back to llHTTPRequest | Functions | Communications


llHTTPRequestThrottle2

Posted here by Kelly Linden:

Short answer: If all objects you own in the sim stop making requests completely, the throttle will completely clear in 200 seconds.

Medium answer: You can start making requests again sooner, however you may also hit the throttle sooner.

Long answer: The throttle uses a moving window to continue to block things that don't slow down. Starting with the first request, requests are counted in 100 second buckets. For 100 seconds from the first request all requests go into a single bucket, 100 seconds later a new bucket is started ... but the last bucket is kept around. If the current bucket has more than the throttle limit (20) then requests will be blocked. If the current total + (% of time remaining in this bucket * total from last bucket) is more than the throttle (20) then requests are blocked.

Simple eh?

So, you don't want to do while(key!=null) loops because you will just hit the throttle quick then be blocked. I would recomend just spacing out each request at least 5 seconds, or if bursts are needed then spacing 5*(requests / burst) seconds.

back | Functions | Communications


llPizza

llPizza(key agent,integer constant)

This function redirects key agent to a predetermined site, given by our constant.
This function was created for April Fools Day 2005 and is kept in for the sake of practical demonstration of global variables, and llLoadURL functions.
My understanding was this wasn't associated with April Fools, and it's a misrepresentation to say it was. It was intended to be in the vein of EQ2 /pizza. The debate over this function was about if it was right for users to post fake functions masquerading as really functions via the "ll" prefix. --BW

Order constants (for llPizza):
Constant | Description
ORDER_PIZZA_HUT | Takes user to Pizza Hut's order page
ORDER_PAPA_JOHNS | Takes user to Papa John's order page
ORDER_DOMINOS | Takes user to Domino's web page

Example code:

integer ORDER_PIZZA_HUT = 0;
integer ORDER_PAPA_JOHNS = 1;
integer ORDER_DOMINOS = 2;
llPizza(key agent, integer constant)
{
    if(constant == ORDER_PIZZA_HUT)
        llLoadURL(agent,"Click Here to Order Pizza from Pizza Hut!","http://www.pizzahut.com/order/");
    else if(constant == ORDER_PAPA_JOHNS)
        llLoadURL(agent,"Click Here to Order Pizza from Papa Johns!","http://www.papajohnsonline.com/index.html");
    else if(constant == ORDER_DOMINOS)
        llLoadURL(agent,"Click Here to Order Pizza from Dominos!","http://www.dominos.com/");
}

default
{
    state_entry()
    {
        llPizza(llGetOwner(),ORDER_DOMINOS);
    }
}

Humor


llRefreshPrimURL()
Reloads the web page shown on the sides of the object.

This feature has not yet been implemented.

Web integration has been axed from 1.7 and won't be ready till further notice.

See llSetPrimURL.


llResetLandBanList()

Removes all residents from the land ban list.

To add a user to the land ban list, use llAddToLandBanList.
To remove a user from the land ban list, use llRemoveFromLandBanList.

Functions | Land


llResetLandPassList()

Removes all residents from the land access/pass list.

To add a user to the land pass list, use llAddToLandPassList.
To remove a user from the land pass list, use llRemoveFromLandPassList.

Functions | Land


llSetMass

llSetMass() does not exist. No evidence has been brought forward to suggest that it will ever exist. There has never been a (recorded) Linden comment on the subject in a 3 year period.

llGetMass


llSetPrimURL

llSetPrimURL(string url)

Updates the URL for the web page shown on the sides of the object.

This feature has not yet been implemented.

Web integration has been axed from 1.7 and won't be ready till further notice.

Also see llRefreshPrimURL.


llSetTimer

llSetTimer is not a valid SL function. Perhaps you are looking for llSetTimerEvent?


llSound
This function has been deprecated. It's been replaced by llPlaySound, llLoopSound, llSetSoundQueueing and llTriggerSound.

Note that llSound does do one thing its replacement functions don't cover: the sound position is "attached" to the object as with llPlaySound, but can be triggered several times in succession like llTriggerSound.

Functions | DeprecatedFunctions | Sound


llSoundPreload

llSoundPreload(key sound)

This is the same function as llPreloadSound. This function is deprecated.

Deprecated Functions | Functions | Sound


llTeleportAgent

This function does not exist!

See FutureRevisions

This function was in the 1.8.0 release notes as a "future function".

Functions | Agent/Avatar | Teleport


llTeleportAttachedAvatar

This function does not exist.

See: llTeleportAgent


llXorBase64Strings

This function has been deprecated in favour of llXorBase64StringsCorrect.

string llXorBase64Strings(string s1, string s2)

Performs an xor on two Base64 strings and returns a Base64 string. s2 repeats if it is shorter than s1.

This function is for encrypting data. Put your data in s1 and your key in s2 to encrypt or put the encrypted data into s1 with the same key in s2 to decrypt again.

Use llStringToBase64 and llBase64ToString for converting to and from Base64 respectively.

While fine for most SL encryption purposes, Xor'ing a string with a password is considered a very weak encryption algorithm. Do not use this for anything that will cost you RL $ if compromised (such as an automated RL - SL currency exchanger). Also, consider carefully how much damage a malicious party might be able to wreak if your encryption is hacked.

This type of encryption is similar to something called a one time pad that is if s2 is the same length as s1, is generated randomly (not pseudo randomly), and s2 is used only once. When used in this way this function is provably unbreakable. However implementing this function in this way is difficult to say the least. However with a little ingenuity and some creative problem solving we might be able to create different variations of this with acceptable reductions in security with minimal resource requirements(execution time, memory). IE have a new key created for each message maybe based on some universally changing constant like the server timestamp.

And as it turns out the quickest way to weaken xor type encryption is to use the key more than once, either by repeating a small key across a longer message or to use the same key on more than one message. It is only strong if the key is used only once. So this type of encryption when used with the same key over and over is supposedly rather trivial to break.

Example:
Note: this example code always produces the same result as llXorBase64StringsCorrect, but only the same result as llXorBase64Strings when s2 is longer than s1 (unless s2 is a multiple of 3 bytes in length -see discussion at end of page).

default {
    state_entry() {
        string  data = "I like bananas."; // the data we want to encrypt
        string  pass = "monkey"; // the encryption key

        string  crypt;  // for storing the encrypted data

        // convert both data and key to base64 and encrypt the data with the key
        // then store the encrypted data in 'crypt'
        crypt = llXorBase64Strings(llStringToBase64(data), llStringToBase64(pass));

        llSay(0, crypt); // this could now be used to email the data around
        // someone without the correct key wouldn't be able to (easily) get the plaintext

        // decrypt it again, using the same key, and convert back to plaintext
        llSay(0, llBase64ToString(llXorBase64Strings(crypt, llStringToBase64(pass))));
        // as you can see, applying the same key to encrypted data again decrypts it
    }
}

Tip: If you see an "=" character in the middle of your encrypted string, you probably forgot to first convert your data to Base64.

If you want to exchange data that passed through this function from outside of LSL, the following Python snippet might be helpful to you:

from base64 import b64encode, b64decode

def XorBase64Strings(s1, s2):
    """Emulates the LSL function llXorBase64Strings:
    Performs an exclusive or on two Base64 strings and returns a Base64 string. s2 repeats if it is shorter than s1."""
    s1 = b64decode(s1)		# internally this works with
    s2 = b64decode(s2)		# the decoded strings
    while len(s2) < len(s1): s2 += s2		# repeat s2 until it's at least as long as s1
    out = ""
    for index in range(len(s1)):		# take each character from s1
        out += chr( ord(s1[index]) ^ ord(s2[index]) )		# and XOR it with one from s2
    return b64encode(out)

data = "I like bananas."		# the data we want to encrypt
ckey = "monkey"		# the encryption key

crypt = XorBase64Strings(b64encode(data), b64encode(ckey))		# encode & encrypt

print crypt	# the base64 encoded result

decrypt = b64decode(XorBase64Strings(crypt, b64encode(ckey)))		# decrypt & decode

print decrypt		# the decrypted and base64 decoded data

# verify
if decrypt == data: print "This function seems to work..."
# this string is from calling the LSL function with the same parameters
if crypt == "JE8CAg4cTQ0PBQQXDBxA": print "... in the same way as the LSL function."

This function does the same thing as the one above, only in PHP:
<?php
function XorBase64Strings($s1, $s2)
{
    $s1 = base64_decode($s1);
    $s2 = base64_decode($s2);

    while(strlen($s2) < strlen($s1))
        $s2 .= $s2;

    return base64_encode($s1 ^ $s2);
}
?>

'1.9.0(18)' vs 'pre/post-1.9.0(18)' implementation

The 1.9.0(18) Grid update introducted a new implementation of this function. It was re-written to fix a sim crash bug, however, the output deviates from the original pre-update version. The 1.9.0(18) implementation is simpler. The difference is shown below through pseudo-code. The 1.9.0(21) update reverted this function's behavior to the pre-1.9.0(18) behavior and introduced a new llXorBase64StringsCorrect with the 1.9.0(18) behavior.

1.9.0(18) implementation

1. Decode s1 and s2 from Base64 into byte strings
2. Bit-wise XOR corresponding bytes of s1 and s2 in sequence. If s2 is shorter than s1, it is simply repeated (i.e. the index into s2 is the index into s1 % length of s2).
3. Re-encode the result byte string into Base64 encoding and return as result

Pre/Post-1.9.0(18) implementation

This function's original implementation is more efficient, but gives a different result than the new one, only if s2 is shorter than s1 and s2 isn't a multiple of 3 bytes/chars long. As of 1.9.0(21), this is the behavior of this function.

1. Convert the Base64 encoding of s1 and s2 into a sequence of 6bit values. (by converting each Base64 character into its 6bit index - see Wikipedia Base64).
2. Bit-wise XOR corresponding 6bit values of s1 and s2 in sequence. If s2 is shorter than s1, it is simply repeated (i.e. the index into s2 is the index into s1 % length of s2).
3. Re-convert the resulting sequence of 6bit values into their Base64 characters (by using the 6bit values as indices into the Base64 character set)

The reason this differs from the new implemenation, is that the length of the byte string represented by s2 may not be a multiple of 3 bytes long. The initial encoding of s2 into Base64 form involves taking each set of 3 bytes (3x8=24bits) 6 bits at a time and converting to 4 Base64 characters (4x6=24bits). However, if not a multiple of 3 bytes there would have been either 2bytes (16bits) or 1byte(8bits) left over at the end. The Base64 encoding scheme dictates padding to a multiple of 6bits with a 0 bit to obtain the final Base64 character. Hence the Base64 encoded form of s2 may have either 4bits (16 - 6x2) or 2 bits (8 - 1x6) of 0 padding at its end. This padding is included when s2 is repeated above in the case when s2 is shorter than s1. Hence the different output.

Here is an example of different output.

If you XOR these two strings using llXorBase64Strings:
llXorBase64Strings("OnAxODcyMzRhYmNkZWZnaGkgYWJjZEBlZmdoLmlqa2wubW5v", "QUJDREVGR0hJSktMTQ==")

the 'pre/post-1.9.0(18)' implementation will produce
"ezJyfHJ0dHwoKCgoKGJzTF1kNQYX4NTB0qO4byspLyloKiYm"

the '1.9.0(18)' implementation gave:
"ezJyfHJ0dHwoKCgoKCclKy1lJyUrLQouKiopbCouLippJScl"

(which is the same result you'll get from the new llXorBase64StringsCorrect function)

(If you really need code compatible with the current ('pre/post-1.9.0(18)') implementation of llXorBase64Strings, IM Cenji Neutra for a C# implementation)

Functions | DeprecatedFunctions | Crypto | Communications


local

In general terms, local (vs. global) describes something specific to a small, localized area. In LSL something can be local to script's scope or to a rotation axis' local coordinates.

local variable
event handlers

script | scope | global | rotation


LocalCoordinate

Local Coordinates

An abbreviated form of "object-local coordinates", local coordinates assume the origin (<0,0,0>) of the x, y, and z axes to be the same as the center of the object. Object-local coordinates are mostly used when dealing with child prims, in which case the origin is the center of the parent prim.

I used to think that local rotations were impossible through LSL, but I recently stumbled upon the rotation section which clearly shows an awesome way to get local rotating working very well. Just go to the "Combining Rotations" section to check it out! - Dave Zeeman the idiot.

We now have llGetLocalRot and llSetLocalRot for extra easy local rotation. Hooray for progress! -Nep
Coordinates | Global Coordinates | Region Coordinates | Region Corner


Local Variables

A local variable is a variable that exists within a code block. (Between an opening and closing brace -- { }) They are only accessible from within the same context (scope) or area of the script where they were declared. (Compare with global variables, which can be accessed and referred to in any part of the script.)

All code within the local variable's code block has access to the variable, provided it follows the local variable declaration. (Remember, operations execute in the order in which you write them!) This includes code blocks declared within the local variable's code block.

Example:
myFunction() {
    anInteger = 5; // ILLEGAL!!! anInteger hasn't been declared yet!

    integer anInteger; // Declaration (creation) of a local variable.

    if(someCondition) {
        anInteger = 4; // Valid!
    }

    anInteger = 12; // Valid!
}

default {
    state_entry() {
        anInteger = 122;
        // ^^^^ ILLEGAL!!! anInteger was declared within the CodeBlock of myFunction(),
        // So technically, it doesnt exist outside that CodeBlock.
        // In order to use anInteger again, you have to re-declare it within state_entry(), however,
        // even if you do, it will have no connection to anInteger in myFunction().

        integer anotherInteger; // Declaration (creation of a global variable).
        anotherInteger = 3; // Valid!

        if(someCondition) {
            anotherInteger = 492; // Valid!

            if(anotherCondition) {
                anotherInteger = 122; // Valid!
            }
        }
    }

    touch_start(integer n) {
        anotherInteger = 12; // ILLEGAL!!! anotherInteger was declared within state_entry()'s CodeBlock.
        // It must be re-declared to use it in touch_start.

        if(someCondition) {
            integer yetAnotherInteger; // Declaration.
            yetAnotherInteger = 4; // Valid!

            if(someOtherCondition) {
                yetAnotherInteger = 1239; // Valid!
            }
        }

        yetAnotherInteger = 1223;
        //^^^^ ILLEGAL!!! yetAnotherInteger is declared within the CodeBlock of if(someCondition),
        // it cant be used **outside** of if(someCondition)'s CodeBlock.
    }
}

You can do many odd quirky things if you give two variables the same name. Espicially if they're a local and global variable.
Here's a situation you do NOT wanna be in:
Example (NOT RECOMENDED CODING PRACTICE):
integer myInteger = 3; //This is a global variable

integer myFunction(float myFloat)
{
    integer myInteger = 6; //This is a local variable, with the same name as a global.
    // It is seriously bad coding practice, although legal, to define variables like this. Doing this is called "shadowing" a variable (I think).
    // Any changes made to myInteger within this function affect the variable declared here, NOT the global variable.
    // Anyplace within this function that uses myInteger, will be talking about the integer above, NOT the global variable.

    return myInteger;
}

default
{
    state_entry()
    {
        myInteger = 2; //This sets the global variable to 2
        llWhisper(0,(string)myFunction(1.0)); //This will whisper 6
        llWhisper(0,(string)myInteger); //This will whisper 2.

        // Note that myFunction did not change the value of the global, they are seperate variables.
    }
}

Compare with GlobalVariables.

Variable


LockmeisterBaseFurniture

Main script to put in the root/main poseball, the one that get cuffed

//KDC sample lockmeister furniture/device master script version 1.0
//
//INFO: this is a very basic multi point pose script, it combine the basic poseball style posing and the lockmeister pinging functions.

//NOTE: this script must be in the root prim of the object for working properly.
//...
//
//KDC, kyrah design concept

//this list contain the different lockmeister mooring points
//to check for, add/remove the ones you will use using the list at:
//http://www.lslwiki.com/lslwiki/wakka.php?wakka=exchangeLockMeisterSystem
//to get the proper codes
list requests = ["lcuff","rcuff"];

//this is the offset you want to avatar to sit from, it is a 3 direction vector, X, Y, Z and it is relative to the root prim of your object.
//NOTE: if the vector is 0,0,0 the script will NOT work
vector sit_offset = <0,0,0.01>;

//this is the rotational offset for the avatar position
vector sit_rotation = <0,0,0>; // pitch yaw and roll are in degres here.

string animation = "put your anim here";//the animation wich must be in the same prim as this script.

/////////////////////////////////////////////////////
/////////////////SCRIPT START HERE///////////////////
/////////////////////////////////////////////////////

//few lazy constants
integer link = CHANGED_LINK;
integer set = LINK_SET;
string stop = "LMstop";
string N = "";
integer lockm_chan = -8888;
integer trigger = PERMISSION_TRIGGER_ANIMATION;
//some more
integer chan_handler;
integer count = 0;
default
{
    state_entry()
    {
        //setting the pose offset for the chained user.
        llSitTarget(sit_offset,llEuler2Rot(sit_rotation*DEG_TO_RAD));
    }
    changed(integer change)
    {
        // sit/unsit detected
        if((change & link) == link)
        {
            key avatar = llAvatarOnSitTarget();

            if(avatar == NULL_KEY)//unsit
            {
                llMessageLinked(set,0,stop,N);//stop particle chains
                if(llGetPermissionsKey() != NULL_KEY)//flush animation
                    llStopAnimation(animation);
                llResetScript();//reset script
            }
            else //sit
                llRequestPermissions(avatar,PERMISSION_TRIGGER_ANIMATION);
        }
    }
    run_time_permissions(integer perms)
    {
        if((perms & trigger) == trigger)//we got animation permission
        {
            llStopAnimation("sit");//flush base sit anim
            llStartAnimation(animation);//play our anim
            chan_handler = llListen(lockm_chan,"","","");//open the listener
            llSetTimerEvent(20.0);
            integer i;
            for(i=0;i < llGetListLength(requests);i+=1)
                llWhisper(lockm_chan,(string)llAvatarOnSitTarget()+llList2String(requests,i));
                //we run quickly all the ping messages
        }
    }
    listen(integer channel,string name,key id,string message)
    {
        if(llGetSubString(message,0,35) == (string)llAvatarOnSitTarget())//we filter only LockM messages for us
        {
            message = llDeleteSubString(message,0,35);//removing the header
            if(llGetSubString(message,-2,-1) == "ok") //its a message of the "ok" type (ping return)
            {
                message = llDeleteSubString(message,-3,-1);//removing the ok tag
                if(llListFindList(requests,[message]) != -1)//its a message we are looking for
                    llMessageLinked(set,0,message,id);
            }
        }
    }
    timer()
    {
        llListenRemove(chan_handler);
        llSetTimerEvent(0.0);
    }
}

anchor script, this script go into each of the chain "emitters"

//KDC sample lockmeister furniture/device anchor script version 1.0
//
//INFO: this is a very basic multi point pose script, it combine the basic poseball style posing and the lockmeister pinging functions.

//NOTE: this script must be in the prims wich are the chain "emitters" of the object for working properly., it is suggested to "orient the anchor prim (if you use invisible anchors) so that the z axis of the anchor point roughly toward the emission direction
//...
//
//KDC, kyrah design concept

string mooring_point = "rcuff";//here you define a working mooring point, for the kit to work the same mooring point must be present here and in the list of the root script.

chain(key target)
//customize the particles to your need , see here for more infos, http://www.lslwiki.com/lslwiki/wakka.php?wakka=llParticleSystem
{
    llParticleSystem([
    PSYS_PART_FLAGS,PSYS_PART_FOLLOW_VELOCITY_MASK|PSYS_PART_TARGET_POS_MASK|PSYS_PART_EMISSIVE_MASK,
    PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_ANGLE,
    PSYS_PART_START_SCALE,<0.08,0.08,0.0>,  //particles size X and Y
    PSYS_PART_MAX_AGE,2.0, //age of particles in seconds
    PSYS_SRC_OUTERANGLE,0.0,
    PSYS_SRC_BURST_RATE,0.001,//how often to release particles
    PSYS_SRC_BURST_SPEED_MIN,0.1,//speed of particles
    PSYS_SRC_BURST_SPEED_MAX,0.1,
    PSYS_SRC_TEXTURE,"",//your custom texture
    PSYS_SRC_TARGET_KEY,target,
    PSYS_SRC_BURST_PART_COUNT,1,//how many particles per burst
    PSYS_SRC_ACCEL,<0,0,0>//add a little bit of negative Z if you want the chain to "dangle" a little
    ]);
}

/////////////////////////////////////////////////////
/////////////////SCRIPT START HERE///////////////////
/////////////////////////////////////////////////////
default
{

    link_message(integer snum,integer num,string str,key id)
    {
        if(str == mooring_point)//we draw a chain
            chain(id);
        else if(str == "LMstop")//we stop it
            llParticleSystem([]);
    }
}


LockmeisterOpenSource

Project started by Lynn Kukulcan for making lockmeister compliant items easily.

The scripts are in coloured syntax and notecards in simple monospace text

Lockmeister Chain Script
string  NotecardData;
key     NotecardDataID;
integer NotecardLineBeingRead = -1;
integer NotecardLineTotal     = -1;

list    Settings;

DataServer ( key QueryID, string Data )
{
    if ( QueryID == NotecardDataID )
    {
        // Just get the number of lines in the notecard being read.
        //
        if ( NotecardLineTotal == -1 )
        {
            NotecardLineTotal = ( integer ) Data;
        }
        // This is the first data read from the notecard.  Clear the NotecardData Variable by Overwriting it with New
        // Data.
        //
        else if ( NotecardLineBeingRead == 0 )
        {
            NotecardData = Data;
        }
        // Additional data from the notecard.  Append it to the NotecardData Variable instead of Overwriting it.
        //
        else if ( NotecardLineBeingRead > 0 )
        {
            NotecardData = NotecardData + "<|>" + Data;
        }

        // Increment Line Counter.
        //
        NotecardLineBeingRead = NotecardLineBeingRead + 1;

        // If we have not read the last line, then get the next line of data from the notecard.
        //
        if ( NotecardLineBeingRead < NotecardLineTotal )
        {
            NotecardDataID = llGetNotecardLine ( llGetScriptName ( ) + " - Data", NotecardLineBeingRead );
        }
        // We've read all the data from the notecard.
        //
        else if ( NotecardLineBeingRead == NotecardLineTotal )
        {
            // Parse to the Settins List.
            //
            Settings = llParseString2List ( NotecardData, [ "<|>" ], [ ] );

            // Say we're done!
            //
            llSay (0, llGetScriptName ( ) + " Initialized." );
        }
    }
}

Initialize ( )
{
    // Say we're starting to Initialize this script.  In this case, we're loading a menu.
    //
    llSay ( 0, llGetScriptName ( ) + " Initializing ... " );

    // Initiate the data server event by asking for the number of lines in the notecard associated with this script.
    //
    NotecardDataID = llGetNumberOfNotecardLines ( llGetScriptName ( ) + " - Data" );
}

LinkMessage ( integer LinkNumber, integer LinkChannel, string LinkData, key LinkUUID )
{
    // Parse incoming Linked Data.
    //
    list DataList = llParseString2List ( LinkData, [ ":" ], [ ] );

    // Lockmeister Reply.
    //
    if ( LinkChannel == -8888 )
    {
        // This refers our part.
        //
        if ( llList2String ( DataList, 0 ) == llList2String ( Settings, 1 ) + " ok" )
        {
            llParticleSystem
            (
                [
                    PSYS_SRC_TARGET_KEY, ( key ) llList2String ( DataList, 1 ),
                    PSYS_PART_START_SCALE, < .125, .125, .000 >,
                    PSYS_PART_START_COLOR, < 1.0, 0.0, 0.0 >,
                    PSYS_PART_FLAGS,
                    PSYS_PART_TARGET_POS_MASK | PSYS_PART_EMISSIVE_MASK
                ]
            );
        }
    }
    // This is a Sit On Me Message.
    //
    else if ( llList2String ( DataList, 0 ) == "Sit On Me!" )
    {
        // Someone else is talking to us.
        //
        if ( llList2String ( DataList, 1 ) == llList2String ( Settings, 0 ) )
        {
            // Avatar Disounted Other Object!
            //
            if ( llList2String ( DataList, 2 ) == "00000000-0000-0000-0000-000000000000" )
            {
                llParticleSystem ( [ ] );
            }
        }
    }
}

default
{
    dataserver ( key QueryID, string Data )
    {
        DataServer ( QueryID, Data );
    }

    link_message ( integer LinkNumber, integer LinkChannel, string LinkData, key LinkUUID )
    {
        LinkMessage ( LinkNumber, LinkChannel, LinkData, LinkUUID );
    }

    state_entry()
    {
        Initialize ( );
    }
}

Lockmeister Chain Script - Data
VQMUSB Bound<|>lcuff
Specifies which Object in the Link Set we're listening to.
Specifies the Lockmeister Attachment Point we're looking for.

Lockmeister Ping
string  NotecardData;
key     NotecardDataID;
integer NotecardLineBeingRead = -1;
integer NotecardLineTotal     = -1;

list    Settings;
string  AvatarKey;

DataServer ( key QueryID, string Data )
{
    if ( QueryID == NotecardDataID )
    {
        // Just get the number of lines in the notecard being read.
        //
        if ( NotecardLineTotal == -1 )
        {
            NotecardLineTotal = ( integer ) Data;
        }
        // This is the first data read from the notecard.  Clear the NotecardData Variable by Overwriting it with New
        // Data.
        //
        else if ( NotecardLineBeingRead == 0 )
        {
            NotecardData = Data;
        }
        // Additional data from the notecard.  Append it to the NotecardData Variable instead of Overwriting it.
        //
        else if ( NotecardLineBeingRead > 0 )
        {
            NotecardData = NotecardData + "<|>" + Data;
        }

        // Increment Line Counter.
        //
        NotecardLineBeingRead = NotecardLineBeingRead + 1;

        // If we have not read the last line, then get the next line of data from the notecard.
        //
        if ( NotecardLineBeingRead < NotecardLineTotal )
        {
            NotecardDataID = llGetNotecardLine ( llGetScriptName ( ) + " - Data", NotecardLineBeingRead );
        }
        // We've read all the data from the notecard.
        //
        else if ( NotecardLineBeingRead == NotecardLineTotal )
        {
            // Parse to the Settins List.
            //
            Settings = llParseString2List ( NotecardData, [ "<|>" ], [ ] );

            // Say we're done!
            //
            llSay (0, llGetScriptName ( ) + " Initialized." );
        }
    }
}

Initialize ( )
{
    // Say we're starting to Initialize this script.  In this case, we're loading a menu.
    //
    llSay ( 0, llGetScriptName ( ) + " Initializing ... " );

    // Setup our Listener.
    //
    llListen ( -8888, "", NULL_KEY, "" );

    // Initiate the data server event by asking for the number of lines in the notecard associated with this script.
    //
    NotecardDataID = llGetNumberOfNotecardLines ( llGetScriptName ( ) + " - Data" );
}

LinkMessage ( integer LinkNumber, integer LinkChannel, string LinkData, key LinkUUID )
{
    // Parse incoming Linked Data.
    //
    list    DataList = llParseString2List ( LinkData, [ ":" ], [ ] );
    integer Counter  = 0;

    // This is a Sit On Me Message.
    //
    if ( llList2String ( DataList, 0 ) == "Sit On Me!" )
    {
        // We're talking to ourselves.
        //
        if ( llList2String ( DataList, 1 ) == llGetObjectName ( ) )
        {
            // Avatar Upon Us!
            //
            if ( llList2String ( DataList, 2 ) == "00000000-0000-0000-0000-000000000000" )
            {
                AvatarKey = llList2String ( DataList, 2 );
            }
            else
            {
                // Store the Avatar Key for the Incoming Messages.
                //
                AvatarKey = llList2String ( DataList, 2 );

                // Go through the Settings List.
                //
                while ( Counter < llGetListLength ( Settings ) )
                {
                    // Call out all the Lockmeister Parts specified in the Settings Notecard.
                    //
                    llSay ( -8888, AvatarKey + llList2String ( Settings, Counter ) );

                    // Increment Count.
                    //
                    Counter = Counter + 1;
                }
            }
        }
    }
}

Listen ( integer Channel, string SpeakerName, key SpeakerUUID, string SpeakerMessage )
{
    // This is referring to our avatar.
    //
    if ( llGetSubString ( SpeakerMessage, 0, 35 ) == AvatarKey )
    {
        llMessageLinked
        (
            -1, -8888, llGetSubString ( SpeakerMessage, 36, llStringLength ( SpeakerMessage ) - 1 ) + ":" +
            ( string ) SpeakerUUID, NULL_KEY
        );
    }
}

default
{
    dataserver ( key QueryID, string Data )
    {
        DataServer ( QueryID, Data );
    }

    link_message ( integer LinkNumber, integer LinkChannel, string LinkData, key LinkUUID )
    {
        LinkMessage ( LinkNumber, LinkChannel, LinkData, LinkUUID );
    }

    listen ( integer Channel, string SpeakerName, key SpeakerUUID, string Message )
    {
        Listen ( Channel, SpeakerName, SpeakerUUID, Message );
    }

    state_entry()
    {
        Initialize ( );
    }
}

Lockmeister Ping - Data

lcuff
rcurr
llcuff
lrcuff

Sit On Me!

string  NotecardData;
key     NotecardDataID;
integer NotecardLineBeingRead = -1;
integer NotecardLineTotal     = -1;

list    Settings;

string  AvatarKey;

Changed ( integer Change )
{
    // Link Change.
    //
    if (Change & CHANGED_LINK)
    {
        // Be sure this is truly our Change.
        //
        if ( ( string ) llAvatarOnSitTarget ( ) != AvatarKey )
        {
            // Delay Link Message by Time Specified.
            //
            llSleep ( ( float ) llList2String ( Settings, 0 ) );

            // Get Avatar Key.
            //
            AvatarKey = ( string ) llAvatarOnSitTarget ( );

            // Transmit this to everyone else.
            //
            llMessageLinked ( -1, 0, "Sit On Me!:" + llGetObjectName ( ) + ":" + AvatarKey, NULL_KEY );
        }
    }
}

DataServer ( key QueryID, string Data )
{
    if ( QueryID == NotecardDataID )
    {
        // Just get the number of lines in the notecard being read.
        //
        if ( NotecardLineTotal == -1 )
        {
            NotecardLineTotal = ( integer ) Data;
        }
        // This is the first data read from the notecard.  Clear the NotecardData Variable by Overwriting it with New
        // Data.
        //
        else if ( NotecardLineBeingRead == 0 )
        {
            NotecardData = Data;
        }
        // Additional data from the notecard.  Append it to the NotecardData Variable instead of Overwriting it.
        //
        else if ( NotecardLineBeingRead > 0 )
        {
            NotecardData = NotecardData + "<|>" + Data;
        }

        // Increment Line Counter.
        //
        NotecardLineBeingRead = NotecardLineBeingRead + 1;

        // If we have not read the last line, then get the next line of data from the notecard.
        //
        if ( NotecardLineBeingRead < NotecardLineTotal )
        {
            NotecardDataID = llGetNotecardLine ( llGetScriptName ( ) + " - Data", NotecardLineBeingRead );
        }
        // We've read all the data from the notecard.
        //
        else if ( NotecardLineBeingRead == NotecardLineTotal )
        {
            // Parse to the Settins List.
            //
            Settings = llParseString2List ( NotecardData, [ "<|>" ], [ ] );

            // Say we're done!
            //
            llSay (0, llGetScriptName ( ) + " Initialized." );
        }
    }
}

Initialize ( )
{
    // Say we're starting to Initialize this script.  In this case, we're loading a menu.
    //
    llSay ( 0, llGetScriptName ( ) + " Initializing ... " );

    // Initiate the data server event by asking for the number of lines in the notecard associated with this script.
    //
    NotecardDataID = llGetNumberOfNotecardLines ( llGetScriptName ( ) + " - Data" );

    // needed for llAvatarOnSitTarget to work
    //
    llSitTarget ( < 0, 0, 0.1 >, ZERO_ROTATION );
}

default
{
    changed(integer Change)
    {
        Changed ( Change );
    }

    dataserver ( key QueryID, string Data )
    {
        DataServer ( QueryID, Data );
    }

    state_entry ( )
    {
        Initialize ( );
    }
}

Sit On Me! - Data

0.75
Delay before sending Link Messages.


LockMeisterSystem

the lockmeister system is a keyring of commands used for enhanced functions in bondage toys, its main function is to get the keys needed for particle chains, but as the protocol grow more and more commands are added. This protocol is maintained and designed by Kyrah Abattoir , im her inworld if you have suggestions.

Main features:
-simple to use
-flexible
-fully backward compatible
-sensorless key grabbing (possible on child prims too) for particle chains (several mooring points)
-boot/shoes animation overrider controling (on/off)
-color propagation in a lockmeister item set

All lockmeister items listen on channel -8888.
Its strongly recomended to use only the llWhisper function for sending the commands

message shape: <avatar key><command>

current ping commands:
rcuff - ask for a ping of the right wrist cuff
lcuff - ask for a ping of the left wrist cuff
llcuff - ask for a ping of the left ankle cuff
rlcuff - ask for a ping of the right ankle cuff
handle - ask for a ping from an object type "leash handle"
collar - ask for a ping for the collar's front ring
--the following havent yet been implemented--
thead - ask for a ping of a top head ring (on head harnesses , trainer gags or hoods)
lbit - ask for a ping of the left ring of a bit
rbit - ask for a ping of the right ring of a bit
lnipple - ask for a ping of the ring of the left nipple
rnipple - ask for a ping of the ring of the right nipple

these commands return the following pattern:
<avatar key><command> ok

they are used to "ping" the cuff attachment point and if it answer we can get its key
when the message come back, you can grab the chaining point from the id field of the listen event

special commands
col<vector> - order a color change (used in lockmeister collars to tint the cuffs like the collar) OPTIONAL
booton/bootoff = commands sent by collar objects, askin for the shoes animation overrider to be stopped

a basic lockmeister furniture need to be compliant:

-test the presence of the cuffs it need when the avatar sit on it
-unsit the avatar after a few seconds if the cuffs do not respond (thsi is optional, but on suspensions sutff its highly recommended due to the awkward look if no chains are visible)
-if cuffs respond, draw the chains properly
-must provide a free cuff set or the required attachments it need to work properly (you can get mines)

if you plan to integrate the lockmeister system to your products, contact KyrahAbattoir inworld to get the aproval logo


LOD

LOD (level-of-detail) is a method of changing something's detail level (hence the name) relative to how far that thing is from the camera on-the-fly (dynamically, real-time, etc). In Second Life, LOD specifically refers to terrain, tree, primitive, and object/avatar polygon complexity. For example, a sphere may be made up of 128 polygons originally but, say, at 10m distance from the camera, LOD will reduce that polygon count to 64 polygons; at 15m, 32 polygons, etc. Avatars have 4 LODs. Given the lack of hidden surface removal (HSR) in SL, LOD is a necessity in order to reduce lag and improve framerate. However, occlusion helps.

LOD also includes texture mipmapping.

SL's preferences has various "detail" options (object, flexible, tree, avatar) that will adjust the LOD limits.

It is the opinion of some, that since SL 1.9.1, the interface options for setting detail preferences has become less intuitive. So much so that they feel it confuses the users.

Links

  • Wikipedia: LOD

camera | polygon | land | prim | object | texture | avatar | flexible | occlusion


LSL 101

The Complete Newbie's Guide to Scripting in Second Life

Table of Contents

Foreword: Using This Wiki
Hello, Avatar! A Simple Script, Line By Line
States And Events
Syntax Revisited
Variables
Logic
An Introduction to Primitive Parameters
The Simple Poseball

Appendices

Do's and Don't's of Scripting
Common Errors
Solutions To Challenges

Welcome to the LSL Wiki Mirror 7-7-7! If you're unsure how you got here, don't be afraid - this guide is made just for you.

LSL is a (mostly) clear and straightforward language. The way it's structured and how it's written make sense and provide for easy readability regardless of prior programming experience. Whether your final goal is a simple sound script or a chess computer, this guide will help you get your feet wet.

If you have no coding experience, don't worry! This guide is written with that in mind. If you're already an expert coder, especially with C++, click here for the quick and dirty guide.

Homepage | Tutorial | CrashCourse | Glossary


LSL101Appendix

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch5 - Ch6 - App 1 - App2>

Appendix 1: Do's and Don't's of Scripting

A mind is a terrible thing to waste...

Adapted from this thread, here's something extra that others found useful.

Do:

1) Learn from others' scripts available for free.
Available on the Scripting Library and Wiki, as well as countless places in-world, these examples are the necessary first step to cracking into LSL.

2) Ask questions often, here, on the forums, and in-world.
For many people, this is the only way to learn effectively. There's no shame in not knowing how to do something.

3) Try to do new things with old commands.
Test the limits of the commands you come across. Notice any patterns, nuances, and bugs that may occur.

4) Use states and personal functions when tasks are repeated often.
Doing so greatly reduces the volume (and frustration) of code you must write.

5) There is no shame in Trial and Error; use it.
We all make mistakes. I've been known to spend several days to tackle very simple problems at times, and people seem to have a high regard for my abilities. We're all human.

6) Use multiple scripts as needed.
That's what they're there for. If you run out of room or usefulness in one script, start a second. Don't be afraid.

7) Read the context-based help.
One of the best-kept secrets of LSL is also its simplest. By highlighting any command typed in properly, you can see a tooltip on its syntax and usage. As for figuring out how it's spelled? Try this Wiki. Or use the box on the lower left corner of the script editor.

8) Set realistic goals for yourself.
Achieve them by accomplishing real tasks. Say to yourself: "I want to make this really cool killer app in Second Life" and strive to create it. Make these goals realistic.

9) Work smarter, Not harder.
Once you have a fair deal of coding experience under your belt, ask yourself how something might be done better - even if it's been done before. Working smarter means servers are happier; working harder means the reverse.

10) Code, Code, Code
Nothing beats experience. Get out there and do something.

Don't:

1) Don't push yourself too hard.
It's easy to get frustrated if you cannot tackle a problem. There's a fine line between creation and obsession. Know your limits, and strive to extend them at a realistic pace.

2) Don't use stuff that you know is harmful. If you have to use it, be polite:
A lot of the complaints filed on the boards and in Second Life stem from people failing to use simple common sense. Anyone can be better than this if they're willing to try to do so. Will you?

3) Don't expect LSL to have a neat solution for everything:
LSL is a language created to give us tools, not necessarily solve all common problems in a nice, neat package. Because a task does not have an LL function does not mean it cannot be done. It also does not mean the Lindens should "just add it." It means you should take a whack at it first.

4) Don't expect functions to do exactly what you want all of the time:
Learning is in order. LSL is half bending LSL to your will, half bending to fit its design. The same can be said of any coding language.

5) Don't overuse dynamics and community functions:
Set reasonable limits on physics, push, and player-related functions. Physics is the single-most laggy ability of Second Life, second only to poor scripting. Abuse of messages and ban scripts are the single most annoying part of Second Life.

6) Don't get carried away:
Just because you can do something doesn't mean you should. Always weigh in the possible benefits and concerns of scripts you write. The community can be less then accepting at times; keep them mind.

7) Don't abuse some functions:
A few listeners, a few sensor loops, and a few timers are all okay; Second Life was built to handle these. A couple hundred in an object are not okay. A couple hundred objects with a few of these are not okay. If confronted with this problem, think it through prior to working on it.

8) Don't expect to know everything:
I use the Wiki extensively for reference. Keep good notes, and refer to them often if you're forgetful.

9) Don't delete something when you're done:
Save a copy! It might prove useful later. If you think the community might be interested, post it to a public script library.

10) Don't use scripting to plot World Domination:
Unless you want to find me waiting with my Nerf Bat. ;)

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch5 - Ch6 - App 1 - App2>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Appendix2

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch5 - Ch6 - App1 - App 2>

Appendix 2: Common Errors

99 compiler errors in the code,
99 compiler errs.
Fix one bug, compile it again,
2,459 errors in the code!

This section is devoted to common errors you will run across. While there's already a great section on these, the existing section probably doesn't appeal to more than professional coders.

Before I begin, you may want to look at the definition of a compiler.

Here are examples of errors you will run across, listed by relative frequency:

Syntax Error

By far the most common problem. Syntax errors mean the code could not compile because there are one or more problems with how the code has been physically written. To remedy this, simply double-check your code for any typos.

Stack-Heap Collision

Your script ran out of memory while it was running, likely because of a memory leak or similar problem. Revise the code and try again.

Math Error

Caused when a math operation does something that basic math can't handle (e.g. imaginary numbers, divide by zero, etc). The most common reason for this error is your code is dividing by zero somewhere.

Name Not Defined Within Scope

Another really common one. This means that the function or variable name you are trying to use has not been declared yet, is out of its scope, or you accidentally typed it wrong. To remedy this problem, check the variables and functions where the compiler has placed the cursor.

Type Mismatch

The variable type does not match the type you need or are trying to use. To remedy, check the variable or typecast it into a new type.

Function Call Mismatches Type or Number of Arguments

The function variables in the call do not match those in the actual function header. If a function is defined this(integer one, float two), then it requires a call to it that looks similar. For example, this(1,2.0); would be a proper way to call it.

Name Previously Defined Within Scope

The antithesis of the "not defined" error, this just means you used the same name twice. Change it and try again.

Tried to [Animate/Take Controls/Take Money/Etc] but Permissions Not Set

Permissions must be supplied for some actions, like taking money, starting an animation, changing the links of an object, or animating another avatar. These are requested with llRequestPermissions and handled in run_time_permissions.

Object Out of Range

If your client cannot see the object, it cannot properly interact with it. Get closer and try again.

Can't enable physics for objects interpenetrating other objects

Pretty self-descriptive. Occasionally this will fire when enabling phantom, but this is likely a bug.

Cannot rez object - parcel is full

This parcel is at its maximum number of objects. Move somewhere else.

Only objects under 31 primitives can enable physics

Pretty self-explanatory.

Error in parameter [yadda yadda]

In several list-based functions, like llParticleSystem, you will receive this error for supplying bad data. Try again at the area specified.

Uninitialized Variables Can't Be Included in a List

Pretty cut and dried, yes? Declare your variables!

Use of vector or quaternion method on incorrect type

Also pretty cut and dried. Only vectors accept vector commands. Only rotations accept quaternion commands.

Lists can't be included in lists

A list cannot be nested in another list. To add a list inside of another list, use functions such as llListReplaceList, llList2List, or simply adding the two lists together.

Return statement type doesn't match function return type

When a function is written to return a value, it must return the type defined in its preceeding variable definition. For example:

integer num()
{
    // This function is returning an integer, because "integer" is written before the function name.

    // This return would throw this error
    return "Yar.";

    // This one would work properly
    return 1;
}

Function returns a value but return statement doesn't

Similar to the last error, you can't just write return;. You must supply a value if the function requires it.

Not all code paths return a value

Where loops and if-statements are present, you may choose to have different function returns from different paths. Remember to supply a default return statement if none of the paths work.

Dead code found beyond return statement

Now here's one I've never seen before. While code should *technically* be able to run in this case, the presence of an error means the compiler doesn't like it.

Basically, if you plan to make use of return statements, don't add code after the function tries to exit.

Global functions can't change state

The compiler does not allow for state changes from functions users have created. While there is a hack for this scenario, it is likely better to follow the compiler on this one.

Expression must act on LValue

You can only assign values to variables. For example, you cannot do something like 2 = 3; because it makes no sense.

Token buffer overflow/ Not enough space for parser stacks/ Parser stack overflow

Your function's buffer blew up. Try again with fewer or simpler values.

Byte code assembly failed -- out of memory

The compiler failed because your script's code is too large to even just compile it. Try cutting down on the stuff you've typed or split the scripts with a communications system (example).

Too many listens

Your script is trying to open too many listener channels at once. Try consolidating channels and using llListenRemove.

Bounds check error

The compiler missed a bad value for something and it screwed up in your code. This is usually the result of using jumps poorly in code, or otherwise trying to do something that the compiler is not built to handle.

Hit Sandbox Limit

For objects in sandbox mode, this is just a notifier that the object has been moved 10 meters or across a sim boundary.

llSetPrimitiveParams Error Running Rule [Add number and Type Here]

Bad list data has been supplied to llSetPrimitiveParams at that supplied rule number. Check the list.

Script trying to illegally [access/give items to/start script in] [add name here]

Object or script permissions are not properly configured in this object. Common reasons for this are: The object is not yours, the object is flagged "No Modify," or the object has not had its pin set properly for script access.

Asset Failure

Occasioanally, Second Life's asset server becomes too overloaded with tasks and cannot save your script. In these cases, you should receive a pop-up warning to that effect. Simply try compiling/saving the script at a later time and save it in another place (like a text editor) for now.

User-Created Errors

Occasionally you will receive error messages created by other users. These are readily identifiable, because they usually will appear in local chat or a blue script box that does not look official. In these cases, ask the creator what's going on.

Chat Overrun

A deprecated error, but worth noting anyway. Second Life chat is limited to 255 characters. Fortunately, chat commands should now just truncate instead.

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch5 - Ch6 - App1 - App 2>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Chapter1

<Foreword: How To Use This Wiki>
<Table of Contents>
<Next chapter: States And Events>

Hello, Avatar!

A Simple Script, Line By Line

In the beginning, there was only Philip Linden.
Philip gazed upon the empty sims,
Devoid of life and people,
And was Sad.

Then, Philip had an Idea.
Populating the vast world with people,
Philip chose to give them the power to change the world.
That power was but one Word.

That Word was "LSL."
And it was Good.

LSL is appropriately the stuff of gods in Second Life, though you needn't be a god to master it!

Creating A Script

To begin writing code, you need to know how to create a script. For our purposes, you can create a script in one of three ways:

1. Right-click the ground, and select create from the pie menu, then left-click on the ground. This should create a default cube prim and open the Edit window.
Click the "More >>" button in the lower right hand corner of the edit window (if it is shown).
Click the "Content" tab, and then click the "New Script..." button.
The script editor window should appear.

2. (PC Users and Mac users with more than one mouse button) Right-Click a folder in your Inventory, and select "New Script."
Double-Click the newly created script.
The script editor window should appear.

3. Create a script in an outside editor, then copy and paste it into SL via the above methods.

(From now on, "Create a script" will mean any of the above. "Object" will mean either a prim or a set of linked prims.)

Do #1 now. What is this? Some code appeared!

It should look like this:
default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

The classic introduction to a programming language is a program that says "Hello, world!" It also gives you a starting point on which to build your own program. Such was the wisdom of Philip that you get a ready-to-run "Hello, Avatar" script automatically whenever you create a new script.

Don't panic! This stuff is easier than it looks!

The Default State

The first thing you see in your new script is the word "default". This is what's called a "state" -- as in "state of being". Every LSL script must contain a default state, aptly named "default", and code within it in order to work. A script can contain multiple states, but we'll get to that later on.

The default state contains code that runs first. "default" tells the compiler (the program that converts your code into something that will run in SL) "okay folks, we're starting in here." On the line following default, you should see an open brace ({). This tells the compiler that the information after it is contained within default. If you follow the edge of the screen straight down from the first open brace after default you'll find default's closing brace (}) on the last line in the script. Default's closing brace tells the compiler "default's code ends here." The space between a set of braces is called a scope (or code-block ). All the text between the default state's opening and closing braces is said to be contained within its scope.

A scope can contain other ("nested") scopes. In this example, default's scope contains both state_entry's and touch_start's scope. As you can see, each scope has its own opening and closing brace. Every scope that is opened with an opening brace must be closed with a closing brace, or else the script edit window will display "Syntax error" when you try to save your script.

See? This isn't so hard!

Events

An event is a named code-block that is triggered (and the code within its scope run) by something that happens within Second Life. When we add code inside an event's scope, we create an "event handler;" this code will respond to the event's occurrece by handling it.

Event state_entry

The first bit of code that actually does something is included within event state_entry's scope. Let's take a closer look at it:

// <snip>
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }
// <snip>

Whenever the script enters its default state, this triggers the state_entry event. So, the code within state_entry's scope is the first thing that runs. Here are ways in which this can happen:

1) Compiling (saving) the script.
2) Resetting the script. There is a "Reset" button in the script editor window. Also, LSL provides function llResetScript that you can use to make a script reset itself.

(From now on, "reset the script" will mean any of the above.)

* Taking the containing object into your inventory, or rezzing it in-world, does not reset its script. See the article about event on_rez for a helpful discussion of this point.

The event's name is followed by a pair of parentheses. for now, let's leave their purpose a mystery! A pair of curly brackets marks the start and end of state_entry's scope. One statement is within its scope.

Statements

The line beginning with "llSay" is a statement. Statements are chunks of text that do something. Each statement must end with a semicolon (;). Think of the semicolon at the end of a statement like the period at the end of a sentence (and think of the compiler as your pedantic 3rd grade grammar teacher.) Usually, every statement consists of a single line of text.

Saving The Script

In the script editor, click "Save". This compiles and resets the script. If the script is in an object rezzed in world, you should see a Chat message like this:

Object: Hello, Avatar!

No, the object isn't just saying "hi" by coincidence. This is happening because resetting the script triggered the default state's state_entry event, whose scope includes the statement that begins with llSay.

Persistence

By now, in the spirit of science, you may have "touched" the object to see if it will say "Hello, Avatar!" again. Instead, it says "Touched." That reply comes from the touch_start event. Why didn't the state_entry event reply?

1) You touched the object, so that's the event to which it responded.
2) Our new script hasn't "ended;" it is already in its default state, and nothing has happened to trigger default's state_entry event again. Even if you quit SL and reconnected, you'd still find that the script is already in its default state.

An LSL script is "persistent." Its state persists. The current values of its variables persist. You can reset it, of course, but that doesn't "stop" an LSL script from persisting; it just resets its state. Once saved, an LSL script only desists from persisting if you:

1) Delete the script from the object that contains it.
2) Remove the containing object from the SL world by deleting it.

(From now on, "Removing the script" means either of the above.)

* Taking the containing object from in-world into your inventory does not interrupt its persistence. Taking the object only makes its script inactive. When you later rez the object from your inventory in-world, its status will be just as it was when you took it.

Functions

The statement starting with llSay is a call to function llSay. A function is a named code-block that does something interesting when you give it the information it wants. It might be a code-block located inside of your script; or it might reside outside of your script in "some sharable place."

Any function whose name starts with two lowercase L's is a Linden function that resides in the SL environment. So, llSay is a Linden function. These bits of code are created by the Lindens to provide scripters with new abilities. In practice, Linden functions will do the heavy lifting in all of your scripts.

Passing Parameters

The pieces of information you give a function are called parameters.

When you call a function, or any other type of named code-block, and give it pieces of information, you are "passing parameters" to it. You pass parameters within the call's parentheses (()). The parentheses and any parameters they enclose make up a "parameter list."

Code-blocks need varying information. To make one work right, you have to pass it exactly the parameters it needs in the order that it expects them. If you are passing more than one parameter, separate each parameter from the next with a comma. If a parameter is optional, and you don't include it in your call, the code-block will assign it a default value.

If you aren't going to pass any parameters, put a pair of parentheses in the call anyhow, with nothing inside of them. This empty parameter list tells the code-block "Don't look at me! You'll have to get your information somewhere else." (Now you may be starting to suspect what the parentheses in event state_entry() are for!)

Function llSay

Let's dissect llSay! (Or, more precisely, our script's call of function llSay.)
// <snip>
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }
// <snip>

llSay is a function that chats text on a channel that can be "heard" by objects or avatars from about 20 meters away.

llSay expects you to give it two parameters, the channel on which to talk on and the text to say. Avatars chat on channel 0, which is why "Hello, Avatar!" appears on your screen and in your chat history when the script is reset. Objects can chat on channels other then 0, but the chat won't appear onscreen. So, non-0 channels are useful for private communication between two (or more) objects.

The text to say is surrounded in double-quotes ("). These tell the compiler "all the text within these double-quotes is a single value. Take it literally -- that is, don't try and make it into statements, events, or even mashed potatoes -- give it to the function *just* as it is." That's why text in double-quotes, and numbers that are off by themselves, are called literals. They're information you want the compiler to use literally. We'll get to non-literals (variables) later on.

Remember to end the statement with a semicolon (;)! Forgetting this semicolon is the most common mistake in an LSL script.

Event touch_start

The next event you will see is touch_start. Strictly speaking, this event doesn't contribute anything to the saying of "Hello, Avatar!" Apparently, Philip in his wisdom realized that newbies would like to see an example of a different kind of event. This code looks very similar to the preceding commands:

touch_start(integer total_number)
{
    llSay(0, "Touched.");
}

This event looks like a function call, because its name seems to be followed by a parameter list. Actually, an event is akin to a function rather than a function call. (Don't feel bad; you've never seen a function. How could you have known?) Instead of parameters, this list contains a variable: integer total_number.

As with a function, this variable is set to the value of a parameter. One interesting difference; Second Life itself supplies the parameters for events! When defined in an event, variables are given values by actions the sim has taken. This is very useful, especially in events like money. In this case, total_number defines the number of people touching the object at that time. The advantages of knowing the number of "touchers" may seem dubious right now! Please take it on faith that we need this number for detected functions, and that someday you may be glad to know it!

But for now, all we're doing is calling function llSay to say "Touched" every time "Touch" in the object's pie-menu is clicked on by any user. Here is our old friend llSay again! We're passing llSay a parameter list that's pretty much like the one we used before: channel 0, and text "Touched".

Congratulations! You've successfully deciphered your first script!

<Next chapter: States And Events>
<Table of Contents>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Chapter2

<Int - Fo - Ch1 - Ch 2 - Ch3 - Ch4 - Ch5 - Ch6 - App1 - App2>

Chapter 2: Syntax Revisited

Every coder has learned to dread these words:

Syntax Error

These words scare all matter of mice and men. They are the scourge of the internet, striking fear into the hearts of millions. And now, you must face them.

Actually, syntax errors in Second Life are very easy to avoid and fix, provided you know what you're doing. The following section will provide a handy review of tips for writing perfect code.

Braces and Parentheses ALWAYS need to be closed.

Think of it as virtual housekeeping. Just as in real life, once you open a door, you must remember to close it so things that go bump in the night don't come inside. Braces are there so that one line of code can refer to several lines of code (In essence, they say "This code belongs to that code.")
Take this code for example:
if (grassIsGreen)
{
    llSay(0, "Woo-hoo!");
}
The opening brace states that "code after here, until my closing brace, belongs to this if-statement".

Braces are usually (but not required to be) spaced so that each set of braces are the same distance away from the left-margin, like this:
{ // Opening brace A
    { // Opening brace B
        { // Opening brace C

        } // Closing brace C
    } // Closing brace B
} // Closing brace A
The Second Life LSL editor automatically indents for you after you type an opening brace :)
Always remember to close all braces and parentheses that you open in your code. If you have three open braces, you must have three closing braces. Period.

For more information, see codeblock and brace.

Remember your semicolon!

Unless a line of code requires braces ({}), chances are it requires a semicolon (;) at the end of it. So put it there, soldier!

Sometimes braces can be omitted.

But these are rare exceptions. If you find a place that should have braces and doesn't in someone's code, look at it very carefully. Chances are there will be visible clues to why that's the case.

Math is very easy.

Arithmetic is very easy in LSL | Here are some basic guides.
+ | Addition
- | Subtraction/Negative sign
/ | Division
* | Multiplication
% | Modulus (Cross-Product)
> | Greater than
>= | Greater than or equal to
< | Less than
<= | Less than or equal to
llSin | Sine
llCos | Cosine
llTan | Tangent
llAbs | Absolute value
llRound | Round to nearest whole value

For more, see math.

Note that some other operations exist. Occasionally, you will see stuff like "++," "/=," etc. I'll explain this later, so don't worry.

Variables must always be declared.

While we have not yet discussed variable declaration, you must declare variables before using them in every piece of code you write.

Double-slashes are comments.

// This is a comment. We use these to leave notes in our code.
// Comments are extremely useful for figuring out what a coder is doing.
// Likewise, they're helpful in letting others know what *you're* doing!

// All you need to write a comment are two slashes before a line.
// You can also "comment out" code if it's not working properly in this manner!

Not everything needs to be inside braces.

As you'll learn later, global variables and other things can be defined without any braces at all!

If it doesn't look right, Ask.

If all else fails, ask another coder. Several Scripting Mentors are available to help. You can also ask for help on the forums or contact me.

<Int - Fo - Ch1 - Ch 2 - Ch3 - Ch4 - Ch5 - Ch6 - App1 - App2>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Chapter3

<Previous Chapter: States And Events>
<Table of Contents>

Variables

Nothing can have value without being an object of utility.

- Karl Marx

The "objects of utility" here are variables.

But First ...

A word about architecture. If you've messed about with computers before, you probably think that data is stored in files and databases, far away from programs. But Second Life is an object-based system. In LSL, the data is inside the script -- in variables.

  • "But, where does the data go when the script ends?" It never does! LSL scripts are persistent.
  • Some complex multi-script objects include a script whose sole purpose is to store and retrieve data for other scripts.

What Is A Variable?

A variable is a named memory slot for data (one or more values).

A script can only hold a maximum of 16 KB (kilobytes) of data. That is 16,384 bytes (an alphabetic character occupies one byte). This limit includes data that exists when you reset the script and stuff typed in and stored during its lifetime. So, it's to your advantage to use variables efficiently.

Declaring a Variable

To use a variable, you must first declare it. To do this, specify the variable's data-type and name.

// If you don't declare a value, the variable will be assigned a default value.

string name;
// "string" is the type. "name" is the name.
// "" is the value, because you didn't specify a value.

integer num = 1; // You can also declare a value. This is not required in LSL.

Data-types

There are lots of ways to process, display and store values. For example, you handle a monetary value and a date differently. (Try calculating a percentage of a date sometime!) So, the set of methods you need for dealing with a given value is as important a piece of information as the value itself. This "set of methods" is called the value's data-type.

  • Literal values like "Hello, avatar!" and 3.14159 use formatting and special symbols to show LSL their data-types
  • You specify the data-type of an LSL variable when you define it. Whenever LSL needs the data-type of a variable, it will "remember" your definition.

There are many data-types in the computer realm. These are the ones LSL currently uses:

Integer

An integer is a 32-bit value with a range from -2147483648 to 2147483647. Interestingly, integers can only hold whole numbers, as their name implies.

Integer math is of note, because values are truncated, NOT rounded. As such, integer math looks something like this:

// Returns "3," NOT "3.5"
integer test = 7 / 2;

Float

A float is a 32-bit floating point value. Unlike integers, floats can store decimals as well as whole numbers, at the expense of precision.

Float math is performed as anyone that lived through gradeschool may expect.

Q: If this is the case, why use integers? Shouldn't I just use floats for everything?

A: That totally depends on your coding style and the requirements for specific functions and events.

Q: What about doubles?

A: LSL doesn't have them.

Vector

A vector is a series of three floats, named X, Y, and Z (in that order).

Vector math can be very tricky. Here's a resource that might help.

You can also treat parts of a vector as if they were floats. Here's an example:

vector this = <0.0,1.0,2.0>;

this.x = 0.0; // The X component.
this.y = 1.0; // The Y component.
this.z = 2.0; // The Z component.

Rotation

A rotation is a series of four floats, this time named X, Y, Z, and S (in that order).

Rotations are defined in quaternions, which for many coders is synonymous with "pain in the ass."

For now, here's all you need to know:

- <0,0,0,1> is equal to no rotation.
- If you are uncomfortable with quaternions, try eulers.
- llGetRot and llSetRot are your friends.

Here's a good resource if you want to learn quaternions.

String

A string is a series of characters, like "The sky is blue." No frills here.

Strings can be added and subtracted from quite easily. See string.

Key

A key is a special type of string, used to store unique identifiers for the objects in Second Life. Also known as UUIDs, key variables function exactly like strings. Some functions even take strings and keys interchangably, like llStartAnimation, llLoopSound, and llSetTexture.

For all intents and purposes, keys are strings in denial.

List

A list is a series of several variables in one data type. It may include any of the above types and be as short or long as you like.

However, bear in mind that a list, as with all variables, is limited by the amount of free memory.

Lists on their own are worthy of long discussion. For now, here are the basics:

- To add to a list, simply use "+" or "+=" like so:

list this = [];

this += 1; // Adds the integer "1" to this list.
this += "2"; // Adds the string "2" to this list.

this = [1,"2","x","The sky is blue."]; // You can also define it thusly
// Each value in a list is separated by a comma

- To manipulate lists or see their contents, several functions exist. See lists.

Q: What about arrays?

A: No soup in LSL.

Q: I'm getting a syntax error. What's going on?

A: Make sure you defined your list properly and used commas between each entry.
Also note that you can only type out lists to a certain number of entries (72 or so).
While lists can store many times this number on their own, a user can manually enter only so many at a time.

For more information, see lists and appendix 2 of this guide.

Translating a value to a different data-type

Data-types matter. For example, a function needs its parameter value to have a particular data-type.

But the value that you want to send to a function may be trapped in a variable that's the wrong type. It's no good forcing it; LSL is not smart enough to realize that it needs to typecast a value before using it. It's only smart enough to tell you that the data-type is wrong.

I'm sure you're probably wondering: "But can't I make this wrong-type value digestible somehow?"

Yes, you can! To do this, typecast a value in one of two ways:

- Implicit typecasting: Stuff that is just understood by the compiler when you type it. For example, 2.0 is understood to be a float, whereas 2 is understood to be an integer. (But implicit typecasting only works with literal values, not variables.)

- Explicit Typecasting: Actually telling the program what type you want this object to be. Here's how you do it.

integer number = 1;

// Typecasting an integer variable as a string,
// and copying its value to a string variable
string not_a_number = (string)number;

// You can also do this with literal values

// Typecasting a string as an integer,
// and copying it to an integer variable
number = (integer)"12";

// Typecasting a string as a vector,
// and copying it to a vector variable
vector vect = (vector)"<1,1,1>";

Watch your variable types! Second Life can be very anal about them.

The next example uses explicit typecasting and concatenation (okay, we haven't talked about concatenation yet but it's not hard) to assemble a string parameter value "on the fly:"

default
{
    state_entry()
    {
        llWhisper (0, "Greetings, prisoner number " + (string)6 + "." );
    }
}

When compiled, the object containing this script confides:

Greetings, prisoner number 6.

Global Variables versus Local Variables

LSL has two classes of variables - local and global. Global variables are available to code throughout the entire script. Local variables, on the other hand, are available only to code inside their scope.

To show how local and global variables act, let's look at yet another script:

integer global = 1; // A global variable. This is available to the entire script

default
{
    state_entry()
    {
         integer local = 2; // A local variable.
    }
    // Our variable "local" is not available at this point.
    // Local variable access ends where the closer brace ends their scope.

    // Think you understand? Let's try again
    touch_start(integer total_number)
    {
         integer test = 3; // Watch this

         if(test == 3)
         {
              llOwnerSay((string)test);
         }
         // Can you still get to test here?
         // Yep!
     }
     // But not here!
}
// Our global variable is still available, even after this point!
// Naturally, you should choose your variable placement to best fit your needs.

  • Sometimes you will find a local variable with the same name as a global one. While this is allowed, it's considered bad coding. You and LSL may get different ideas about which of the identically-named variables the code is using.
  • Prefix a variable's name with its type only once, when you declare it. If you specify the type again when you set it "Just to make sure," then you're declaring it again. That means you're defining a new variable with an identical name -- sure to be a source of trouble!

<Next chapter: Logic>
<Table of Contents>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Chapter4

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch 4 - Ch5 - Ch6 - App1 - App2>

Chapter 4: Logic

"What do you mean invalid parameters?! 9,000 gigs of RAM and it can't answer a simple question!"
- Earthworm Jim

LSL is an extremely robust language, but only once you know how to make stuff do what it's supposed to! This section is devoted to teaching the inner workings of how to manipulate variables, syntax, loop behavior, and other useful stuff.

Everyone that knows C or C++ is dismissed, as LSL resembles that to the letter.

Variables Revisited

Now that we've a better understanding of how this stuff works, let's return to variables for a bit.

Like C++, the variable accepting data appears to the left. The rest of the spam appears on the right. For example:

integer this = 1; // Stores "1" into "this"
integer two = 2; // Stores "2" into "two"

this = 1 + 2; // Stores "1 + 2," or "3" into "this"
this = two; // Stores the value of "two" into "this"

Typically, variable math (eg. 1 + 2) can only occur within a function or event. While you can store values fairly easily, the compiler may occasionally get upset.
For this reason, state_entry can be extremely useful.

There are also a series of special commands you may use to change variables. In addition to Linden functions, here are a couple commands you may run across:

integer i = 0; // Just a quick variable

++i; // Increments (adds one to) the value of i
i++; // Same thing, only it does so after the line of code is executed

--i; // Decrements (removes one from) the value of i
i--; // Same thing, except it happens after.

i += 1; // Shorthand for "i equals i plus one." Very handy.
i -= 1; // "i equals i minus one."
i *= 1; // "i equals i times one."
i /= 1; // "i equals i divided by one."

i = TRUE; // "TRUE" is a constant for 1.
i = FALSE; // "FALSE" is a constant for 0.

Equal versus Double-Equal

You may notice that some parts of the code use a single equal (=), whereas others use it twice(==). This is by design.

A single equal sign is used to declare a value for a variable, like the above. A double-equal is used to evaluate a value for a variable, and is generally used in loops and if-else statements (see below).

True/False and If/Else Statements

If and else statements are the meat and potatoes of LSL.

To start such a statement, begin with an if, like so:

if(TRUE)
{
}

Note the curly braces and the "TRUE" in parentheses. This is there for a reason. An If Statement requires a condition to be met and evaluate to TRUE (1). If this condition is not met, the code within the curly braces is ignored completely.

In LSL, an if statement will work with any value greater than zero. For example, "if(15)" would run the code inside. This is an interesting caveat for some coders.

If statements need not have curly braces after them. Sometimes, they're omitted entirely. When this happens, the next line of code is considered to be conditional - that is, it will only run if the condition is met.

How does one meet these conditions? There are a variety of ways:

integer one = TRUE;
integer zero = FALSE;

if(!one)
{
    // This will never execute. In LSL, the exclamation point, or "Not" command,
    // flips the conditional. Not True must equal False, and likewise, not False must be True.
}
if(!zero)
{
    // In other words, this would run
}

if(one == TRUE)
{
    // If one equals true, this statement runs.
    // The double equal returns one if the values match,
    // and zero if they do not.
}
if(zero == FALSE)
{
    // So this would run too.
}

if(zero < one)
{
    // Here we see another conditional. This is TRUE,
    // since zero IS less than one.
}

key stuff = "Yar";
if(stuff)
{
    // Interestingly, you can toy with the values of keys
    // and strings with conditionals. Try it!
}

Once you have an if statement, you can place an else statement directly after it.

A simple else will run if the if statement preceeding it fails. You can also use "else if" to create chains.

For example:

if(FALSE)
{
}
else if(FALSE)
{
}
else
{
    // Only this code will run.
}

For more information, see IfElse.

And and Or

Same commands, vastly different execution.

A single amperstand (&) is a binary and. In other words, it will evaluate the bits of the current variable.

A single vertical line (|) is a binary or. This handy device will flip a desired bit.

Double amperstands and vertical lines are comparison ands and ors. They are used in if statements and loops.

Example:

if(change & CHANGED_LINK)
{
    // If change is greater than zero, and the bits of CHANGED_LINK are on, do stuff.
}

llSetStatus(STATUS_PHANTOM | STATUS_PHYSICS, TRUE);
// Using Phantom + Physics bits to define a status.

if(TRUE && FALSE)
{
    // Will never run. Both parts must be TRUE.
}

if(TRUE || FALSE)
{
    // Will run, since only one part needs to be TRUE.
}

No idea what any of this means? Try this and this.

Loop Behavior

There are several ways to create a loop. These are ripped almost straight out of C++.

The while loop runs code while a condition is TRUE. Like an if statement, the code does not run when the condition is false. Unlike an if statement, a while loop will run infinitely until the condition is broken.

For example:

while(TRUE)
{
    // Runs infinitely.
}

integer i = TRUE;
while(i == TRUE)
{
    // Runs once
    i = FALSE;
}

The do-while loop is basically a while loop that always executes once.

do{
    // Runs this no matter what
}
while(FALSE);

The for loop creates a simple flow of events with three parameters, like so:

integer i;
for(i = 0; i < 10; ++i)
{
    // Start i as 0, loop while i is less than ten, add one to i each step.
    // This will run ten times
}

Lost? Try this.

Functions that Return Values

Some functions can be used as values. For example, you may see "number = llSin(2);" Some functions return values, while others do not. These functions can be used as if they were a value.

You can see which ones return values here.

Global Functions

Global functions (or user functions) can also be created. These functions can only be created outside of states, and look something like this:

float i_am_a_function(integer item)
{
    // Here's what we just defined. This function returns a float value.
    // It also accepts one parameter, which is defined here as "item"
    // "item" will act as if it has been declared, since it can be in the start of a function like this.
    item = 2;

    // To call this function in the code, you would write something like "i_am_a_function(1)"

    // Because this function returns a value, you MUST include this line:
    return 0.0; // Returns a float.
}

States

States are very useful. Since there's already an excellent tutorial on it, how about I send you there?

Order of Operations

Within math and if statements, note that certain commands execute in a specific order.

Operation (Higher ones executed first) | Description
() [] . | Parentheses, Square Brackets, Dot Operator
! ~ ++ -- | NOT, One's Complement, Prefix Increment, Prefix Decrement
* / % | Multiply, Divide, Modulus
+ - | Addition, Subtraction
<< >> | Left Shift, Right Shift
< <= > >= | Less Than, Less Than Or Equal To, Greater Than, Greater Than or Equal To
== != | Comparison Equal, Comparison Not Equal
& | Bitwise AND
^ | Bitwise XOR
| | Bitwise OR
&& | Comparison AND
|| | Comparison OR
= += -= *= /= %= ++ -- | Value functions, Suffix Increment, Suffix Decrement

In general, it is considered good coding practice to just use a whole lot of parentheses if you're unsure at the order of operations.

Phew. Long, wasn't that?

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch 4 - Ch5 - Ch6 - App1 - App2>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Chapter5

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch 5 - Ch6 - App1 - App2>

Chapter 5: An Introduction to Primitive Parameters

Particles and SetText and Spam, Spam, Spam, Eggs, SetText and Spam, Spam Spam and...

One of the most useful things a script can do is change around the properties of a primitive. Here's a simple introduction to some of these parameters.

All of these properties are permanent (even when the script is removed), so make sure to save a copy of your work before using these!

Floating Text
llSetText is the simplest of parameters. This will allow you to add floating text to your prims.

Particles and Textures
llParticleSystem, by contrast, is rapidly becoming one of the most complex systems around. Fortunately, many residents have gone out of their way to pierce the miasma of the particle system. Here is one such example. Here is a more efficient one.
llCollisionSprite lets you change the displayed sprite when the object hits something (or vice versa).
llSetTextureAnim can set a texture animation.

Sounds
llLoopSound allows you to loop a sound constantly in an object.
llPlaySound plays a sound once, with the object as the emitter.
llTriggerSound triggers a sound, but does not associate it with the object, so the sound does not follow the object.
llCollisionSound changes the sound when a prim hits something (or vice versa).

Sit Targets and Camera Stuff
llSitTarget allows you to modify the sit target of a prim.
llSetSitText and llSetTouchText allow you to edit the text that appears in the pie menu for certain actions.
You can also modify where the prim sends your camera when you sit on it.

Script-Related Goodies
llSetRemoteScriptAccessPin can be used for sending scripts to objects with llRemoteLoadScriptPin.

Other Primitive Parameters
llSetPrimitiveParams includes all the other core primitive stuff, including shape, size, appearance, position, rotation, kitchen sink, scale...
For more, see here.

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch 5 - Ch6 - App1 - App2>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Chapter6

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch5 - Ch 6 - App1 - App2>

Chapter 6: The Simple Poseball

Poseballs? Posecubes?

Now that all that is out of the way, we can write a real script that might be useful: a poseball.

Poseballs have been around as long as custom animations in Second Life. Simple in design, these little items require four parts:

1) A sit target
2) A way to show/hide the poseball
3) An actual animation
4) A way to animate the connected avatar

This simple tutorial will help you create a poseball using a default animation.

Starting Once Again
Create a new script. We should be back to this again:

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

First, we can slice into the state_entry event. Since we need a sit target, let's add one. Let's also add some text for good measure:

default
{
    state_entry()
    {
        llSitTarget(<0,0,1>,<0,0,0,1>); // Set the target one meter above the center of the prim
        llSetSitText("Pose!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

Next, we need a way to make the poseball appear and disappear. We can do this easily. Using llSetLinkAlpha, we can make the entire object appear or disappear with a simple touch! Let's try a boolean variable this time.

integer hidden = FALSE; // Stores whether the object is visible

default
{
    state_entry()
    {
        llSitTarget(<0,0,1>,<0,0,0,1>); // Set the target one meter above the center of the prim
        llSetSitText("Pose!");
    }

    touch_start(integer total_number)
    {
        if(hidden)
        {
           hidden = FALSE;
           llSetLinkAlpha(LINK_SET,1,ALL_SIDES);
        }
        else
        {
           hidden = TRUE;
           llSetLinkAlpha(LINK_SET,0,ALL_SIDES);
        }
    }
}

Good! Next we need an event that will be called when an avatar "sits" on the object. We can use the changed event, along with the constant CHANGED_LINK. We can also use llAvatarOnSitTarget to figure out who we want to animate.

integer hidden = FALSE; // Stores whether the object is visible

default
{
    state_entry()
    {
        llSitTarget(<0,0,1>,<0,0,0,1>); // Set the target one meter above the ground
        llSetSitText("Pose!");
    }

    changed(integer change)
    {
        if(change & CHANGED_LINK) // If someone has sat on, or "linked," to this prim...
        {
            key agent = llAvatarOnSitTarget();
            if(agent)
            {
                llStopAnimation("sit");
                llStartAnimation("stand");
            }
            else
            {
                llStopAnimation("stand");
            }
        }
    }

    touch_start(integer total_number)
    {
        if(hidden)
        {
           hidden = FALSE;
           llSetLinkAlpha(LINK_SET,1,ALL_SIDES);
        }
        else
        {
           hidden = TRUE;
           llSetLinkAlpha(LINK_SET,0,ALL_SIDES);
        }
    }
}

Great! Now compile that code and sit ("Pose!") on it. You should see a blue bar warning telling you that permissions are not set.

Permissions? What are those? Well, certain actions require that a person consent to them first. Check out here for some examples.

Let's use run_time_permissions and llRequestPermissions to finish our poseball.

integer hidden = FALSE; // Stores whether the object is visible

default
{
    state_entry()
    {
        llSitTarget(<0,0,1>,<0,0,0,1>); // Set the target one meter above the ground
        llSetSitText("Pose!");
    }

    changed(integer change)
    {
        if(change & CHANGED_LINK) // If someone has sat on, or "linked," to this prim...
        {
            key avataronsittarget = llAvatarOnSitTarget();
            if( avataronsittarget != NULL_KEY )    //Someone is sitting on the object
            {
                // Comment out this code:
                //llStopAnimation("sit");
                //llStartAnimation("dance1");
                // Before animating, first check if we have permission to do so:
                if ((llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) && llGetPermissionsKey() == avataronsittarget) {
                    // If we do, we can animate:
                    llStopAnimation("sit");
                    llStartAnimation("dance1");
                } else {
                    // If we dont, ask for them:
                    llRequestPermissions(avataronsittarget, PERMISSION_TRIGGER_ANIMATION);
                    // We'll animate in the run_time_permissions event, which is triggered
                    // When the user accepts or declines the permissions request.
                }
            }
        }
    }

    run_time_permissions(integer perm)
    {
        if(perm)
        {
            // Place the code here!
            llStopAnimation("sit");
            llStartAnimation("stand");
        }
    }

    touch_start(integer total_number)
    {
        if(hidden)
        {
           hidden = FALSE;
           llSetLinkAlpha(LINK_SET,1,ALL_SIDES);
        }
        else
        {
           hidden = TRUE;
           llSetLinkAlpha(LINK_SET,0,ALL_SIDES);
        }
    }
}

Congratulations! You've written your first script and taken your first steps into the world of LSL scripting. Feel free to reference these pages should you ever get lost or need to find your way.

<Int - Fo - Ch1 - Ch2 - Ch3 - Ch4 - Ch5 - Ch 6 - App1 - App2>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Foreword

<Table Of Contents>
<Next chapter: Hello Avatar!>

Foreword: Using This Wiki

The LSL Wiki Mirror 7-7-7 was originally established by Catherine Omega and hosted at BadGeometry.com. Since then, other users have contributed to help it grow into the most complete resource for both amateur and professional Second Life scripters alike. The Lindens eventually integrated it into the official SL site. Chances are you're viewing this from there.

Navigating the Wiki | This wiki is divided into five main sections
Homepage | Here you'll find links to the rest of the wiki
Functions | The main Linden functions list
Events | The main Linden events list
Glossary | The glossary of terms
Script Library | Useful examples and code snippets from other residents

Many other sections and pages exist, but chances are these are the ones you will use most.

Q&A

Q: What's a "wiki", anyway?
A: As defined by Wikipedia, a wiki is "a web application that allows users to add content, as on an Internet forum, but also allows anyone to edit the content."
For more information, please visit the above link.

Q: So I can add material to this site?
A: That's right. Just create an account, and you're ready to go.

Q: Should I wish to contribute, are there any rules I should know about?
A: One main one: Respect the work of others. Snide remarks, vandalism, and belittlement only anger other users. Also, refrain from editing personal pages, unless there is very good reason. Leave a comment on a page if you are unsure of whether or not you should edit it. See the wiki etiquette page for a more complete list.
Remember, access to the wiki is a privilege, not a right.

Q: What should I do if I find a page that has been vandalized?
A: You can either contact the page owner immediately or attempt to fix the page yourself.
Because wikis store previous versions of all pages, restoring lost content is very easy. Simply press "Show Revisions", click on the date of a previous (unvandalized) edit of a page, and then click the "Re-edit this old revision" button and save that other revision.

<Next chapter: Hello Avatar!>
<Table Of Contents>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101solutions

<Table of Contents>

Solutions To Challenges

Such accidents I have always taken philosophically, looking on them as a kind of insurance against more terrible ones.

- Alberto Santos-Dumont, 1904

We hope that you are looking at this solutions page out of idle curiosity, after solving a challenge yourself -- not too quickly nor too easily, but with just enough effort to yield a pleasant glow of achievement and an enhanced understanding of LSL.

If that is not the case, and on the contrary you are stuck, frustrated, and about to call it a night, then we hope that these solutions will help you master the situation! And we hope that you will now contribute to this wiki to better explain the point that was bothering you, and smooth the path for those who follow!

States And Events Challenge: Tickle Box

The point of this challenge is to use states and events, the backbone of every LSL script, to model successive stages of in-world behavior. A sub-point is to give practice in using the Wiki Search to look up functions.

  • Instead of function llOwnerSay you could use llSay or llWhisper. I chose llOwnerSay in a bid for less chat-spam in-world. Only the object's owner can hear llOwnerSay. Note that llOwnerSay needs no channel parameter.
  • Instead of function llResetScript, you could use command state default; . But, this way, you get to look up another function. 8-)

default
{
    touch_start(integer total_number)
    {
        llOwnerSay("Now, touch me again.");
        state tickled;
    }
}

state tickled
{
    touch_start(integer total_number)
    {
        llOwnerSay("Thank you!  That felt good.");
        llResetScript();
    }
}

<Table of Contents>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101StatesAndEvents

<Previous Chapter: Hello, Avatar!>
<Table of Contents>
<Next Chapter: Syntax Revisited>

States And Events

Whoever desires to found a state and give it laws, must start with assuming that all men are bad and ever ready to display their vicious nature, whenever they may find occasion for it.

- Niccoll? Machiavelli

In LSL, we desire to found a state and give it events! Or, better yet, several states -- each corresponding to a stage in the behavior that we want to script.

Arrgh, Why Can't I Just Start Coding?

Indeed, why bother with architecture at all? Just grab a lot of lumber and nails and have at it, until you get tired, or run out of lumber or nails. But if you work with LSL's nature instead of against it, you'll write better scripts and have more fun doing it.

LSL Architecture

An LSL script consists of:
  • Optionally, global variables and functions. (But you aren't supposed to know about these yet!)
  • At least one state, named default. Optionally, additional states.
  • Optionally, some events within each state.
If you have programmed before, you are probably familiar with flow charts, a graphical tool for designing programs in other languages. (If you aren't, so much the better; you won't be burdened with preconceptions!) Anyway, a useful graphical equivalent to an LSL script is a matrix of states and events.

Let's take the Hello, Avatar! script as an example, and see what it looks like as a matrix:

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

Event | default
state_entry | Say Hello, avatar
touch_start | Say Touched.

The leftmost column lists events; states (this script just has one state) are listed in the top row. Where events and states intersect, we can write brief notes about what the event does in this state.

Talk about belaboring the obvious! But remember, this script does practically nothing; it just serves as a template for the script you want to write. Suppose you want the same event to be handled in more than one way, depending on some condition? You could, of course, test for the condition inside the event and branch to different sections of code. But it's easier to create a state for every variation of the condition (or anyway, it would be easier in a non-trivial example).

Two States, What A Thought!

Here's an example; the Tickle Box. The first time you touch it, it asks you to touch it again. The next time you touch it, it thanks you. The Tickle Box matrix looks like this:

Event | default | tickled
touch_start | Say Now, touch me again. Enter state tickled | Say Thank you! That felt good. Enter state default

What techniques do we need to write the Tickle Box script that we didn't already see in Hello, Avatar?
  1. A way to define a second state
  2. A way to move from one state to another

How To Define More States

Now here is a place where what has up until now been a boringly symmetrical language shows some character.
  • If you define any state other than default, you need to prefix it with the word state or you'll get an error.
  • But if you prefix default with state, you'll get an error.

Can you say Works as designed?

Moving right along, here is the code to define the tickled state:

state tickled
{
...
}

By the way, putting your terminating bracket in right away like this, and inserting the code that goes between the brackets later, is a good habit. It keeps your brackets-in and brackets-out balanced. Big time saver here! You're very welcome.

How To Move From One State To Another

Inside an appropriate event in state #1, use the state command to enter state #2. For example:

// snip
state tickled;
// snip

How To Search This Wiki

To write your own script, you'll need to know how to find events, commands, etc. that you don't already know. In the bottom left corner of any page of this wiki, you'll see a text box labeled "
only search the lslwm ". To do a search, type a word (such as the name of a command) into the search box and push <Enter> .

Like most programming manuals, searching this wiki works best if you already know the name of the thing you want to use and you just need to check some details about how to use it. Until you get a feel for the language, you may find yourself groping around for a word that gets useful results. We'll try to help by giving you good words! Also ...

  • Try guessing the name of the command (but, unfortunately, searching on <say>, <print> and <display> didn't lead me to the article on llSay!)
  • If you know a similar or related command, search on it; you will often find a list of similar commands
  • If you're looking for a function, try prefixing your search word with <ll> and capitalizing it -- for example, llSay. There are so many Linden functions that you're likely to hit on one of them this way.

The Tickle Box Challenge

I'm sorry, did you think you would find the whole Tickle Box script down here? Ha-ha, you're so silly. That's your assignment! Get in-world, create a box, and create the script in it that we've been talking about.

  • In the Contents tab, create a new script. You'll get a "Hello, Avatar!" template script.
  • Edit it, copying the parts you can use more than once and erasing the parts you don't need.

To make this challenge more interesting (and practice searching this wiki),

  • Do the talking parts with a function other than llSay.
  • Enter state default from state tickled by a means other than the state command. (Hint; what state does a script enter when you reset it?)

When you're done (or ready for help), here is the solution. No peeking!

<Table of Contents>
<Next Chapter: Syntax Revisited>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSL101Template

<Table of Contents>

Chapter 999: A Page Title Has Five Equal Signs

A quotation.

A remark.

An internal heading has four equal signs (or less)

Here is where text typically goes. Since we need some kind of sample text here anyway, let's use it to explain how to make a new page! To make a new page,
1. Edit this page (or any page that includes somethhing with a format that you want to use).
2. Copy all of the text that is inside the edit box to your clipboard.
3. Change the URL to that of a nonexistent page, and hit <Enter> .
4. The wiki will ask if you would like to create the page. Say "yes;" an empty editor displays.
5. Paste your copied text into the empty editor.
6. Change your new page, just as if you were editing an exisitng page.

<Table of Contents>
Homepage | LSL101 | Tutorial | CrashCourse | Glossary


LSLEthics

Like any multi-user environments users need to consider their impact upon the environment and other users. They must do so not just because it might be the right thing to do, but because the rules mandate it.

Many LSL features have built in delays & limits to reduce abuse and to better distribute the limited resources. These limits are not intended to stop abuse, only limit it. It is still possible to weasel around around these limits and most cases scripting around these limits is not considered an abuse of the tools (not to mention the only solution in most cases).

Some function delays can be mitigated by using multiple scripts and calling the function indirectly with a Link Message. Excessive abuse of this is frowned upon but use of this technique is wide spread (like using it to avoid delays on llDialog or llEmail). Rarely is a script delay the only limit on a feature for this exact reason. Link messages used in such a fashion can also cause sim lag if the number of scripts receiving the event is large.

In many situations it is up to the user to self regulate their scripts and sim resources so it is important to write efficient scripts.


LSLFunctionRequest

How-to Guide for Requesting Functions

While this wiki is a great resource for information on LSL, it is the second worst (slightly better then shouting underwater) way to request a function. You will be trolled if you post feature requests on this wiki. So to spare us all the drama, this guide is here to help in the processes of requesting features.

Write a Feature Request for an LSL Function
Write a short description and the syntax for the function listing all parameters. List all ways the function may fail and how it should handle those failures. Basically, write up an entry that could be used for the official documentation. If it's a controversial feature, request be sure to argue the reason why the function is needed.

Submit Your Request
There are a few places you can post feature requests (listed in the order of success rate):

Location | Format | Chances of not being seen by a Linden | Comment
Kelly Linden's LSL Suggestion Cylinder | Notecard | 0% | If you are going to suggest an LSL function this is the way to do it.
SL Vote | Post | 10 -> 20% | It's a good way, but the proposal might be ignored
Hotline to Linden Forum | Post | 10 -> 20% | Your proposal might be ignored; they don't like it when you do this.
Feature Suggestions Forum | Post | 15 -> 30% | Chances are good your request will be read, but it might also get lost.
Scripting Tips Forum | Post | 80 -> 100% | Sure your proposal will be talked about by the scripting community, but nothing will happen about it; if you're lucky a Linden might see it.
This Wiki | New Page | 110% | Not only will your request most probably won't be seen by a Linden but your request will be trolled.

Example Requests

Actual feature request for llGetInventoryType, dropped on Kelly Linden's LSL Suggestion Cylinder
integer llGetInventoryType(string asset)
asset is the name of an item in the inventory of the object.
the return would be an inventory flag. In the event that the asset did not exist in the object
INVENTORY_ALL would be returned, the user would not be alerted to the inventory failure
(contrary to what happens with the other inventory functions).

INVENTORY_ALL
INVENTORY_TEXTURE
INVENTORY_SOUND
INVENTORY_LANDMARK
INVENTORY_CLOTHING
INVENTORY_OBJECT
INVENTORY_NOTECARD
INVENTORY_SCRIPT
INVENTORY_BODYPART
INVENTORY_ANIMATION
INVENTORY_GESTURE

Q: WHAT official documentation? -- EepQuirk
A: The documentation that ships with the client. It's what the "Help" button in the script editor opens. -- BlindWanderer

Q: I'm on the Teen Grid. Is there any way someone can send the notecard in my name to Kelly Linden's LSL Suggestion Cylinder? -- SmileyBarry

Functions


LSLGroups

See ScriptingGroups


LslHomePage

Welcome to the LSL Wiki Mirror 7-7-7! This is an effort to document the secrets of LSL, the scripting language of Second Life. This Wiki is intended as an open documentation resource to help scripters of all skill levels. Anyone can contribute, so feel free to jump in.

First time here? See LSLwiki for information on how to contribute and use the LSL Wiki Mirror 7-7-7.

Getting started: LSL Reference: Groups of related functions:
Whether you're new to scripting or an experienced programmer, these pages should help you use LSL.

Introduction
Examples
Tutorial

AlternativeEditors
Errors
Hacks
KnownBugs
Annoyances/Gotchas
Lag
Revisions What's new in LSL?
StyleGuide

ScriptLibrary 		Already know how to script, and just need the reference?

Constants
Events
FlowControl - if, else, while & friends
Functions
Operators
States
Types
  • float
  • integer
  • key
  • list
  • rotation
  • string
  • vector
Variables
  • Agent and Avatar
  • Collisions
  • Communications
  • Dataserver
  • Detected
  • Inventory
  • Link
  • Lists
  • Math
  • Movement
  • Particles
  • Sensors
  • Sounds
  • String
  • Texture
  • Vehicles
  • WorldFunctions

Second Life and LSL are the property of Linden Lab. Portions of this site are copyright © Linden Lab. Please don't sue us. Linden Lab is not affiliated with the maintainers of this wiki. This site is provided "as-is". If you post something here, expect to see it changed. Hosting, administration and sarcastic comments provided by Tyrell Corporation. Tyrell Corporation, eschewing obfuscation for over 1 years.


LSL SandBox

-Please use this page to practice LSL. Do not abuse the page. Such as spamming, deleting other's work, Inappropriate behavior.

NOTE-To start a script double click the page to edit, then type in blank space %*%(lsl) your script %*% without the stars

EXAMPLE-
default
{
       state_entry()
        {
            llSay(0, "Hello Avatar!");
        }
       touch_start(integer num_detected)
        {
            llSay(0, "Touched.");
        }
}


LSLStyleGuide

See StyleGuide.


LSLWiki

LSL Wiki Mirror 7-7-7

This page contains information pertaining to the LSL Wiki itself. If you're looking for the actual content, go back to the homepage.

Want to contribute?
"How do I create a Wiki entry, anyway?" - See WikiFormatting.

Contributors - People who have contributed to the LSL Wiki Mirror 7-7-7.
WikiUsers - Registered wiki users.

Personal Stuff | Planning | How-To
MyChanges - Changes you've made. MyPages - Pages you've created. | Suggestions - Got a suggestion to improve the LSL Wiki Mirror 7-7-7? Post it here. WantedPages - Pages linked to by another wiki page, but that don't yet exist. ToDo - Remaining tasks to complete. WikiQuestions - Questions to answer. OrphanedPages - Pages no longer linked to. | LSLFunctionRequest - How do I request an LSL function? CommentDeletion - How do I delete comments? PageDeletion - How do I delete pages? AboutACLs - How do I control page permissions? (ACLs) WikiEtiquette - What should I do on the wiki? WikiFormatting - How do I use the LSL Wiki Mirror 7-7-7's formatting commands? WikiRenamingPages - How do I rename pages? WikiStyleGuide - How should I structure my wiki entries? WikiSandbox - Where can I experiment with wiki formatting? WikiTemplates - list of wiki templates
Rules
AboutACLs WikiEtiquette WikiStyleGuide

News

2005-05-05: Installed patches fixing the actions\header.php problem, as well as a syntax highlighting file updated to 1.6.3.

2005-05-04: Thanks to JimLinden for fixing the last remaining issue the wiki was having here at SecondLife.com. As of now, we can view past revisions and easily revert pages. (See the "Show revisions" link at the bottom of every page.) Thanks for your help, Jim!
  • actualy he missed fixing the search link at the top of the page, change needs to be made in "actions\header.php"
  • I'm sorry, I'm not sure what you're referring to. -Catherine

2005-03-11: To make pages editable only by registered users, set the edit ACLs to "?".

2004-09-15: When viewing comments, there is now a "Delete this comment" link after each of your own comments. Also removed some junk from the delete handler. -Ez

2004-09-03: Added the missing CSS classes for colorized tables. This should make large tables like llSetPrimitiveParams somewhat easier to read.

2004-08-28: Added a favicon.ico file and a Mycroft search applet. "What's that yellow thing? Why does it suck so much?" It's a cube. It's got little "angry" eyes. Bad... geometry? No? Shut up, I hate you all. "Good job, Cat". Oh, and I also took the liberty of making the icons background transparent. Ama wishes it had a mohawk. MoleculorSatyr wishes it was grey, so it could be all alien-ish. Chris likes it as it is, good work nekochan :-3

Rewrote the LSL syntax highlighter from scratch. It now officially kicks ass. Also changed the colors to be a bit more contrasting. -Ez

OldNews

HomePage | TextSearch | OrphanedPages


LSLwikicopyright

LSL Wiki Mirror 7-7-7 Copyright Policy

Since its inception, the LSL Wiki Mirror 7-7-7 has followed an implicit copyright model: all wiki content may be freely copied, modified and distributed for noncommercial use. The exception to this is example scripts, which should be regarded as public domain, except where noted in the license attached to their source code.

Effective March 3, 2007, all content on the LSL Wiki Mirror 7-7-7 should adhere to this policy, with Fair Use exceptions: quotes and excerpts from forum posts, Linden Lab messages, etc.

HomePage | LSLWiki


LSLWikiHelp

Page Coming Soon


Machinima

Machinima is the technique of filming a movie using a real-time 3D engine, rather than rendering a frame at a time. It's much faster and requires fewer resources, but the end result looks less like 'Toy Story'. It can be a very effective way of telling a story.

A popular example of machinima is Red vs. Blue, filmed in Bungie's game Halo.

For more information, see the sites machinima.com and machinima.org.

I want to put up a tutorial on scripting for machinima. Someone want to start?

Scripting Machinima in SL
With any form of machinima, the desired effects of the director are balanced with the features of the game engine being used to create the final product. In Second Life, you have several features at your disposal:

  1. A physics engine (Havok 1.0)
  2. The ability to script any object
  3. Fairly limitless actor pool, very customizable "costumes"
  4. Camera controlling script functions
  5. Custom animations

You may say "But llTakeCamera isn't done yet!?", but you can use llSetCameraAtOffset and llSetCameraEyeOffset to just the same effect.
In this instance, the recording user would sit on an object which would then manipulate the camera and it's own position based on user input or even notecard directions.
You can have another user trigger scripted events via voice, using non-0 chat channels. That way, you can get scripted objects to do their thing without messing the output video up with text. (However, you could also have the objects trigger on a line from an actor, assuming you're not dubbing in voice over a text-less video)

There's your start. I don't know what to add beyond that... Yeah. - AyAn4m1Noir
Vehicles
Camera

something about how to set up cameras and stuff?


MagnetScript

**__Joker Opus's EarthQuake Script__**

' ' NOTE- This was created by Joethecatboy Freelunch and Joker Opus. It is not to be stolen. You are free to mod this, but please do not steal ' '

So enjoy this as you please, and have fun!
//////////Created, September, 2006.///////////////
///-By Joethecatboy Freelunch and Joker Opus///
    //If you plan to copy, mod it, don't steal//
//*~*Commentry by Joker Opus*~*//

float BlastRadius = 20;//This indicates the radius in which the push is effected. In this case, 30 meters
float Power = 21.0;//This is the power that the object shakes agents at.

default
{
    on_rez(integer param)
    {
        llResetScript();//Scripts always get messed up if you dont have the script reset on rez if there is a Listen event. Because if it doesnt reset, it thinks the owner is still the person who made the script.

    }
    state_entry()
    {
        llOwnerSay("OWNAGE");
        llListen(0,"",llGetOwner(),"");
    }
    touch_start(integer num_detected)
    {
        llSensorRepeat("",NULL_KEY,AGENT|PASSIVE,20.0,PI,.01);//Sensor repeat is scanning over and over for the targets within 20 meters. including objects (hence passive)
    }

    sensor(integer j)
    {
        vector MyPos = llGetPos();  //gets my position..
        integer i;
        for (i=0;i<j;i++){
        if (llDetectedKey(i) == llGetOwner())
        {
            i++;
        }
        else{
        float Distance = llVecDist(MyPos, llDetectedPos(i) );//Makes it so it wont effect the owner
        llPushObject(llDetectedKey(i), <0.0, 0.0,Power * llPow(Distance,3.0)>, ZERO_VECTOR, TRUE);
        llPushObject(llDetectedKey(i), <0.0, 0.0,Power * 2 * llPow(Distance,3.0)*-1>, ZERO_VECTOR, TRUE);//This is the part thats actually pushing the people
        }
        }
    }
    listen(integer channel, string name, key id, string message)
    {
        if (message == "die")
        {                              //You can add a die timer, but I made a comand to delete the object, if you say 'die' it deletes =)
            llSensorRemove();
            llDie();
        }
    }
}


magnitude

Magnitude is, in mathematical terms (and most relevant to LSL), a length of a vector.
  • normalized vector/unit vector: a vector of magnitude 1

Functions

llVecMag

vector


Mainframe1

Hi there .. you have visited my user page .. you must be bored or nuts, either way.

my inworld name is AdorableLunatic Agnon or otherwise known as Lyndell Aleixandre
the first name is my primary account but after discovering that i cannot use the forums with that account for some strange reason .. (and no i was not banned, infact i have never used the forums with that name) i decided to create an ALT for myself to properly use the forums (read, not post) this alt is lyndell aleixandre .. and is my main avatar now although i still use my old account.

this was AFTER i asked the lindens to look into it several times, back before Linden Labs didnt give 2 farts about customer support.

to this day it has still not been resolved.

thats my life story, now for somthing of my real life ... i am a programmer /software architect getting the hang of this horrible language called lsl, im quite adept now.
inworld i am a sim administrator for hire, i know about lag, i know allot about it and im anal for removing it, however .. i like weapons, so . theres a bit of irony.

i mostly specialise inworld with lists, communications, sensors, and anything else EXCEPT rotations, math etc ... although i do know about them i avoid them allot and will shoot you if you ask me about them ;-)

this concludes the medeocre introduction to my user page, if you are still bored look closely at the dot below and turn your head to the side .. if you concentrate long enough and stare enough , you will see the matrix and a hippo will appear hehind you and try to do very inappropriate things to your rear end... watch out.

.


MapAPI

This is a page devoted to
http://secondlife.com/developers/mapapi/

The Map API for Second Life.

First tip: This was driving me nuts: Coordinate system. The one used in the Map API is just the Global coordinates divided by 256.
Hope this helps.

How to use Sim Names to make teleport link:
Put this in your header, after the code for the map api

<script type="text/javascript">
    function gotoSLPoint(simname, x, y)
    {
        var foo = new SLPoint(simname,x,y);
        return gotoSLURL(foo.x, foo.y);
    }
</script>
Then use it thus:
<a href="javascript:gotoSLPoint('Ahern', 128, 128)">Goto Ahern</a>


Mask

A mask is another term for a bitfield used to only retrieve specific values from another bitfield.

Example:
If there is a bitfield 01010101 but only the middle 4 bits are desired, bitwise AND (&) it with the mask 00111100. This works because anything ANDed with a 0 is 0, and anything ANDed with a 1 remains unchanged, allowing the unwanted bits to be stripped.

A mask is also another name for a texture that has transparency--a masked texture.

bitwise | bitflags | texture


Mass

Mass is the measure of translational inertia.

The mass of a prim or object, as reported by llGetMass or llGetObjectMass, depends only on its size and shape, not on its material type. The units are lindograms.

Functions

llGetCenterOfMass
llGetMass
llGetObjectMass

SL gravity is <0,0,-9.8> m/s2. According to Newton's Second Law, an object may be given a desired acceleration with:

vector desired_acceleration;
float mass = llGetMass();
llSetForce(mass * (desired_acceleration + <0,0,9.8>), FALSE);

Notes

  • Changing material type has no effect on density and therefore mass.
  • Hollowed prims tend to have less mass and twisted prims tend to have more mass but there are some oddities (see energy).
  • In linksets, the parent returns the sum of all the masses in the set, while the children return only their own.
  • Avatar mass isn't affected by attachments but will change depending on avatar height (but not fatness, thickness, muscle, etc). Attachments seem to adopt/report the avatar's weight.
  • Sitting down means your weight is added to the sat-on object. It seems implemented as temporarily being one of the children in the linkset.

Physics | Energy


Material

In traditional 3D, material makes up a polygon's surface, specifying its geometry style, color, texture, lighting, etc.

In SL, each individual prim can be made of one of several different material types in a more physical sense. Material type can be changed by editing a prim/object, going to the "Object" tab, and clicking the "Material" drop-down menu in the bottom left corner of the window. Each material has its own set of characteristics, such as default collision sounds and friction, and possibly some other properties relating to the physics engine, though details are unclear.

The following list is sorted by friction, not the order it appears in the edit box.
Material | Description
Light | Emits light visible when the "Local Lighting" preference is enabled (can cause client slowdown if abused). Prevents shadow on the surface of a prim made from this material. Has the least friction, slightly less than glass. Deprecated, will show as "Fullbright(legacy)" for lit prims before SL 1.9.1
Glass | Fairly clearly has the least friction (useful for roads, vehicles, or for sliding down)
Metal | Slightly more friction than glass.
Plastic | More friction than glass and metal.
Wood | Default material all new prims are made of. More friction than plastic.
Rubber | More friction than wood. Doesn't seem to bounce higher. Possibly loses less energy in collisions, though; someone could check that
Stone | More friction than rubber.
Flesh | Seems to have slightly more friction than stone.

Useful Tips: When using "Light" to light a room up at night, a combination of llSetPrimitiveParams and llGetTimeOfDay can be used to change the material from "Light" to something else at the appropriate times. Also, instead of making every prim material "Light", make a separate, invisible, phantom light prim to simulate the light source.

Hopefully other Wiki users will update this page with facts about the different material types to fill in what we don't have listed. As more details are obtained, the Details column could probably be expanded to separate categories like friction, 'bounce', and other such things. Feel free to add in what you know, or comment in what you suspect.

Prim | Friction


Mathematics

See math.


math error

A math error is encountered when a script tries to evaluate an expression that returns a nonreal or complex answer. This includes divide-by-0-type errors. If the answer is i, it's gonna return a math error.

At least as of SL 1.5.5, it is possible that these math errors don't actually pop up until you use the variable the imaginary number exists in.

foo = llSqrt(-1);

Won't give an error until you use foo.

errors


Maths

See Math.


MD5

The MD5 Message-Digest Algorithm

MD5 was developed by Professor Ronald L. Rivest of MIT. What it does, to quote the executive summary of RFC1321, is:

The MD5 algorithm takes as input a message of arbitrary length and produces as output a 128-bit "fingerprint" or "message digest" of the input. It is conjectured that it is computationally infeasible to produce two messages having the same message digest, or to produce any message having a given prespecified target message digest. The MD5 algorithm is intended for digital signature applications, where a large file must be "compressed" in a secure manner before being encrypted with a private (secret) key under a public-key cryptosystem such as RSA.

In essence, MD5 is a way to verify data integrity, and is much more reliable than checksum and many other commonly used methods.

RFC1321 / llMD5String / Crypto / Functions


MediaWiki

MediaWiki is an HTML-generating wiki application, like the WakkaWiki we're using now, except much better.
MediaWiki looks much more professional then WakkaWiki, and has a ton of features Wakka doesn't support, like image uploading, nicer looking page history comparisons, and automatic redirect generation.

Recent comment made by LL on the future of the wikis
http://forums.secondlife.com/showthread.php?t=58100


Memory

Memory is the running space for data and programs in a computer.

Different compilers and runtimes have different rules about how scripts are executed/interpreted. The following comments are based on typical compiler and runtime behaviors.

In LSL, each script runs in its own separate 16KB section of simulator memory called a "sandbox". In theory, scripts are not allowed to manipulate other script's or the simulator's data stored in memory (they are kept in their own "sandboxes").

There are 3 areas of a script's memory space:

stack: Local variables and other bookkeeping information (discussed here) are stored in the stack. When a function X is called, the following happens: (1) the location of the statement of the calling function (the one that calls X) is pushed onto the stack, (2) as function X is executed, any variables declared within X (these are called local variables) are pushed (saved) onto the stack, and finally, (3) when function X ends its execution, any value that it returns is pushed onto the stack. To return to the next statement of the calling function (i.e., the statement after the one that calls X), the stack already contains that information (see #1), and if any value is returned from X, the stack contains that, too (see #3). When the calling function resumes, the stack will be restored to the way it was before function X was called.

heap: A heap is more or less what it sounds like: a "pile" of data. The contents of strings, keys and lists are all stored on the stack.

statics: This is where all the script's global variables are stored.

bytecode: LSL is compiled into bytecode which is what is executed to run the script. This also resides within the 16KB of free space for the script.

16KB = stack + heap + bytecode

The 16KB chunk for a script is allocated as follows:

High Memory:
Stack
Low Memory:
Bytecode + Statics + Heap

The (Bytecode + Statics) space is fixed. The Stack and Heap are not fixed: the Stack grows downwards towards the Heap space, and the Heap grows upwards towards the stack.

Stack-Heap Collision Errors

When the script starts, data is pushed onto the stack starting at the High Memory location and downwards. Every time you create a string, list or key, heap space is allocated, and if there isn't enough space the heap is expanded. This means that as more and more data is pushed onto the stack (e.g., more and more function calls are nested or lots of local variables are declared), and more and more data is stored in the heap there's a danger that the stack will run into the heap! That is, the stack's data will start to write over the heap data. Fortunately, this condition is reported by the system as a Stack-Heap Collision Error.

To regulate the amount of memory you use, remember the following:

1. Don't keep large lists and strings hanging around once you're done with them. Note that, even if they're not going to be used again, the contents of all local variables stay hanging around until they're overwritten or the function returns.

2. Reduce the amount of nesting of function calls. The more nested your calls are (e.g., A calls B, which calls C, which calls D, etc...), the more memory is retained in the stack. Your trade-off will be between writing long, flat functions, or splitting a function into subfunctions so that code is more readable and reusable.

3. Consolidate your variables, the tighter your code and the fewer the variables used, the less memory required. Only use intermediate variables while you write the code and remove as many as you can once you have the code working. By using general global buffers you can save memory but at the expense of readability, not to mention you have to be very aware of who writes to them and when.

4. Pay attention to what you pass into a function or assign to a variable. Variables are passed by value to a function, so they are copied when you call a function. The deeper the number of calls passing the same variable, the greater the number of copies. Pass only what you need. If possible, use shorthand to reduce the amount of data passed (e.g., instead of passing a string "5094", try passing an integer 5094). Also, when you assign an expression or variable to another variable, you are actually copying its data to the variable you are assigning it to.

5. When you use the same data, hold it in a global variable instead of repeating it in place in your functions. For example, if you use the string "Hello there" multiple times in your code, store it in a global (string HELLO_THERE = "Hello there";) and refer to it. This will create just one copy of the data in the heap. (Some compilers are smart about this.)

6. Understand how much memory is consumed by each type of variable. For example, Lists use alot of memory. Sometimes it's possible to use a string instead (also see CSV).

7. If you run into memory limitations in a script, create another script and pass information between them using link messages.

8. Recursive functions. First, watch out for infinite recursion (a function that calls itself forever). Since a recursive function eats up stack space, transform your recursive function into an iterative function. You can usually do it (check the web for examples on recursive functions).

9. Function calls are expensive, if you can reduce the number of them in your code, it will save you a large chunk of memory.

10. Don't expect the compiler to implicitly typecast your constant integers to floats at compile time; it won't (except for globals). You can save memory by writing your constant that are being stored into floats, vectors or rotations as proper floats.

A script's memory usage can be determined by using llGetFreeMemory.

Heap | Stack | Errors


Memory Usage

Important note: The information on this page is obtained from methodical testing, and contains a degree of speculation regarding some of the inner workings of LSL. While the numbers have a high degree of confidence from the author, the conclusions based on the numbers are not meant to be definitive or authoritative, and are subject to much change as more information is revealed.

These results were obtained from empirical testing and observation using the llGetFreeMemory function, and working around its reporting limitations. The results are believed to be accurate, with experience from using these values as a reference giving a high degree of confidence that they are such. This information is of value to scripters who wish to know just how much memory they may be using in their scripts' data structures, and is quite useful in helping avoid the dreaded stack/heap collision error.

Variable Type | Memory Usage (bytes) as Standalone Variable | Memory Usage (bytes) as List Element | Notes
integer | 10 | 15 |
float | 10 | 15 |
vector | 18 | 23 |
rotation | 22 | 27 |
string | 18 + 1 per character | 12 + 1 per character |
key | 18 + 1 per character | 12 + 1 per character | Keys are basically identical to strings; generally, their normal values are 36 characters in length, so the base size plus 36 is a common value for how much space they take up.
list | 21 | n/a | This is simply the basic space taken up by a list variable, no matter if it has 0 or a hundred elements. The size of the elements stored within is given in the third column of this table.

As you can see, there is quite a bit of overhead for each declared variable. As global variables, they take an extra 6 bytes each over their minimum data representation. Numeric-type list elements all have an extra 5 bytes of overhead above this, which is most likely pointer values, since they are often described as being implemented as linked lists. In fact, from the size of the list variable, it would infer it is comprised of 3 such links; probably a head pointer, a tail pointer, and possibly a count. Why these values are 5 bytes (40 bits) given that the addressable space for each script is only 14 bits is a mystery. Strings similarly have a curious amount of overhead, most likely a pointer to the actual string data on the stack or heap, and a length. In addition, why strings/keys lose the 6 bytes of overhead when put into a list (plus not suffering an additional 5 byte overhead from the link data), and the other types do not is another mystery.

Reproducing the data

To reproduce this data, use the following baseline script, and then insert the specified variable declarations as globals:

Script Free Memory (bytes) Difference from Baseline (bytes)
default {
    state_entry() {
        llOwnerSay((string)llGetFreeMemory());
    }
}
 16172 (baseline) 0
integer i;
 16162 10
float f;
 16162 10
vector v;
 16154 18
rotation r;
 16150 22
string s;
 16154 18
string s="";
 16154 18
string s="Some characters to test this theory!";
 16118 54 (18 + 36)
key k;
 16154 18
key k="";
 16154 18
key k="01234567-89ab-cdef-0123-456789abcdef";
 16118 54 (18 + 36)
list l;
 16151 21
list l=[];
 16151 21
list l=[12345];
 16136 36 (21 + 15)
list l=[1.2345];
 16136 36 (21 + 15)
list l=[<1.2,3.4,5.6>];
 16128 44 (21 + 23)
list l=[<1.2,3.4,5.6,7.8>];
 16124 48 (21 + 27)
list l=[""];
 16139 33 (21 + 12)
list l=["Some characters to test this theory!"];
 16103 69 (21 + 12 + 36)
key k="";
 16121 51 (18 + 21 + 12)
key k="01234567-89ab-cdef-0123-456789abcdef";
 16049 123 (18 + 36 + 21 + 12 + 36)
list l=[12345,1.2345,<1.2,3.4,5.6>,
        <1.2,3.4,5.6,7.8>,
        "Some characters to test this theory!",
        "01234567-89ab-cdef-0123-456789abcdef"];
 15975 197 (21 + 15 + 15 + 23 + 27 + 12 + 36 + 12 + 36)

Anticipated questions:

Q: Why does it use more memory if you assign the values inside of the events?
A: First, you have to remember that llGetFreeMemory is a low-water mark. In other words, once you use X amount of memory, even if you free it up, the function will still report (16384 - X) free memory. That said, temporary copies of variables use up stack space, as well as all the pass-by-value parameters in function calls, all adding more overhead which is reported as part of the memory use. Expressions also tend to create multiple temporary variables which hold intermediate results. As a result, you will see the amount of memory used double, triple, or go even higher.

Q: What about literal constants in code?
A: It is somewhat difficult to determine exactly, because even variable assignments appear to make multiple copies at times. However, it appears that they use about the same, if not the same amount of memory as if they were each defined as a global variable. Also bear in mind that the LSL compiler is NOT in any way, shape, or form, an optimizing compiler. Thus, it does not do things like "constant folding", where the exact same constant value used in multiple sections of the code is only stored once, with all instances in the code referencing that same value.

Memory | Types | Variables


MerothJunge

Programmer in RL, scripter in SL. Can life be better? :) I find LSL a good toy to play with, just like about any scripting language, but many times annoyed by the many limitations it imposes (although I can understand that). Started a huge scripting project, though I don't think I'd have the energy to finish it, so for now I just fiddle around making small toys.

Always at your disposal if you need help scripting.

I'm currently constantly making new things, and improving myself. If you have a good idea for a cool scripted item, contact me :)


messageid

(any content on this page was lost to spam. Nothing in the revert history)


Meter

The meter/metre is the unit used to measure distance in Second Life (and most parts of the real world). Simulators are 256x256 (65536 square) meters in size.

Units


Mono

There have been various references made about Second Life scripting being retooled with Mono sometime in the near future.

Purported features of Mono in Second Life:
  • Substantial speed improvements ("300 to 500 times")
  • It has been stated that at some future date after the launch of Mono in Second Life other languages such as C# will be added.

Languages Other Than LSL

Any language that has a CIL compiler can be run on Mono and, in theory, used as a scripting language in SecondLife. This includes C#, Visual Basic .NET, Nemerle, IronPython, Boo, Delphi.NET, etc. However, various statements made by BabbageLinden and others on the Second Life forums seem to indicate that other languages will not be immediately available to use in Second Life scripts. After the launch of Mono, scripting will still be restricted to LSL for some time. No ETA has been given as to when other languages will be available for use in Second Life scripting. A list of supported languages in Mono can be found here.

External Compilation of Second Life Mono Scripts

As there are more than a few CIL compilers out there, it should be possible to compile to your own CIL bytecode from your favorite language. If you could upload this directly to Second Life then you would be able to use your favorite language and IDE to compile your Second Life scripts. All you would need are the ll* function library assemblies. Or even just some kind of stubs to those assemblies. Such stubs would probably not be that hard to create on your own, actually.

Pages that reference Mono:
  • Babage Linden's blog, in particular, this post from April indicates that Mono-based scripts will run 300 to 500 times faster than LSL scripts!
  • Bytecode
  • KeknehvPsaltery
  • TauNino

Gordon, I took the liberty of making your links to c++ and c# point somewhere. =) I set them to external links, rather than writing a new page for them because A. There is already more written than I could write in one night or week and B. I don't think it follows the LSL-oriented nature of the wiki to be dedicating whole pages to other languages. =) - St. Psaltery

Sounds good to me though I think I would have prefered the Wikipedia entries myself. :) - GordonPrior 14/Jan/2006 13:48
compiler


Mouselook

Mouselook is the first-person camera view in SL as seen from the eyes of the avatar. When in this mode the mouse is used to move the camera around, hence the name. Mouselook is often used for weapons, vehicles, and grabbing objects.

When using the llTakeControls function and the control event it is important to remember that the values returned by the same buttons can be different depending on if the avatar is in mouselook mode or not. For example, by default A will turn the avatar left in normal mode and trigger CONTROL_ROT_LEFT in the control event. However, in mouselook mode, A will cause the avatar to strafe left (move left without turning) and trigger CONTROL_LEFT.

If a "Mouselook" button is desired to appear at the bottom of the SL window, look at llTakeControls for instructions.

When "Show Avatar in Mouselook" is enabled, in preferences, the user's avatar is visible in mouselook mode, as well as any attachments (except those attached anywhere on the avatar's head).

Holding the "Alt" key in mouselook will show a hand-cursor to click on things while the camera isn't moving with the mouse.

A sitting agent can be forced into mouselook using llForceMouselook.

control | Weapons | Vehicles


MP3

MP3 is a popular form of audio compression, but is (unfortunately) not supported by the SL uploader.

MP3 stands for MPEG, audio layer 3.

Sound | File Formats


MPEG4

MPEG4 is the newest release of the video codec, MPEG. It is much more efficient than most other video encoding algorithms, and enables most DVDs to be packed onto a single CD. The older, less efficient MPEG2 technology is what DVD players use. SecondLife supports MPEG4 via Quicktime.


Multithreading

Multithreading can be used to pass delayed tasks to other scripts by link messages.

Warning: The practice of multithreading to eliminate script delays is a mildly dangerous one at best, and holds the potential to bring a server to its knees, or outright crash it at its worst. Please, for the love of the hippos, try to refrain from multithreading unless it is absolutely necessary for a script to have no script delay. A multithreaded script can cause problems for dozens or even hundreds of users if done needlessly. Multithreading can cause unnecessary strain on sims or other servers, and should be avoided when possible.

An example of multithreading would be having a script that would need to send an email every time something occured. By using llMessageLinked, another script in the same object could be told to send the email instead, thereby sparing the main script from llEmail's built-in 20-second delay.

Example added by MarcEisenberg:
This is the mailing system script. Only one of these scripts in the linked object hierarchy is necessary:
integer LINKMSG_SEND_EMAIL = 1451363;

default {
    link_message(integer sender_num, integer num, string str, key id) {
        if (num == LINKMSG_SEND_EMAIL) {
            list mailparam_list = llCSV2List(str);
            string mailto_str = llBase64ToString(llList2String(mailparam_list, 0));
            string mailsubj_str = llBase64ToString(llList2String(mailparam_list, 1));
            string mailbody_str = llBase64ToString(llList2String(mailparam_list, 2));
            llEmail(mailto_str, mailsubj_str, mailbody_str);
        }
    }
}

This is the script that tells the mailing system it needs to email. Include this at the top of any script that will send mail in a linked object hierarchy:
integer LINKMSG_SEND_EMAIL = 1451363;

MT_SendEmail(string mailto_str, string mailsubj_str, string mailbody_str)
{
    llMessageLinked(LINK_SET, LINKMSG_SEND_EMAIL,
                    llList2CSV([llStringToBase64(mailto_str),
                                llStringToBase64(mailsubj_str),
                                llStringToBase64(mailbody_str)]),
                    NULL_KEY);
}

Then, messages are sent using this: MT_SendEmail("email@domain.com", "Subject", "This is my message");

The message will be passed off to the mail-handling script.

IMPORTANT! Please note that this example uses the number field in the linked messages as an identifier for the type of message. If there are scripts in a linked object that send messages in another way, this script's link handling will need to be modified to match (or there may be problems with miscommunications between objects).

Communications


MyChanges

You're not logged in, thus the list of pages you've edited couldn't be retrieved.
Also see MyPages


MyPages

You're not logged in, thus the list of your pages couldn't be retrieved.
Also Visit MyChanges


MySpace

This has been created to clean up the wanted pages... go to MarcEisenberg and request that the camel cased words from his site be linked to or removed. This page will be deleted once that has been completed.


MySQL

MySQL is a relational database management system, which means it stores data in separate tables rather than putting all the data in one big area. This adds flexibility, as well as speed. The SQL part of MySQL stands for "Structured Query Language," which is the most common language used to access databases. The MySQL database server is the most popular open source database in the world.


NamesProtocolProposal

Version 1.1 Name Proposal v2

Name Structure

All interactive object names MUST have the following name structure

8 digit Hexidecimal classification bitfield, Seperator, Species Name

Formatted

0x00000000|Species Name

Hexidecimal Classification

0xVTMSSIFF

V - Version(4 bits)

Version number changes if the compatibility of the name or the chat protocol changes. Version will only use the 3 least significant bits of this portion to avoid turning the number negative. Version 1 objects MUST NOT communicate with a version they don't understand. Version 1 objects MAY communicate with a different version if they understand it. Version 1 objects SHOULD abort communications if they recieve an unexpect response from any version. When sending a message to a different version Version 1 objects MUST format the message according the protocol of the recieving version. The first 3 characters in the name of Version 1 objects MUST be "0x1"

T - Toggles(4 bits)

bit assignment - UFWD

Version 1 contains space for 4 binary toggles. These Toggles are not broken down by species type and apply across the board if toggled on.

U - Unassigned
F - Flys(Hex 4) This toggle MUST be ON if the object is currently capable of directed flight(MAY be on for ballistic flight) This toggle MUST be off if the Dead toggle is on.
W - Water This toggle indicates whether an object spends its time on Land or in Water. This toggle MUST be on if the object spends all its time in the water. This toggle SHOULD be on if amphibious object spends most of it's time in the water. This toggle MAY be on if an amphibious object spends most of it's time on land.
D - Dead(Hex value 1) This toggle MUST be turned on if the object is dead and is still interactive.

Currently Dead is the only state based toggle. Flys and Water are stateless. Ex an object Flys regardless if it's currently flying. Amphibious creatures MUST keep water either toggled on or off regardless if they are in the water or on land currently. Toggles MUST be combined in more than one should be on.

M - Major Classification(4 bits)

bit assignment - URAP

U - Unassigned
R - Robot(Hex 4)This toggle MAY be combined with A or P Hex 6 in this slot indicates a robotic animal. Hex 5(Robotic Plant) MUST NOT be assigned unless the robot draws resources from the ground in competition with plants.
A - Animal(Hex 2) This toggle MUST NOT be combined with Plant.
P - Plant(Hex 1) This toggle MUST NOT be combined with Animal.

SS - Sub Classification: modifies Major Classification(8 bits)

Animal Subclassification

bit assignment - UUUUUCOH

U - Unassigned
C - Carnivore(HEX 04)
O - Omnivore(HEX 02)
H - Herbivore(HEX 01)

Plant Subclassification

bit assignment - UUUUTBLS

U - Unassigned
T - Tree(HEX 08)
B - Brush(HEX 04)
L - Leafy(HEX 02)
S - Seed(HEX 01)

To the animal world plant classifications indicate shape and difficulty to eat. To the plant world classifications indicate the layers plants draw resources from.

Subclassifications in Version 1 MUST NOT be combined. Be aware the same Subclassification bits will be set on a Carnivore as on Brush.

I - Intra Species Classification(4 bits)

bit assignment - AAAA

A - Available These bits MAY be assigned as the species developer sees fit. Usage of these bits SHOULD be minimal and SHOULD only be used to indicate major differences. Ex between seed and plant of the same species.

F - Food Types Offered(8 bits)

Animal Food

bit assignment - UUUUUUUM

U - Unassigned
M - Meat(HEX 1)

Plant Food

bit assignment - UUWRLNSF

U - Unassigned
W - Wood(Hex 20)
R - Root(Hex 10)
L - Leaf(Hex 08)
N - Nut(Hex 04)
S - Seed(Hex 02)
F - Fruit(Hex 01)

Food Offered bits are stateless if your object can offer the type it MUST set the flag even if 0 is currently available. Food Offered bits MUST be combined if your object offers more than one type.

Seperator

& - Used to split between the Classification and the Species Name

Species Name

Human readable name for your species. Name SHOULD be consistent across all objects of your species. Exception is major stages ex Species Name Species Name Seed.

Example Names

0x10102208&Cannon Plant
0x1010822C&Palm Tree


NamingConventions

LSL uses names of the form "llAction" where the prefix "ll" stands for "Linden Lab" and is used to denote a Linden-made function.

In LSL scripts, Catherine recommends naming global variables with the prefix "g" as in "gVariableA". Some scripters prefer to also specify the type in the variable itself, as in "vector gvVariableA = <23,39,0>;" or "float gfVariableA = 31.9;" This is a matter of personal preference and can be useful for keeping those "Type Mismatch" errors down.

The LSL Wiki Mirror 7-7-7 uses the name of a function call, handler, or constant for WikiNames.

StyleGuide / Tutorial / Examples


NaN

NaN stands for Not a Number. NaNs are used to represent the results of operations that are not real numbers or are too big to fit into a float and therefore cannot be adequately represented with a float.

In SL, generating a NaN (by 1.0/0.0) will crash the script.

NaNs in General

NaNs can behave quite differently among the many and varied implementations of computer-based arithmetic. For example, Java has special NaNs to represent positive infinity and negative infinity. NaNs can be represented by any one of a range of float bit patterns. Some implementations apply further meaning to individual NaN values, such as whether an exception should be raised when it is encountered in an operation.

See Wikipedia's NaN entry.

Q:What is the simplest way to make it so NaNs does not get an auto link?
A:Unfortunately, to put double-double quotes ("") around the phrase. I wish there was some better way :-/ -Chris

Operators


newview

newview.exe was the name of the SecondLife Windows client which was renamed in 1.6 to SecondLife.exe.

usage: SecondLife.exe [options]

Options

-option <argument1> <argument2> | Description
-skin <directory> | load skins\<directory>\skin.xml as the default skin (incomplete)
-korean | activate (incomplete) Korean UI translation
-spanish | activate (incomplete) Spanish UI translation
-techtag <param> | unknown (but requires a parameter)
-simple | ?
-yield <ms> | yield some idle time to local host (changed from -cooperative)
-ignorepixeldepth | ignore pixel depth settings
-url <url> | handles secondlife://sim/x/y/z URLs
-nothread | run VFS (Virtual File System) in single thread
-nomultiple | block multiple viewers (secondlife.exe instances)
-multiple | allow multiple viewers
-safe | reset preferences, run in safe mode
-noconsole | hide the console if not already hidden
-login <first> <last> <password> | log in as a user
-set <variable> <value> | specify value of a particular configuration variable; can be used multiple times in a single command-line
-settings <filename> | specify configuration filename; default is "settings.ini"
-logfile <logfile> | change the log filename
-log |
-noinvlib | do not request inventory library
-noutc | logs in local time, not UTC
-errmask <mask> | 32-bit bitmask for error type mask
-debugst | debug mask
-local | run without simulator
-purge | delete files in cache
-noprobe | disable hardware probe
-nofmod | FMOD is the API used to distort sound while moving
-nosound | no sound
-noaudio | no audio (different thank above?)
-quitafter <seconds> | SL quits after elapsed time
--autologin |
-skin |
-helperuri |
-loginuri <URI> | login server and CGI script to use
-space | deprecated; use "-user" instead
-user <user_server_ip> | specify user server in dotted quad
--dmz | See Grids::DMZ (DeMilitarized Zone)
--agni | See Grids::Agni, Main Grid
--colo | See Grids::Colo, Preview Grid
--siva | See Grids::Siva, Preview Grid
--durga | See Grids::Durga, Preview Grid
-outbw <number> | set outgoing bandwidth
-inbw <number> | set incoming bandwidth
-drop <number> | drop number% of incoming network packets
-port <port number> | Set the TCP port for the client; useful to run multiple instances of SL on the same local home network. Values that may work: 13000 and 13001 (Valid numbers are 13000 to 13050)
-dialog | some arcane dialog box that is impossible to raise
-previous |
Not exactly sure what they do, and if you don't pass the right number of parameters in the right format, the client will crash...--BW

Grids

Name | Desciption | User Server | Login CGI
DMZ | LL Internal Grid | userserver.dmz.lindenlab.com | https://login.dmz.lindenlab.com/cgi-bin/login.cgi
Agni | Main SL Grid | userserver.agni.lindenlab.com | https://login.agni.lindenlab.com/cgi-bin/login.cgi
Colo | Old Preview Grid (1.6--) | userserver.colo.lindenlab.com | https://login.colo.lindenlab.com/cgi-bin/login.cgi
Siva | New Preview Grid (1.7) | userserver.siva.lindenlab.com | https://login.siva.lindenlab.com/cgi-bin/login.cgi
Durga | New Preview Grid (1.8) | userserver.durga.lindenlab.com | https://login.durga.lindenlab.com/cgi-bin/login.cgi
Debug


NonLSL

I will add non LSL Related links here!

why? this is the LSL Wiki Mirror 7-7-7, the normal location for non LSL links are on your own page...

There seem to be a lot... (like on Ownership/Creatorship ect...) As they are not posted on the main page, they are harder to find. A bunck of links like the main page might help.

you mean like links that go off site that are scattered through the documentation? Thats a cool idea.

yes, like sprite ect...

i don't want to encourage more off topic pages on the scripting wiki. This isn't the educate the masses about computers and gaming technology, this is the LSL scripting wiki. sprite is one of the pages i begrudingly accept, as it is on the outer boarder of LSL. EepQuirk and I do not agree on the future of the wiki. I'd rather see terms like sprite defined on the support wiki or another site. -- BW

Not even things like object ect...

Object is cool, as it's the name used for a linked set of prims, but terms like KiB, GIF have less application. They really belong on the support wiki; as does most of fileformat

If I get around to it, I meant things like object...


Non-Physical

Any task (object or prim) that has its physical attribute disabled is non-physical, thus acting in the absence of gravity. The property is set with a checkbox in the object edit dialog box. By default, physical is disabled.

Non-physical tasks are moved and rotated using kinematic scripts. For a complete listing, see dynamics.

Dynamics | Physical


Noob

Short for "newbie" (also known as n00b, newb, and n3wb), a noob is new user to SL who usually makes it overly obvious that they're a newbie by asking questions like: "Where am I?", "How do I fly?", or "What's your name?".


Normal

A normal is the direction (usually represented by a vector--a line) a polygon's face points.

llGetSunDirection
llGroundNormal

Normalized | Polygon | Face | Vector


normalized

A normalized sound has been amplified so that its loudest point is at the maximum possible level without clipping.

A normalized vector has a magnitude of 1; practically, this means that llVecNorm will give the same vector.

normal


Normalized Vector

A normalized vector, also known as a unit vector, is a vector that returns an llVecMag (vector magnitude) of 1 (it points 1 unit in a direction).

Normalized vectors are found using llVecNorm.

Note: not to be confused with a normal.

Functions | Vector


Notecard

A notecard is a Second Life inventory item (basically a text file), and has a key that can be acquired manually by right-clicking a notecard and selecting "Copy UUID to clipboard". Notecards can contain any type and number of inventory items embedded into the text (objects, clothes, textures, other notecards, landmarks, etc.).

Notecards are limited in size to 64KiB of text.

Notecards can be read using llGetNotecardLine() unless they contain any inventory items as described above.

Functions
Function | Description
llGetNotecardLine | This function fetches line number of notecard name and returns the data through the dataserver event.
llGetNumberOfNotecardLines | Returns number of lines in the notecard name via the dataserver event.
llGiveInventory | Gives a notecard. The notecard's contents are displayed, and the user has the option to keep or discard the it by clicking buttons at the bottom of the notecard window. This is an easy way to create a popup text display.

Events
Event | Description
changed | Triggered when a notecard (or other inventory item) is added or removed from an object.
dataserver | Triggered when calls to llGetNotecardLine or llGetNumberOfNotecardLines returns data.
Functions | Communications


Notice

**Joker Opus- Sensor Notice**

This is a script used to send a notice to anyone within the sensor range. Like an artificial linden notice.

///////////Created by Joker Opus, December///////////
default
{
    touch_start(integer num_detected)
    {
        integer i;
        llSensorRepeat("",NULL_KEY,AGENT,512,2*PI,0.001);//This scans on touch
    }
    sensor(integer count)
    {
        integer count = 50;
        integer i;
        for(i=0;i<count;i++)
        {
            llDialog(llDetectedKey(i), "Hi all, I am testing this!", ["Ok"], 12345); //This is dialog, it looks like a notice, and it says "Hi all, I am testing this!" and it has an [ok] for a button, indicating the notice look. and the OK gets rid of the pop up.
        }
    }
}

Q: Can I use this anywhere I want? So if i go to a sandbox, and I want to tell everyone something, I can just use this?

A: Yes, its sensor is limited though, so you can add alot around a parcel you own, and have them all activate when you tell the mother to using llsay on a channel or something.


Null

Unlike many modern programming languages, LSL lacks an explicit null keyword (variables cannot be assigned null). null is usually used to indicate that the variable holds no value. In LSL, "zero values" are used in its place. Often these values have a meaning equivalant to null when passed to specific functions as parameters or when returned by a function.

Zero Values
Type | Literal | Constant | Description
string | "" | none | empty string
list | [] | none | empty list
integer | 0 | FALSE* | integer zero
float | 0.0 | none | float zero
vector | <0.0, 0.0, 0.0> | ZERO_VECTOR | zero-magnitude vector
rotation | <0.0, 0.0, 0.0, 1.0> | ZERO_ROTATION | identity rotation
key | "000000-0000-0000-0000-000000000000" | NULL_KEY | special reserved key
* Only used in boolean expressions.

Functions

llParseStringKeepNulls

Value | Constant | Functions | Reset


objects

See object.


ObjectType

Object Types

There are 4 object types in Second Life that have constant names: AGENT, ACTIVE, PASSIVE, and SCRIPTED. (Note that the meaning of "object type" here has no relation with types of values.)

When using a function like llDetectedType, the return value will be an integer with any valid object type constants ANDed together.

AGENT indicates an agent, ACTIVE indicates a physical object(*), PASSIVE indicates a non-physical object and SCRIPTED indicates the presence of a script (though scripts in attachments on agents don't seem to trigger this).

Agents always return AGENT, or AGENT & ACTIVE when moving. Objects return PASSIVE if they're not moving, regardless of whether or not they have physics enabled. Moving physical objects return ACTIVE, scripted objects return PASSIVE & SCRIPTED, and scripted physical objects return ACTIVE & SCRIPTED.

Case: | Returns AGENT: | Returns ACTIVE: | Returns PASSIVE: | Returns SCRIPTED:
Agent standing still | YES | NO | NO | NO
Agent moving | YES | YES | NO | NO
Agent with scripted attachments | YES | NO | NO | NO
Non-physical object | NO | NO | YES | NO
Scripted Non-physical object | NO | NO | NO | YES
Physical object standing still | NO | NO | YES | NO
Physical object moving | NO | YES | NO | NO
Scripted Physical object standing still | NO | NO | YES | YES
Scripted Physical object moving | NO | YES | NO | YES

Since multiple values can be returned by functions relating to object type, code like this won't work:

if (llDetectedType(i) == AGENT)

So use a test like:

if (llDetectedType(i) & AGENT)

Object | Detection


Occlusion

Occlusion (occlusion culling or occlusion rendering), basically means not performing rendering calculations on blocked/covered things which shouldn't be shown anyway. SL's occlusion rendering is based on an octree ("oct" meaning 8, as opposed to bintree) that divides up a sim into a hierarchy of cubes, with each cube containing the 8 smaller cubes that take up the same volume. Tight-fit bounding boxes are then generated for the objects within each cube and its children, which can be shown via the Debug > Rendering > Info Displays > Octree and Occlusion menu options. Hardware occlusion queries are performed against the hierarchy using an algorithm designed to find the topmost occluded node for a given branch in the tree over the span of several frames. Terrain, prims, avatars, and trees--any polygon rendered to depth--can act as occluders and will occlude other prims and terrain, as well as avatars and their attachments. As of 1.14.0, the simulation of particles, flexiprims, and LOD recalculations are also affected by occlusion rendering (with "octree" on, anything with a box--the bounding box--around it, is occludable).

Enable Debug > Rendering > Info Displays > Octree to see various occlusion indicators (see Debug for more info).
As of 1.14.0, enable Debug > Rendering > Info Displays > Occlusion to highlight geometry that has failed the occlusion test (i.e. what the renderer thinks is visible).

...occlusion culling has nothing to do with privacy. It only has to do with not drawing things that are just going to be drawn over by things which obscure them, from the camera's perspective. You can't "see" it, because anything that is visible should always be drawn. The only way you can tell is by turning it on and off and watching the change in framerate.

...the algorithm is a smart one, designed to not impact framerate when nothing is being culled and everything is visible. (Yadweb Linden, 4/14/06)

Links

  • Nature of the Beast: Occlusion Culling Discussion: forum thread with more technical info from Runitai Linden, 4/20/06
  • Wikipedia: occlusion culling
  • Out of Sight, Out of My SL: An easy guide to occlusion culling
Client | Sim


Octal

The octal numeral system is the base 8 number system, and uses the digits 0?7. This means one octal digit can be stored in 3 bits.

Wikipedia's definition

Hexadecimal


offset

Definition

The distance away from a point in space, described by the X, Y, and Z axes. In SL, an offset of <10,0,0> means "10m away from the object in the positive direction of the object's X axis," while an offset of <-4,3,10> means "4m in the negative on the X axis, 3m in the positive on the Y, and 10 in the positive on the Z."

Can also be used in reference to textures, at which point the definition merely loses its Z axis and becomes how much the texture is shifted across the surface of the object in question.

Warning

Offsets are local to the object in question, which means if you rotate an object 180 degrees on all axes, what was once positive becomes negative, and so forth. Rotating the object only 90 degrees on a single axis could mean that that object's former Y axis relative to the simulator is now it's X or Z axis, depending on how you rotated the object.

To sum it up, if you set an offset to <10,0,0>, then rotate the object to any rotation possible, the offset will be 10m in the object's positive X axis direction, but globally may be somewhere between the Z and Y axes compared to the object, or something altogether else.

Rotation


OGG

Ogg is the name of Xiph.org's container format for audio, video, and metadata.

Information on the audio codec can be found on The Wiki and www.vorbis.com


OldNews

Stuff pertaining to the management of our wiki, listed chronologically from newest to oldest. This is an archive page created to reduce clutter on the LSLWiki page.
See LSLWiki for the most recent news.

2004-08-26: Fixed the XML icon at the bottom (revisions). Added "show revisions" so it's easier to find. Found some less obtrusive icons for external links. Made the formatter generate proper </li> end tags. Fixed the homepage to be valid XHTML/CSS. Added acronym support.

2004-08-25: Added support for anchors. New tag is =#anchor#=. Also [[#anchor]] is now working as expected to match this. Removed the huge margins from the code blocks (blank line at top and bottom). Now there is really no reason left not to use code blocks for anything that is code (which removes the need for annoying manual escaping things like // comments).

2004-08-24: Created a LSL syntax highlighter for code blocks, which also converts events and functions into links to this wiki. It's not finished yet, but useable. Fixed some stuff to pass W3C validation tests.

2004-08-23: Moved the external links stuff from CSS to PHP (so should now work properly for those with retarded browsers). Made links to missing pages more sexy (you can now click the word and don't have to hit the tiny question mark behind it anymore). Made code boxes more sexy too! Added an icon for mailto links. Feel free to submit better icons for that ;)

2004-08-21: Implemented the suggestion of marking external links (with a small arrow). You need a browser that understands CSS 2.0+ attribute selectors for this. Changed color of missing pages. CatherineOmega fixed the cut&paste from code boxes.

2004-08-20: Successfully moved the LSL Wiki Mirror 7-7-7. Thanks to NadaEpoch for setting it up and providing the webspace (cPanel makes my eyes bleed :P), and also many thanks to our previous host BelMuse!


Omega

Aside from being a Greek alphabet letter, omega is used in LSL in llTargetOmega for client-side rotation and llGetOmega for rotation speed in the server side physics model.

Uses radians/second

Rotation dynamics


Opacity

Opacity is a degree of texture/prim/side transparency which, in most LSL functions, is determined by an alpha value or set in the object edit dialog texture tab.

There is a bug where overlapping/intersecting transparent prims (or ones with a masked texture on them) will cause fragmentation and parts of the prims to show through other prims. Also, transparent prims cannot have any shininess (environment mapping) but can be bumpmapped.

Texture | Alpha


OpenGL

OpenGL is the 3D API SL uses to render (display) its 3D graphics from its propietary 3D engine.

...as far as I can tell there is unanamous approval of OpenGL over DirectX within the LL developer pool. Primarily for portability, but also for helping to maintain sanity. I don't expect we'll be switching to DirectX soon. (Andrew Linden, 8/6/05)

SL


operation

See operators.


operator

See Operators.


OR

See Bitwise.


Orbit

To get an avatar into orbit, you need to use an orbiter.
Definition: An orbiter is sometimes considered a bouncer, or something that throws an avatar many thousands of meters high. A true orbiter is one that is designed to crash the target avatar. Irresponsible use of orbiters resulted in the push restrictions on land.

Orbiters work by using many prims to simultaneously llPushObject an avatar or physical object, bypassing the built in restrictions on llPushObject, and giving it a very high velocity.

Orbiting another user could be considered a TOS violation and definitely a CS violation. See LSLEthics.


origin

In mathematics, the origin is the point in which all of the axes intersect. In SL, it can be represented as ZERO_VECTOR, or <0,0,0>.


Owner

The owner of an object is the user who rezzed it. The "owner" of an agent is the agent itself, so an object rezzed by an agent will be owned by that agent. An object rezzed by another object owned by a group will be owned by the same group.

This can be different from an object's creator.

Functions

llGetOwner
llGetOwnerKey
llOwnerSay

Agent/Avatar | Creator | Group


Page Deletion

Only the owner of an LSLWiki page can delete it. The deletion, along with any comments, is permanent.

To delete, put a "/delete" after any URL, provided you are the page owner, and press enter. After a moment, you will be asked to confirm the deletion.

For example, "http://secondlife.com/badgeo/wakka.php?wakka=PageDeletion/delete" will delete the page "PageDeletion".

Note that it is case-sensitive: "/delete", not "/Delete"!

LSLWiki | WikiStyleGuide | WikiFormatting | CommentDeletion


Page Index

Also see MyPages.
#
-AllarKamloops-
1337
31337

A
AakanaarLaSalle
AaronLevy
AbdielBrodsky
AboutACLs
AceCassidy
AceDuport
ACL
AclBot
ACTIVE
AdamKaupas
AdamMarker
AdamZ
AdamZaius
AdrianaCaligari
AeolusWaves
AfirZ
Agent
AgentandAvatar
AgentCase
AgentRevolution
AidenProudhon
AimeeW
AirSeaAltimeter
AislinWallaby
AjDasilva
AlanKiesler
AlbertFool
AlecFleming
AlendrilMullen
AlexanderDaguerre
AlexEdo
AlhelorMaginot
Alk
AlkenanK
AllarKamloops
AlleleSemple
ALL_SIDES
AlmareaLumiere
AlondriaLeFay
alpha
AlphaChannel
AlphaZaius
AlphaZaiusTG
AlphazeroSugar
AlphonsJano
AlternativeCommunications
AlternativeEditors
alternative_communications
AltPickPocket
AmaOmega
AmethystRosencrans
AmOu
AND
AndreaKleene
AndrewLinden
AndrewMontagne
AndreylSatyr
AngelaGe
AngelaSalome
AngelaSalome2
AniamIngmann
animation
AnimationLister
AnimationNames
Animations
AnkhMorporkSalsman
AnneDroid
Annoyances
AnorcaCalamari
AnorTabla
AnotherFool
AnuStatosky
API
ApolliaPirandello
ApotheusSilverman
AppleUnit01
AprilHeaney
ArbitarBasiat
ArbusFahid
arccosine
arcsine
arctangent
ArdaXi
AresDesmoulins
ArgentSmith
ArgentStonecutter
argument
arguments
ArgusStravinsky
arithmetic
AritoCotton
ArkLehane
ArmitageNilsson
AshByrne
AsiraSakai
ASoS
asset
AssetPermissions
Assignment
AsterLardner
AstroCondor
attach
attachment
attachments
AtteAderdeen
attribute
AtwaHadlee
at_rot_target
at_target
audio
Autobahn
AutobahnRoad
AvaGanache
Avatar
AwsoonnRawley
axis
AyAn4m1Noir
AzadineUmarov

B
BabaYamamoto
BabbageLinden
backlink
BafiliusGoff
BaneDarrow
banking
BarFoo
BarryKaiousei
Base64
BasicerSL
BasicScriptingGroup
BazCalros
BeatfoxXevious
BelMuse
BenBacon
Benchmarks
BenFassbinder
BensonGrommet
BernardoRockwell
beta
BhavinPoindexter
BigJohn
BigJohnJade
Binary
BinoArbuckle
BirdRaven
BirosTiros
bit
bitfield
bitflag
bitflags
bitmap
bitmask
BitShift
BittToll
Bitwise
BlackCheetah
BlackRaven
BlayneUk
BlindWanderer
block
BlOOdWiZE
BluemanSteele
BluMunky
BMP
BobBailly
BobBauhaus
BobBrightwillow
BoboOgopogo
bodypart
BondHarrrington
Boobies
Boolean
BooleanLogic
Bosozoku
BosozokuKato
BoundingBox
brace
braces
bracket
brackets
BrentLinden
BrianLivingston
BrianMifflin
BriggiBard
BronYAurStomp
BubBuhCuh
BuckminsterHamilton
BuckyBarkley
bug
BuhBuhCuh
build
BuildersBuddy
bumpiness
bumpmap
BunnyBrewster
BurnmanBedlam
BurntPage
BusterPeel
ButcherCassidy
ButtonPusherJones
BVH
byte
Bytecode

C
cache
CadroeMurphy
call
CallingCard
CamelCase
Camera
CamperDave
CamperDaveProudfoot
CandyPeart
CaoimheArmitage
CapCase
CaptainBarmy
CarlDeElsweyre
CashMere
CatFart
CatfartGrayson
CatherineOmega
CazzjBrearly
cell
CenjiNeutra
center
ChadStatosky
ChageMcCoy
ChandraPage
changed
channel
character
characters
CharlesFauna
CharlieRezillo
Chasingred3Ixtab
Chat
CherokeeTek
child
ChildRotation
ChimanFassbinder
ChrisAltman
ChrisAshton84
ChrisKnox
ChristopherOmega
ChromalBrodsky
ChuckBeckett
CidJacobs
CiennaRand
circumference
CirrMarat
clever
client
ClientAssetKeys
clothes
clothing
clrchanger
code
CodeBlock
CodesForAvacados
ColkHerbst
collision
Collisions
collision_end
collision_start
color
ColorScripts
colour
CometAero
commandsexample
comment
comments
commons
Communication
Communications
compile
compiler
ComradePodolsky
concatenate
ConceptOverview
condition
ConditionalStatements
ConsoleOption
constant
Constants
content
Contribute
Contributors
control
controls
Coordinate
coordinates
CorisonsNino
CorSter
CorsterMousehold
CortoMaltese
CorvaRaven
CorvidaePeregrine
CoryLinden
cosine
CPU
Crash
CrashCourse
CrashCourse1
CrashCourse2
CrashCourse3
CrashCourse4
CrashCourse5
CrashCourse6
CrashCourse7
CrashCourse8
CrashCourse9
CreaTine
creator
CronStardust
crosshair
CrossLament
CrossQuartermass
Crypto
CrystalshardFoo
CSS
CSV
CuaCurie
CubeosBoffin
CubeyTerra
CVS
CyclopeanSprocket

D
DakotaCallahan
DaleGlass
DallasWilliamson
damage
DamaniosThetan
DamekTretiak
DamienRutherford
damp
DancinPanther
DancinSteve
DanielBenton
DanielLuchador
DanLinden
DanMedici
DaRiK
DarkDeciple
DarkHarbinger
DarkJustice
DarksideEldrich
DarzusStrutt
dataserver
DavanCamus
DaveZeeman
DavidGall
DavidSoon
DavoHuber
Debate
Debug
DebugConsole
debugging
DEBUG_CHANNEL
declare
DedricMauriac
deed
default
degrees
DEG_TO_RAD
delay
delete
DelimachusSachertorte
DelionLeroux
DellChristensen
DelphBunny
DeltaCzukor
DeneriaSholokhov
DennisPadar
deprecated
DeprecatedFunctions
DerekJones
DerekTesla
Detected
DevanSieyes
DevonCassidy
dialog
DirtyMcLean
DivadWheeling
DjinnFizz
DmitriDaguerre
DnateMars
DoctordaffyCure
DokEnkephalin
DolmereTalamasca
DoLpHpUn
DolusNaumova
DolynFoley
DominicWebb
DonLinden
DoodusMoose
DoopsLomax
DoozerSurface
DorMan
DoS
DoteDoteEdison
DotOperator
double
DougLinden
DoWhile
DownloadItem
DraconisNeurocam
DragonEccleston
DraGonLord
DraXen
DrDrebin
DriftwoodNomad
DuffleBunnysFromMars
DunselHodgson
DustinQuinn
DustinTrilling
Dynamics
DyneTalamasca
DysonDion

E
EagleBird
EarleSwenson
EataFrankfurter
EataKitty
EchoOmega
EcoAlgae094
EcoAnimal09e
EcoCommV1
EcoFoodSpore1
EcoName
EcoNameA0
EconResource
Ecosystem
EcosystemDisclaimer
EcosystemWorkingGroup
editor
EditPlus
editWindow
eeped
EepQuirk
EepTwin
EggyLippmann
EllePollack
EllsWort
EloisePasteur
ElsbethPadar
else
ElteeStatosky
email
EmWarrior
Encapsulation
endian
energy
engine
EnjiaPenguin
environment
EOF
EpikTetus
EponymousDrake
Equality
Error
errors
EscortDeFarge
estate
EternusGupte
Euler
Evaluate
EvanOud
Event
EventHandler
Events
Example32BitHash
ExampleAnimationBasic
ExampleAnimationScript
ExampleArgumentMultiplexing
ExampleAttachmentTypeChecker
ExampleAuthenticatedChat
ExampleBrowserXMLRPC
ExampleControls
ExampleEventOrder
ExampleFixedPrecision
ExampleGetNewInventoryName
ExampleGetRootPos
ExampleGetRootRot
ExampleGun
ExampleHomeAlarmSystem
ExampleHUDCommunication
ExampleKey2Channel
ExampleLeftOrRight
ExampleLinkMessage
ExampleListConversion
ExamplellHTTPRequest
ExamplellPushObject
ExampleLookAt
ExampleMoney
ExampleNotice
ExampleNumberConversion
ExampleParticleScript
ExampleParticleScript1
ExamplePointChildAtTarget
ExamplePositionOnASphere
ExampleProximityDoor
ExampleRotToVecAngle
ExampleRPC2PHP
Examples
ExampleSafeListConversion
ExampleScripts
ExampleSelfReplication
ExampleSides
ExampleStates
ExampleStatesConcise
ExampleStringVector
ExampleText2CapAlt
ExampleText2Leet
ExampleVectorGradient
Exchange
exchangeGigasVendor
exchangeHttpdb
exchangeLockGuard
exchangeLockGuardItem
exchangeLockGuardSource
exchangeLockMeisterSystem
ExchangePacketChat
ExchangeScriptModule
exchangeSecondSimpleSyndication
exchangeSimpleLink
exchangeTLML
exchangeTLMLGenerator
exchangeTLMLRenderer
exchangeTLTP
exchangeTLTPBrowser
exchangeToolDiscovery
exchangeTrustNet
ExperimentllRezObject
expression
EyanaYohkoh
EzharFairlight

F
F32
face
FairgeKinsella
FakirFlagstaff
FALSE
FanZjie
FanZjie1
FanZjie11
FateKnox
FaustHarbinger
FearlessLeader
FeatureRequest
FeemLomax
FeFe
FelixMcCoy
FiendishSatyr
FIFO
fileformat
FionaFielding
FireFox
FirstLife
Flarn2006
flexible
FlipperPA
float
FlowControl
Flying
FlyingTakoTips
FolcoBoffin
FollowCam
FooBar
FoolishFrost
for
force
fork
formatSetText
forumreferences
FOV
FoxdieGhia
FoxDiller
FoxOxbar
Foxxfire12Thatcher
FrameConsole
framerate
FrAncis
FrancisC
FrancisChung
FransCharming
FreakusFreeloader
FredExtraordinaire
FreqoutManray
friction
FrimkronPascal
FritzRuml
FroglegHornpipe
fullbright
Function
FunctionIndex
functionLibrary
FunctionRezAllObjects
Functions
FunctionStateChangeHack
FunctionUnescape
FuRaz
FutureRevisions

G
GabrielSpinnaker
GaiusGoodliffe
GaneshBling
GarrukHamsun
GarthFairlight
GedLarsen
GelobMagpie
GenePoole
GenLevy
GentlespiritPanther
geometry
GeorgeFlan
GeraldPerhaps
gesture
ghost
GIF
GigsTaggart
GilesCela
GinaJane
GinnyPoppy
glitch
global
GlobalCoordinate
GlobalCoordinates
GlobalVariable
GlobalVariables
Glossary
GMT
godmode
GordonPrior
GoshuaLament
Goto
GottaLoveForcedCaps
grab
GrathHazlitt
gravity
GrazelCosmo
GreenFate
GretchenMann
GreyGooFence
grid
GridMonkey
grief
griefing
GriffinOud
GrimLupis
GrimmyMoonflower
ground
group
GroupObjects
groups
GrumbleLoudon
GUI
GuitarheroDougall
GulliverLinden
gun
GunzourYellowknife
GuzarFonzarelli
GwynethLlewelyn

H
Hacks
HadenMarshall
HadHod
HaniLeb
HankRamos
HansBaldwin
HaravikkMistral
HarleenGretzky
Havok
HawksterWestmoreland
HazVega
HeadsUpDisplay
Heap
HeidiPinkney
HelloAvatar
HelloWorld
HenriLemieux
HerbertHoggard
HeweeZetkin
Hex
Hexadecimal
HillaryMelville
HinosermRebus
Hippo
HiroPendrag0n
HiroPendragon
HiroYamamoto
home
HomePage
HomePageindex.php?...
HomePageindex.php?title=Main_Page
HoochAltman
HotTempura
HowDoI
HTTP
httpHeaderCrunch
http_response
HUD
HughPerkins
humor
HunsValen
HuntingHare
HydroNots
HyMe

I
IanLinden
IceBrodie
IceBrodie2
IceSemple
IconSerpentine
identifier
IF
IfElse
IGS
IM
impulse
indent
InputOutput
InputTrilam
instance
InstantMessage
instruction
InstructorGoode
integer
integer firemode
interface
interpenetrate
Interpolate
IntobeHamsun
Introduction
IntrovertPetunia
Inventory
Invisiprim
IO
IoneLameth
IridescentEnzyme
IronFerris
IsabelleAquitaine
IsAgArD
IshtaraRothschild
island
Item

J
JaapBergman
JacekAntonelli
JacklineHugo
JaclynMaltese
JacobxGoff
JadeLily
Jadz0rConover
JafeMoxie
JagHarker
JakeBailly
JakeLaprade
JakeXu
JamalMfume
JamesLinden
JarhynWilde
JasonAlcott
JasonHashimoto
JassinPain
JavierPuff
JayDTorgeson
JeffCaldera
JeffreyGomez
JeroenWaisMan
JesradSeraph
JesseMalthus
JesterKnox
JiffiesManimal
JillianCallahan
JimBunderfeld
JimBunderfeld2
JimLinden
JimmyLoveless
JippenFaddoul
JoeyAxon
JoeyMarlin
JohnathanDoolittle
JohnPallister
JohnSojourner
JohntronThirty
Joint
JokerOpus
JonasBakalava
JonathanShaftoe
JontteGremlin
JopsyPendragon
JordanCotton
JoSapeur
JpatrickMaltese
JPEG
JPEG2000
JulianFate
JulianPoppy
jump
JuneTate
JuranJumador
JusticeMonde

K
KafkaSolvang
KagePixel
KaiNyak
KairaOverdrive
KalelVenkman
KalemikaDougall
KamiHarbinger
KankerGreenacre
KarizonHatfield
KarizonHetfield
KatharineBerry
KayinZugzwang
KaylaStonecutter
KeedonPollack
KeikoRau
KeilaronTomba
KejentoDrake
KeknehvPsaltery
KellyLinden
KendraBancroft
KenzingtonFairlight
KermittQuirk
KevinLomax
KexGodel
key
KeylessShackle
keys
keyword
KiB
kilobyte
KimmieLoveless
KithylinPerth
KitProudfoot
KitsuNico
KittenLulu
KlawsDusk
KnownBugs
KokiriSaarinen
KonigmannLippmann
KoyukiMichabo
KralPlayfair
KrashnburnHillquit
KriAyakashi
KristianMing
KurtZidane
KyrahAbattoir
KzPasteur
KzzchPrefect

L
l33t
LaenTeazle
Lag
LallanderParvenu
LambdaMoose
Lambert
LanceLeFay
LanceMirabeau
Land
landmark
land_collision
land_collision_end
land_collision_start
LaserPascal
LasivianLeandros
LaunaFauna
LavanyaHartnell
LazarusLong
LazarusLumiere
LazinkMaeterlinck
LeafAlbion
LearjeffInnis
LeeLinden
LeffardLassard
LeonardChurchill
LeopardLoveless
LeptonLeandros
LesWhite
LevKamenev
LexFile
LexNeva
LexNevaForgotHerPassword
LeynaBachman
Liaisons
Library
LibraryAirLockDoor
LibraryAirSeaAltimeter
LibraryAltPickPocket
LibraryASCII
LibraryAttachInvis
LibraryAvatarInSim
LibraryBarometer
LibraryBezierCurveDemo
LibraryBuildersBuddy
LibraryBuildersTabs
LibraryCameraFollower
LibraryCamera_Sync
LibraryCardReader
LibraryCaseInsensitiveListenRezzer
LibraryCBAgentTracker
LibraryCBButtonAbstractionLayer
LibraryCBInboundDataParser
LibraryCBKeywordSearchHandler
LibraryCBLogoutButtonView
LibraryCBMenuColorScrolling
LibraryCBMenuDataController
LibraryCBMenuLineButtonScrolling
LibraryCBMenuLineButtonView
LibraryCBMenuScrollButtonView
LibraryCBMenuTextArea
LibraryCBMenuTextScrolling
LibraryCBNotecardDisplayer
LibraryCBOutboundRequestRelay
LibraryCBRepositoryInterface
LibraryCBStartupShutdownHandler
LibraryCBStatusMonitor
LibraryCBStatusTextArea
LibraryCBXyTextDriver
LibraryChannelNumberGenerator
LibraryChatbots
LibraryChatCodec
LibraryColorScripts
LibraryComandToAgent
LibraryContentBrowser
LibraryConversationAI
LibraryDanceBall
LibraryDayofWeek
LibraryDetectedParticles
LibraryDialogModule
LibraryDolynFoley
LibraryDoor
LibraryDualTimers
LibraryDumpPrimParameters
LibraryElizaChatbot
LibraryEmailModule
LibraryEmailParse
LibraryEventLottery
LibraryFadeTexture
LibraryFastFind
LibraryFixRepeatsPerMeter
LibraryFlightAssist
LibraryFloat2Hex
LibraryFPPAddingFunctions
LibraryFPPBrain1
LibraryFPPBrain2
LibraryFPPClass1
LibraryFPPClass2
LibraryFPPClass3
LibraryFPPClass4
LibraryFPPFAQ
LibraryFPPIntroduction
LibraryFPPLinkMessages
LibraryFppMain
LibraryFPPMovement1
LibraryFPPMovement2
LibraryFPPMovementTuning
LibraryFPPParticles
LibraryFPPReturn2Owner
LibraryFPPVoice
LibraryFractalGenerator
LibraryGeneralMenuEngine
LibraryGeneralMenuEngineExample1
LibraryGetBinaryString
LibraryGetDayOfWeek
LibraryGiveInventory
LibraryGoGame
LibraryGoGame2
LibraryHSL2RGB
LibraryInstantMessageAnyone
LibraryInventoryLoader
LibraryInventoryMenuModule
LibraryKeknehvParticles
LibraryKeyboardScript
LibraryLindenAIML
LibraryLinkNumsScript
LibraryListenTexture
LibraryMilliTime
LibraryMitzFader
LibraryMultiChannelListener
LibraryMultidimensionalArray
LibraryMultiTitler
LibraryNoticeGiver
LibraryNumRs
LibraryParametricSurfaceRezzer
LibraryParticleGarden
LibraryParticleSignboard
LibraryPasswall
LibraryPathfinder
LibraryPathfinderDatabase
LibraryPathfinderPerl
LibraryPlanePontoon
LibraryPolygonFormer
LibraryPortRingCreator
LibraryPoseBall
LibraryPositionHUD
LibraryPrecisionTime
LibraryPrimLister
LibraryPrimTorture
LibraryProductUpdateServer
LibraryPseudoRandomGenerator
LibraryPublicBillboard
LibraryRadar
LibraryRadioStation
LibraryRandomHoverTextColor
LibraryRealTele
LibraryReduxOneLineDoor
LibraryRopeConstraint
LibraryRotationFunctions
LibraryRuntimeEnvironment
LibrarySailboat
LibrarySelfUpgradingScript
LibrarySelfUpgradingScriptAlt
LibrarySelfUpgradingScriptEnhanced
LibrarySenRot
LibrarySenWarp
LibrarySerialize
LibrarySetTextOnce
LibrarySettingsNotecard
LibrarySimpleCypher
LibrarySimpleHoverTextRemovalOnSit
LibrarySiRiSInABox
LibrarySLPrint
LibraryStridedLists
LibrarySwarm
LibrarySynchronousDescReturn
LibraryTeleporter
LibraryTextureSwitcher
LibraryThermometer
LibraryTimeElapsed
LibraryTimelessLinkedDoor
LibraryTimerEnhancer
LibraryTimestampDifference
LibraryTrueTele
LibraryVendor
LibraryVigenereCipher
LibraryWarpPos
LibraryWarpPosFix
LibraryWindTurbine
LibraryXyText12
LibraryXyTextFonts
LIFO
light
lighting
LikeKookies
LillaniLowell
limits
Linden
LindenAIML
LindenBuck
LindenDollar
LindenLab
LindenLabOther
Lindens
LindenScriptingLanguage
LindenVehicleTutorial
lindogram
line
Link
linkmessage
LinkMessages
link_message
LinnaSubagja
LionelForager
Lisp
list
listen
listen?show_comments=1
Lists
Literal
LittlesuzeAlexander
LiveJournal
LL
llAbs
llAcos
llAddToLandBanList
llAddToLandPassList
llAdjustSoundVolume
llAllowInventoryDrop
llAngleBetween
llApplyImpulse
llApplyRotationalImpulse
llAsin
llAtan2
llAttachToAvatar
llAvatarOnSitTarget
llAxes2Rot
llAxisAngle2Rot
llBase64ToInteger
llBase64ToString
llBreakAllLinks
llBreakLink
llCeil
llClearCameraParams
llCloseRemoteDataChannel
llCloud
llCollisionFilter
llCollisionSound
llCollisionSprite
llCos
llCreateLink
llCSV2List
llDeleteSubList
llDeleteSubString
llDetachFromAvatar
llDetectedGrab
llDetectedGroup
llDetectedKey
llDetectedLinkNumber
llDetectedName
llDetectedOwner
llDetectedPos
llDetectedRot
llDetectedSex
llDetectedType
llDetectedVel
llDialog
llDie
llDumpList2String
llEdgeOfWorld
llEjectFromLand
llEmail
llEscapeURL
llEuler2Rot
llFabs
llFloor
llForceMouselook
llFrand
llGetAccel
llGetAgentInfo
llGetAgentSize
llGetAlpha
llGetAndResetTime
llGetAnimation
llGetAnimationList
llGetAttached
llGetBoundingBox
llGetCameraPos
llGetCameraRot
llGetCenterOfMass
llGetColor
llGetCreator
llGetDate
llGetEnergy
llGetForce
llGetFreeMemory
llGetGeometricCenter
llGetGMTclock
llGetInventoryCreator
llGetInventoryKey
llGetInventoryName
llGetInventoryNumber
llGetInventoryPermMask
llGetInventoryType
llGetKey
llGetLandOwnerAt
llGetLinkKey
llGetLinkName
llGetLinkNumber
llGetListEntryType
llGetListLength
llGetLocalPos
llGetLocalRot
llGetMass
llGetNextEmail
llGetNotecardLine
llGetNumberOfNotecardLines
llGetNumberOfPrims
llGetNumberOfSides
llGetObjectDesc
llGetObjectMass
llGetObjectName
llGetObjectPermMask
llGetObjectPrimCount
llGetOmega
llGetOwner
llGetOwnerKey
llGetParcelDetails
llGetParcelFlags
llGetParcelMaxPrims
llGetParcelPrimCount
llGetParcelPrimOwners
llGetPermissions
llGetPermissionsKey
llGetPos
llGetPrimitiveParams
llGetRegionCorner
llGetRegionFlags
llGetRegionFPS
llGetRegionName
llGetRegionTimeDilation
llGetRootPosition
llGetRootRotation
llGetRot
llGetScale
llGetScriptName
llGetScriptState
llGetSimulatorHostname
llGetStartParameter
llGetStatus
llGetSubString
llGetSunDirection
llGetTexture
llGetTextureOffset
llGetTextureRot
llGetTextureScale
llGetTime
llGetTimeOfDay
llGetTimestamp
llGetTorque
llGetUnixTime
llGetVel
llGetWallclock
llGiveInventory
llGiveInventoryList
llGiveMoney
llGodLikeRezObject
llGround
llGroundContour
llGroundNormal
llGroundRepel
llGroundSlope
llHTTPRequest
llHTTPRequestThrottle1
llHTTPRequestThrottle2
llInsertString
llInstantMessage
llIntegerToBase64
llKey2Name
llList2CSV
llList2Float
llList2Integer
llList2Key
llList2List
llList2ListStrided
llList2Rot
llList2String
llList2Vector
llListen
llListenControl
llListenRemove
llListFindList
llListInsertList
llListRandomize
llListReplaceList
llListSort
llListStatistics
llLoadURL
llLog
llLog10
llLookAt
llLoopSound
llLoopSoundMaster
llLoopSoundSlave
llMakeExplosion
llMakeFire
llMakeFountain
llMakeSmoke
llMapDestination
llMD5String
llMessageLinked
llMinEventDelay
llModifyLand
llModPow
llMoveToTarget
llOffsetTexture
llOpenRemoteDataChannel
llOverMyLand
llOwnerSay
llParcelMediaCommandList
llParcelMediaQuery
llParseString2List
llParseStringKeepNulls
llParticleSystem
llPassCollisions
llPassTouches
llPizza
llPlaySound
llPlaySoundSlave
llPointAt
llPow
llPreloadSound
llPushObject
llRefreshPrimURL
llRegionSay
llReleaseCamera
llReleaseControls
llRemoteDataReply
llRemoteDataSetRegion
llRemoteLoadScript
llRemoteLoadScriptPin
llRemoveFromLandBanList
llRemoveFromLandPassList
llRemoveInventory
llRemoveVehicleFlags
llRequestAgentData
llRequestInventoryData
llRequestPermissions
llRequestSimulatorData
llResetLandBanList
llResetLandPassList
llResetOtherScript
llResetScript
llResetTime
llRezAtRoot
llRezObject
llRot2Angle
llRot2Axis
llRot2Euler
llRot2Fwd
llRot2Left
llRot2Up
llRotateTexture
llRotBetween
llRotLookAt
llRotTarget
llRotTargetRemove
llRound
llSameGroup
llSay
llScaleTexture
llScriptDanger
llSendRemoteData
llSensor
llSensorRemove
llSensorRepeat
llSetAlpha
llSetBuoyancy
llSetCameraAtOffset
llSetCameraEyeOffset
llSetCameraParams
llSetColor
llSetDamage
llSetForce
llSetForceAndTorque
llSetHoverHeight
llSetInventoryPermMask
llSetLinkAlpha
llSetLinkColor
llSetLinkPrimitiveParams
llSetLinkTexture
llSetLocalRot
llSetMass
llSetObjectDesc
llSetObjectName
llSetObjectPermMask
llSetParcelMusicURL
llSetPayPrice
llSetPos
llSetPrimitiveParams
llSetPrimURL
llSetRemoteScriptAccessPin
llSetRot
llSetScale
llSetScriptState
llSetSitText
llSetSoundQueueing
llSetSoundRadius
llSetStatus
llSetText
llSetTexture
llSetTextureAnim
llSetTimer
llSetTimerEvent
llSetTorque
llSetTouchText
llSetVehicleFlags
llSetVehicleFloatParam
llSetVehicleRotationParam
llSetVehicleType
llSetVehicleVectorParam
llShout
llSin
llSitTarget
llSleep
llSound
llSoundPreload
llSqrt
llStartAnimation
llStopAnimation
llStopHover
llStopLookAt
llStopMoveToTarget
llStopPointAt
llStopSound
llStringLength
llStringToBase64
llStringTrim
llSubStringIndex
llTakeCamera
llTakeControls
llTan
llTarget
llTargetOmega
llTargetRemove
llTeleportAgent
llTeleportAgentHome
llTeleportAttachedAvatar
llToLower
llToUpper
llTriggerSound
llTriggerSoundLimited
llUnescapeURL
llUnSit
llVecDist
llVecMag
llVecNorm
llVolumeDetect
llWater
llWhisper
llWind
llXorBase64Strings
llXorBase64StringsCorrect
local
LocalCoordinate
LocalCoordinates
LocalVariables
LockmeisterBaseFurniture
LockmeisterOpenSource
LockMeisterSystem
LOD
lol
loop
LordJason
LordKaos
LSL
LSL101
LSL101Appendix
LSL101Appendix2
LSL101Chapter1
LSL101Chapter2
LSL101Chapter3
LSL101Chapter4
LSL101Chapter5
LSL101Chapter6
LSL101Foreword
LSL101solutions
LSL101StatesAndEvents
LSL101Template
LSLdotPHP
LSLeditor
LSLEthics
LSLFunctionRequest
LSLGroups
LslHomePage
LSLSandbox
LSLStyleGuide
LSLTutorials
LSLwiki
LSLwikicopyright
LSLWikiHelp
LucAubret
LuccaKitty
LucifTias
LudivicoSzymborska
LukeRamona
LumiereNoir
LumpyTapioca
LurSachs
LyndynT

M
m
Machinima
MacLifer
MadamGZagato
MadasaHornet
MaerouCzukor
MafluenceKilian
MagicianThorn
MagnetScript
magnitude
MaiglynaGoodliffe
Mainframe1
MajorTomHebert
MakoMabellon
MalachiPetunia
MalarthiBehemoth
ManyBiggles
MapAPI
MarcEisenberg
MarcusEnnui
MarcuwSchnook
MarissaAkula
MariusMaeterlinck
MarkGjellerup
MarkovBrodsky
MarkSmith
MarsramGretzky
MartinMommsen
MartinMorris
MasakazuKojima
mask
mass
material
Math
Mathematics
MathError
Maths
MatWarf
MaxCase
MaxwolfGoodliffe
MaxxMonde
McGrawDillon
McLean
MD5
MeatwadExtraordinaire
MechageoDannunzio
MechaniqueThirty
MediaWiki
MeghanMagpie
MeiyoSojourner
MeLightKorvin
MemeticDivision
memory
MemoryUsage
MeraPixel
MerothJunge
messageid
meter
MeyermagicSalome
MichaelFirefly
MichaelKyuzo
MichaelRichards
MichaelSmall
MidianStreeter
MikeMos
MikeyD
Millie17
MingChen
MiniCooper
MiniSlogo
MinskOud
MiseryMoonlight
MisnomerJones
MitzpatrickFitzsimmons
MoldRat
MoleculorSatyr
MonahanWake
money
Mono
MooshGoodliffe
MoriashMoreau
MorphWollongong
MorseDillon
MotorHappy
mouselook
movement
moving_end
moving_start
MP3
MPEG4
MsMarcel
MsoLambert
MtnessExcelsior
multithreading
MyChanges
MyChanges?bydate=1
Mycroft
MyPages
MySpace
MySQL

N
NadaEpoch
NadaEpochs
name
NamesProtocolProposal
NamingConventions
NaN
NargusAsturias
NarmoturPendragon
NashBaldwin
NataruOdets
NathanStewart
NcaRalph
NealNomad
NeardHarbinger
NebSoyer
NebSvarog
NelvaynaCyr
NeoRebus
NepenthesIxchel
NeverRust
NewbieNag
NewfiePendragon
newview
NexiiM
NexusNash
NickiamSolvang
NicolaEscher
NightSpy
NightspyRebus
NikAbbot
NikEbisu
NikolaiWeber
NilesArgus
NoelMarlowe
NoeticEidolon
NogardCodesmith
NolanSion
NomadPadar
NonLSL
NonnuxWhite
NonPhysical
noob
normal
normalized
NormalizedVector
nornalbion
notecard
Notice
not_at_rot_target
not_at_target
no_sensor
null
NULL_KEY
Nxxx23Wallaby

O
Object
objects
ObjectType
object_rez
ObsidianStormwind
occlusion
Octal
OddityBeeks
OdysseusFairymeadow
offset
OGG
OldNews
OlmySeraph
omega
OmeiTurnbull
on_rez
OoPsGalatea
opacity
OpenGL
operation
operator
Operators
OptimystPaster
OR
OrangE
OrangeE
Orbit
OrdinalMalaprop
origin
OrphanedPages
OrphanedPages
orthogonal
owner
OzSpade

P
PabloVilas
PadraigStygian
PageDeletion
PageIndex
PandaronCodesmith
PantharOrlowski
PappyPopinjay
parallel
parameter
parameters
ParametricDebate
parcel
parent
parentheses
parsing
ParticleLibrary
ParticleLibrarySmoke
Particles
pass
PASSIVE
PastaMann
PatchLamington
PDT
penetrate
PepplarSklar
PeriMuir
permission
permissions
PERMISSION_CHANGE_LINKS
PERMISSION_DEBIT
PersialHebert
PeterLameth
PeterNewell
PetProtocol
PetProtocol015
PetProtocol021
PetProtocol031
PetProtocol032
PetProtocol21
PetreLamar
pfft
phantom
PhooieFeng
PhotonFugazi
PHP
physical
Physics
PI
piecestoofarapart
PieMenu
PieroPadar
PierreJosephProudhon
PIN
pitch
PI_BY_TWO
PlexLum
PlowboyLifestyle
PlutoniumDragonfly
PolAris
polygon
PolymeraseEnzyme
PoppyKolache
pos
position
PrakCurie
PranakhanKhondji
precision
preferences
Preview
Prim
Primitive
prims
PrioSerpentine
PST
PsykePhaeton
PublicDomain
PUBLIC_CHANNEL
PvP

Q
QarlFizz
qpn6ph9q
quaternion
Quaternions
QubiusQuinn
query
queue
QuickReferenceGuide
QuickTime

R
RadeGalatea
radian
Radians
RAD_TO_DEG
RaemusPatel
RaffaelePirandello
raise
RalanGall
RalphDoctorow
Random Vector
randomVector
rating
RavenRaide
RawHeightMap
ReadyJack
RealPageIndex
RealtimeModeling
RebeliahMartinez
RecentChanges
RecentChanges
RecentlyCommented
RecentlyCommented
recursion
RecursiveRezzing
RedLegPlasma
ReduxDengaku
Region
RegionCoordinate
RegionCoordinates
RegionCorner
ReleeBaysklef
ReloadUIXML
relog
Remote data
remote_data
RemovalPetition
reset
resetting
resident
ResultsTiki
ResunaOddfellow
return
Revisions
rez
RezForce
RGB
RhaegysNyak
RiannMaltese
RichardPinkerton
RichardSolvang
RichRandall
RickardRoentgen
RickCarlos
RickL
RiniRampal
RL
RobDemar
RobertaDalek
RobertLuchador
RobinsonArgonaut
RobKubrick
RobotMann
RocketRoentgen
RodrickHarrington
roll
root
rotate
rotation
Rotations
RPC
run_time_permissions
RyAgent
RyanDayton
RyanDayton2
RyanDayton3
RyanKHart
RyeDinMeiji
RysidianRubio

S
SableTill
SaiHarbinger
SamhainBroom
SamuelFrost
SanchoPanza
Sandbox
SapatEngawa
SargeRvBFeng
SatchmoPrototype
SatorCanetti
SatoshiBeeper
ScalarTardis
scale
SchitsoMonkey
SchizzySapeur
SchwartzGuillaume
SciamachyMoran
scope
ScopiPascal
Scott69
script
ScriptDelay
SCRIPTED
ScriptedDrama
ScriptingGroups
ScriptingMentors
scriptingTeachers
ScriptLibrary
ScriptPermissions
ScriptSecurity
ScrollingXyText
ScXx
SeagelNeville
SeanGorham
SeanHadlee
SeanHeavy
second
SecondLife
SecondLifeDirectory
SecondProctor
SeifertSurface
SelfReplication
semicolon
SendaoGoodman
sensor
Sensors
SenukaHarbinger
SeraRawley
Serialization
SeronisZagato
server
SevenEightTwo
SgeoNewbie
shadow
ShadowBailly
ShadowGretzky
shadows
ShadowWeaver
ShadusStonebender
ShallonThereian
shape
shear
shiny
side
Sidebar
SiGiSBarmy
SigmaLime
sign
SignpostMarvMartin
SilarsisLioncourt
SiliconPlunkett
SiljeRussell
Sim
SimNames
SimonRaven
Simulator
SimulatorNames
sine
SiobhanTaylor
SionZaius
SirGrelling
SiRiSAsturias
SirlorStonecutter
SiroMfume
SirParadox
sit
skin
SkyHoney
SL
SlgfS
SlitchNilsson
slntc
SmileyBarry
SmileyScripts
SmittyJensen
Sn0rph
snapshot
SniperUmm
SnowyMontpark
SNTP
SodCutter
SodPages
SomeScripter
SophiaMirabeau
SophiaPertwee
Sound
Sounds
sourcecode
SovereignTaov
SozaPatton
SpideyCela
SpinnerPoutine
Splat1Edison
Splat1Edsion
sprite
SpudsMilk
SQRT2
Stack
StackHeapCollision
StanislausNing
StaraxStatosky
StarkHuszar
State
StateKeyword
statement
States
state_entry
state_exit
status
STATUS_PHYSICS
Stavr0Mycron
StefanNilsson
Step
SteveMahfouz
StevenKramer
SteveSteed
StickMan
StinkyQueso
StoneMason
StoneSelf
StormBasiat
StPsaltery
StreamWarrior
stride
StrifeOnizuka
string
Strings
StryfeLowell
stupid
StyleGuide
subroutine
SuezanneCBaskerville
Suggestions
SummerShatner
SWF
SwiftusGlitterbuck
SwitchSpectre
SylphLightworker
SylvarGould
SynergyBelvedere
syntax
SyntaxError
SynthalorMandelbrot
SysOp

T
TalarusLuan
TalilaLiu
tangent
TaperPirandello
TarasBalderdash
target
task
TasmanPerth
TateruNino
tau
TauNino
Telehub
teleport
TemplarBaphomet
Tempnotes
TemporaryOnRez
terrain
TesterOmega
TestingBard
TestrieWood
TetsuTengu
text
TextSearch
TextSearch?phrase=CONTROL_RIGHT
TextSearch?phrase=DATA_NAME
TextSearch?phrase=DEG_TO_RAD
TextSearch?phrase=FALSE
TextSearch?phrase=INVENTORY_NOTECARD
TextSearch?phrase=TRUE
TextSearch?phrase=ZERO_ROTATION
TextSearch?phrase=ZERO_VECTOR
texture
TextureConsole
TextureResorces
TextureResources
Textures
TexturingSides
texturing_sides
TGA
ThaddeusNing
TheaBlanc
TheGrue
ThenarOddfellow
TheodorePolonsky
TheRaystonn
TheRayven
TheWeasel
ThingThatIsNotaGame
ThomasEidson
ThraxisEpsilon
ThrixxEnnui
TiffanyStrauss
TigerCrossing
TigseN
Time
TimelessPrototype
timer
timestamp
TimeToTime
TimRiker
TinBling
TinkerLaFollette
TinkerThetan
TiPBaKeR
TjGillies
ToDo
TonelessTomba
TonyTigereye
tooltip
torque
touch
touch_end
touch_start
ToY
transform
TravenSachs
TravisLambert
TreadWhiplash
tree
TremorGlz
TrentMondrian
TrepCosmo
TreyKawabata
trigonometric
TrimdaHedges
TrimmingHedges
Troubleshooting
TroyWesterburg
TRUE
TungstenBanjo
TupacWallace
Turnni1
Tutorial
TutorialJukebox
TutorialRobotics
Tutorials
TutorialVehicle
TwistZaius
TwosComplement
TWO_PI
TylerFerland
TylerOdets
type
Typecast
Types

U
UI
UltraEdit
Unary
Unicode
UniqueNerd
units
UnitVector
unlinked
UnotiQuonset
UnsignedInteger
URL
User
UserDefinedFunction
UserInterface
UserSettings
UsherCaldera
UshuaiaTokugawa
UTC
UUID
UV

V
VaguegirlPetty
VaLeNtE
value
VanhalMcGettigan
VanlerEverett
VanlerOdets
variable
Variables
vector
vehicle
Vehicles
VehicleTutorial
VektorLinden
velocity
VelosoLippmann
VeloxSeverine
vendor
VeritasKennedy
VershanaAmarula
vertex
ViceroLambert
video
VinceB
VincentNacon
VinciCalamari
VirtualMachine
VirusCollector
void
volume
VolumeDetect
Vorbis
VxShaw

W
WakkaWiki
WantedPages
Water
Weather
WednesdayGrimm
WenNomad
WheelFizz
while
whitespace
Wiki
WikiAlex
wikicomment
WikiEtiquette
WikiFormatting
WikiName
WikiNames
WikiPage
WikiQuestions
WikiRenamingPages
WikiSandbox
WikiSpoon
WikiStyleGuide
WikiTemplates
WikiTrevor
WikiUsers
WilBury
WillMoxie
WinAmp
wind
WinterGullwing
WinterSilverman
WizNordberg
WolfEnfield
WolfWings
WolfWingsMajestic
word scramble
world
WorldFunctions
WrongPageSetup
WufuzzCarnot

X
XaiPierce
XanderFisher
XenonLinden
XML
XMLRPC
XMLRPCDiscussion
XMLRPCImplementations
xor
XuguMadison
XylorBaysklef
XyText
XyTextMod

Y
yaw
YbNormalMan
YeusePinkdot
YiffyYaffle
YleriTokhes
YohanAlthouse
YumiMurakami
YuuByou

Z
ZacharyZamboni
ZachZamboni
ZackyPicnic
ZacWolf
ZangetsuOe
ZapKot
ZaraVale
ZarfVantongerloo
ZebraNorth
ZeeraXi
ZelMing
ZenMondo
ZenoConcord
ZeppZaftig
ZERO_ROTATION
ZERO_VECTOR
ZindorfYossarian
ZingoMars
ZobeidZuma
ZodiakosAbsolute
ZonaxD
ZonaxDelorean
ZulguroCamelCaseWakkaWiki

User Pages

AdamZaius
AgentCase
AlexanderDaguerre
AmaOmega
AshByrne
AstroCondor
AyAn4m1Noir
BelMuse
BinoArbuckle
BlindWanderer
BrianLivingston
BrianMifflin
BuckminsterHamilton
BuhBuhCuh
ChadStatosky
CatherineOmega
Chage
ChrisKnox
ChristopherOmega
ChromalBrodsky
CiennaRand
CrossLament
DaveZeeman
DevonCassidy
DraconisNeurocam
EepQuirk
EzharFairlight
EchoOmega
EscortDeFarge
FearlessLeader
FoXxfire12Thatcher
FreqoutManray
GabrielSpinnaker
GenePoole
GoshuaLament
GuzarFonzarelli
HankRamos
HansBaldwin
HazVega
HiroYamamoto
StoneSelf
IceBrodie
JimBunderfeld
JordanCotton
JulianFate
JusticeMonde
KafkaSolvang
KeilaronTomba
KeknehvPsaltery
KexGodel
MaxxMonde
MichaelSmall
MoleculorSatyr
NeoRebus
OlmySeraph
PsykePhaeton
RadeGalatea
ReadyJack
RysidianRubio
RobertLuchador
RyanDayton
SamuelFrost
SchitsoMonkey
SiobhanTaylor
StefanNilsson
TinkerThetan
ViceroLambert
WednesdayGrimm
XylorBaysklef
ZacharyZamboni
ZenoConcord


parallel

A geometric term describing two lines that who do not have a point of intersection.


Parameter

A parameter (also sometimes called an argument) is a value passed to a function. Inside a function's code, a parameter name is used like a local variable.

Parameters work the same whether they are passed to built-in functions (those that start with "ll"), event handler functions, or user-defined functions. The value passed to the function as a parameter is computed from an expression, which may be a literal value, a variable, or some more complex operation on other values.

The resulting type of a parameter value must match the declared type of the parameter in the function. If the user forgets to pass the correct type of values in the correct order to a function, the compiler gives an error: "Function call mismatches type or number of arguments".

Example:

string myString = "hello!";
list myList = ["ll"];
list anotherList = ["o"];
llParseString2List(myString, myList, anotherList);
llParseString2List("hello", ["ll"], ["o"]); // same as previous line

The expressions myString, myList, anotherList, and the literal values "hello", ["ll"], and ["o"] result in values passed as parameters to the llParseString2List function.

In a user-defined function declaration such as the following:

myFunction(integer anInt, string aString)
{
    integer myInt = anInt + 42;
    string myString = aString + "hello";
}

When the function myFunction is called, it needs to be passed an integer and a string to run correctly.

string str = "foo";
integer n = 1;
myFunction(n, str);
// or:
myFunction(1, "foo");

If the value of a parameter is changed inside the function with an assignment statement, that change has no effect on the value passed to the function in the function call. In particular, if the parameter expression is a variable, the variable will not be changed by calling the function. This behavior is known in computer science as pass by value. The function is given a completely new copy of the variable so, in this scenerio, the object will say "2".

myFunction(integer n) {
    n = 4;
}

default {
    state_entry() {
        integer a = 2;
        myFunction(a);
        llSay(0, (string) a);
    }
}

Because of this, keep in mind that passing functions large string or list values may cause a "Stack-Heap Collision" error if a copy of the size of what passed can't fit in the script's remaining available memory.

"Start Parameter"

This is a value often refered to in the LSL documentation. Script resets set this value to 0. Functions can explicitly set the start parameter of a remotely loaded script (using llRemoteLoadScriptPin) or all scripts in a rezzed object (using llRezObject or llRezAtRoot). It is the same value as the integer parameter passed to on_rez when the object is rezzed. llGetStartParameter returns this value.

Functions | Variable | Local Variables


ParametricDebate

Parametric Model Debate

Debaters: Eep, BW, Chris

  • Bandwidth: While parametric models (prims) are somewhat restrictive, it means SL requires only very small amounts of data to describe each prim, making efficient object data streaming possible. Though it would be more efficient for many classes of items, they would have to be static as to stay under the bandwidth limits. Since facsimiles of most mesh-based models (meshes) can be made with prims, in LL's mind, this is an advantage for prims but limits what can be modelled (without overlapping/intersecting prims) and creates many covered, hidden polygons (which cause more framerate hits).

  • Transformation: Additionally, prims can be transformed in parametric ways without it having to be specified by the modeler in advance (keyframe animation), something that meshes cannot do.
    • 1. Obviously, non-parametric models can't be transformed parametrically, but meshes can be transformed vertex-by-vertex, polygon-by-polygon--UNLIKE parametric models -Eep
    2. And bandwidth-intensive to change it vertex-by-vertex. -BW
    • So what? You're gonna complain about bandwidth yet dismiss how bandwidth-wasteful SL already is (redownloading GUI images/sounds each session, for example)? -Eep
      • 1. Arent they cached? -Chris
      2. GUI images & sounds are shipped with the client, why do you think it's so large? -BW
      • It's mostly large because of ..app_settings/static_data.db2 and, after an inspection of it, the GUI images appear (12.3MB TGAs of 25MB) to be in there (well, everything specified in viewerart.ini anyway but I know the standard terrain textures, collision sprites/sounds, and default wood texture are redownloaded EVERY session. -Eep
        • But they aren't part of the GUI. -BW
          • Yes they are. Try to close a window right away and you'll notice the lack of a "close window" sound until it mindlessly redownloads EACH SL session. While the GUI images may not be redownloaded, the GUI sounds are. -Eep

  • Keyframe Animation: Would bloat the size of any mesh. For the versatility and flexibility of prims, mesh modeling does not come close. -BW
    • 1. And parametric models don't come close to the versatility and flexibility of meshes. -Eep.
    • Can they in the same amount of bandwidth? -BW
    2. By changing the prim type, the number of polygons changes as does the number of textureable faces, also its vertex setup for those faces while using minimal bandwidth. I maybe be a bit out of the loop on this, but I haven't seen this sort of thing done before with meshes, and I cannot imagine it would use the same bandwidth. -BW

  • Arguments For Meshes:
    • Describe objects that parametics cannot do efficiently (which is any non-primitive shape) but there is a point where the number of prims in an object consume more bandwidth then it's mesh equivalent; at this point prims are used less as building blocks and more as polygons.
      • Which goes back to why meshes are better overall. However, parametric primitive shapes could be animated within meshes, too. -Eep
        • If meshes are better, why are there more cons then pros? Meshes are better suited for describing complex static objects but overall are not better, as described in the next section. -BW

  • Arguments Against Meshes:
    • 1. level-of-detail transformation is CPU-intensive and imperfect; parametrics transform without hassle. -BW
    1. There is no point in having this discusion if they cannot be rendered in real time. -BW
    2. Because of how prims transform, the modeler doesn't have to waste time building multiple LOD's, saving bandwidth. -BW
    • For a mesh to have multiple LOD would require extra bandwidth. -BW
      • LOD levels can be created dynamically. -Eep
        • But can it be done on hundreds of meshes at the same time, in real time? A while back LL changed the LOD calculations on objects to be object-based instead of prim-based, resulting in a dramatic improvement to framerate. If parametric LOD calculations cause lag, just imagine the effect with meshes; dynamic mesh LOD calculations are more expensive. -BW
        • Have to have the entire mesh before dynamic LOD calculations can be done; or it will have to re-done -BW
        • Dynamic LOD could complicate animation -BW

2. Meshes use more bandwidth and would have to be streamed entirely while parametrics only stream the non-default parameters. -BW?
  • Not true: specific vertices/polygons can be altered without transmitting the entire mesh each update; how do you think terrain modification works? It's a mesh! -Eep
    • 1. It's a mesh, true, but only to a certain extent. LL is able to take liberties with terrain because it can be described using a texture (look at how estate owners can set their land properties). -Chris
    2. The terrain is a bad example because a good portion of it is constant and built into the client. -BW
    1. Um, no it's not. Terrain can be modified on-the-fly just as primitives can be and it's downloaded just as objects are. Oh, guess what else is a mesh: avatars. Turn on cloth rendering in prefs ("Bump Mapped & Cloth") and watch the av's hair/clothing vertices animate with, oddly, no bandwidth impact. -Eep
    1. The terrain is in part constant - the texture and color is stored/computed locally by the client. -Chris
    2. Also, avatars are parametric--that's why we use sliders to manipulate the av properties rather then uploading a mesh itself. The cloth movement is vertex manipulation done by a DLL that comes with the client download, so it naturally wouldn't affect bandwith. -Chris
    1. If avatars are parametric (and there are quite a few sliders!), ANY mesh can be made parametric! -Eep
    • Each mesh must still (at least initially) be described vertex-by-vertex. Even if users are able to dynamicly load parametric vertex manipulation code for their particular mesh into the client software (effectively creating their own primitive), all of this requires significant amounts of bandwith. -Chris
      • I doubt it--and certainly not as "significant" as you and BW think. -Eep
        • Actualy the av meshes use morphs. Every time a vertex is affected by a morph, it is redefined in that morph's definition. For example, the av head contains 1132 vertices, 1844 polygons (highest LOD), and 93 morphs (not including hair or eyes, 32,554 redefined vertices). The head then has 5 LODs which recycle the main definition's vertice/morphs and just redefine the polygons. The other 4 levels respectively have 1290, 590, 277, and 184 polygons. Total size: 1,650,567 bytes, vertices: 54,336 bytes 3%, morphs: 1,568,554 bytes 95%, polygons: 25,120 bytes 1.5% (the other 0.5% is for headers etc). As you can see, morphs take up a significant percentage of that. But the head isn't a good example because expressions are morphs. The lower body contains 977 vertices, 1654 polygons, and 24 morphs (5771 redefined vertices). 5 LOD, polygons (1158, 662, 330, 249). Total size: 352,059 bytes, vertices 46,906 bytes 13%, morphs 280,090 bytes 79.5%, polygons 24,328 bytes 6%, (2% for headers etc). The point of this exercise: morphs are the simplest form of animation; morphs bloat the size of the mesh quite alot. That said, LL's format isn't that efficient for streaming. What it is designed for is render speed. The format is ideal for being read directly into memory. While compression (etc) could make the streaming footprint less, it wouldn't address the issue that the mesh es are huge when decompressed. Currently, a sim full of objects is only a few megabytes. A single mesh can be well over a MB (like the av head). A sim full of meshes would eat a lot of memory (it is my understanding that the av mesh is only ever stored once in memory). 1000 meshes could eat 1GB of ram.-BW
          • Keyframe animation then for parts of meshes. My point is: most meshes don't NEED to be fully animated, but the benefits of meshes exceeds just animation, as I've already explained (framerate, detail, etc). Oh and where did you get the models? Do you have a viewer or something? I'd like to check it out myself... Anyway, my dealings with RenderWare script (RWX) files in Active Worlds shows that the binary versions of ASCII files can be SEVERELY compressed--and that the ASCII versions tend to have a LOT of duplicate info (material stuff mostly). I'm sure those file sizes could be brought down substantially. Regardless, I don't understand why morphs have to be so large when they should just be vertex ranges or something. -Eep
          • The avatar mesh morphs don't redefine the entire mesh just the verties that are being changed. LL's format is very basic, it only supports morphs, 3 vertex polygons, uvw mapping, remaps, joint table; what a remap is i do not know, i got the strings from dumping the text from the exe. What I do know about remaps they are vertex pairs. The W portion of the UVW is optional and has to be enabled by a flag in the header (eyes for example don't use W). Morphs cannot effect W. I don't think it's right for me to expose LL's format or to distribute the tool i wrote to decode them.-BW
    2. The terrain mesh's U, V, X, & Y values are constant; only the Z value changes.-BW
    • It still adds to bandwidth--OH NOES! <eyeroll> -Eep
      • And those constant parts are not streamed to the client, they are built-in. Like I said before, the terrain is a bad example. Any user mesh will be unable to take advantage of any constants built into the client. -BW
        • Terrain and avatars are examples of meshes (parametric or not) that have their vertices manipulated. -Eep
    3. You have to have the mesh before you can change its shape, meaning the entire mesh has to be streamed first. -BW
    1. You have to have the parametric model before you can change its shape, too. -Eep
    2. True, but if I'm understanding BW correctly, you need the entire mesh; with parametrics you need just one prim. -Chris
    3. Chris understands what I'm saying. When you download the client, the parametric models are already computed and in the executable (hard-coded), meaning that when you login only at most 2-dozen variables are needed to describe a prim. To describe a 100-vertex mesh would be something on the order of 500 variables--adding a single morph could double that. Modifing it vertex-by-vertex would be highly inefficient.
    • Then, obviously, a mesh could have specific parts of it set as parametric. Come on, guys, think relatively please... -Eep
      • And setting that up adds to the size of the mesh requiring... more bandwidth. Not to mention, now you are arguing for parametrics. -BW
      • The default avatar mesh comes with the client download, the vertices of the mesh itself are never streamed--just the parametric vertex manipulation parameters. How would you go about implementing a parametric mesh? I would understand an argument for texture-mapped primitives (having a surface defined by a texture, much like terrain) included in the edit tools, but we haven't yet discussed the implications of meshes in relation to Havok. -Chris
        • It all takes bandwidth--and there are pros and cons to parametric-only or mesh-only designs, but the bottom line is: it's all vertices and polygons in the end. I'd rather have higher framerate (and more optimized, efficient models) than more bandwidth (which is becoming more and more moot with broadband connections, while framerate is ALWAYS an issue). Why is LL so concerned with model bandwidth when it's wasteful in SO many other areas like updates (mindless whole-client redownloads) and texture/sound redownloads EACH SL session? It's hypocritical! -Eep
          • Because redownloading an asset each session is only a small percentage of the assets sent to the client. Is it really redownloading them, or just decoding them from the client cache? The cache is unsorted and can contain 26,000 assets, it could take a while to find and decode an asset. -BW
            • If SL takes THAT long to freakin' find and decode its own cache files, that's just pathetic... -Eep

3. A sim full of custom meshes could easily exceed 1GB (see bandwidth above). -BW
4. Asset sizes larger then the average image (see bandwidth above). Seeing how long it takes for images to download people would complain about mesh rez times. -BW
5. Are too rigid for the bandwidth constraints; they cannot compete with the flexibility of parametrics in the same amount of bandwidth. -BW
6. Steep learning curve associated with most modeling applications. Most SL users find Photoshop a steep learning curve and that's a walk-in-the-park compared to 3D Studio and Maya. -BW
7. Animating a mesh would have negative effects on sim performance as it would require recalculating the bounding box via dynamic LOD (prim bounding boxes are about the same as rendering objects with LOD turned all the way down). Seeing as the sim has more responsibilities than the client to perform in real-time, it is not practical. Also the bounding box generated may be unsuitable. -BW
  • Bounding boxes can be set with each keyframe. -Eep
8. The complexity of a mesh's bounding box may be too great for Havok to handle in real-time. -BW?
  • Bounding boxes don't have to change, but even if they do, it's a freakin' BOX. If Havok can't handle dynamic bounding boxes, it sucks. -Eep
Streaming subdivision surfaces: http://www.blender.org/modules/verse/index.php
A list of papers, some of which are about streaming 3d, http://graphics.im.ntu.edu.tw/~robin/plist.html

More discussion: 1
prim


Parcel

A parcel is a block, or "lot", of land in a sim with a minimum size of 4x4m, not necessarily contiguous (connected), and can be split and joined with other parcels of the same owner (but may not cross a into another sim).

Q: Is there a way to determine how large a parcel is, or if another object or agent is on that parcel?
A: No, but you can find out if an object or agent is over an owner's land using llOverMyLand or llGetLandOwnerAt (assuming you know the position of the target object).
A2: If you own the land, you can find out who has objects on it by right-clicking anywhere on your land, clicking on the object's tab, and hitting the refresh button near the bottom.
Land | Simulator


Parent

Linked objects have a very limited hierarchy. There is one parent (also called the root prim) and 1 or more children. The parent prim is the default prim selected when a linked set is selected with the built-in tools and is highlighted in yellow. When selecting multiple prims to create a linked set, the last prim selected will be the parent.

The number of prims (for link functions) is determined according to the following:
  • If it is a single prim, not linked, then it is number 0.
  • If it is the last prim selected before choosing to link, then it is link number 1 (parent).
  • If it is the second to last prim selected before choosing to link, then it is link number 2 (a child).
  • If if is the first prim selected to be part of the link, then it will have the highest link number, equal to the number of prims in the linked set.

Object | Prim | Link | Child


parentheses

See brackets.


parsing

Simple Parsing Script
(Type "say:(message)", where (message) is what the object Says, Referred as B in script)

default
{
    state_entry()
    {
        llListen(0,"",llGetOwner(),"");
    }
    listen(integer channel, string name, key id, string message)
    {
        list data=llParseStringKeepNulls(message,[":"],[]); //this is the actual parsing string, converting it into a list, breaking up by each ":"

        //now converting and setting list into strings....
        string A = llList2String(data,0);
        string B = llList2String(data,1);  // 1 is the 2nd list data from a list itself. You can add more and more, like this command below.
        //string C = llList2String(data,2);
        //string D = llList2String(data,3);
        //string E = llList2String(data,4);
        //etc.. etc..

        if ( A == "say") //check what the first data was.
        {
            //then apply what first data is passed for.

            llSay(0, (string)B); // don't take (string) out, because B is a string.... what (string) does, converts B into Chat.
        }
    }
}
Credit For Scripting goes to Vincent Nacon


Particle Library

This is a collection of open-source particle systems.

If you would like to add a particle system to the list, please do so in alphabetical order, with each field filled in. The pages should be prefixed with the name "ParticleLibrary" (e.g., "ParticleLibrarySmoke). The total amount of particles may be obtained by the formula: total particles = age * (particles per burst / burst rate).

Name | Particle Amount | Custom Texture? | Description | Particle Engine
Smoke | 100 | YES | A nice, smooth, white smoke for chimneys. | KPS

Particle engines:
Abbreviation | Full Name
KPS | Keknehv's Particle Script


ParticleLibrarySmoke

A nice, smooth particle system for chimneys and other smoking objects. It has white smoke.

// Keknehv Psaltery's Particle Script v1.1
// 1.0 -- 5/30/05
// 1.1 -- 6/17/05

//     This script may be used in anything you choose, including and not limited to commercial products.
//     Just copy the MakeParticles() function; it will function without any other variables in a different script
//         ( You can, of course, rename MakeParticles() to something else, such as StartFlames() )

//    This script is basically an llParticleSystem() call with comments and formatting. Change any of the values
//    that are listed second to change that portion. Also, it is equipped with a touch-activated off button,
//    for when your particles go haywire and cause everyone to start yelling at you.

//  Contact Keknehv Psaltery if you have questions or comments.

MakeParticles()                //This is the function that actually starts the particle system.
{
    llParticleSystem([                   //KPSv1.0
        PSYS_PART_FLAGS , 0 //Comment out any of the following masks to deactivate them
    //| PSYS_PART_BOUNCE_MASK           //Bounce on object's z-axis
    | PSYS_PART_WIND_MASK             //Particles are moved by wind
    | PSYS_PART_INTERP_COLOR_MASK       //Colors fade from start to end
    | PSYS_PART_INTERP_SCALE_MASK       //Scale fades from beginning to end
    | PSYS_PART_FOLLOW_SRC_MASK         //Particles follow the emitter
    | PSYS_PART_FOLLOW_VELOCITY_MASK    //Particles are created at the velocity of the emitter
    //| PSYS_PART_TARGET_POS_MASK       //Particles follow the target
    //| PSYS_PART_EMISSIVE_MASK           //Particles are self-lit (glow)
    //| PSYS_PART_TARGET_LINEAR_MASK    //Undocumented--Sends particles in straight line?
    ,

    //PSYS_SRC_TARGET_KEY , NULL_KEY,   //Key of the target for the particles to head towards
                                                //This one is particularly finicky, so be careful.
    //Choose one of these as a pattern:
    //PSYS_SRC_PATTERN_DROP                 Particles start at emitter with no velocity
    //PSYS_SRC_PATTERN_EXPLODE              Particles explode from the emitter
    //PSYS_SRC_PATTERN_ANGLE                Particles are emitted in a 2-D angle
    //PSYS_SRC_PATTERN_ANGLE_CONE           Particles are emitted in a 3-D cone
    //PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY     Particles are emitted everywhere except for a 3-D cone

    PSYS_SRC_PATTERN,           PSYS_SRC_PATTERN_ANGLE_CONE

    ,PSYS_SRC_TEXTURE,           "2ca57748-4944-de53-b301-8d0efbafb2c6"     //UUID of the desired particle texture
    ,PSYS_PART_MAX_AGE,          5.0                //Lifetime, in seconds, that a particle lasts
    ,PSYS_SRC_BURST_RATE,        0.05               //How long, in seconds, between each emission
    ,PSYS_SRC_BURST_PART_COUNT,  1                  //Number of particles per emission
    ,PSYS_SRC_BURST_RADIUS,      5.0                //Radius of emission
    ,PSYS_SRC_BURST_SPEED_MIN,   1.5                //Minimum speed of an emitted particle
    ,PSYS_SRC_BURST_SPEED_MAX,   2.0                //Maximum speed of an emitted particle
    ,PSYS_SRC_ACCEL,             <0.0,0.0,0.8>      //Acceleration of particles each second
    ,PSYS_PART_START_COLOR,      <0.8,0.8,0.8>      //Starting RGB color
    ,PSYS_PART_END_COLOR,        <0.2,0.2,0.2>      //Ending RGB color, if INTERP_COLOR_MASK is on
    ,PSYS_PART_START_ALPHA,      0.9                //Starting transparency, 1 is opaque, 0 is transparent.
    ,PSYS_PART_END_ALPHA,        0.0                //Ending transparency
    ,PSYS_PART_START_SCALE,      <0.8,0.8,0.0>      //Starting particle size
    ,PSYS_PART_END_SCALE,        <4.3,4.3,0.0>      //Ending particle size, if INTERP_SCALE_MASK is on
    ,PSYS_SRC_ANGLE_BEGIN,       -.3                //Inner angle for ANGLE patterns
    ,PSYS_SRC_ANGLE_END,         .3                //Outer angle for ANGLE patterns
    ,PSYS_SRC_OMEGA,             <0.0,0.0,0.0>       //Rotation of ANGLE patterns, similar to llTargetOmega()
            ]);
}

default
{
    state_entry()
    {
        MakeParticles();                //Start making particles
    }

    touch_start( integer num )            //Turn particles off when touched
    {
        state off;        //Switch to the off state
    }
}

state off
{
    state_entry()
    {
        llParticleSystem([]);        //Stop making particles
    }

    touch_start( integer num )        //Turn particles back on when touched
    {
        state default;
    }
}


Pass

To transfer a value to a function via a parameter.

LSL


PDT

Pacific Daylight Time (PDT) is the time zone in the west coast of the United States (where SL's servers are located) that occurs from the first Sunday in April at 2AM to the last Sunday in October at 2AM. PDT = GMT-7.

time | PST | GMT


penetrate

SL penetration, or interpenetration as written in an error, is when something (prim, object, avatar) intersects another prim/object/avatar or the ground. Penetration will usually stop physical objects from staying physical and give an error.

One way to force a prim/object to remain physical at all times (depending on timer interval):

default
{
    state_entry()
    {
        llSetTimerEvent(1); // generate a timer event every 1 second
    }

    timer()
    {
        llSetStatus(STATUS_PHYSICS,TRUE); // set physical status every 1 second
    }
}

Another way is to use a collision event:

default
{
    collision()
    {
        llSetStatus(STATUS_PHYSICS,TRUE); // set physical status whenever object collides
    }
}

physical | errors


permission

See permissions.


Permissions

There are two types of permissions in LSL: script permissions and asset permissions. Asset permissions are used to control the distribution and use of an inventory. Script permissions are granted by the user to a script so it can act upon the user.

The base permission mask contains the permissions the object had when it was given to the current owner. In theory, no other mask can have more permissions then the base mask. (If they do, this is a bug in permissions and should be reported.)

Script Permissions Functions/Events

llGetPermissions
llGetPermissionsKey
llRequestPermissions
run_time_permissions

Script Permissions
perm Value | Value | Action | Granter
PERMISSION_DEBIT | 0x2 | take money from agent's account | owner
PERMISSION_TAKE_CONTROLS | 0x4 | take agent's controls | anyone
PERMISSION_TRIGGER_ANIMATION | 0x10 | trigger animation on agent | anyone
PERMISSION_ATTACH | 0x20 | attach/detach from agent | owner
PERMISSION_CHANGE_LINKS | 0x80 | change links | owner
PERMISSION_TRACK_CAMERA | 0x400 | track the agent's camera position and rotation
PERMISSION_CONTROL_CAMERA | 0x800 | control the agent's camera

Asset Permission Functions
Function | Description
llGetInventoryPermMask | get inventory item's permissions for a particular mask
llGetObjectPermMask | get object's permissions for a particular mask
llSetInventoryPermMask (not implemented) | set inventory item's permissions for a particular mask
llSetObjectPermMask (not implemented) | set object's permissions for a particular mask

Asset Permission Constants
Mask | Value | Description
MASK_BASE | 0 | get object's base permissions
MASK_OWNER | 1 | get owner's current permissions
MASK_GROUP | 2 | get object's active group's permissions
MASK_EVERYONE | 3 | get permissions everyone has on object
MASK_NEXT | 4 | get next owner's permissions

Asset Permission | Value (in hex notation) | Description
PERM_ALL | 0x7FFFFFFF | move/modify/copy/transfer permissions
PERM_COPY | 0x00008000 | copy permissions
PERM_MODIFY | 0x00004000 | modify permissions
PERM_MOVE | 0x00080000 | move permissions
PERM_TRANSFER | 0x00002000 | transfer permissions

Object | Asset Permissions | Script Permissions


PetProtocol

The goal is to develop a protocol that would allow pets to communicate and interact with other pets and avatars.
http://forums.secondlife.com/showthread.php?t=30768

Newest to oldest:
PetProtocol032
PetProtocol021
PetProtocol015


PetProtocol015

Back to PetProtocol main page

version 0.15

pet protocol (note)

The goal is to develop a protocol that would allow pets to communicate and interact with other pets and avatars.

1) easy to understand and modify
2) open source which anyone can copy for free from the forum.
- Please do not post your ideas to this thread if you don't want other people to use it. =]
- It is completely acceptable if you wish to use any or all of the code in this post in your pets and sell them for profit.

We need some scale to show where the animal fits into the food chain and show what other animals would be concidered a threat, prey, or playmate.

food chain 0.0 - 1.0
*This scale is off the top of my head and Very likely to be changed.*
0.0 grass
0.1 sheep
0.15 birds, mice
0.2 house cats
0.3 dogs
0.7 hippo
0.8 shark
0.9 lion
1.0 godzilla

A pet will chat it's status on a the introduction channel (9000) once every 10 seconds.

(If 2 pets decide they want to chase each other they agree to communicate more often on some other channel.. need to think about that more.)

Here are the attributes to be chatted every 10 seconds. (a + sign indicates it's a required attributes, otherwise it's optional)
attribute example
fight 0.3 what would the pet approach
flee 0.4 what would the pet run from
type dog
subtype beagle
action chase, hide, beg, eat
target sylvester/grass/bp (use target's key instead of name?)
offsetR relative offset from target. <-5,0,0> would follow 5m behind target
offsetA absolute offset from target. <-5,0,0> would follow 5m west of target
- - - -
inferred info (can be detected when the pet chats)
name spot (pet's name = object name)
key a0deec52-f289-7608-7064-a8bcf480e6d8
distance distance in meters, often a radius

now to list some behaviors or "action" (a + sign indicates a target is required - such as another pet or avatar)
+ eat
+ chase
+ avoid
+ follow/swarm/flock
+ bump/push
+ ride/perch
+ mimic
play dead
speak
sit
stay
sleep
+ jump over
dig
+ circle
+ play tag
+ beg
find
give/deliver
retrieve/fetch
meet
face/look

example introduction string:
"introduction + fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|owner + offsetR| <-5,0,0> + group|6500"

example command string:
"command + action|follow + target|mykey + offsetR|<-5,0,0>"

[ question + knowncommands|1 ]

[ answer + knowcommands|1 + follow| + sit| ]

.pet (script)

string gBehavior_0; // this pet's behavior
string gBehavior_1; // one other pet's behavior
string gCurrentAction;
list gActions; // Action probabilities
float gTime = 0; //sum of all action probabilities
integer gL1; // listen handler for owner
integer gL2; // listen handler for pet introduction channel
integer gL3; // listen handler for group channel
integer gL4; // listen handler for pet's private channel
integer gIntroduction_channel = 9000;
integer gGroup_channel = 1; //this value should be defined in the read_behavior function;
integer gPet_key;
key gOwner;

initialize()
{
gOwner = llGetOwner();
llListenRemove(gL1);
llListenRemove(gL2);
llListenRemove(gL3);
//integer llListen(integer channel, string name, key id, string msg);
gL1 = llListen (0, "", gOwner,"");
gL2 = llListen (gIntroduction_channel, "", "", "");
gL3 = llListen (gGroup_channel, "", "", "");
gPet_key = (integer)llDumpList2String(llParseString2List(llGetSubString(llGetKey(), 27, 37),
        ["-","a","b","c","d","e","f"], []), "");
        // the numbers in the last 10 digits of this pet's key.
//llWhisper(0, (string)gPet_key + " "+ (string)llGetKey());
gL4 = llListen (gPet_key, "", "", "");
llSetTimerEvent(10); //move this to a better place
read_behavior();
}

read_behavior()
{
// read behavior from notecard, email, or XML-RPC
read_notecard();

gBehavior_0 = "fight|0.3 + flee|0.4+ type|dog + subtype|beagle + action|watching + target|master + group|6500";
llWhisper(0,(string)gBehavior_0);

gActions = [
    40,"sleep",
    1,"speak",
    20,"follow + target|owner",
    10,"evade + target|owner + offsetR| <-0,0,0> + distance|4.5",
    10,"command + action|follow + target|mykey + offsetR|<-5,0,0>"
    ];

    integer i;
    integer max = llGetListLength(gActions);
    for (i=0;i<max;i+=2)
    {
        gTime = gTime + llList2Float(gActions, i);
        llWhisper(0,(string)gTime);
    }

}

read_notecard()
{
// Read description
// Read actions list
}

write_behavior() {// IM's or emails owner the current behavior
}

// mod: mood modifier:
//      < 1 will only do the first mod% of the behaviors
//      = 1 will do behaviors exactly as specified (will do a behavior every time)
//      > 1 has increasing chance of doing nothing
// time: the sum of all 'chances' much faster to calculate this ahead of time then as you go
// behaviors: A list that looks like [chance, "behavior_command", chance, "behavior_command"
//        ex: [0.4, "bark", 0.2, "sit"] or [4, "bark", 2, "sit"]
// Num and target are sent with the link message.  Perhaps always call this with the key of
// any currently targeted animal?  Will work fine if 0 and "" are used respectively.
//
// Example call, although behavior should be a list stored in a variable made from reading a notecard...
// choose_behavior(1,0.6,[0.4,"sleep",0.2,"bark"],0,"");
choose_behavior(float mod, float time, list behaviors, integer num, key target)
{
    float choice = llFrand(time * mod);
//    llWhisper(0,"time = "+(string)time);
//    llWhisper(0,"choice = "+(string)choice);
    integer i;
    integer max = llGetListLength(behaviors);
    for (i=0;i<max;i+=2)
    {
        if (choice < (float)llList2String(behaviors,i))
        {
            //Do this action!
            llMessageLinked(LINK_SET,num,llList2String(behaviors,i+1),target);
//            llWhisper(0,llList2String(behaviors,i+1));
llSetText(llList2String(behaviors,i+1), <0.5,0.5,1>,1);
            return;
        }
        else
        {
            choice -= llList2Float(behaviors,i);
        }
    }
}

introduce()
{
    llSay(gIntroduction_channel, "introduction + "+gBehavior_0);

}

default
{
    state_entry()
    {
        initialize();
    }

    on_rez(integer start_param)
    {
        initialize();
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }

   listen(integer channel, string name, key id, string message)
   {
//  if id = gOwner
//  if id = pet_key
//  if id = pet_group

//gBehavior_1 =
llSetText(llKey2Name(id)+": "+(string)message, <1,0,0>,1);
llSleep(0.01);
llSetText(llKey2Name(id)+": "+(string)message, <1,1,1>,1);
//llWhisper(0,(string)message);
// (respond to requests)
// (respond to questions)
// (make note of introductions)
    }

    changed(integer change)
    {
// read_notecard;
    }

    timer()
    {
// if not in the middle of behavior??
introduce();
//choose_behavior(float mod, float time, list behaviors, integer num, key target)
choose_behavior(1,gTime,gActions,0,"");

// stay_close_to_owner();
    }

}

beagle behavior (note)
May want to use a delimiter other than "," for vectors and quats. Maybe "/" or "*" ?

// default description
// [fight (0-1), flea (0-1), type, subtype, default action, target, rotation, group channel]
// example: 0.3, 0.4, "Dog", "Beagle", "sitting", "", <0,0,0>, 6500;

description = "fight|0.3 + flee|0.4+ type|dog + subtype|beagle + action|watching + target|owner + group|6500"

// next are the actions this pet will do and their percentage change.
// they don't need to add up to 1. they are relative to their total.

sleep, 0.3;
chase, 0.2;

// next are the actions this pet will do if specific pet types are detected.

cat, chase, 1.0;
mouse, evade, 1.0;

// next is the percentage chance that this pet will obey commands
// the 1st number is chance to obey a command from a pet with a higher fight number than this pet, ie. a pet concidered a threat.
// the 2nd number is chance to obey a command from a pet that is neither a threat nor prey
// the 3rd number is chance to obey a command from a pet that is prey
// the 4th number is chance to obey a command from its owner
// the 5th number is chance to obey a person other than the owner
// obey, threat, neutral, prey, owner, other_avatar
obey, 1.0, 0.5, 0.1, 0.9, 0.5;
follow (script)

// follow script
//

startup()
{
    llSensorRemove();
    llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y, TRUE);
    llSetStatus(STATUS_ROTATE_Z, TRUE);
    llSetStatus(STATUS_PHANTOM, TRUE);
    llSensorRepeat( "", llGetOwner(), AGENT, 100, PI, 0.5 );
}

default
{
    state_entry()
    {
        startup();
    }
    on_rez(integer param)
    {
        startup();
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "follow")
            llWhisper(0, "follow");

                llSensorRepeat( "", id, AGENT, 100, PI, 0.5 );

    }

    no_sensor()
    {
        llSetTimerEvent(30); // the ride may be lost, die in 30 seconds
    }
    timer(){
        llWhisper(0, "cannot find target ");
        //llDie();
    } // haven't detected the user for 30 seconds, so delete the ride
    sensor( integer total_number )
    {
        vector vTarget = llDetectedPos(0);
        rotation rTarget = llDetectedRot(0);
        vector vOffset = <-2.0,0,-0.2>; // offset from the agent's center

        vector vDestination = vTarget + vOffset * rTarget;
//        llLookAt(vDestination, 2, 0.2);

        llMoveToTarget(vDestination, .1);

        llLookAt(vTarget + <0,0,vOffset.z>, 1, 0.1);

//        llMoveToTarget(vTarget, 0.5);
        llSetTimerEvent(0.0); // turns off the die timer
    }
    touch_start(integer total_number)
    {
        llSay(0, "stopit.");
    }
}

speak (script)

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "speak")
            llWhisper(0, "bark");
    }
}

COMMAND (script)

// this script is untested

// COMMAND script (output)
// If chosen action is to command other pets to do an action
// this script replaces constants with values, and sends it out.

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (llGetSubString(str, 0, 6) == "command")
        {
            llWhisper(0, "outgoing command is " + str);
            integer wordBegin;
            wordBegin = llSubStringIndex(str, "|owner");
            if (wordBegin != -1)
            {
                llDeleteSubString(str, wordBegin, wordBegin+6);
                llInsertString(str, wordBegin, (string)llGetOwner());
                llWhisper(0, "modified command is " + str);
            }

            wordBegin = llSubStringIndex(str, "|mykey");
            if (wordBegin != -1)
            {
                llDeleteSubString(str, wordBegin, wordBegin+6);
                llInsertString(str, wordBegin, (string)llGetKey());
                llWhisper(0, "modified command is " + str);
            }

        }
    }
}


PetProtocol021

Back to PetProtocol main page

version 0.21
*note: the "obey" function in the .pet script is unfinished and needs to be commented out to compile.
*todo: the .pet script needs to keep an array of the last few (10?) pets it has come in contact with, probably indexed by their gPet_key. The gPet_key is the last section of their UUID with the letters stripped out. The array will be used by the "obey" function to determine what the commanding pet's "food chain" number is.

pet protocol (note)

The goal is to develop a protocol that would allow pets to communicate and interact with other pets and avatars.

1) easy to understand and modify
2) open source which anyone can copy for free from the forum.
- Please do not post your ideas to this thread if you don't want other people to use it. =]
- It is completely acceptable if you wish to use any or all of the code in this post in your pets and sell them for profit.

We need some scale to show where the animal fits into the food chain and show what other animals would be concidered a threat, prey, or playmate.

food chain 0.0 - 1.0
*This scale is off the top of my head and Very likely to be changed.*
0.0 grass
0.1 sheep
0.15 birds, mice
0.2 house cats
0.3 dogs
0.7 hippo
0.8 shark
0.9 lion
1.0 godzilla

A pet will chat it's status on a the introduction channel (9000) once every 10 seconds.

(If 2 pets decide they want to chase each other they agree to communicate more often on some other channel.. need to think about that more.)

Here are the attributes to be chatted every 10 seconds. (a + sign indicates it's a required attributes, otherwise it's optional)
attribute example
fight 0.3 what would the pet approach
flee 0.4 what would the pet run from
type dog
subtype beagle
action chase, hide, beg, eat
target sylvester/grass/bp (use target's key instead of name?)
offsetR relative offset from target. <-5,0,0> would follow 5m behind target
offsetA absolute offset from target. <-5,0,0> would follow 5m west of target
- - - -
inferred info (can be detected when the pet chats)
name spot (pet's name = object name)
key a0deec52-f289-7608-7064-a8bcf480e6d8
distance distance in meters, often a radius

now to list some behaviors or "action" (a + sign indicates a target is required - such as another pet or avatar)
+ eat
+ chase
+ avoid
+ follow/swarm/flock
+ bump/push
+ ride/perch
+ mimic
play dead
speak
sit
stay
sleep
+ jump over
dig
+ circle
+ play tag
+ beg
find
give/deliver
retrieve/fetch
meet
face/look

example introduction string:
"introduction + fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|owner + offsetR| <-5,0,0> + group|6500"

example command string:
"command + action|follow + target|mykey + offsetR|<-5,0,0>"

[ question + knowncommands|1 ]

[ answer + knowcommands|1 + follow| + sit| ]

.pet (script)

string gBehavior_0; // this pet's behavior
string gBehavior_1; // one other pet's behavior
string gCurrentAction;
list gActions; // Action probabilities
list gObey; // obey values for threat, neutral, prey, owner, other avatar
float gTime = 0; //sum of all action probabilities
integer gL1; // listen handler for owner
integer gL2; // listen handler for pet introduction channel
integer gL3; // listen handler for group channel
integer gL4; // listen handler for pet's private channel
integer gIntroduction_channel = 9000;
integer gGroup_channel = 1; //this value should be defined in the read_behavior function;
integer gPet_key;
key gOwner;

initialize()
{
gOwner = llGetOwner();
llListenRemove(gL1);
llListenRemove(gL2);
llListenRemove(gL3);
//integer llListen(integer channel, string name, key id, string msg);
gL1 = llListen (0, "", gOwner,"");
gL2 = llListen (gIntroduction_channel, "", "", "");
gL3 = llListen (gGroup_channel, "", "", "");
gPet_key = (integer)llDumpList2String(llParseString2List(llGetSubString(llGetKey(), 27, 37),
        ["-","a","b","c","d","e","f"], []), "");
        // the numbers in the last 10 digits of this pet's key.
//llWhisper(0, (string)gPet_key + " "+ (string)llGetKey());
gL4 = llListen (gPet_key, "", "", "");
gTime = 0;
llSetTimerEvent(10); //move this to a better place
read_behavior();
}

read_behavior()
{
// read behavior from notecard, email, or XML-RPC
read_notecard();

gBehavior_0 = "fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|master + group|6500";
llWhisper(0,(string)gBehavior_0);

gActions = [
    40,"sleep",
    1,"speak",
    20,"follow + target|owner",
    10,"evade + target|owner + offsetR| <-0,0,0> + distance|4.5",
    10,"command + action|follow + target|mykey + offsetR|<-5,0,0>"
    ];

// the following sums the probabilities of the possible actions
    integer i;
    integer max = llGetListLength(gActions);
    for (i=0;i<max;i+=2)
    {
        gTime = gTime + llList2Float(gActions, i);
        llWhisper(0,(string)gTime);
    }

gObey = [1.0, 0.5, 0.1, 0.9, 0.5];

}

read_notecard()
{
// Read description
// Read actions list
}

write_behavior() {// IM's or emails owner the current behavior
}

// mod: mood modifier:
//      < 1 will only do the first mod% of the behaviors
//      = 1 will do behaviors exactly as specified (will do a behavior every time)
//      > 1 has increasing chance of doing nothing
// time: the sum of all 'chances' much faster to calculate this ahead of time then as you go
// behaviors: A list that looks like [chance, "behavior_command", chance, "behavior_command"
//        ex: [0.4, "bark", 0.2, "sit"] or [4, "bark", 2, "sit"]
// Num and target are sent with the link message.  Perhaps always call this with the key of
// any currently targeted animal?  Will work fine if 0 and "" are used respectively.
//
// Example call, although behavior should be a list stored in a variable made from reading a notecard...
// choose_behavior(1,0.6,[0.4,"sleep",0.2,"bark"],0,"");
choose_behavior(float mod, float time, list behaviors, integer num, key target)
{
    float choice = llFrand(time * mod);
//    llWhisper(0,"time = "+(string)time);
//    llWhisper(0,"choice = "+(string)choice);
    integer i;
    integer max = llGetListLength(behaviors);
    for (i=0;i<max;i+=2)
    {
        if (choice < (float)llList2String(behaviors,i))
        {
            //Do this action!
            llMessageLinked(LINK_SET,num,llList2String(behaviors,i+1),target);
//            llWhisper(0,llList2String(behaviors,i+1));
llSetText(llList2String(behaviors,i+1), <0.5,0.5,1>,1);
            return;
        }
        else
        {
            choice -= llList2Float(behaviors,i);
        }
    }
}

introduce()
{
    llSay(gIntroduction_channel, "introduction + "+gBehavior_0);
}

// OBEY function
// Any incoming commands are sent to this function. This script determines
// if the command will be obeyed based on values from the pet's behavior
// setup. If the command will be obeyed, it will be sent out to the other
// scripts in the pet.
// There are 5 parameters for the obey variable,
// (threat, neutral, prey, owner, other avatar),
// each ranging from 0 to 1.
// If the parameter is 0, the pet will never obey a command from something
// in that class, if it's 1 the pet will always obey that command.

obey ( string command, string this_pet, string commander )
{
    float this_pet_flee =
    float this_pet_fight =
    float commander_flee =
    float commander_fight =

    // test if commander is a threat
    if ( llList2Float(commander,fight) > this_pet.flee )
    {
        if (llList2Float(obey,1) > llFrand(1)) // calculate chance of obeying the command
            llMessageLinked(command);
    }
    // neutral
    if ( commander.fight > this_pet.flee )
    {
        if (llList2Float(obey,2) > llFrand(1)) // calculate chance of obeying the command
            llMessageLinked(command);
    }
    // prey
    else if ( this_pet.fight > commander.flee )
    {
        if (llList2Float(commander,3) > llFrand(1)) // calculate chance of obeying the command
            llMessageLinked(command);
    }
    // owner
    else if ( other == gOwner )
    {
        if (llList2Float(commander,4) > llFrand(1)) // calculate chance of obeying the command
            llMessageLinked(command);
    }
    //other
    else if ( other != gOwner )
    {
        if (llList2Float(commander,5) > llFrand(1)) // calculate chance of obeying the command
            llMessageLinked(command);
    }
}

default
{
    state_entry()
    {
        initialize();
    }

    on_rez(integer start_param)
    {
        initialize();
    }

   listen(integer channel, string name, key id, string message)
   {
//  if id = gOwner
//  if id = pet_key
//  if id = pet_group

//gBehavior_1 =
llSetText(llKey2Name(id)+": "+(string)message, <1,0,0>,1);
llSleep(0.01);
llSetText(llKey2Name(id)+": "+(string)message, <1,1,1>,1);
//llWhisper(0,(string)message);
if (llGetSubString(message, 0, 6) == "command")
    obey(message, gBehavior_0, gBehavior_1); // respond to commands
// (respond to requests)
// (respond to questions)
// (make note of introductions)
    }

    changed(integer change)
    {
// read_notecard;
    }

    timer()
    {
// if not in the middle of behavior??
introduce();
//choose_behavior(float mod, float time, list behaviors, integer num, key target)
choose_behavior(1,gTime,gActions,0,"");

// stay_close_to_owner();
    }

}

beagle behavior (note)
May want to use a delimiter other than "," for vectors and quats. Maybe "/" or "*" ?

// default description
// [fight (0-1), flea (0-1), type, subtype, default action, target, rotation, group channel]
// example: 0.3, 0.4, "Dog", "Beagle", "sitting", "", <0,0,0>, 6500;

description = "fight|0.3 + flee|0.4+ type|dog + subtype|beagle + action|watching + target|owner + group|6500"

// next are the actions this pet will do and their percentage change.
// they don't need to add up to 1. they are relative to their total.

sleep, 0.3;
chase, 0.2;

// next are the actions this pet will do if specific pet types are detected.

cat, chase, 1.0;
mouse, evade, 1.0;

// next is the percentage chance that this pet will obey commands
// the 1st number is chance to obey a command from a pet with a higher fight number than this pet, ie. a pet concidered a threat.
// the 2nd number is chance to obey a command from a pet that is neither a threat nor prey
// the 3rd number is chance to obey a command from a pet that is prey
// the 4th number is chance to obey a command from its owner
// the 5th number is chance to obey a person other than the owner
// obey, threat, neutral, prey, owner, other_avatar
obey, 1.0, 0.5, 0.1, 0.9, 0.5;
follow (script)

// follow script
//

startup()
{
    llSensorRemove();
    llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y, TRUE);
    llSetStatus(STATUS_ROTATE_Z, TRUE);
    llSetStatus(STATUS_PHANTOM, TRUE);
    llSensorRepeat( "", llGetOwner(), AGENT, 100, PI, 0.5 );
}

default
{
    state_entry()
    {
        startup();
    }
    on_rez(integer param)
    {
        startup();
    }

    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "follow")
            llWhisper(0, "follow");

                llSensorRepeat( "", id, AGENT, 100, PI, 0.5 );

    }

    no_sensor()
    {
        llSetTimerEvent(30); // the ride may be lost, die in 30 seconds
    }
    timer(){
        llWhisper(0, "cannot find target ");
        //llDie();
    } // haven't detected the user for 30 seconds, so delete the ride
    sensor( integer total_number )
    {
        vector vTarget = llDetectedPos(0);
        rotation rTarget = llDetectedRot(0);
        vector vOffset = <-2.0,0,-0.2>; // offset from the agent's center

        vector vDestination = vTarget + vOffset * rTarget;
//        llLookAt(vDestination, 2, 0.2);

        llMoveToTarget(vDestination, .1);

        llLookAt(vTarget + <0,0,vOffset.z>, 1, 0.1);

//        llMoveToTarget(vTarget, 0.5);
        llSetTimerEvent(0.0); // turns off the die timer
    }
    touch_start(integer total_number)
    {
        llSay(0, "stopit.");
    }
}

speak (script)

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "speak")
            llWhisper(0, "bark");
    }
}

COMMAND (script)

// this script is untested

// COMMAND script (output)
// If chosen action is to command other pets to do an action
// this script replaces constants with values, and sends it out.

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (llGetSubString(str, 0, 6) == "command")
        {
            llWhisper(0, "outgoing command is " + str);
            integer wordBegin;
            wordBegin = llSubStringIndex(str, "|owner");
            if (wordBegin != -1)
            {
                llDeleteSubString(str, wordBegin, wordBegin+6);
                llInsertString(str, wordBegin, (string)llGetOwner());
                llWhisper(0, "modified command is " + str);
            }

            wordBegin = llSubStringIndex(str, "|mykey");
            if (wordBegin != -1)
            {
                llDeleteSubString(str, wordBegin, wordBegin+6);
                llInsertString(str, wordBegin, (string)llGetKey());
                llWhisper(0, "modified command is " + str);
            }

        }
    }
}


PetProtocol031

page deleted. -bp

Back to PetProtocol main page


PetProtocol21

Back to PetProtocol main page


Phantom

If an object is phantom, it means that the rules of solid matter do not apply to it. It does not collide with anything except the terrain--objects and avatars can freely pass through it-- it does not become transparent.

Phantom objects can be physical, and they will collide with land. Such collisions will trigger the appropriate events (land_collision, land_collision_start and land_collision_end). Setting a physical object to phantom lightens the load on the sim because less collision calculations will be performed.

Objects can be set to phantom using llSetStatus, llSetPrimitiveParams, or a checkbox in the editor dialog.

A phantom object will also not trigger a collision, collision_start or collision_end event on any scripts it contains. The exception to this rule is llVolumeDetect. Setting it to TRUE will cause solid objects to pass through the object, but collision_start and collision_end events will still fire. (collision events are not triggered when llVolumeDetect(TRUE) is set.)

If llVolumeDetect is enabled, llGetStatus(STATUS_PHANTOM) will still return FALSE, however. A phantom object with llVolumeDetect(TRUE) will not trigger collision events.

Oddly, physical llVolumeDetect-enabled objects will not collide with land; they'll fall right through and go off-world. The reason is that llVolumeDetect uses a feature of the Havok physics engine which only works for objects that are totally collisionless.

For more, see the llVolumeDetect page.

Prim | Constants | Collisions


Physical

Physical is the attribute associated with objects and prims that enables physics (essentially, enables inertia--gravity). It is set with the "Physical" checkbox in the edit-object dialog box or using the LSL functions llSetStatus (STATUS_PHYSICS) or llSetPrimitiveParams. By default physical is disabled.

Physical tasks may be moved and rotated using kinetic functions. For a complete listing, see dynamics.

A physical object will come to a halt (settle)--and even lose its physical status if it interpenetrates something in the process of settling--but it depends on the object's velocity and/or llSetBuoyancy. An object with a continuous impulse/collision will tend to never settle (unless it interpenetrates something). A negatively buoyant object proportionally bounces around more before it settles. A physical joint sometimes never settles or can settle quickly depending on what's around (still researching it but joints are flaky).

Another potentially useful observation about physical objects is that they can currently exist at z-coordinates up to 4096.0m before being considered off-world and returned to the owner's lost and found folder.

Physics | Dynamics | Interpenetrate


Pieces Too Far Apart

The rules on how this error works need to be clarified. Is it 30m center-to-center? Is that "prim origin" or "center of mass"? Most likely the second.

10x10x10 objects will only link if they are within 32m of each other. Smaller objects mean much smaller distances. Two 1x1x1 meter objects, for example, need to be something like within 7m of each other.

In unusual cases, the link will succeed or fail depending on the order in which the primitives are selected. See below where it suggests that this is only the case in "sparse" links

Andrew Linden's Link Algorithm

When linking two or more primitives in SL the operation may fail if the pieces are too far apart. I've had a few requests for details about the algorithm used to determine whether a collection of primitives can be linked or not, and have decided to post the details here for those who want them.

So here is the algorithm in pseudo-code. First, let me declare some definitions and constants...

AABB = axis aligned bounding box

// The AABB of a primitive is the box that contains the sphere
// (centered on the primitive's center of mass (as computed by
// Havok, not the "primitive's geometric center)) that contains
// the primitive for all rotations.

float MAX_LINK_PAIR_DISTANCE = 1; // cap max dist btw tiny prims
float MAX_LINK_DISTANCE = 32;     // cap max dist btw large prims

... and then there are two key functions...

// --------------------------------------------------------------
// can_link(collection_of_primitives)
// --------------------------------------------------------------
AABB box_list[] = array of AABB's of the collection_of_primitives

// first do quick check of the absolute size of final linked collection

AABB total_box = tight box that contains the centers of all
                 the boxes in box_list[];

float L = length of diagonal of total_box;

if (L > MAX_LINK_DISTANCE) return FALSE;

// The link did not fail due to its sheer size, however if the
// individual pieces are too small then the link may still fail.

total_box = box_list[0];
int linked_box_count = 1;

while (linked_box_count < total_box_count
       &&  the_last_pass_did_not_fail)
{
    the_last_pass_did_not_fail = FALSE;
    for (i=0; i<total_box_count; i++)
    {
        if (box_list[i] not yet linked)
        {
            if we_can_link(box_list[i] with total_box)
            {
                total_box = smallest_union_box(total_box and box_list[i]);
                the_last_pass_did_not_fail = TRUE;
                total_box_count++;
            }
        }
    }
}

return (linked_box_count == total_box_count)

// --------------------------------------------------------------
// can_link(box_a with box_b)
// --------------------------------------------------------------

// the link can fail if the centers of the boxes are too far apart
// or if the distance between the boxes is too small for the sum
// of their sizes

float da = diagonal of box_a;
float db = diagonal of box_b;
float ab = distance between centers of box_a and box_b;

return (ab < MAX_LINK_DISTANCE
        &&  ab < (da + db + MAX_LINK_PAIR_DISTANCE));

A few final comments...

(1) It is possible that this algorithm may change in the future, however we'll try hard not to make it more restrictive, since this would cause some currently linked objects to cease to link. I mention this because when we move to Havok-2 it is possible that the computations of the AABB's of the various pieces may change (for instance it may be that tight spheres will be used instead of AABB's as the proxy collision objects of some objects).

(2) The link test is done server-side only so there is no way to bypass it by cracking the client. In fact, virtually all operations that have any effect on the world fail or succeed on the server rather than the client. For some operations we also check on the client so we can disable the UI for those that are guaranteed to fail. The reason we don't do that for linking is that the client knows nothing about the physics engine and we use information from the physics engine to determine link success.

(3) The astute may have noticed that there is a slight order-of-operations failure for collections of clumps of very small primitives where the clumps themselves are spaced such that they just barely link, and each clump barely links itself. For such a scenario the link will succeed or fail depending on the order in which the primitives are selected. This is a known problem, however all of these objects are sparse (mostly empty space) and barely fit into the set of objects that we wanted to support for linking.

Link Distance Related to Prim Size

In particular note from the psuedo code above, in the first box there are two variables that describe the link distance cap for large and small prims:

float MAX_LINK_PAIR_DISTANCE = 1; // cap max dist btw tiny prims
float MAX_LINK_DISTANCE = 32; // cap max dist btw large prims

At the bottom of the second code box is 'can_link' which does the check. The comment there says:
// the link can fail if the centers of the boxes are too far apart
// or if the distance between the boxes is too small for the sum
// of their sizes
The outcome of this is:
* All prims, no matter how small, with centers within 1 meter of each other are linkable.
* No prims, no matter how large, can be linked if their centers are further than 32 meters apart.
* In all other cases the max link distance is related to prim size

Prim | Link


Pie Menu

A pie menu is a user interface element that the Second Life client displays when right-clicking any in-world object, avatar, or the ground. It's like a menu, but pie-shaped.

The text of two of the pie menu's items can be modified: the "Sit" and "Touch" menu items, using llSetSitText and llSetTouchText respectively.

Pie menus are a very good user interface concept. Once the location of the different buttons is learned, a pie menu can be accessed very quickly, and without any reading necessary. Detractors contend that the time needed to learn the location of each function is lengthened by what is percieved to be a non-linear list. The Lindens are clearly proponents of pie menus.

Client | User Interface


PIN

A PIN (Personal Identification Number) is a number (in SL context, an integer) that is used as a password.

Functions

llSetRemoteScriptAccessPin
llRemoteLoadScriptPin

LSL


pitch

Angle of rotation around the Y-axis. Compare with roll and yaw.

Vehicle


Polygon

A polygon (also called a "face") is a flat/planar surface made up of at least 3 vertices that form a "triangle" (or a "quadrilateral" if 4 vertices) which, when combined with many other polygons, create a primitive.

There is currently no way to create individual polygons in SL. There are ways to mimic their properties using certain primitives, however. Jeffrey Gomez uses a cube to simulate a triangle in his 3D Model Importer.
Primitive | Vertex


pos

Short for position.

Did you want llGetPos, llGetLocalPos orllSetPos?


Precision

The Havok physics engine has some limitations on simulation precision.

Spatial precision

The Havok 1.x spatial (space-related) precision is 0.1 meters.

This means

* an object might collide with another object within 10 centimeters of the actual object borders, causing objects to 'float' over other objects, get 'stuck' between them or 'bounce' off them prematurely.
* an error will occur if a prim/object is tried to be made physical but is within 10cm from another prim/object/avatar or the ground.

Also worth noting, is that the spatial precision is about 'distance' not 'position', which means that this phenomenon can't be corrected by moving the 'stable' object.

Temporal precision

Also, as the collision detection is time-frame based, at a maximum frame rate of 45 fps. Every collision is made against the area of the prim, which means the thickness is irrelevant.

This means that fast moving objects and avatars can pass thou other objects, as they will already be on the other side of them in the next time-frame.

To counter this, you should make sure that the object never travels farther than it's own bounding box within one time frame.

Be aware that the fps rate depends on sim load, so err on the cautious side.

Floating point precision

floating point variable types have a proportional level of precision based on the value. This allows them to have a greater range then integers. Floats have 23 bits of precision which means about 7 accurate digits (in base ten). A float only needs 10 digits to accurately describe it's value (that is in scientific notation, excluding the exponent).

physics | Havok | collisions | float


Preview

See revisions for past revisions.

Preview change log.

http://secondlife.com/preview
http://secondlife.com/tiki/tiki-index.php?page=Preview+Release+Notes

1.14.1(0)

March 23, 2007
Public Voice Beta

= Changes:
  • Teleport Home disabled for new users on an Orientation Island
  • Share With Group disabled after object is deeded to group

LSL changes:
  • string llStringTrim(string src, integer trim_type)
    • STRING_TRIM_HEAD: trim all leading spaces in src
    • STRING_TRIM_TAIL: trim all trailing spaces in src
    • STRING_TRIM: trim all leading and trailing spaces in src

Bug fixes:
    • New groups are instantly searchable
    • Fixed disappearing alpha HUD prims
    • Fixed menu bar processing keystrokes when moused over
    • Fixed detached IM windows not resizing
    • Fixed animated textures when using llSetColor, llSetLinkColor, or PRIM_PROPERTIES
    • Fixed HUD object movement when logging in at a no-script area
    • Fixed HUD objects not loading new textures
    • Fixed HUD objects becoming invisible the first time they are attached from inworld
    • Fixed 'IM All Contacts In Folder'
    • Fixed a viewer crash in the name cache
    • Fixed Undo resetting position only on root prim
    • Fixed Texture Picker search not showing results
    • Fixed IM window reverting to default size
    • Fixed overriding stand-up animation freezing you in place
    • Fixed Appearance mode showing back of avatar

1.12.4(1)

November 3, 2006
Other changes:
  • Chat, IM, and script chat (llSay/llWhisper/llShout) max length increased to 1023 characters
  • '/me' emotes properly in IM (e.g. '/me laughs' becomes '[your name] laughs')

LSL changes:
  • New script commands
    • integer llGetObjectPrimCount(key id)
      • Returns the prim count for any specific object in a sim
    • list llGetParcelPrimOwners(vector pos)
      • Returns a list of up to 100 agents who own objects in the parcel, and the number of objects they own
    • integer llGetParcelMaxPrims(vector pos, integer sim_wide)
      • If sim_wide is TRUE, returns the total maximum number of prims allowed on the parcel
      • If sim_wide is FALSE, returns the maximum number of prims this individual parcel supports
    • integer llGetParcelPrimCount(vector pos, integer category, bool sim_wide)
      • If sim_wide is TRUE, returns the number of objects on all parcels owned by the same owner in the category specified
      • If sim_wide is FALSE, returns the number of objects on this specific parcel in the category specified
      • Categories are:
        • PARCEL_COUNT_TOTAL: all prims on the parcel(s)
        • PARCEL_COUNT_OWNER: prims owned by the parcel owner
        • PARCEL_COUNT_GROUP: prims not owned by the parcel owner, but set to or owned by the group of the parcel
        • PARCEL_COUNT_OTHER: prims not in GROUP or OWNER
        • PARCEL_COUNT_TEMP: All temp on rez prims
        • PARCEL_COUNT_SELECTED: All selected or ridden prims
    • list llGetParcelDetails(vector pos, list details)
      • Returns of a list of the details (PARCEL_DETAILS_NAME, PARCEL_DETAILS_DESC, PARCEL_DETAILS_OWNER, PARCEL_DETAILS_GROUP, PARCEL_DETAILS_AREA) in the order they are in the details list, for the parcel at pos

Bug fixes:
  • Script energy recalculated on link and unlink
  • Multiple inventory items opened at once open in one tabbed window
  • Fixed object buoyancy when setting STATUS_ROTATE_X, Y,and Z in a list
  • Fixed a client crash for llLoadURL

1.11.2(1)

July 28, 2006

Other changes:
  • llRequestAgentData can now return the resident's account type
    • Example usage: llRequestAgentData(llGetOwner(), DATA_PAYINFO);
    • No Payment Info on File: 0
    • Payment Info on File: 1
    • No Payment Info on File AND Payment Info Used: 2 (Only Beta/Lifetime users fall under this status)
    • Payment Info on File AND Payment Info Used: 3
  • Added PARCEL_FLAG_RESTRICT_PUSHOBJECT to llGetParcelFlags, and REGION_FLAG_RESTRICT_PUSHOBJECT to llGetRegionFlags()

Bug fixes:
  • Both the scripted object and the target of llPushObject must be in push-allowed areas
  • Fixed an exploit

Revisions


prims

See prim.


PST

Pacific Standard Time (PST) is the time zone in the west coast of the United States (where SL's servers are located) that occurs from the last Sunday in October at 2AM to the first Sunday in April at 2AM. PST = GMT-8.

To get the current time in PST, see llGetWallClock.

time | PDT | GMT


PublicDomain

Public Domain is the concept of an object being "owned" by everyone, therefore giving everyone the right do with it as they wish to.

This most often applies to the source-code for an application, or code-snippet. The pertinence to LSL is obvious, I would hope.

In defense of the concept of Public Domain is the Free Software Foundation. They have created a license, by which creators may attempt to maintain the Public Domain nature of their work (The successful nature of this defense is debatable). The license mentioned is the GPL, or General Public License.

Related to the GPL, but different from it in that it inhibits the commercial use of an object or idea, is the LGPL or Lesser General Public License.


PvP

PvP stands for "Player Versus Player".

Usually implies shooting at each other, but any multiplayer competition could be considered PvP


quaternion

Synonym for the type "rotation". It can be used in place of the keyword rotation in LSL
(It isn't syntax-highlighted, but you can add it by modifying your "keywords.ini" file).

See quaternions.


Quaternions

(continued from rotation)

To set the elements of the rotation type directly, compute the quaternion as follows. To represent a rotation of a given angle around a given axis, first, convert the axis to a unit vector (a vector of magnitude 1). Take the x, y, and z components of the unit vector and multiply them by the sine of half the angle (these are the x, y, and z elements of the quaternion). The s element of the quaternion is equal to the cosine of half the angle.

The following function performs this in LSL:

rotation make_quaternion( vector axis, float angle )
{
    vector unit_axis = llVecNorm( axis );
    float sine_half_angle = llSin( angle/2 );
    float cosine_half_angle = llCos( angle/2 );

    rotation quat;
    quat.x = sine_half_angle * unit_axis.x;
    quat.y = sine_half_angle * unit_axis.y;
    quat.z = sine_half_angle * unit_axis.z;
    quat.s = cosine_half_angle;

    return quat;
}

This example duplicates the function llAxisAngle2Rot. Note that the angle must be specified in radians rather than degrees.

For a rotation around just the x-axis, the quaternion becomes simpler to compute, because in this case the unit axis is <1,0,0>:

rotation x_rot = <llSin( angle/2 ), 0, 0, llCos( angle/2 )>; // rotate around x-axis

Similarly, a rotation around just the y-axis or just the z-axis is also simpler to compute:

rotation y_rot = <0, llSin( angle/2 ), 0, llCos( angle/2 )>; // rotate around y-axis
rotation z_rot = <0, 0, llSin( angle/2 ), llCos( angle/2 )>; // rotate around z-axis

A rotation of angle 0 around any axis is represented the same way. Consider that the sine of 0 is 0, while the cosine of 0 is 1. As a result, no matter what axis is chosen, the null rotation is equal to <0,0,0,1>.

To find the inverse of a rotation, negate the last element of the quaternion:

inverse_quat = quat;
inverse_quat.s = -quat.s;

To find the inverse, negate the first 3 elements:

inverse_quat = < -quat.x, -quat.y, -quat.z, quat.s >;

If you negate all 4 elements of a quaternion, you end up with the same effective rotation.

"Quaternions are the things that scare all manner of mice and men. They are the things that go bump in the night. They are the reason your math teacher gave you an F. They are all that you have come to fear, and more. Quaternions are your worst nightmare." -- Confuted

This is so inappropriate. I understand why folks think this is funny, but come on. Why scare foks off? Quanternions aren't out of reach for most of us. Just takes a little patience. Note "Confuted" goes on to say "Okay, not really. They aren't actually that hard. I just wanted to scare you. "

Additional Resources:
Mathworld
Delphi3D
GameDev.net
Programming Rotations with Quaternions
Michel Isner's guide to Quaternions - arguably the best and clearest tutorial on rotation representations.
Rotation | Types


Query

A query is a request for information. In LSL, queries are most often associated with the dataserver event.

LSL | Dataserver


queue

A queue is a sequence of things waiting to do something. In programming, and LSL scripting, this can be many things such as list values/parameters, function arguments, event handler, variables, etc.

dictionary.com entry

script | FIFO | LIFO | Events


QuickReferenceGuide

Quick Reference Guide to LSL

(Barely begun. All of this info may be found elsewhere in more complete form.)

Operators

Operation (Listed by order of execution) | Description
() [] . | Parentheses, Square Brackets and Dot Operator
! ~ ++ -- | NOT, One's Compliment, Increment, Decrement
* / % | Multiply, Divide, Modulus
+ - | Addition and Subtraction
<< >> | Left Shift, Right Shift
< <= > >= | Less Than, Less Than Or Equal To, Greater Than, Greater Than or Equal To
== != | Comparison Equal, Comparison Not Equal
& | Bitwise AND
^ | Bitwise XOR
| | Bitwise OR
&& | Comparison AND
|| | Comparison OR
= += -= *= /= %= | Value functions

Flow Control

if (condition) { statements } else { statements }
while (condition) { statements }
do (condition) while { statements }
for (initialization; test; update) { statements }
jump label; ... @label
return simple-value // from function or event handler
state name { ... } // define the named state handler
state name; // change to named state
UserDefinedFunctions:
type functionName(type parameter1, type parameter2, ... type parameterN) { statements }

Types

float, integer, key, list, rotation, string, vector

Variables

Declaration:
type name;
or
type name = value;

Constants

PI TWO_PI PI_BY_TWO (pi / 2) DEG_TO_RAD RAD_TO_DEG SQRT2 (square root of 2)
TRUE FALSE
NULL_KEY (empty key, sometimes treated as string)
EOF (returned in the dataserver event for last line of notecard)

ZERO_ROTATION (<0,0,0,1>)
ZERO_VECTOR (<0,0,0>)

Object Types
AGENT | 1
ACTIVE | 2
PASSIVE | 4
SCRIPTED | 8
States

Events

LSL is an event-driven language. Scripts can provide event-handlers, like moving_end or touch_start. If a script provides a handler for an event, and an event of this type occurs, the simulator will add this event to the script's event queue. The script's event handlers will then be called in the order the events are queued in (FIFO). According to Cory Linden, the event queue can hold up to 64 events.

Changing states clears the event queue.

Different states can have different event handlers. Be wary of bugs though.

The function llMinEventDelay may be of some use in coordinating events.

LSL events in alphabetical order:

Event: | Triggered:
at_rot_target | when a rotational target set with llRotTarget is reached
at_target | when a target set with llTarget is reached
attach | when an object attaches or detaches from agent
changed | when certain aspects of the object are changed (inventory, color, shape, scale, texture, link)
collision | while task is colliding with another task
collision_end | when task stops colliding with another task or stops being penetrated while llVolumeDetect is set
collision_start | when task starts colliding with another task or starts being penetrated while llVolumeDetect is set
control | when one of the controls taken with llTakeControls is pressed/held/released
dataserver | when task receives asynchronous data
email | when task receives email
land_collision | when task is colliding with land
land_collision_end | when task stops colliding with land
land_collision_start | when task starts colliding with land
link_message | when task receives a link message sent via llMessageLinked
listen | when task is within hearing range of chat matching the critera set in llListen
money | when money is given to task
moving_end | when task stops moving
moving_start | when task begins moving
no_sensor | when llSensor or llSensorRepeat results in no targets sensed
not_at_rot_target | when a rotational target set with llRotTarget is not yet reached
not_at_target | when a target set with llTarget is not yet reached
object_rez | when task rezzes in another task using llRezObject
on_rez | when task is rezzed (from inventory or another task)
remote_data | any kind of XML-RPC communication (New in 1.3)
run_time_permissions | when an agent grants run-time permissions to task that were requested via llRequestPermissions
sensor | Result of llSensor or llSensorRepeat functions
state_entry | on any transition into the state and on startup
state_exit | on any state transition out of the state
timer | in intervals set by llSetTimerEvent
touch | while agent is clicking on task
touch_end | when agent stops clicking on task
touch_start | when agent first clicks on task

Built-in Functions

- Agent/Avatar

A user's presence in Second Life is represented by two entities: an agent, which is a client's presence within a simulator, and an avatar, which is the visual representation of an agent that people see and interact with.

-- Agent Functions:
llAddToLandPassList (key agent, float hours) | Adds agent to the land pass list for the specified number of hours.
llEjectFromLand (key pest) | Ejects pest from land that you own.
integer llGiveMoney (key destination, integer amount) | Transfer amount of Linden Dollars from script owner to destination. Requires PERMISSION_DEBIT
llInstantMessage (key user, string message) | Sends an IM to user containing message.
llRequestAgentData (key id, integer data) | This function requests information of type data (DATA_ONLINE, DATA_NAME, DATA_BORN, DATA_RATING) about agent id.
integer (key id) | Returns TRUE if the agent/object associated with id has the same active group (and is currently in the same simulator) as the object the script is attached to. FALSE otherwise.
llSetSitText (string text) | Replaces "Sit Here" in the object's PieMenu with text.
llSetTouchText (string text) | Replaces "Touch" in the object's PieMenu with text.
llTeleportAgentHome (key id) | If the scripted object is over owner's land, teleports agent id immediately to that agent's home location, with no confirmation.
-- Agent Events:
dataserver Triggered by llRequestAgentData.
money Triggered by paying an object.

-- Agent & Avatar Functions:
llAvatarOnSitTarget
llGetAgentInfo
llGetPermissions
llGetPermissionsKey
llKey2Name
llReleaseControls
llRequestPermissions
llSetCameraAtOffset
llSetCameraEyeOffset
llSitTarget
llTakeControls
llUnSit

-- Agent & Avatar Events:
control
run_time_permissions
changed Triggered by sit.

-- Avatar Functions:
llAttachToAvatar
llDetachFromAvatar
llGetAgentSize
llGetAnimation
llGetAnimationList
llGetAttached
llPointAt
llStartAnimation
llStopAnimation
llStopPointAt

-- Avatar Events:
attach

- Communications
- Collisions
- Dataserver
- Detection
- Dynamics (movement/physics)
- Inventory

- Links

Up to 255 prims can be linked into a single object. One is the parent and the rest are children.

llBreakLink (integer linknum) | Delinks the object with the given linknumber linknum (requires permission PERMISSION_CHANGE_LINKS be set).
llBreakAllLinks () | Delinks all objects in the link set (requires permission PERMISSION_CHANGE_LINKS be set)
llCreateLink(key target, integer parent) | Attempts to link the object containing the script and target (requires that permission PERMISSION_CHANGE_LINKS be set).
llDetectedLinkNumber |
llGetLinkKey |
llGetLinkName |
integer llGetLinkNumber() | Returns what number in a link set the script is attached to (0 means no link, 1 the root/parent, 2 for first child, etc).
llGetNumberOfPrims |
llMessageLinked |
llPassCollisions |
llPassTouches |
llSetLinkAlpha |
llSetLinkColor |

Events

link_message

Constants

(Used by the functions llMessageLinked, llSetLinkAlpha, and llSetLinkColor.)
Constant | Value | Description
LINK_ROOT | 1 | Modifies the root prim in an (but not in a single prim, which is 0)
LINK_SET | -1 | Modifies all prims in the object.
LINK_ALL_OTHERS | -2 | Modifies all other prims in the object besides the prim the function is in.
LINK_ALL_CHILDREN | -3 | Modifies all child prims in the object.
LINK_THIS | -4 | Modifies the prim the script is in.

- Lists
- Math
- Particles
- Primitives
- Sensors
- Sounds

- Strings
- Targets
- Textures
- Time
- Transformation: Rotation/Scaling/Translation
- Vehicles
- World


QuickTime

QuickTime (MOV) is an efficiently-compressed digital video file format developed by Apple Computer, Inc., and a rival to MPG/MPEG as the most popular video file format used on the Internet. It is used for the delivery of video, animation, and sound through a webpage or website (and now through SL). QuickTime files use a fair compression scheme which makes them good for Internet use. Viewing QuickTime files on other than a Macintosh requires the download and installation of the QuickTime plug-in from Apple.

QuickTime can be utilised by land owners in SL via the LSL commands llParcelMediaCommandList and llParcelMediaQuery.

file formats


Radian

A radian is a measurement of angle based upon a fundamental property of circles. Its unit is that angle where the amount of circumference subtended is equal to the radius of that circle (which turns out to be 57.296 degrees). There are 2 * PI radians in a circle (360 degrees).

Although degrees are more commonly used in day-to-day references, radians are more common in computer- and science-related tasks. All LSL functions that require angles use radians but SL's object editor uses degrees in the rotation fields.

Radians are most commonly used in conjunction with PI. One full circle (360 degrees) equals 2 * PI (TWO_PI) radians, one half-circle (180 degrees) equals PI and 90 degrees equals one-half of PI (PI_BY_TWO).

Conversion from radians to degrees, and vice versa, is easy with the constants RAD_TO_DEG and DEG_TO_RAD.

Types | Quaternions | Rotation | Constants


Radians

See radian.


Raise

To "Raise" an event is to trigger it.

Think about it as in "Raising someone from the dead", which would trigger their brain.


rating

Functions

llRequestAgentData
llRequestSimulatorData

Links

  • SL History Wiki: rating
  • SL Wiki: rating


RawHeightMap

See Support:RawTerrainFile.


RealPageIndex

This page exists because the PageIndex page actually uses the {{Backlinks}} tag instead of the {{PageIndex}} tag.

Didn't realize {{PageIndex}} was working again, I've changed the PageIndex to use {{PageIndex}} again. We were using the {{Backlinks}} template because it was the only one that provided the same list and worked ({{PageIndex}} hadn't worked for over a year). You can delete this page then. --BW


Realtime Modeling

(or the art of building stuff that look good and don't kill FPS)

Realtime vs. Pre-rendered

A common confusion encountered by persons not into game design is toward the differences between pre-rendered 3D and realtime 3D.

What realtime 3D is for: games, live applications, realtime simulations
What pre-rendered 3D is for: movies, animations, illustrations

Definitions

frame: one single image composing an animation that will be played on the screen
rendering: the processing needed by the computer to create a frame
fps (frames per second): how many frames can be rendered in a single second

Pre-rendered 3D Facts

- It can take several minutes to hours to render a single frame (most movies run at 25 frames per second).
- The rendering time depend of the quality of the picture wanted
- The available rendering time depend of the rendering machine power and the amount of machines dedicated to it. (The term "render farm" is used to designate a group of computers used to render 3D frames.)
- machines from a render farm are dedicated to the sole task of rendering frames.

Realtime 3D Facts

- The rendering time cannot exceed a fraction of a second.
- The available rendering time is limited as we need to render at least 25 frames per second to give an illusion of movement
- The machine used to render is usually not of a professional level and usually a single computer.
- The machine is usually NOT dedicated to the image rendering as several other processes are running in the background (sound, AI, networking, physics).

Optimizing work for Second Life

Now that this is known, what is wanted, basically, is to get as much detail as possible while keeping the framerate higher than 25 FPS (the minimal framerate to keep a smooth feeling of movement).

In game design, the key of success is to take as many shortcuts and tricks as possible to speed up the rendering process. Commercial games like Doom 3 use BSP culling, normal mapping, and zoning, that are good tricks to determinate what can and cannot be seen and add a lot of details on low polygon objects.

Most of these tricks cannot be used in Second Life; due to it's ever changing nature, a list can't be prebuilt of what can be seen and what cannot from every point in the grid|world. So the only way to improve the global experience is to learn how to build the smart way.

LOD: level of detail

Some shapes that have curves usually use what is called a LOD (level of detail) that allows making an object less detailed as the camera is far from it and more detailed as the camera approaches. As an example of LOD, the SL cylinder looks more round when close and seems to look more like a 5-sided prism when seen from a distance. This allow to spare some triangles from far objects. Second Life's LOD, according to my observations, scales objects from 5 to 24 subdivisions, so a cylinder seen from very close will have 24 sides and seen from a great distance will have 5 sides.

Polygons & Primitives

The common measurement in object complexity in Second Life is the primitive count (not polygons). However, it is very inaccurate as each primitive does not weigh equally on the rendering engine.

Each primitive, avatar, tree, and terrain is composed of triangles for the 3D engine behind Second Life. Every shape is constructed using triangles of various size that are fitted like pieces of puzzle. As stated, each primitive does not "cost" the same number of triangles. Thus, some primitives will dramatically increase the number of triangles to render while some will cost almost nothing.

Here is a rough classification of the different prims sorted from low triangle (tri) count to high triangle count:
(Note: the values do not take in account the twist/cut/hollow operations as they make things worse.)

updated the 28 of july 2006

Prim Type | Far LOD | Close LOD | -180 +180 Twist
prism | 8 tri | 8 tri |
cube | 12 tri | 108 tri | 1350 tri
cone | 15 tri | 168 tri |
cylinder | 20 tri | 192 tri |
ring | 30 tri | 144 tri |
half sphere | 22 tri | 600 tri |
tube | 60 tri | 144 tri |
sphere | 25 tri | 576 tri |
torus | 50 tri | 1152 tri |
To compare:
-SL's average torus-based hair: ~48000 tri (one attachment)
-Alyx (detailed main character) from Half-Life 2: ~15000tri (complete character)
-Combine soldier from Half-Life 2: ~9000tri (complete character)

As is understandable, a good builder in SL, on top of trying to use as less shapes as possible, will prefer the use of pyramid prisms and cubes compared to toruses and tubes, and a judicious choice of textures to bring some details (nuts, bolts, cables) without using primitives.

a usefull trick is that if a cube is tapered of at least .01 it snap back to its lowest lod (12 triangles) thus will cost 9 time less triangles than untapered

When a primitive is twisted, some of its sides will start subdividing to compensate the rotation. For example, a cube, twisted by:
- 0-29 degrees: length sliced in 2
- 30-44 degrees: length sliced in 3
- 45-58 degrees: length sliced in 4
- 59-72 degrees: length sliced in 5

Textures & Memory

Another problem is related to textures and graphic cards. When a computer is rendering a frame, it will delay a lot of the work to the graphics card, like putting textures on triangles. This mean the textures must be loaded into the graphics card memory.

An average graphic card is shipped with 128MB to 512MB of memory onboard. Most picture formats are compressed to save disk space. However, in the graphics card memory, the textures MUST be uncompressed which means that what will determine the size a texture's use in graphics card memory is not dependent on file size but on picture resolution. Second Life (and games) work with textures sized as multiples of 2 (like 256x256 or 512x512--but they don't need to be of a square size).

Here is "roughly" the amount of memory a texture uses in graphics card memory based on it's resolution. The memory-use formula for textures is: width*height*4 bytes. If there is alpha|transparency in the texture, the memory used is doubled and add 50% for mipmapping. This means:

256x256 texture = 0.39MB (656 in a 256MB card)
512x512 texture = 1.56MB (164 in a 256MB card)
1024x1024 texture = 6.3MB (40 in a 256MB card)

256x256 texture+alpha = 0,78MB (328 in a 256MB card)
512x512 texture+alpha = 3.13MB (81 in a 256MB card)
1024x1024 texture+alpha = 12.6MB (20 in a 256MB card)

As shown, it seems there isn't a LOT of room to store all textures, so once memory is used up in a graphics card, what happens? Well, the used up textures are unloaded from graphics card memory back to system memory and the textures still needed to process are loaded into graphics memory--it's called "swapping", and even if it's fast it still adds more and more to the time needed to render a single frame (don't forget, 25 frames per seconds is needed!)

So, on top of keeping the triangle count low, using large and detailed textures is not a compensation or the cure will be worse than the disease.

Alpha & Rendering

Every now and then the terrible alpha flickering/sorting bug comes up. This happens usually when two transparent textures are too close or seen from a weird angle.

The heart of the problem is that the alpha textures are rendered separately. Usually the engine starts to draw the closest triangles and proceeds slowly to the furthest one, but since the transparent textures are, well, transparent, the 3D engine renders them AFTER the others and in reverse order. Sometimes the ordering isn't right and one texture appears on top of the other when it shouldn't. There are methods to prevent this problem but they usually slow down the rendering and do not correct the next problem.

The fact the alpha textures need a separate "pass", and has to make pixel value additions to simulate the transparency, considerably slow down the rendering when there are a lot of passes. So, two reasons to limit the number of transparent textures are to:
-prevent the alpha flickering/sorting bug
-lighten the load on the 3D engine

A common rule of game designers when creating game levels is to avoid (as often as possible) that the player sees at the same moment more than two alpha textures overlapping (like seeing through no more than two lenses at a time).

Polygon | Camera


Recursion

Recursion is a programming method in which a routine calls itself. Recursion is an extremely powerful concept, but it can strain a script's memory allocation.

subtractTillTwo(integer value) {
    if (value > 2) {
        subtractTillTwo(value - 1);
    } else if (value == 2) {
        llSay(0, "Value is two!");
    }
}

default {
    state_entry() {
        subtractTillTwo(35);
    }
}

To determine a script's memory usage: llGetFreeMemory

script | memory


Recursive Rezzing

(Sorry this isn't pretty yet. I'm just starting to use the Wiki and don't have all the formatting down.)I formatted the code snippets for you.-DolusNaumova

To make objects reproduce themselves, they have to rez a copy of themselves. The fastest way to do this is to place a copy of the parent object (including its running scripts) back into itself and then use llGiveInventory to copy the parent to the child for the child to copy on down the line.

One drawback to this during development is that every time you change the parent, you have to reinsert the child back into the parent to update the code that it contains. This is sort of a chicken-and-egg game that is both time consuming and mind-bending.

The other problem this creates is that if you are building on your object, every time you rez it, it will start to do its thing since the script has to be running.
This can be alleviated if you give every child object a non-zero parameter when calling llRezObject. Then in the object check the start_param in on_rez; if zero then don't run, if non-zero then act normal. This doesn't stop the chicken/egg annoyance during testing though. -NeverRust

In order to avoid these problems, and keep most of the script separate from the object in the parent, you can do this:

1. Make your object. Put a simple script in it like this:
default
{
    state_entry()
    {
     llSetRemoteScriptAccessPin(foo);
    }
}

where foo = an integer and not an actual variable.
You never have to change this script unless you want to change the PIN.

2. Create a rezzer object that will rez the first instance of the thing that will grow. It has a separate rezzer script in it that includes this:
default
{
    touch_start(integer total_number)
    {
        llRezObject("Object Name", <x, y, z>, ZERO_VECTOR, ZERO_ROTATION, param);
    }

    object_rez(key child)
    {
        // Give inventory items. This way the script doesn't have to be running in the rezzer.
        llGiveInventory(child, "Object Name");
        llRemoteLoadScriptPin(child, "Object Name Script", foo, TRUE, param);
    }
}

<x, y, z> is the position vector you define for the child to start at
param is a parameter to be passed to the child
foo is the SAME integer you put into the actual object pin setter.
llRemoteLoadScript will start the script after it is received.
Add one llRemoteLoadScript call for each of the scripts the child should have (EcoName definitions, locomotion, etc.)

3. The code in the script that makes the object reproduce has similar llRezObject code triggered by whatever you decide should allow the object to reproduce. It also needs the SAME object_rez event handler as is in the rezzer.

4. Put the object with the PIN script into the rezzer object along with the rezzer script and the object's reproduction script. Now you can start a new round of objects. No muss, no fuss.

Ecosystem | Rez


Region

Another name for Simulator.


Region Coordinate

A region coordinate describes a position with an origin at zero elevation in the southwest corner of the simulator the position is in (<0,0,0>). This means that a point in simulator A may have the same vector value as a point in simulator B, yet represent two seperate positions. See global coordinates for more information about this ambiguity.

Region coordinates are the x-y-z values in the "edit" window, and are the values returned by llGetPos.

Do NOT confuse with region corner.

Simulator | Coordinate | Local Coordinate | Global Coordinate | Region Corner


Region Corner

A region corner is a special type of global coordinate that represents the southwest corner of a simulator.
It is in the form <x, y, 0> and is the return value of llGetRegionCorner.

Region corners can also provide very efficient cross-simulator travel, see Global Coordinate for more details.

Coordinates | Global Coordinates | Region Coordinates | Local Coordinates


ReloadUIXML

Reloads the UI (user interface) elements described by XML files in the app_settings subfolder of the SecondLife installation directory.

Currently only a few preference and info panes are set up to be modified in this manner, and there is zero documentation.

Play with these at your own risk.

Debug


relog

To relog is to restart SL (quitting the client and reloading it) and relog(on) to SL. Relogging fixes the immediate effects of many SL bugs and annoyances, including (but not limited to) ghosted objects (remain visible after deletion) and avatar attachments (won't detach), bent avatar legs that won't become fixed after sitting on a prim, time desynchronizations (i.e. clouds), etc.

SL | annoyances


RemovalPetition

Petition to Ban Users

A wiki is one of the greatest medium for disseminating knowledge, it allows the users to contribute their knowledge and understanding to the collective, so others can benefit. In every corner of society there is bound to be disagreements and emotions that will overshadow and stain even the best of intentions. Our little part of society is no different.

Over the past 21 months this wiki has been graced with many contributers, some writing entire tomes, others correcting grammar; we value these individual. Problems have arisen when the authors don't agree, and sadly that's what this page is for when there is no other recourse.

As to provide a more accurate accounting of the communities position there is a section for names who do not feel the petition is warranted. If someone is on the fence they may leave a comment in the debate section, but should not sign the petition. People should only sign if they are active wiki authors and agree or disagree with the petition. At the time of signing they may leave a comment.

Petition to ban EepQuirk from the wiki
Update
Eep Quirk announced on 7/13/2006 on his personal web site that he's been banned permanently from Second Life, as such, it would seem he's much less likely to make edits on this Wiki. Our petition, and the sister counter-talk are both fairly irrelevant.

I move that both be archived (together) on a page linked from here, for historical reasons. -- Ice
I'm cool with that. -BW
Over the past 10 months we have had the fortune and misfortune of having Eep Quirk as an author. In the beginning there were some debates with him over rules but they were mostly settled. These rules I speak of are the wiki styling

At this point I'm at my whits end, he refuses to follow these rules;
  • Rolls back other authors changes.
  • Locks his pages so people can not edit them.
  • Obsesses over his personal formatting style, and insists on the entire wiki using it despite, and conflicting with, published guidelines for formatting.
  • Disrespect of other Wiki authors, verbal accusations towards said authors for their contributions and corrections.
  • Regular recreation of his pages to silence debate (his method of removing unwanted comments).

With the pages locked other authors cannot edit his pages; knowledge and understanding cannot be added to them. More importantly his pages (like many pages) are inaccurate, when these inaccuracies are discovered they can not be corrected. When he rolls back a change another author has made he is second guessing the new information, and in some of the instances making the page inaccurate.

All these go against the core wiki culture and etiquette.

Debate with him over these issues as they first arose has so far only had the effect of him taking more and more drastic measures when the debates ended in a way not to his liking (he didn't start out locking pages). Currently there is a grass roots movement to ban him from editing pages. WrongPageSetup

I override this site's stylesheets because I don't like it. Standards are relative; relative to MY standard, this site's "meta data and tags" (stylesheets) conflict with my tastes. This site needs alternate styles. - EepQuirk

"Standards are relative; relative to MY standard" says it all.

Inflamitory Comments:
(with the word "d*mn" in them)
Comment1360
Comment1229
Comment1079
Comment1369
Comment1499 - The page this comment on was recreated by Eep - Yahoo Cache - Comment1499
Yahoo Cache - Reset
Comment1260
Comment1268
(just plain inflamitory)
Comment1080 - it should be noted that the creation of "Shadows" was in responce to him locking "Shadow"

General Arrogance:
Text - 2/18/06

(his profile, as of 2005-09-05)
website

Mmm, wiki...

? used to be an ASCII enemy in some old game I forget the name to...

Griefers, trolls, and wiki nazis/vultures (IceBrodie, SchitsoMonkey, BlindWanderer--on his way!) suck!

LEAVE LINKS LOWERCASE!!!!!! CamelCase is stupid! Capitalized titles are also stupid when they're not proper/pronouns!

Petition Authors:
Strife Onizuka/BlindWanderer
WolfWings Majestic/WolfWings
Ice Brodie/Icebrodie

Signatories in agreement with the petition:
Name | Comment
Strife Onizuka/BlindWanderer | His pages should be deeded to someone else and he should be blocked from editing the wiki
ZC | Some of his contributions have been of value (I don't like CamelCase either) but persistent uncooperative destructive behavior must be delt with.
WolfWings Majestic | Useful contributions sadly overshadowed by destructive assaults on accepted community formatting and editorial rules he disagrees with.
Cirr Marat | From what I've seen, personally assaults other users, and when said users complain, he stands in his opinions and retorts. In addition, locking pages from being edited by other users is COMPLETELY against the whole concept of a wiki.
Schitso Monkey | I completelly and whole-heartedly agree with all of you. Eep has done nothing but hurt this wiki and the people who use it. If there ever was a person who needed to be banned, Eep is definetly it.
Cid Jacobs | His personal vendettas against other members, and his overall disrespect for "most" other authors sounds like a good reason to add my signature.
TiPBaKeR | After readin the various 'discusions', I do not think his overall contribution is positive
Dolus Naumova | I find it interesting that while he calls other users "wiki vultures" he has made the vast majority of edits to wiki pages.
ReadyJack | I tried to stay out of this but at this point I sure hope he is removed soon.
Corisons Nino | I agree with everyone else. Especially Schitso Monkey
Burnman Bedlam | After reading this page the day I registered as an author, I decided to look around the comments to see for myself what has been going on. It seems that most of his comments include insults, and personal attacks. His behavior, in many cases, violates the Terms of Service/Community Standards and should not be tolerated. He needs to either learn how to be civil, or move on from this wiki to something more suited to his personality.
Samuel Frost | I cannot ignore his locking of the pages found on WrongPageSetup. His is not the master of this wiki and it his not his place to lock these pages. For that, I will sign this. Should these pages be unlocked and the personal attack against Grimmy Moonflower be removed, I will remove my signature from this listing.
Dirty McLean | I think Eep is a smart and talented guy, but he always has sand in his vagina. His negativity outweighs his usefulness on this Wiki.
PrioSerpentine | Nothing new to say. Nothing nice to say.
Thraxis Epsilon | I believe his continued behaviour speaks for itself
Chasingred3Ixtab | He has alot off good things to say... But before he says them, he has to b--ch about how this wiki sucks... If it sucks, stop writing on it if you don't like it. He has made many contributations, but as previously stated, his negitivity outweighs the pain of having him write on this wiki
AtteAderdeen | Everything has already been said.

Signatories who disagree with the petition:
Name | Comment

Debate:

This petition is open to debate please post here and ask question here, but use this format - BW

I give up, he's messed with legitimate pages, created useless ones, and has a personal attack against me on his user page, a clear TOS violation, if he doesn't go, I will no longer add my input to this Wiki. - Ice

While he is a smart guy, and fleshed out the wiki into new areas (beyond just the stricktly LSL refrence into more general topics); he's always had his own opinion of the rules. In the time I've been here I've always tried to get along with him peacably. I've always been open to try new things. On the lowercase links debate, i can see his side, but i don't feel it is somthing that needs to be done; spelling - yes, accuracy - yes, grammer - yes, lowercase links - no; that debate was when things started to go sour. - BW

Some of his content has been useful, but the sheer fact that he refuses to unlock the ACL from a page that has known errors, insisting that he maintains absolute editorial control in a flagrant and direct assault on the core design principles of the Wiki, leads me to circumvent his ACL blockades when I run across them at this point until some degree of sanity is injected back into the situation. There's very little debate needed on this, the general concensus from the majority of the Wiki is clear, ACLs should not lock out registered users from editing a page. He has pointedly refused to ever do so, which means that either: He needs to be removed from the Wiki; All the pages he created have to be routed around and relevant links locked to prevent him from removing the new routing; Everyone except him needs to leave the Wiki, effectively giving up. I don't see the last option as even remotely feasable, and the second option is a possible option but still a pain, leaving the first option as far and away the most likely. - WW

It seems a little harsh - he has done useful work, has the occasional point like the rest of us (loose convention isn't always best, though a personal crusade against it isn't either) and sometimes seems to be responded to questionably. He locks because he knows things will be edited and it's become a little polarised against him now. Still, he just doesn't listen to people other than himself when anything is argued against, which can be frustrating as it means precedence of his opinion in the end - edits against his decree will just mean another counter-edit by him, and I personally have more interesting things to do than try to argue for more than a few comments as that becomes useless and/or flaming. It'd be okay if it counter-edits were well argued like talk in wikipediae, but it looks like mutual monologues here. --IL

I'm not out to get Eep, and I don't want drama on the wiki; though drama is what we have. His contributions to the wiki are important, but his methods are driving away other contributers. His actions go against the grain. I would love this to be more like wikipedia, but it's not; wakka is an antiquated wiki engine. I will dance around sensitive topics only for so long before i'll call a spade a spade. Eeps has made many positive contributions but the prevelent trend has been to ax other users contributions. The wiki culture is not something that agrees with Eep. He doesn't like the idea of not having control and doesn't like debate. If he can't cope he shouldn't be an author. Part of the problem is there isn't a place to debate here on the wiki, debates have spread across many pages; following some of the debates is downright difficult. --BW

I have to agree with BW's comment. I've only made minor contributions to the Wiki. After reading many, I'm sure not all, of the arguments as I've come across them, I am very cautious about contributing as I do not want to start another. -- TB

Not only is he a rude obnoxious ass but more importantly I feel that the vast majority of his edits don't contribute substantially to the quality of the information in this wiki. Especially from a content standpoint. As mentioned previously he defies wiki standards that are in place to make it easier to cooperatively develop the content. In many cases pages become harder to maintain and update as well as harder to read and understand because they're cluttered with excessive formatting. Also, the vibe his presence creates here (drama) is not condusive to a friendly cooperative environment. His trolling discourages submissions from other users. He's quick to ridicule and challenge the contributions of new users who need to be encouraged. And he seems determindly resistant to input from numerous members of the community to adjust his behavior. The wiki community has effectively dealt with other problem children but Eep seems to be incapable of cooperation. I haven't spoken in world to anyone (who uses the wiki) who doesn't think he's a serious problem, and I know lots of scripters who use the wiki.--ReadyJack

Since becoming aware of the RemovalPetition, Eep has made his personal page unreadable. I think it's a bit late to hide the evidence. --BW

It might be worth reporting Eep for each and every infraction of the Terms of Service and Community Standards. It's one thing to debate a point, it's another thing entirely to attack and insult people. It really is out of hand. - BurnmanBedlam

ACL of this page currently:

Read | Write | Comment
? | !EepQuirk | ?


Reset

Reset means many different things but it generally means when something is put (or "set") back to a previous condition, state, form, or time. There are several different kinds of things that can be reset, and different ways of performing a reset in each case.

Note: rezzing an object does not reset the scripts in it; change the state of the scripts, or reset any other aspect of an object.

State

Each script has a state that is distinct from other things that are sometimes called state, such as the state of variables and properties of prims. When a script is reset (see below), its state is set to the default state which is called default, and the default state_entry event is triggered.

Any events in the event queues of a script are cleared when the state is changed.

Scripts

A script can reset itself or another script back to the condition it started in. This is equivalent to when a script is compiled when it is saved after changes, or when the reset button is pressed or reset menu item is selected. Another aspect of the condition of a script is whether it is running. If an error occurs a script stops running and cannot be reset by another script.

Functions
llResetOtherScript
llResetScript
llGetScriptState
llSetScriptState
llGiveInventory
llRemoteLoadScriptPin

Global Script Values

When a script is reset, the value of each global variable is set to its initial value. There are also a few other global values hidden in scripts that act like global variables. Global variables may be explicitly set by a script's code and other hidden global values are set in other situations described in the following table.

Category | Related Functions
time | llGetAndResetTime, llResetTime

Object/Prim Values

While each script contained in a prim has its own state, it is important to note that each prim and the object it is contained in maintains many values or properties that are separate from the scripts and, once set, the script is no longer needed.

With few exceptions, these are not reset when a script in a prim is reset. There are often special functions for setting the values or resetting to the default state. The default value is often one of these: 0 (zero), FALSE, constants (like ZERO_VECTOR, ZERO_ROTATION, NULL_KEY), or an empty string ("").

Some values are used only in the client.

Categories of values that can only be manipulated by scripts:

Category | Related Functions and Notes
start parameter | llGetStartParameter, llRezObject, llRemoteLoadScriptPin on_rez - reset by script reset(?)
PIN | llRemoteLoadScriptPin, llSetRemoteScriptAccessPin
client-side texture animation | llSetTextureAnim
client-side rotation | llTargetOmega
client-side particles | llParticleSystem
prim hover text | llSetText
physics |
permissions |
others | llGetStatus, llSetStatus, llGetPrimitiveParams, llSetPrimitiveParams

User

Some things in SL require the user to perform a reset (more details).
  • The user may reset the camera by pressing the Esc(ape) key and/or closing windows (depending on one's preferences), this is useful to know when using vehicles that capture the camera.

State | Value | Time


resetting

Reset

In SL terms, a reset generally means to put (or "set") back to a previous condition, state, form, or time. There are several different kinds of things that can be reset, and different ways of performing a reset in each case.

Note: rezzing an object does not reset the scripts in it, change the state of the scripts, or reset any other aspect of an object.

State

Each script has a state that is distinct from other things that are sometimes called state, such as the state of variables and properties of prims. When a script is reset (see below), its state is set to the default state which is called default, and the default state_entry event is triggered.

Any events in the event queues of a script are cleared when the state is changed.

Scripts

A script can reset itself or another script back to the condition it started in. This is equivalent to when a script is compiled when it is saved after changes, or when the reset button is pressed or reset menu item is selected. Another aspect of the condition of a script is whether it is running. If an error occurs, a script stops running and cannot be reset by another script.

Functions
llResetOtherScript
llResetScript
llGetScriptState
llSetScriptState
llGiveInventory
llRemoteLoadScriptPin

Global Script Values

When a script is reset, the value of each global variable is set to its initial value. There are also a few other global values hidden in scripts that act like global variables. Global variables may be explicitly set by a script's code and other hidden global values are set in other situations described in the following table.

Category | Related Functions
time | llGetAndResetTime, llResetTime

Object/Prim Values

While each script contained in a prim has its own state, it is important to note that each prim and the object it is contained in maintains many values or properties that are separate from the scripts and, once set, the script is no longer needed.

With few exceptions, these are not reset when a script in a prim is reset. There are often special functions for setting the values or resetting to the default state. The default value is often one of these: 0 (zero), FALSE, constants (like ZERO_VECTOR, ZERO_ROTATION, NULL_KEY), or an empty string ("").

Some values are used only in the client.

Categories of values that can only be manipulated by scripts:

Category: | Related Functions: | Events:
start parameter | llGetStartParameter, llRezObject, llRemoteLoadScriptPin | on_rez - reset by script reset
PIN | llRemoteLoadScriptPin, llSetRemoteScriptAccessPin |
client-side texture animation | llSetTextureAnim |
client-side rotation | llTargetOmega |
client-side particles | llParticleSystem |
prim hover text | llSetText |
physics |
permissions |
others | llGetStatus, llSetStatus, llGetPrimitiveParams, llSetPrimitiveParams Explain these in greater detail, please. |

User

Some things in SL require the user to perform a reset (more details).
  • The user may reset the camera by pressing the Esc(ape) key and/or closing windows (depending on one's preferences), this is useful to know when using vehicles that capture the camera.

value | state | time


Resident

Another name for a user.

User


Rez

When you create an object within SL, you "rez" it. Yes, it's a verb. Let's conjugate it! "I rezzed a prim." "I will rez a prim." "I am rezzing a prim."

The term comes from the 1980s movie Tron, in which the word "de-rezz" was used as shorthand for saying that an object had "de-resolved", or faded out. So, to "rez" an object is to make it come into resolution.

Performance Advisory
Always use temp-on-rez when rezzing a large number of "disposable" objects such as bullets or other projectiles. Doing this allows them to automatically be cleaned up, should they break or encounter no-script land. This is both good scripting practice, as well as good for your sim and neighbours. It also keeps you and your customers happy by not getting complaints sent after them.

Functions

llGodLikeRezObject | Only usable by Lindens; placed here for the sake of completeness.
llRezAtRoot | Rezzes an object at the coordinates of the root prim in a linked object.
llRezObject | Rezzes an object at a point less than 10m away from the rezzor object.
llSetPrimitiveParams | Sets the temp-on-rez property of an object. This makes it automatically die, regardless of whether or not it has entered no-script land, or if its script has failed. All bullets should be set to temp-on-rez.

Events

on_rez | triggered in the rezzed object as soon as it rezzes.
object_rez | triggered in the rezzor object after it rezzes the new object.

Q: That's great, but how do I delete a prim?
A: To delete or "de-rez", you want llDie.

Object | Prim


RezForce

See llRezObject.


RGB

Red-Green-Blue (RGB) is the color designation format used on computer monitors, TVs, and other light-emitting displays. It is subtractive--color is subtracted to get black. Contrast with CMYK (Cyan/Magenta/Yellow/blacK), used in the print world, which is additive.

See texture for LSL functions that use RGB.

texture


RL

RL is an abbreviation for "Real Life", or the world in which you live. Problems arise when the barriers between RL and SL break down.


roll

Angle of rotation around the X-axis. Compare with pitch and yaw.

Vehicle


Root

The "main" prim in a linked object. Also referred to as the "parent" prim.

For instance, ExampleGetRootRot uses "root" rather than "parent". Also, Linden constants and functions tend to use "root" rather than "parent".

Object | Prim | Link | Child | Parent


Rotations

See Rotation.


RPC

Remote Procedure Call - a way of invoking a function on another computer system and optionally returning a value (or just a confirmation).

See XML-RPC.

XML-RPC


SableTill

Speed tests

++x vs x=x+1 vs x++

default
{
    state_entry()
    {
        while(TRUE) {
            integer iter=20000;
            integer time = llGetUnixTime();
            llOwnerSay("++x "+(string)iter);
            integer x=0;
            for(x=0;x<iter;++x) {
            }
            llOwnerSay((string)(llGetUnixTime()-time));

            time = llGetUnixTime();
            llOwnerSay("x=x+1 "+(string)iter);
            x=0;
            for(x=0;x<iter;x=x+1) {
            }
            llOwnerSay((string)(llGetUnixTime()-time));

            time = llGetUnixTime();
            llOwnerSay("x++ "+(string)iter);
            x=0;
            for(x=0;x<iter;x++) {
            }
            llOwnerSay((string)(llGetUnixTime()-time));
        }
    }
}

Results
[4:24]  Object: ++x 20000
[4:26]  Object: 76
[4:26]  Object: x=x+1 20000
[4:27]  Object: 76
[4:27]  Object: x++ 20000
[4:28]  Object: 94
[4:28]  Object: ++x 20000
[4:30]  Object: 79
[4:30]  Object: x=x+1 20000
[4:31]  Object: 77
[4:31]  Object: x++ 20000
[4:33]  Object: 94

Conclusion
++x is the same as x=x+1, but x++ is slower unless you need that pre-incremented value.

Artificial Life
Lame swarming script

ChangeLog
2006-08-04
Lots of bugfixes
Added a LagMonitor which causes fish to die on lag spikes
Added a predator mode.
2006-08-06
Improved the bootstrap.
Fixed some bugs in the predator mode.

Instructions for usage:
Create a notecard and put the stuff from FishNote into it. Copy each of the other boxes below and save the text in a script with the same name as the title here.
Create a prim, just a random plywood box is fine. First place the notecard inside. Every other script reads this notecard so it needs to be there first. Then place the Init script in, it sets a few values including the object name which every other script uses so it's helpful to put it in first. Then transfer all the other scripts in any order.

It's perfectly fine to leave certain scripts out. For example if you want your fish to float away on their own then leave out NearOwner. If you don't want the text to appear above each fish then leave out the Stats script.

One useful thing to do is to make a container, put a temp-on-rez fish inside it. Then write a little script to llRezObject the fish on a timer. If you make one once every 10 seconds you'll end up with about 6 in the swarm if you make it rez once per second you'll end up with 60 and the sim might start to strain a little. If you set your objects to be temp-on-rez then you can set the lifetime to something ridiculous like 600 since the simulator deletes temporary objects over about 60 seconds old.

The Repel and Attract functions work based on Object name so you can set up two swarms or make new fish ignore the old ones by changing the name parameter in the FishNote notecard.

If you can't get any of this to work just contact me in game and I may be able to transfer all the scripts to you or at the very least help you get it working.

FishNote
fishname=Fish 4.0
predname=Pred 4.1
heartbeat=0.5
friction=0.6
maxpush=2.0
lifespan=600
quiet=true
phantom=false
temp=false

Attract
//default values
float maxpush=3;
float heartbeat=1;
integer quiet=TRUE;
string fishname="Fish 4.0";

string scriptname;
float scan_dist=0.5;

default {
    state_entry() {
        scriptname = llGetScriptName();
    }

    //mostly used to receive initialised parameters.
    link_message(integer sender_num, integer num, string str, key id) {
        list cmd = llParseString2List(str, [":", "="], []);
        if(llList2String(cmd, 0) == "PARAM") {
            //llOwnerSay(str+":"+llList2String(cmd, 1)+":"+llList2String(cmd, 2));
            if(llList2String(cmd, 1)== "maxpush") {
                maxpush = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "heartbeat") {
                heartbeat = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "quiet") {
                quiet = llList2Integer(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "fishname") {
                fishname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "EOF") {
                if(!quiet) {
                    llOwnerSay(scriptname+":"+"heartbeat="+(string)heartbeat);
                    llOwnerSay(scriptname+":"+"maxPush="+(string)maxpush);
                    llOwnerSay(scriptname+":"+"quiet="+(string)quiet);
                    llOwnerSay(scriptname+":"+"fishname="+(string)fishname);
                }

                if(llGetAttached()!=0) {
                    return;
                }
                state run;
            }

        }
    }
}

state run
{
    state_entry()
    {
        llSetTimerEvent(heartbeat);
    }

    timer() {
        //benefit of this over SensorRepeat is we get objectname every time
        //since it has a tendency to change around.
        llSensor(fishname, NULL_KEY, ACTIVE | PASSIVE, scan_dist, PI);
    }

    sensor(integer num_detected) {
        //new magic idea, get the average vector of all detected items
        //push away from that!
        integer count=0;
        vector target=<0,0,0>;
        for(count=0;count<num_detected;++count) {
            target+=llDetectedPos(count);
        }
        target/=num_detected;

        //theoretically 0 should be the nearest
        vector push = -((llGetPos()-target));
        //3, it's the magic number.
        push/=3;

        //limit to maxPush in power
        if(llVecMag(push)>maxpush) {
            if(!quiet)
                llOwnerSay(scriptname + "Warning! Pushing "+(string)push);
            push = push*maxpush/llVecMag(push);
        }
        //pass message to stats module
        llMessageLinked(LINK_SET, (integer)(llVecMag(push)*1000), "STATS:"+scriptname, NULL_KEY);

        push*=llGetMass();
        llApplyImpulse(push, FALSE);
    }

    //should send a message when attract is 0
    no_sensor() {
        //we may not be scanning far enough.
        if(scan_dist<64)
            scan_dist*=2;
        llMessageLinked(LINK_SET, -(integer)scan_dist, "STATS:"+scriptname, NULL_KEY);
    }

}

CardReader
//-------------------------------------------
// SampleSettingsReader.lsl
// Written by Konigmann Lippmann
// Thanks to Apotheus Silverman for his MultiItem Vendor...
// It seems like everything I write is based on it in one way or another.

// Globals
integer iNotecardIndex;
integer iNotecardCount;
integer iNoteCardLine;
key kCurrentDataRequest;
string sSettingsNotecard;

default
{
    state_entry()
    {
        //llOwnerSay(llGetScriptName() +":"+"entry");
        integer iii;

        iNotecardCount = llGetInventoryNumber( INVENTORY_NOTECARD );
        iNotecardIndex = 0;
        if( iNotecardCount > iNotecardIndex )
        {
            sSettingsNotecard = llGetInventoryName( INVENTORY_NOTECARD, iNotecardIndex );
            iNoteCardLine = 0;
            kCurrentDataRequest = llGetNotecardLine( sSettingsNotecard, iNoteCardLine );
            iNotecardIndex++;
        }
        if( iNotecardIndex == 0 )
        {
            llOwnerSay("Using Default Values." );
        }
    }

    dataserver( key kQuery, string sData )
    {
        list lSetting;

        kCurrentDataRequest = "";
        if( sData != EOF )
        {
            //llOwnerSay(llGetScriptName() +":"+sData);
            llMessageLinked(LINK_SET, 0, "PARAM:"+sData, NULL_KEY);
            kCurrentDataRequest = llGetNotecardLine( sSettingsNotecard, ++iNoteCardLine );
        }
        else
        {
            iNotecardIndex++;
            if( iNotecardIndex < llGetInventoryNumber( INVENTORY_NOTECARD ) )
            {
                sSettingsNotecard = llGetInventoryName( INVENTORY_NOTECARD, iNotecardIndex );

                iNoteCardLine = 0;
                llGetNotecardLine( sSettingsNotecard, iNoteCardLine );
            }
            else
            {
                llMessageLinked(LINK_SET, 0, "PARAM:EOF", NULL_KEY);
            }
        }
    }
}

Flee
//default values
float heartbeat=1;
//sometimes things go weird and want to push the object a million miles away
//this restricts that to an upper limit.
float maxpush=1;
integer quiet=TRUE;
string predname;

string scriptname;
float maxCubed;
float scan_dist=0.5;

default {
    state_entry() {
        scriptname = llGetScriptName();
    }

    //mostly used to receive initialised parameters.
    link_message(integer sender_num, integer num, string str, key id) {
        list cmd = llParseString2List(str, [":", "="], []);
        if(llList2String(cmd, 0) == "PARAM") {
            //llOwnerSay(str+":"+llList2String(cmd, 1)+":"+llList2String(cmd, 2));
            if(llList2String(cmd, 1)== "maxpush") {
                maxpush = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "heartbeat") {
                heartbeat = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "quiet") {
                quiet = llList2Integer(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "predname") {
                predname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "EOF") {
                if(!quiet) {
                    llOwnerSay(scriptname+":"+"heartbeat="+(string)heartbeat);
                    llOwnerSay(scriptname+":"+"maxPush="+(string)maxpush);
                    llOwnerSay(scriptname+":"+"quiet="+(string)quiet);
                    llOwnerSay(scriptname+":"+"predname="+(string)predname);
                }

                if(llGetObjectName()==predname) {
                    return;
                }

                if(llGetAttached()!=0) {
                    return;
                }

                maxpush*=3;
                maxCubed = llPow(maxpush, (1.0/3.0));

                state run;
            }
        }
    }
}

state run
{
    state_entry() {
        llSetTimerEvent(heartbeat);
    }

    timer() {
        llSensor(predname, NULL_KEY, ACTIVE | PASSIVE, scan_dist, PI);
    }

    sensor(integer num_detected) {
        //theoretically 0 should be the nearest
        vector push = ((llGetPos()-llDetectedPos(0)));
        //for some reason it sometimes shoots off in random directions, apply this limiter
        if(llVecMag(push)>maxpush) {
            if(!quiet)
                llOwnerSay(llGetScriptName() + " Warning! Pushing "+(string)push);
            push=push*maxpush/llVecMag(push);
        }

        if(llVecMag(push) > llVecMag(<maxCubed, maxCubed, maxCubed>)) {
            push=<0,0,0>;
        } else {
            float newMag = maxpush - llVecMag(push);
            push=push*newMag/llVecMag(push);
        }

        //this message goes to stats module which sets text
        llMessageLinked(LINK_SET, ((integer)(llVecMag(push)*1000)), "STATS:"+scriptname, NULL_KEY);

        push *= llGetMass();
        llApplyImpulse(push, FALSE);

    }

    //should send a message when attract is 0
    no_sensor() {
        //we may not be scanning far enough.
        if(scan_dist<64)
            scan_dist*=2;
        llMessageLinked(LINK_SET, -1, "STATS:"+scriptname, NULL_KEY);
    }
}

Friction
//default values
float friction = 0.3;
//we apply a little friction once per heartbeat seconds
float heartbeat=1;
//sometimes things go weird and want to push the object a million miles away
//this restricts that to an upper limit.
float maxpush=1;
integer quiet=TRUE;
string fishname;
string predname;

string scriptname;

default {
    state_entry() {
        scriptname = llGetScriptName();
    }

    //mostly used to receive initialised parameters.
    link_message(integer sender_num, integer num, string str, key id) {
        list cmd = llParseString2List(str, [":", "="], []);
        if(llList2String(cmd, 0) == "PARAM") {
            if(llList2String(cmd, 1)== "maxpush") {
                maxpush = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "heartbeat") {
                heartbeat = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "quiet") {
                quiet = llList2Integer(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "fishname") {
                fishname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "predname") {
                predname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "friction") {
                friction = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "EOF") {
                if(!quiet) {
                    llOwnerSay(scriptname+":"+"heartbeat="+(string)heartbeat);
                    llOwnerSay(scriptname+":"+"maxPush="+(string)maxpush);
                    llOwnerSay(scriptname+":"+"quiet="+(string)quiet);
                    llOwnerSay(scriptname+":"+"friction="+(string)friction);                              }

                //no real harm in running this while attached really.
                if(llGetAttached()!=0) {
                    return;
                }
                state run;
            }
        }
    }
}

state run
{
    state_entry()
    {
        scriptname = llGetScriptName();

        llSetTimerEvent(heartbeat);
    }

    timer() {
        //calculate friction to apply
        vector push = -llGetVel() * friction;
        //apply upper limit
        if(llVecMag(push)>maxpush) {
            push=maxpush*push/llVecMag(push);
            if(!quiet)
                llOwnerSay(llGetScriptName() + " Warning! Pushing "+(string)push);
        }
        //this message goes to stats module which sets text
        llMessageLinked(LINK_SET, ((integer)(llVecMag(push)*1000)), "STATS:"+scriptname, NULL_KEY);
        push*=llGetMass();

        llApplyImpulse(push, FALSE);

     }
}

Init
string fishname = "Fish";
string predname = "Predator";
integer lifespan=360;
integer phantom=TRUE;
integer temp=TRUE;
integer quiet;

string scriptname;

default {
    state_entry() {
        scriptname == llGetScriptName();

        llResetOtherScript("CardReader");
    }

    //mostly used to receive initialised parameters.
    link_message(integer sender_num, integer num, string str, key id) {
        list cmd = llParseString2List(str, [":", "="], []);
        if(llList2String(cmd, 0) == "PARAM") {
            //llOwnerSay(str+":"+llList2String(cmd, 1)+":"+llList2String(cmd, 2));
            if(llList2String(cmd, 1)== "quiet") {
                quiet = llList2Integer(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "lifespan") {
                lifespan= llList2Integer(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "fishname") {
                fishname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "predname") {
                predname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "EOF") {
                if(!quiet) {
                    llOwnerSay(scriptname+":"+"quiet="+(string)quiet);
                    llOwnerSay(scriptname+":"+"fishname="+(string)fishname);
                    llOwnerSay(scriptname+":"+"predname="+(string)predname);
                    llOwnerSay(scriptname+":"+"lifespan="+(string)lifespan);
                    llOwnerSay(scriptname+":"+"phantom="+(string)phantom);
                    llOwnerSay(scriptname+":"+"temp="+(string)temp);
                }

                state run;
            }

        }
    }
}

state run
{
    on_rez(integer huh) {
        llResetScript();
    }
    state_entry()
    {
        if(llFrand(1) < 0.9) {
            llSetColor(<1, 1, 1>, ALL_SIDES);
            llSetObjectName(fishname);
        } else {
            llSetColor(<1, 0, 0>, ALL_SIDES);
            llSetObjectName(predname);
        }
        //Setting the buoyancy to 1 cancels out the gravity.  The object will now float.
        llSetBuoyancy(1);

        //allow rotation on any axis
        llSetStatus(STATUS_ROTATE_X,TRUE);
        llSetStatus(STATUS_ROTATE_Y,TRUE);
        llSetStatus(STATUS_ROTATE_Z,TRUE);

        //phantom is handy for some reason
        llSetStatus(STATUS_PHANTOM,phantom);

        //physics is annoying
        llSetStatus(STATUS_PHYSICS,TRUE);

        llSetText("", <0,0,0>, 0);

        llSetTimerEvent(lifespan);

        llSetPrimitiveParams([PRIM_TEMP_ON_REZ, temp]);

        //boot strap is pain!
        //here we will reset all other scripts
        //if init is restarting then every other damn thing can restart too.
        //this means we run cardReader twice, annoying.
        integer max = llGetInventoryNumber(INVENTORY_SCRIPT);
        integer count;
        string me = llGetScriptName();
        for(count=0;count<max;++count) {
            string other = llGetInventoryName(INVENTORY_SCRIPT, count);
            if(other!=me && other !="CardReader") {
                llResetOtherScript(other);
            }
        }
        llResetOtherScript("CardReader");
        if(llGetObjectName() == predname) {
           vector push = <llFrand(2), llFrand(2), llFrand(2)>;
            push*=llGetMass();
            llApplyImpulse(push, FALSE);
        }

    }

    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }

    timer() {
        llDie();
    }
}

LagMonitor
string giv(integer type,integer i) {
    return llGetInventoryName(type,i);
}
default
{
    state_entry()
    {
        //stupid to check too often since that in itself causes lag.
        llSetTimerEvent(10);
    }

    timer() {
        if(llGetRegionTimeDilation() < 0.65) {
            llDie();
        }
        if(llGetRegionFPS() < 35) {
            llDie();
        }
    }
}

NearOwner

//default values
float maxpush=3;
float heartbeat=1;
integer quiet=TRUE;
string predname;
string fishname;

string scriptname;
float scan_dist=0.5;

default {
    state_entry() {
        scriptname = llGetScriptName();
    }

    //mostly used to receive initialised parameters.
    link_message(integer sender_num, integer num, string str, key id) {
        list cmd = llParseString2List(str, [":", "="], []);
        if(llList2String(cmd, 0) == "PARAM") {
            //llOwnerSay(str+":"+llList2String(cmd, 1)+":"+llList2String(cmd, 2));
            if(llList2String(cmd, 1)== "maxpush") {
                maxpush = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "heartbeat") {
                heartbeat = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "quiet") {
                quiet = llList2Integer(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "predname") {
                predname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "fishname") {
                fishname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "EOF") {
                if(!quiet) {
                    llOwnerSay(scriptname+":"+"heartbeat="+(string)heartbeat);
                    llOwnerSay(scriptname+":"+"maxPush="+(string)maxpush);
                    llOwnerSay(scriptname+":"+"quiet="+(string)quiet);
                    llOwnerSay(scriptname+":"+"predname="+(string)predname);
                    llOwnerSay(scriptname+":"+"fishname="+(string)fishname);                                                     }

                if(llGetAttached()!=0) {
                    return;
                }
                if(llGetObjectName() == predname) {
                    return;
                }

                state run;
            }
        }
    }
}

state run
{
    state_entry()
    {
        llSetTimerEvent(heartbeat);
    }

    timer() {
        llSensor("", llGetOwner(), AGENT, scan_dist, PI);
    }

    sensor(integer num_detected) {
        //slightly prefer to float around above my head
        vector push = -((llGetPos()-llDetectedPos(0)-<0,0,2>));
        //3, it's the magic number.
        push/=3;

        if(llVecMag(push)>maxpush) {
            push = push*maxpush/llVecMag(push);
            if(!quiet)
                llOwnerSay(scriptname + "Warning! Pushing "+(string)push);
        }
        llMessageLinked(LINK_SET, (integer)(llVecMag(push)*1000), "STATS:"+scriptname, NULL_KEY);

        push*=llGetMass();
        llApplyImpulse(push, FALSE);

        llLookAt(llGetPos()+push, 1, 1);
    }

    //if owner cannot be detected then die
    no_sensor() {
        //we may not be scanning far enough.
        if(scan_dist<64)
            scan_dist*=2;
        llMessageLinked(LINK_SET, -(integer)scan_dist, "STATS:"+scriptname, NULL_KEY);
    }

}

Repel
//default values
float heartbeat=1;
//sometimes things go weird and want to push the object a million miles away
//this restricts that to an upper limit.
float maxpush=1;
integer quiet=TRUE;
string predname;
string fishname;

string scriptname;
float maxCubed;
float scan_dist=.5;

default {
    state_entry() {
        scriptname = llGetScriptName();
    }

    //mostly used to receive initialised parameters.
    link_message(integer sender_num, integer num, string str, key id) {
        list cmd = llParseString2List(str, [":", "="], []);
        if(llList2String(cmd, 0) == "PARAM") {
            //llOwnerSay(str+":"+llList2String(cmd, 1)+":"+llList2String(cmd, 2));
            if(llList2String(cmd, 1)== "maxpush") {
                maxpush = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "heartbeat") {
                heartbeat = llList2Float(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "quiet") {
                quiet = llList2Integer(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "fishname") {
                fishname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "predname") {
                predname = llList2String(cmd, 2);
                return;
            }
            if(llList2String(cmd, 1)== "EOF") {
                if(!quiet) {
                    llOwnerSay(scriptname+":"+"heartbeat="+(string)heartbeat);
                    llOwnerSay(scriptname+":"+"maxPush="+(string)maxpush);
                    llOwnerSay(scriptname+":"+"quiet="+(string)quiet);
                    llOwnerSay(scriptname+":"+"fishname="+(string)fishname);
                    llOwnerSay(scriptname+":"+"fishname="+(string)predname);
                }

                if(llGetAttached()!=0) {
                    return;
                }
                if(llGetObjectName() == predname) {
                    return;
                }
                maxCubed = llPow(maxpush, (1.0/3.0));

                state run;
            }

        }
    }
}

state run
{
    state_entry() {

        llSetTimerEvent(heartbeat);
    }

    timer() {
        //benefit of this over SensorRepeat is we get objectname every time
        //since it has a tendency to change around.
        llSensor(llGetObjectName(), NULL_KEY, ACTIVE | PASSIVE, scan_dist, PI);
    }

    sensor(integer num_detected) {
        //new magic idea, get the average vector of all detected items
        //push away from that!
        integer count=0;
        vector target=<0,0,0>;
        for(count=0;count<num_detected;++count) {
            target+=llDetectedPos(count);
        }
        target/=num_detected;

        //theoretically 0 should be the nearest
        vector push = ((llGetPos()-target));

        //for some reason it sometimes shoots off in random directions, apply this limiter
        if(llVecMag(push)>maxpush) {
            if(!quiet)
                llOwnerSay(llGetScriptName() + " Warning! Pushing "+(string)push);
            push=push*maxpush/llVecMag(push);
        }
        if(llVecMag(push) > llVecMag(<maxCubed, maxCubed, maxCubed>)) {
            push=<0,0,0>;
        } else {
            float newMag = maxpush - llVecMag(push);
            push=push*newMag/llVecMag(push);
        }

        //this message goes to stats module which sets text
        llMessageLinked(LINK_SET, ((integer)(llVecMag(push)*1000)), "STATS:"+scriptname, NULL_KEY);

        push *= -llGetMass();
        llApplyImpulse(push, FALSE);

    }

    //should send a message when attract is 0
    no_sensor() {
        if(scan_dist<64)
            scan_dist*=2;
        llMessageLinked(LINK_SET, -(integer)scan_dist, "STATS:"+scriptname, NULL_KEY);
    }
}

Stats
//map of strings to floats, or more precisely strided list.
list map;

set_text() {
    integer size = llGetListLength(map);
    integer count;
    string text="";
    for(count=0; count<size; count+=2) {
        text+=llList2String(map, count)+":"+llList2String(map, count+1)+"\n";
    }
    llSetText(text, <0,0,0>, 1);
}

//will accept link messages from all the other scripts and publish the details as settext
default
{
    state_entry()
    {
    }

    //str will be script name.
    //num will be value multiplied by 1000
    link_message(integer sender_num, integer num, string str, key id) {
        list cmd = llParseString2List(str, [":"], []);
        if(llList2String(cmd, 0) == "STATS") {
            if(llStringLength(str)==0) {
                llOwnerSay("Alert:"+str);
            }
            str = llList2String(cmd, 1);
            integer index = llListFindList(map, [str]);
            if(index==-1) {
                map+=[str, num];
            } else {
                map = llListReplaceList(map, [num], index+1, index+1);
            }
            set_text();
        }
    }

}


sandbox

A simulator or parcel for building/experimenting in SL. "Sandbox" is also used to refer to the 10 meters of movement distance objects are allowed when STATUS_SANDBOX is enabled. In the context of memory, scripts are executed in a protected environment often called a "sandbox". This protected environment ensures that scripts can not affect each other's or the simulator's stored data.

If you're looking for a place to experiment with wiki markup see WikiSandbox.


scope

The scope of an identifier is the region of a script's source code where the identifier may be accessed. If the identifier is defined outside of a state or user-defined function it has a global scope, and it may be accessed anywhere within any of the script (after the definition). If the identifier is defined inside a state, or a user-defined function, or an event handler, then the scope is local to that containing context, and it may not be used outside of that context.

// local

default
{
  state_entry()
  {
    integer gNum = 6;
    // gNum == 6
  }

  touch_start(integer num_detected)
  {
    // gNum not defined
  }
}

// global
integer gNum = 6;

default
{
  state_entry()
  {
    // gNum == 6
  }

  touch_start(integer num_detected)
  {
    // gNum == 6
  }
}
Script | Code | Global | Local | Declare


Script Delay

Script delay (or just "delay") occurs when either explicitly requested by the developer (scripter) (via llSleep) or when certain functions execute built-in delays. The effects of the delay caused by either of these is identical; this page will document these effects.

The functions that automatically delay the script, whether the developer requests it or not, are:

Delay (sec) | Function
20 | llEmail
10 | llGetSimulatorHostname
10 | llLoadURL
5 | llTeleportAgentHome
3 | llGiveInventory
3 | llGiveInventoryList
3 | llRemoteDataReply
3 | llRemoteLoadScript
3 | llRemoteLoadScriptPin
3 | llSendRemoteData
2 | llInstantMessage
2 | llParcelMediaCommandList
2 | llParcelMediaQuery
2 | llSetParcelMusicURL
1 | llCloseRemoteDataChannel
1 | llCreateLink
1 | llDialog
1 | llModPow
1 | llOpenRemoteDataChannel
1 | llPreloadSound
1 | llRequestInventoryData
1 | llRequestSimulatorData
.2 | llGetPrimitiveParams
.2 | llOffsetTexture
.2 | llRotateTexture
.2 | llScaleTexture
.2 | llSetLinkPrimitiveParams
.2 | llSetLocalRot
.2 | llSetPos
.2 | llSetPrimitiveParams
.2 | llSetRemoteScriptAccessPin
.2 | llSetRot
.2 | llSetTexture
.1 | llAdjustSoundVolume
.1 | llGetNotecardLine
.1 | llGetNumberOfNotecardLines
.1 | llMakeExplosion
.1 | llMakeFire
.1 | llMakeFountain
.1 | llMakeSmoke
.1 | llRequestAgentData
.1 | llRezAtRoot
.1 | llRezObject
The delay is now indicated in the mouseover tooltip of each function. If you see any new functions which have a delay, please add them to the list above.

Each of these functions performs its task first, then delays the script the set number of seconds.

When a script is being delayed, the event queue still fills, but in a rather peculiar manner:
Event | If triggered during the delay...
touch_start touch_end | it is only executed once after the delay.
listen | it is executed the number of times it is triggered.
link_message | it is executed the number of times it is triggered, up to the maximum event queue size (64)
Please feel free to add more.

The delay between events can also be changed with llMinEventDelay. By default the delay is .1 seconds between the end of an event and when the next event is fired off (the script sleeps).

Tip

For a specific function to not be delayed (any more than it is with its own delay, if any), put it in a separate script.

events | functions


ScriptedDrama

The following nonsense is an e-mail written by a LSL geek to his partner (another LSL geek) after a crazy night.
Feel free to modify this and add whatever you think fits.

Dear baby:

    How is it going? I'm llEmail()'ing you llCos() I have the urgent need to llSay() something.
    You know we're all llSin()'ners, but I did something that makes me want to jump off the llEdgeOfWorld() and llDie().
    It all started when my friend invited us to his club, which had a huge llRound() dance-llFloor().
    There was a guy who kept llShout()'ing on the mic cool things about me. He had this hot latino llTan() and some
    rock-hard llAbs() that made him really attractive. Then he joined the llSameGroup() I was in and started
    llWhisper()'ing naughty things in my ear. He pretended not to like me, but he did llGiveMoney() to me and told me to
    go buy me a drink. I swear I bought something that looked just like plain llWater()! I had no clue it had alcohol
    or that it would make me feel as if I were flying above the llCloud()'s. Before I knew it, he had done his magic
    and llTeleportAgentHome()'ed me. Only thing is... it was HIS home. When I started sobering up and noticed him
    all over me, I said "Hey! llLookAt() that!" and he turned to see what I was llPointAt(). I saw this as an opportunity
    and llUnSit()'ed from his couch as I llPushObject()'ed him. When he came back down and hit the llGround(), I
    llMakeExplosion()'ed his house and watched him burn to ashes and fly with the llWind().

    But, hey! llListen(), I really want to see you again and hope you will forgive me.
    Alright... Time for me to get some llSleep().

    Sincerely yours,

    llGetMass()/2 (Your other half.)


Script Permissions

Scripts need permission from either the owner or the agent they wish to act on before they perform certain functions, such as debiting money from their owner's account, triggering an animation on an agent, or capturing control inputs. The llRequestPermissions function is used to request these permissions and the various permissions integer constants can be supplied.

Functions/Events
llGetPermissions
llGetPermissionsKey
llRequestPermissions
run_time_permissions

Permissions
perm Value | Value | Action | Granter
PERMISSION_DEBIT | 0x2 | take money from agent's account | owner
PERMISSION_TAKE_CONTROLS | 0x4 | take agent's controls | anyone
PERMISSION_TRIGGER_ANIMATION | 0x10 | trigger animation on agent | anyone
PERMISSION_ATTACH | 0x20 | attach/detach from agent | owner
PERMISSION_CHANGE_LINKS | 0x80 | change links | owner
PERMISSION_TRACK_CAMERA | 0x400 | track the agent's camera position and rotation | anyone
PERMISSION_CONTROL_CAMERA | 0x800 | control the agent's camera | anyone
Unimplemented permissions: |  |  |
PERMISSION_REMAP_CONTROLS | 0x8 | permission to remap agent's controls. | Anyone
PERMISSION_RELEASE_OWNERSHIP | 0x40 | permission to release ownership. the concept of public objects has been removed from SL | Owner
PERMISSION_CHANGE_JOINTS | 0x100 | permission to change joints. | Owner
PERMISSION_CHANGE_PERMISSIONS | 0x200 | permission to change permissions. | Owner
unknown | 0x1 | unknown | Anyone
unknown | 0x800 < Value | unknown | Anyone

Permissions | Asset Permissions


ScriptSecurity

Script security how to

Overview

First, you have to understand that there is no such thing as absolute security. The goal is to make breaking the security so difficult that no one will bother. This means that you should use stronger security measures based on how likely someone will want to break into your system.

The method you use to communicate will determine how secure it is to start with.

There are two parts to secure authentication, and you may only need one. The first part is preventing others from seeing what you are saying, and the second part is being able to trust that a message is coming from whom you think it's coming from. Often in LSL, the latter is really the only thing that matters. If you can ensure that your objects will only listen to each other, does it matter if the information they're sharing can be heard by a third party? You can key the key of a talking object by using llGetOwnerKey(key id) provided that the talking object is in your sim or adjacent? V1.10

Chat

There are 4 billion possible chat channels for short range communications, however it is possible to set up scripts to scan all possible channels over a relatively short period of time. You can make scanners almost useless by using one encrypted channel that then changes the channel that the data is passed on. This reduces how much one needs to rely on encryption, which is expensive in terms of script time.

Making keys using Math

You can also use "Psuedo-Random Number Generators" to generate new keys, however if someone finds out which equation and starting key you are using they can then predict what key is next.

This is probably the fastest method of encrypting data since you can use the Psuedo-Random Number Generator to create a "one time pad" for use with the llXorBase64StringsCorrect

Some examples
1) LibraryPseudoRandomGenerator
2)
x = (x * 2) & 0x3FFFFFF ;
if ( ( ( x & (1<<19) ) == 0 ) ^ ( ( x & (1<<27) ) ) == 0 )  ++X;  //Create one Pseudo-random bit in LSB
Not tested needs it's own section

(AlexanderDaguerre) I don't know where the above generator came from, but I'm guessing that it was copied from some other source. However, if so there are at least two typos in the above code. One of them (caps vs. lower case X) makes it not compile, but that's not very important. The second typo is more serious, as it makes the generator repeat every 19 steps once it settles down. As written, the masking with 0x3FFFFFF means that bit 27 will always be 0 after the mask, which reduces this to a single-tap generator. You can verify this by running the following script and noting what it says about the value b and the intermediate values:
default
{
    state_entry()
    {
        integer x = 0x12345678;
        integer i;
        for (i=0; i<100; i++) {
            x = (x * 2) & 0x3FFFFFF ;
            integer b = x & (1<<27);
            llSay(0, "Intermediate state " + (string)x + ", bit 27 is " + (string)b);
            if ( ( ( x & (1<<19) ) == 0 ) ^ ( ( x & (1<<27) ) ) == 0 )  ++x;
        }
    }
}
Finally, you should be aware that this whole class of generator is known to be insecure for cryptographic applications. The outputs of the generator leak too much about its internal state: if you have seen 2n bits from such a system, you can determine both its current state and the tap sequence. You really should not use this generator (even without the typos) to produce key material.

Signing Messages

from Lex Neva
You can also sign messages using llMD5String. This computes a (theoretically) one-way hash on the input, which can be used to "sign" a message. With every message you send, run llMD5String() on the message with the password stuck at the end. The other end will also run llMD5String() on the message it receives with the password it knows about stuck on the end, and if it gets a result that matches yours, it knows that you had the password when you sent the message.

The only problem is that any eavesdropper can get a message that you send, plus the MD5 "signature", and just send that same message again. This can be a security vulnerability in some cases. To mitigate that, you should instead MD5 a string like this: "<my object key>|<password>|<message>". The other end will do the same to check the authenticity of the message. Since (as far as I know), it's impossible in SL to spoof the source of a message that's received in a listen() event, this is much more secure.

Creating truly random numbers

The llFrand function will return random numbers; however, the numbers it returns will only be truly random if some other random numbers are generated in between in a random fashon. So grab a random number and then another and discard some based on some other event like a timer if you absolutely have to have a truly random number.

Functions

Function | Useage
llXorBase64StringsCorrect | Allows you to use "one time pads" to encrypt data.
llXorBase64Strings | More info on above.
llFrand | Used to create random numbers.
llModPow | Very weak and too slow for most uses.
llMD5String | Used for signing messages

Contributors

grumble Loudon, LaserFur Leonov, Lex Neva, Thraxis Epsilon, Hank Ramos, Hewee Zetkin, AlexanderDaguerre

Threads

http://forums.secondlife.com/showthread.php?t=126143

Notes and To Do


second

A SI (metric) unit of time defined by how long it takes for Cesium 133 to display 9,192,631,770 cycles of radiation.

Units


Second Life

Second Life (SL), created by Linden Lab, is an online, chat, build, VR (virtual reality), 3D environment that allows users to make objects, use custom textures and sounds, terraform land, and script object behaviors using Linden Scripting Language.

"The greatest thing-that-is-not-a-game of all time."

Official website: http://secondlife.com

Note: see newview for secondlife.exe client command-line options.

SL | LSL | Lindens


SecondLifeDirectory

This is a folder on the client's hard drive. It is, by default, C:\Program Files\SecondLife\ , but can change based on install parameters.

This isn't the case on a Mac, of course.

should this be in the LSL wiki? - WG -- I don't think so :) -- Comic Relief perhaps?


Self-Replication

In a worst case scenario, self-replicating objects will get you banned from SecondLife. Usually because they can bring down simulators and/or harass other players. Self-replicating objects that are an annoyance of any fashion are against SecondLife's community standards.

So far, every noticeable case of uncontrolled or excessive self-replicating objects has resulted in threats of banishment from SecondLife. Except in the latest instance where the user was perm banned.

Self-replication requires the use of llGiveInventory. This function call has a built in delay of 3 seconds each time you call it.
It also requires the use of llRezObject, which has a built in delay .1 seconds.

How it's done

See Example of Self Replication

WARNING

To read an example of self-replication gone bad, click here, here, and here. This is just the most recent example, and others involved ants and "green slime".

If you're still looking to perform self-replication experiments, here are a few suggestions:

Precautions

Test everything first. Run test case scenarios where you code in limits on how far the objects can travel. Test things in the island sandbox first.

There are several things that can ensure that you do not get abuse reports if you ever do make a self-replicating object. If you're planning on releasing your script 'in the wild', implement at least two of the following:

  • Add a ttl counter - a counter that counts down one with every replication, and stops replication if the counter reaches zero. This is the most BASIC precaution. An example of this is found in the example of self replication

  • Slip in a distance and/or simulator limitation. This would be especially useful when you have the replication under control and predictable, and want to see what people's reaction to the object will be. Release the object in a single sim only, and kill off the object if it ever leaves that simulator. This will give you a reasonable judge of how people will view your object (Annoyance? Cute? Abuse report worthy?) and will also give you a smaller area to search if you ever have to clean up the objects quickly.

  • Create a function that tests to see if it's after a certain time. If it is, the object should kill itself.

This code kills the object if it's not it's day. (Every dog has one, but necessarily not today)

string daytolive = "2005-07-15";    //The one day that this will be alive for

integer failsafe()            //Are we allowed to live today?
{
    if ( llGetDate() != daytolive )
        llDie();
}

Of course, it is possible to make it live for two or more days by using ORs.

If you want to specify an even narrower life-scope you could check for a certain time-of-day.

integer dieByTime = 120;

failsafe()
{
    if(llGetWallclock() > dieByTime)
        llDie();
}

The above script would kill off the object if the failsafe() function is called any time after 1AM, server time. llGetWallclock is handy for this sort of work.

Common to both, is; be SURE to hardcode the die-by time into the script, rather than letting the script figure out when it should die by on it's own. Yes, it means you having to change the "constant" each time you want to save your script, especially if you're being smart and limiting your tests to five minute intervals, but it's a lot better than having a thousand objects creating copies of themselves every four seconds.

Observe that if you do it in the self-replicating script itself, you MUST put this failsafe function call at the beginning AND end of every event, inside of every do, for, and while loop, and pretty much everywhere else in the script, in order to avoid the test not ever being reached because of infinite loops. (i.e. If your script gets stuck in a while() loop that doesn't have the failsafe, since it's never leaving the event, nor is it ever going anywhere else, it'll never test the failsafe condition.)

Most definitively you should do this test before rezzing the offspring.

  • Add a watchdog timer. The watchdog calls the failsafe function regularly.

  • Have the object listen on a channel for a death command. For example, you could make it listen on channel 300 for anyone saying "die". This might lag the sim up, but if you're making a self-replication script you really shouldn't don't mind that much. This would also make it easy to make something that would ensure them to not bother you.

  • Make the object cost $0L, and set the buying option to "original". This way, people will be able to buy and delete the offending objects themselves.

ExampleSelfReplication


semicolon

;
A semicolon is a colon with the bottom dot replaced with a comma.

In LSL, the semicolon indicates the end of a statement. For example, in this code fragment, a semicolon indicates the end of the command.
llSay( 0, "Hello, Avatar!" );

Because a semicolon indicates the end of a statement, and not whitespace, this means that you can have one statement on multiple lines. This is extremely useful for very long commands, such as llParticleSystem ( like this ).

Any decent modern programming language uses semicolons, or something equivalent, to indicate the end of a statement.

Glossary


Serialization

"...serialization is the process of saving an object onto a storage medium (such as a file, or a memory buffer) or to transmit it across a network connection link such as a socket either as a series of bytes or in some human-readable format such as XML. The series of bytes or the format can be used to re-create an object that is identical in its internal state to the original object (actually a clone). This type of serialization is used mostly to transport an object across a network, to persist objects to a file or database, or to distribute identical objects to several applications or locations.

  • This process of serializing an object is also known as deflating an object or marshalling an object.
  • The opposite operation, extracting a data structure from a series of bytes, is deserialization (which is also referred to as unmarshalling or inflating)."
- Wikipedia entry on Serialization

Most any kind of remote procedure calling(RPC) involves some sort of serialization. In fact inter-script communications in SecondLife is essentially RPC. XML-RPC in SecondLife only seems to support strings and integers.

The example script ExampleListConversion provides a rudimentry sort of serializtion for sending the values of variables between scripts over chat channels. However this script does not address the precision loss that occurs when floats, vectors, and rotations are typecast to strings. This page exists to make LSL scripters aware of this issue, to find a method that can serialize these types with out precision loss, and maybe to champion the cause of having LindenLab create built-in LSL functions to facilitate serialization(especially floats, vectors, and rotations).

Serialization of other types

As far as I know integer, string, and key do not experience any kind of data loss when typecast to and from string and are thus not considered priorities for special serialization support.
lists on the other hand are a whole other animal all together and is in fact what the ExampleListConversion script attempts to seralize.

What would a built-in LSL function to help serialize floats (and consequently vectors, and rotations) look like?

Such a function would probably look and perform like the Float.floatToRawIntBits(float) and Float.intBitsToFloat(int)functions from the Java standard library. This would allow us to typecast the resulting integer to a string and then back to integers on the recieving side and then subsequently converted to the exact same float value that was sent. These functions should be fairly straight forward and relatively simple for LindenLab to implement.

Float Serialization Hack - First Idea - Strip and Multiply

First the exponent of the float would be recorded. Then some method of stripping and recording the most significant digit from the float and then subtracting that from the float then multiplying the float by ten. The process would be repeated until one was reasonably sure to have gotten all significant digits stored in the float. Then the significant digits along with exponent would be encoded into some sort of string representation that could be spoken on a SecondLife chat channel. On the other end the float would be reconstituted possibly by dividing an integer by the exponent typecast to a float. Anyone up for creating an end to end implementation of this? This hack seems like it would be really inefficient. I challenge all you geniuses out there to come up with a more efficient way to do this. Also I would guess there would be some examples where the float does not reconstitute exactly the same on the recieving end. And of course this would not even begin to handle NaNs properly.

BlindWanderer posted LibraryFloat2Hex which seems to get the job quite nicely. I have yet to test it out but it looks like it should do the trick. Thanks BW.
If you want the float-string to be in scientific notation you should see Float to Scientific Notation but be warned, it's slow, for anything that needs speed, you should use LibraryFloat2Hex - BW

I've posted code at LibrarySerialize which uses BW's LibraryFloat2Hex and is pretty much-based on ReadyJack's original code, with a couple of implementation changes. It seems to be holding up pretty well, but I could use some more people hammering it to work out bugs/performance issues. - ShadowGretzky

Q: What is a NaN, as reference three paragraphs above? - St. Psaltery
A: See NaN.


Server

A server is a computer that "serves" data to clients like Second Life. SL servers make up its grid and include simulators and asset servers.

Resources
  • server resources, Andrew Linden, 3/16/05
  • Web 2.0 and Databases Part 1: Second Life

Client | User


Shadow

Note: Object-cast shadows have been temporarily removed as of SL 1.10.

SL's "soft" ("fuzzy", "blocky", aliased, low-resolution) shadows are automatically created for the sim's daytime single light source and are based on the prim's overall size and shape (before tweaking--scale and rotation being the only things that affect shadow shape). Shadows darken the ground, other prims, and avatars (which also have 2 "foot" shadows made from masked textures). Light will lessen all shadows except avatar foot shadows.

Avatar shadows can be removed:
  • Via the Debug menu option Rendering > Types > Alpha - Ctrl-Alt-Shift-2. However, this also disables all transparency and masked textures).
  • In "C:\Program Files\SecondLife\app_settings\viewerart.ini" by changing the value of the line that starts with "foot_shadow.tga" from the key "14e8a47d-1055-0a68-5d55-eafd9ad3da5b" to "11ee27f5-43c0-414e-afd5-d7f5688c351f" (a fully transparent image), and then restarting the client.

Runitai Linden, 5/5/06: Shadows are baked into vertex colors. Only light from the sun is affected by shadows. The updating of shadows happens gradually, updating only a handful of objects per frame, and an object will lose its shadows whenever it changes level of detail, moves, or something between it and the sun moves.

Shadows for moving objects and avatars are rough approximations based on a 5-point sample around the object. Attachments take the shadow value of the avatar they are attached to.

The shadow an object casts is based on its bounding box. To see the bounding box, turn on the octree info display from the debug menu.

Light | Ground | Avatar


shadows

shadow

SL's "soft" (fuzzy) shadows are automatically created for the sim's daytime single light source and are based on the prim's overall size and shape (before tweaking--scale and rotation being the only things that affect shadow shape). Shadows darken the ground, other prims, and avatars (which also have 2 "foot" shadows made from masked textures). Light will lessen all shadows except avatar foot shadows (which can be removed via the Debug menu option Rendering > Types > Alpha - Ctrl-Alt-Shift-2 though this will hide alpha textures as well).

Avatar shadows can be removed by changing the value of the line that starts "foot_shadow.tga" from "14e8a47d-1055-0a68-5d55-eafd9ad3da5b" to "11ee27f5-43c0-414e-afd5-d7f5688c351f" in the "viewerart.ini" file found in the directory "C:\Program Files\SecondLife\app_settings\"; and finaly restarting the client.

light | ground | avatar


Shape

Shape is the basic form of a prim. Currently available shapes are: box (cube), cylinder, prism, sphere, torus, tube, and ring.

Shape can be changed using the built-in editor or via scripting with the llSetPrimitiveParams function.

Tip: To get a perfectly flush half-cut shape (except for prisms but, specifically, for boxes), use .125 and .625 in the cut begin/end fields. For prisms, .158 and .635 work well. Of course, standard increments like 0,.5 and .25,.75, etc, work well for other evenly cut shapes.

Prim | Object


shear

Links

  • SL History Wiki: shear


Shiny

Shine, or reflection, determines how strongly the current sky texture (and water, if the prim is over water) is blended onto the faces of the prim. This effect is also known as environmental texture mapping.

Depending on your interpretation of quantum physics, there may exist some alternate universe where it is computationally feasible to make this option actually make the faces of a prim reflect the world around them like a mirror, but due to the design of SL's 3D engine and the constraints of current computing technology, it is not possible in this universe...at this time. Real-time 3D rendering technology is rapidly advancing...

The shininess of a prim can be controlled with llSetPrimitiveParams.

Enabling reflection will cause the prim/side to darken more with each reflection setting (low, medium, high).

Prim


Side

A side is one face of a prim. Each side has a color, opacity, and texture.

A prim's number of sides can be determined with llGetNumberOfSides and affects all sides with the constant ALL_SIDES. To affect multiple sides (but not using ALL_SIDES), set each prim face individually by making more calls to the texture function (may not work depending on function).

There are some quirks with this: for example, the inside of a hollowed cube counts as one side (even though there are really 4 sides).

One way to get a side number is: enable the debug menu (if it is not displayed to the right of the "Help" menu, press Ctrl+Alt+D). Use the "Select Texture" edit tool, select the side, and press Ctrl+Shift+Alt+T. This will cause SL to show some basic texture information, including the side number.

Texturing sides so they look the same (particularly in hollowed prims) can be frustrating.

Functions

Besides the texture functions, there are other side-specific ones:

llGetAlpha
llSetAlpha
llGetColor
llSetColor
llSetLinkAlpha
llSetLinkColor
llGetNumberOfSides
llGetPrimitiveParams
llSetPrimitiveParams

Primitive Side Numbering

On an axis-aligned box:
0 - top
1 - lesser y
2 - greater x
3 - greater y
4 - lesser x
5 - bottom

Hollow:
0 - top
1 - lesser y
2 - greater x
3 - greater y
4 - lesser x
5 - hollow
6 - bottom

Cut:
"begin" starts at lesser x and y, cut increases counter-clockwise
"end" starts at lesser x and y, cut increases clockwise

0 - top
1 - lesser y
2 - greater x
3 - greater y
4 - lesser x
5 - bottom
6 - cut begin
7 - cut end

Hollow-Cut:
0 - top
1 - lesser y
2 - greater x
3 - greater y
4 - lesser x
5 - hollow
6 - bottom
7 - cut begin
8 - cut end

Primitive


Sidebar

QuickReferenceGuide

Search the Wiki
LSL Style Guide
Script Library
Examples
Protocol Exchange

LSL Reference:
for, do-while, if-else, jump, return, state, while
Functions
Operators:
assignment, binary, bitwise, boolean, unary
States
Types:
integer, float, vector, rotation, key, string, list
Variables

Function Categories:
Agent/Avatar
Collisions
Communications
Dataserver
Detection
Dynamics
Inventory
Links
List
Math
Particles/System
Primitives
Sensors
Simulator
Sound
Strings
Targets
Textures
Transformation:
Time
Vehicles
Video


Sign

A sign is the property of a number representing its direction from zero. Negative numbers are prefixed with the sign "-", as in "-1". Positive numbers can be prefixed with the "+" sign, but are usually left alone. Naturally, zero has no sign.

To change a number's sign in LSL, the unary operator "-" can be used.

Example: integer number=1; number = -number;
Number will be -1.

integer | types


Sim

See Simulator.


SimNames

Simulators have unique names. See the bottom of this page for a complete list.

So far, these names follow certain schemes:

  • The first sims (Bonifacio, Shipley, Natoma, etc.) are named after streets (52KB PDF) in the San Francisco south market area.

  • The northern sims (Argent, Purple, Tan, etc.) are named after colours.

  • The vehicle sims (Balance, Brilliance, Cordova, etc.) are named after ships buried below San Francisco.

  • The southern sims (Stinson, Davenport, Baker, etc.) are named after beach cities in California.

  • The newer southern sims (Phobos, Deimos, Tetheys, etc.) are named after moons in the solar system.

  • The Nova Albion sims (Barcola, Miramare, Sistiana and Grignano) are named after coastal towns near Trieste, Italy.

  • The sims in northeast (Aluluei, Riiki, Quat, etc.) are named after Micronesian Gods.

  • The sims in the east-southeast (Amida, Baku, Yamato, etc.) are named after Kami, divine entities of Japanese mythology.

Maps

List of Sims:
Abbotts - home of the Abbotts Aerodrome (if it flies, there's probably a version of it there)
Ahern - welcome area where new residents arrive after Orientation Island
Akea
Albion
Aleph
Alua
Aluluei
Amaterasu
Ambat
Amida
Anton
Anzere
Apukohai - llGetRegionName actually returns the name as 'Apukohai ' (with a space after the name)
Aqua - a resident-run shopping district
Argent
Arlberg
Arosa
Asim Zahra
Atlas
Avalon - spacethinkdream.com
Ayas
Baker
Baku
Balance - special region for driving vehicles
Barcola - part of Nova Albion
Benten
Bethel - special region for driving vehicles
Bisque
Bitrate City
Black Tarn
Blue
Boardman
Bodega
Bolinas
Bonifacio
BonnyDoon
Brilliant - special region for driving vehicles
Brown
Butsu
Buyeo
Bydalen
Caldbeck - Home of the Caldbeck Airstrip!
Callisto
Cape Destiny
Cayman - home of Elysian Island
Celadon
Chamonix
Chartreuse
Clara
Clementina
Clyde
Cordova (Sandbox)
Cortina
Cowell
Crimson
Da Boom - The first sim the Lindens ever misspelled as well as the first sim to exist, now preserved for posterity. Ironically, their offices are next to De Boom Street in San Francisco. Apparently, nobody noticed the error for a couple years.
Daikoku
DarkWood
Davenport
Davos
De Haro
Deimos
Devel - a hidden sim only accessible by Lindens
Dore
Dosojin
Ebisu
Enceladus
Epimetheus
Europa
Fame - special region for driving vehicles
Federal
Fortuna - special region for driving vehicles
Freelon
Fuchsia
Fudo
Fujin
Funadama
Galleria City
Gama
Game Dev 1 - one of two regions hosting the Game Developer's Competition
Game Dev 2 - same as Game Dev 1 These seem to be gone now, or at least they were down when I was updating this list. :(
Ganymede
Garmisch
Georgean - special region for driving vehicles
Gerstle
Gibson - Full of jerks!
Ginko - A shopping/residential sim run by Ginko Technologies
GNO ILAND
Gray
Grass
Green
Grignano - part of Nova Albion
Grindlewald
Gualala
Hachiman
Hawthorne
Hiaka
Hikuelo
Hina
Hooper
Immaculate
Indigo - this was the first simulator on the mainland auctioned whole, bought by FlipperPA Peregrine.
Io
Isere
Janus
JAVA
Jenner
Jessie - this entire region is a not-safe combat region
Jizo
Kaili
Kaminari
Kanin
Kelham
Kissling
Kitzbuhel (from the Kitzb???hel Alps)
Kojin
Koleamoku
Krittannia
Ku
Kumori
Kuula
Kymeera
Laka
LaLa Land
Leda
LiaisonLand - a hidden sim only accessible by Lindens
Lie
Limantour
Lime
Livigno - home of KDC
Luna - home of the Luna Oaks Galleria shopping mall
Lusk - home of Lusk Estates (newbie housing) and Luskwood (combining Lusk and Wood since 2004!)
Magenta
Mahulu
Manua
Marawa
Maroon
Marunogere
Maui
Mauve
Mavericks
Meribel
Mesede
Metis
Mimas
Minna
Miramare - part of Nova Albion
Miru
Mocha
Mokualii
Montara
Montmartre
Mooaleo
Moritz
Morris - part of the welcome area, and the newbie sandbox
Muir
Myrtle
Nakaa
Natoma
Nowall
Noyo
Oak Grove - home of Busy Ben's vehicle lot
Oberstdorf
Olive - former sandbox, now a mature residential sim. Was the first testing ground for prim limits
Olopue
Ordino
Orelle
Orientation Island 1 - where residents arrive the first time they login to SL
Orientation Island 2 - same as Orientation Island 1
Paikea
Palomarian
Palulop
Pandora
Papa
Periwinkle
Perry
Phobos
Plum
Poliahu
Pomponio
Puea
Purple
Quat
Raiden
Rausch - a mature-rated combat sandbox, not-safe
Riiki
Ritch
Rizal|Sports - contains Linden-run games, including lazer tag and sumo-sphere
Rodeo
Rogla
Rose
Rua
Sage
Seacliff
Seefeld
Shaka
Shinda
Shipley
Shoki
Shomat
Sido
Sistiana - part of Nova Albion
Slate
Solang
Stanford
Stillman
Stinson
Taber
Tan
Tawhaki
Teal
Tehama
Telador Isle - an island sim owned and themed by former URU Live players.
Tenjin
Tethys
Tilitr
Toggenburg
Uba
Ukanipo
Uli
Umber - Midnight City, Home of the best retailers in Second Life!
Uzume
Valmorel
Vaoetere
Vari
Varney
Verbier
Voss
Welsh
Wengen
Yabune
Yamato
Zermatt
Zoe

List of Sims which no longer exist:
Coney - A "theme park" region where tax-free land was granted for popular rides and attractions. Its partner, Luna, was repurposed for use as a shopping centre.
Game Dev 3
Game Dev 4


Simulator Names

This is about 90% of the simulators (missing about 10-20):

7th Heaven
Aasim
Abacus
Abbotts
Abitibi
Abraxas
Achemon
Acrea
Adelphia
Adriatic
Aegean
Afton
Agamok
Agapema
Ahern
Aich
Ainu
Akea
A'ksha Caves
A'ksha Dunes
A'ksha Mesa
A'ksha Oasis
Albata
Albion
Albipectus
Alchera
Alcona
Aleph
Alice
Alleni
Allow Life
Allure
Alompra
Alope
Alston
Alta
Alua
Aluluei
Alviso
Amaterasu
Ambat
Ambleside
Ambulyx
American Samoa
Amicus
Amida
Amplypterus
Amundsen
Andraca
Andromeda
Antigua Bay
Anton
ANWR
Anzere
Apukohai
Aqua
Arafura
Arcata
Arena
Aretias
Argent
Arlberg
Arosa
Asim Zahra
Aswan
Asylum Island
Ataraxis
Athabasca
Athena
Atis
Atlantis
Atlas
Austroplacida
Avalon
Avery
Awa
Ayas
Azov
Azrael's Manor
Badger
Baffin
Baileya
Baird
Baker
Baku
Balance
Balkanski
Baltic
Banda
Barbados
Barcola
Barents
Barton
Batak
Bear
Beauty
Bedazzle Studios
Bedford
Behrii
Beibu
Beirne
Bella
Belleville
Belmondo
Benham
Benicia
Benmore
Benten
Bering
Besma
Bethel
Beverly Hills
Bhaga
Bhima
Biscay
Bishop
Bisque
Bizzare
Black
Black Tarn
Blackslough
Blair
Bliss
Blue
Blue Stone
Blue Lagoon
Boardman
Bodega
Bohai
Bohol
Bolinas
Bombyx
Bonga
Bonifacio
BonnyDoon
Bora Bora
Borden
Boreal
Borrowdale
Bothnia
Bouraq
Bowfin
Bowness
Bradmoor
Bragg
Brampton
Brauer
Brauni
Braunworth
Brazos
Brenner
Bretton
Brilliant
Brindamoor
Britannia
British Isles
Brooks
Brown
Brownlee
Bruin
Buckler
Burandt
Burns
Busiris
Butler
Butsu
Buyeo
Bydalen

Cabinhead
Caddo
Caldbeck
Caliente City
Callahans Isle
Calleta
Callisto
Canfield
Cape Destiny
Capelli
Carlisle
Carnforth
Carolina
Carpentaria
Cartmel
Casa De LaLa
Cass
Catocala
Cavanaugh
Cayman
Cayuga
Cecropia
Celadon
Celebes
Celestial
Celerio
Central Park
Cerisyi
Chamonix
Champlain
Chaos
Chaotica
Chartreuse
Chaska
Chatsubo
Chicagon
Chief
Chinati
ChinaTown
Chocolate City
Chong
Chukchi
Cichlid
Cincta
Cisco
City of Tharna
Clara
Clarksburg
Clay
Clear
Cleary
Clementina
Cloudmont
Clunn
Clyde
Cochran
Coelho
Colona
Columbia
Coniston
Cook
Cordova(Sandbox)
Cortez
Cortina
Cottonwood
Cowell
Cranberry
Crescent
Crimson
Cristat
Crooked
Crystal
Crystal Brooke
Cub
Cyclops
Cyldane
Cypa
Cythera
Da Boom
Dacia
Daikoku
Dalton
Daphnis
Dari
DarkWood
Davenport
Davos
Davvi
De Haro
DeCheau
Dedi
Deep Soul
Deep Woods
Degar
Deimos
Derwent
Desire
Desires Island
Devel (private)
Devils Moon
Devotion
Didugua
Dierli
Digifolks
Disires Island
Disturbing World
Disl
Donders
Dore
Dosojin
Dowden
Doyle
DragonFly Oasis
Dragon Moon
Drawbridge
Dream Space
Drum
Dryas
Duck
Dunlevie
Durango
Dusun
E City
Eagan
Eaton
Ebisu
Eclipse
Ecstasy
Ecstasy Mall
Eden
Edgerley
Egremont
Einstein
Elar Imerra
Eldamar
Eldora
Electra
Elf Haven
Elibia
Elite Island
Ellis Island
ElvenGlen
Embu
Encanto Isla
Enceladus
Enchanted
Epimetheus
Erie
Eros
Erotica

Escape
Ess
Eternity Isle
Euphoria
Europa
Euryalus
Evans
Extasy
Faded Reality
FairChang Island
FairChang Project
Fairytale Island
Falcata
Falconi
Fame
Fantasy Island
Farwell
FataleKlein Island
Federal
Ferne
Ferox
Feted
Fiji
Fina
Fletcher
Flores
Fortuna
Fourmile
Freedom
Freelon
Friesland
Fritts
Frontenac
Fuchsia
Fudo
Fujin
Funadama
FurNation Alpha
FurNation Prime
Furness
Furu
Gali-Gali
Gallinas
Gama
Game Dev 1
Game Dev 2
Game Dev 3
Game Dev 4
Game Dev 5
Ganymede
Gar
Garmisch
Garrison
Garuda
Gealain
Gemella
Gemmed
Geneva
Geometer
Georgean
Georgiana
Gerstle
Gibson
Ginko
Giovanni
Glastonbury
Glorious Ar
Glow Estates
GNO ILAND
Goddess Island
Gogebic
Goguen(Sandbox)
Gotland
Gourdneck
Grace
Grange
Grant
Grasmere
Grass
Gray
Green
Griffin
Grignano
Grindlewald
Grishin
Grizedale
Grote
Grouse
Gualala
Gun
Gunda
Gurley
Guthrie
Gwynedd
Gypsy Moon
Hachiman
Hagen
Hahne
Halfmoon
Hamlin
Hampshire
Hangzhou
Hanson
Hantu
Haploa
Hardknott
Hartwick
Harvey
Haven
Hawaii
Hawkshead
Hawthorne
Hazeltine
Healy
Heaton
Heaven
Helvellyn
Hennepin
Hera
Hiaka
Hibdon
Higgins
Hikuelo
Hina
Hite
Hoffman Designs
Hofzinser
Hogadon
Hogback
Holt
Honawan
Honeoye
Honister
Hoodoo
Hookton
Hooper
Horseshoe
Hotei
Houghton
Hubbard
Hudson
Hughes
Hume
Huron
Hydes
Hyles
Hypatia
Iaconelli
Ibiza
Igbo

Ilideu
Illusion
Illusion Island
ImagineNation
Immaculate
Imperial
Inari
Indigo
Inga
Innu
Io
Ionian
Iris
Ironjaw
Isabel
Isere
Island Sandbox
Isle of Bliss
Isle of Delphi
Isle of Tempest
Isolation
Isora
Istar
Itaipu
Ithys
JAVA
Jaffee
Jamaica
James
Janus
Jarawa
Jenner
Jernberg
Jessie
Jewell
Jiminy
Jizo
Joiner
Jouppi
Juanita
Kafiri
Kagle
Kahului Hideaway
Kaili
Kalanag
Kama
Kamba
Kaminari
Kanaloa
Kane
Kania
Kanin
Kapor
Kara
Kariba
Kawishiwi
Kasba
Keanu Island
Keener
Kekekbaic
Kelham
Kendall
Kerchal
Keswick
Keuka
Key West
Kiha
Kirkby
Kirkstone
Kishijoten
Kissling
Kitchen Korner
Kitzbuhel
Knieff
Kojin
Koleamoku
Kor-Azor
Korriban
Koss
Kovari
Kremer
Kress
Krittannia
Ku
Kumori
Kuula
Kymeera
L'Utopie
Lajonquierea
Laka
Lakeville
LaLa Land
Lane
Lanercost
Laptev
Larsen
Lar Torvis
Las Vegas
Le Cadre
Lebeau
Lebeda
Leda
Leipzig
Lemon
LiaisonLand
LiaisonLand2
Liberty
Lida
Lie
Ligurian
Lily
Limantour
Lime
Lineata
Lippert
Little Italy
Live2Give
Livigno
Loepa
Lonsdale
Loon
Lotus
Louise
Love dAlliez
Loveland
Lovely Thoughts
Lozi
Lucidity
Luna
Luo
Lusk
Maceday
Maculata
Mactaquac
Magenta
Mahulu
Mallard
Mallorca
Malmoe
Maloney
Mandrake
Manitoba
Manua
Marawa
Mare
Margery
Marmara
Maroon

Marunogere
Maryport
Matsushima
Mature Sandbox
Maui
Mauve
Mavericks
Maxim District
McFee
Meins
Menagerie
Meribel
Mesede
Metanastria
Methuselah Isle
Metis
Metropolis
Meyers
Miami Beach
Micronesia
Midgard
Midge
Midnight City
Mieto
Miller
Milu
Mimas
Minerva
Minna
Mirage
Miramare
Mirtoon
Miru
Missauke
Misso
Miyajima
Mocha
Modest
Moen
Mohr
Mokualii
Monaco
Montara
Montezuma
Monti
Montmartre
Mooaleo
Moonlit Skies
Moraine
Moritz
Morris
Mowry
Msitu
Mud
Muir
Muirhead
Mullett
Munck
Munro
Murray
Mykonos
Myrtle
Nakaa
Natoma
Naughty
Nauru
Nautilus
Naveh
Navora
Nefrax
Nepessing
Nessus
Nestor
Netherbeck
Neualtenburg
Neumoegen
Neverland
New Hamlet
Newton
NewYork City
Newcomb(Sandbox)
Newman
Nipigon
No
Noyo
Nuba
NULL
Numbakulla
Nuttall
Nyterave
Oak Grove
Oberstdorf
Oblivion
Obscure
Oculea
Oehler
Oeland
Ohno
Okhotsk
Okinu
Olde London
Olive
Olopue
Omidyar
Ontario
Ordino
Orelle
Orient
Orientation Island 1
Orientation Island 2
Orientation Island 3
Orientation Island 4
Orientation Island 5
Orientation Island 6
Orion
Orwood
Oslar
Oslo
Osterhout
Owasco
Oyster
Paikea
Palau
Palomarian
Palulop
Pando
Pandora
Panther Estates
Panther's Creek
Papa
Paradise
Paradise Family
Parce
Paroo
Parrot Island
ParrotHead Cove
ParrotHead Isle
ParrotHead Island
Passion
Patagonia
Paw Paw

Peace
Pearl
Penan
Penrith
Periwinkle
Pero
Perry
Petite Gaule
Phantasie Isle
Pheosia
Phobos
Phoenix Isle
PI2
Pickerel
Picnic
Picus
Pierce
Pimushe
Pine
Pippen
Plano
Plum
Plush
Poliahu
Pomponio
Pooley
Porcupine
Port Cos
Port Kar
Portage
Private Island Template 1
Provincetown
Pruetti
Pryeri
Puea
Purden
Purple
Purple Butterfly
Quat
Quentin
Rachleff
Raiden
Rainbow Island
Rapture
Rausch
Ravenglass
Reality Faded
Redear
Reflection
Refugio
Remembrance
Riiki
Ritch
Rizal|Sports
Robinson
Rodeo
Rogla
Rose
Rosedale
Rosieri
Ross
Rothenburg
Roxy
Royal
Royal Island
Rua
Rue dAlliez
Rustic
Rutz
Rydal
Sables dAlliez
Sabulella
Sage
Sakai
Sami
Samia
Sammamish
Samoa
Samurai Island
Sanctum Sanctorum
Sanctus Noctum
Sand
Sandra
Sands dAlliez
Sargasso
Sawdel
Sawrey
Scafell
Seacliff
Seascale
Secret
Sedig
Seefeld
Selby
Semang
Seneca
Serenite
Serenity Island
Seto
Severa
Shaea
Shaka
Shamrock
Shelef
Shelter
Shepherd
Shinda
Shiner
Shipley
Shoki
Shona
Shoofly
Sicily
Sido
Sidra
Sikkima
SimCast
SimQuest
Simcoe
SinCity
Sistiana
Skari
Skegemog
Skiddaw
Slate
Slootsville
Slosser
Smith
Smoky
Solang
Solomon
Solway
Sorriesa
Southern Ar
SOUTH I
South Paris
Sphinx
Spittoonie
Spurlock
Stanford
Star
Stardust
Steamboat
Steiger

Stillman
Stinson
Stump
Suduffco
Sugarloaf
Suisun
Sulu
Sundana
Sunrise
SupportForHealing
Superior
Sutherland
Suzhou
Svan
Swain
Sweetbay
Sylvia
Taber
Takalo
Takes
Taco
Tahiti Isle
Talese
Tamarack
Tamsi
Tan
Tantalum
Tasman
Tavarua
Tawhaki
TeaZer Isle
Teal
Tecta
Tehama
Telador Isle
Tenjin
Tenney
Tequila Isle
Terminous
Terminus
Tethys
Thams
Tharu
Thayer
The Claddagh
The Forest
The Future
The Plains
The Port
The Underground
The Wild
The Wilderness
The Woods
The World
Theater District
Themiskyra
Threemile
Thunberg
Thurston
Thyris
Tiger Creek
Tijuana
Tilitr
Timherry Island
Timmerman
Tipsico
Tiretta
Titan
Tofalar
Tofte
Toggenburg
Tokelau
Tompson
Tonga
Torch
Trabala
Tranquility Cove
Transylvania
Tranquility
Tropical Island Deck
Trouble
Tuliptree
Tupi
Tuvalu
Tuvan
Tyrrhenian
UNCEM
Uba
Ukanipo
Uli
Ulverston
Umber
Undulosa
UNLIMITED
Urdu
Utopia
Uzume
Vail
Valmorel
Vampire Empire
Vanuatu
Vaoetere
Vari
Varney
Vashti
Velda
Velox
Veranderde
Verbier
Verloren
Verlorendam
Victoria
Vida de Sol
Vine
Vixens Isle
Voss
Voti
Wakeley
Wall
Walleye
Wards
Warmouth
Waterhead
Waved
Weber
Welsh
Wengen
Western Ar
Westport
Wetheral
Whinlatter
Whiskey
Whitestone
Whitlock
Wichi
Wilmot
Windermere
Wingo
Winnipeg
Winyah
Wixom
Wolof
Yabune
Yamato
Yawgoo
Yelas
Yora
Zaharik
Zazi
Zephyr
Zermatt
Zoe
Zolezzi
Zouk City
Zuni

Sims Pending Removal
Azrael\'s Manor
Amaterasu
Avalon II
Backstage
Cape Destiny
ChinaTown
Frisland
FataleKlein Island
Haven
Monaco
Nowall
Private Island Template 1
PI2
The World
Tropical Island Deck
Utopia

Sims that return the wrong location:
PI2
NULL
Heaven
Kanin
Sand


sine

Sine is a trigonometric function, llSin, when given the angle between the x axis and an object, will return the ratio of the opposite side (y value) and the hypotenuse (distance between you and the object).

The inverse function to sine is arcsine.

cosine | tangent


Sit

Avatars can sit on objects and the ground. Avatars cannot sit on attachments. (One way to fake this would be a separate object that "followed" the target avatar using a sensor and llMoveToTarget, but there would be a noticable delay when the other avatar moved.)

Sitting on an object causes the avatar's mass to be added to the object.

Functions

Function | Description
llAvatarOnSitTarget | Returns the key of the avatar sitting at the current sit target.
llSetSitText | Sets the Sit text on the pie menu.
llSitTarget | Sets the position the avatar will sit at.
llUnSit | Forces a sitting avatar to stand up.

Events

changed: raised (with the CHANGED_LINK flag) when an avatar sits/unsits on an object.

Q: How do I force an avatar to sit on an object?
A: You can't. You can only force them to stand up. It is possible to animate an avatar so they look like they're sitting, but not to control their position with much precision or reliability, at least not without using attachments.

Q: How do I find out the key or name of the object a given avatar is sitting on?
A: The short answer is, you don't. In theory, you could do something with sensors, but this could return many, many false positives.

Agent/Avatar | Target


Skin

A skin is a texture applied on an avatar.

Avatar


SkyHoney

"Note: This function delays the script for 0.2 seconds."

Why?

A: Because who ever developed the API decided that implementors of the API should not be able to call this function more often than every 0.2 seconds. A kind of abuse prevention measure. Whether or not it is the best way to prevent abuse is hotly debated in some circles, as are most of LindenLab 's abuse prevention measures especially the ones in LSL.


SL

SL is the abbreviation for Second Life.

Second Life | Linden Lab


SLNTC

The Second Life National Terror Core

Militant group invested in the practice and study of combat in SL.

Lead by Master Ely Kilby, the "core" is constructing a leage of professional warriors of many classes.

"We will gladly take in all willing learn and serve in the tatical arts."
-Ely

Here, we have a need for new and talanted scriptors/builders. We do offer lessons, so almost anyone can join.

*All those interested, please contact me for a recruitment form.

Sub-Divisions


SmileyScripts

ssExecuteCommand(string cmd, integer num, string str, key id)

Sends cmd, num, str, and id to the SmileyScripts file, from there it is sent to the Mainframe Server, and the server processes the command by identifying cmd and uses the associated needed values, like num, str, and id.

SmileyScripts is a system implemented as a No Modify script file, and is used to communicate with the GridCentral Mainframe In-World Server. Various commands exist, but to hide the actual functionality, you execute these commands using a llMessageLinked template..
For example:
ssExecuteCommand(string cmd, key id)
{
    llMessageLinked(LINK_ROOT, 7413, cmd, id);
}

Or another example, only this one is to call a Rank Identification for the GridCentral Adventures game (Work In Progress):
listen(integer channel, string name, key id, string message)
{
    ssExecuteCommand("GetRank_GCadventures",id);
}

The file is now a work in progress, and will be published to the Teen Grid ONLY since i'm on the Teen Grid, and it's Teen Grid compatible only.


Snapshot

Snapshots are "photographs" taken of a scene in SL. There is not currently a way to take snapshots using LSL. You can only take a snapshot with a client computer to render the image. There isn't actually anything "there" in SL. It's just numbers that are turned into an image on the client.

However, even if you required a client present to do so, automated asset creation is not something the Lindens are eager to implement, as the load on the asset server would be tremendous. This is the same reason that we can only read notecards.

A snapshot is also a specific inventory item, with their own inventory folder and icon. Snapshots differ from textures in that they are created by the snapshot tool within SL, rather than uploaded as a texture. By using snapshot-to-disk, then uploading the resulting image, you will get a texture item, not a snapshot.

Inventory / Camera


Sounds

See Sound.


sourcecode

See code.


sprite

A sprite (also sometimes referred to as a "billboard") is an axis-aligned (always faces the camera, like llRotLookAt), textured polygon used in particles and collision impacts (which are just particles) via llCollisionSprite.

textures


stack

Stack memory is a LIFO-based memory structure.

Stack is used when passing variables into functions. The data is "pushed" onto the stack, then "pulled" off the stack by a function. Each script has it's own stack space which counts towards a script's memory allocation. (This is why it is common to see a script crash when passing complete lists between functions, as it effectively doubles its size temporarily--while passing the information to functions.)

When calling a new function, the stack operates as follows:

Push: Variable A onto stack (make a copy)
Push: Variable B onto stack
Push: Variable C onto stack
Store location
Adjust memory offset
Jump to the function
<do function stuff>
Push: Result
Jump back to stored location
Re-adjust memory offset
Pull or Peak: Result as needed.

The above example is basicly how LSL does it, but not exactly. --BW

memory | FIFO


Stack Heap Collision

This error occurs when the script runs out of memory (the stack and heap pointers, which count from opposite directions, meet in the middle, thus the name "stack/heap collision"). To avoid this, check the available memory before doing any memory-intensive calls (particularly those involving lists) using llGetFreeMemory.

Remember that llGetFreeMemory will not return the current available memory but, rather, the historic free memory--the lowest amount of free memory that the script has ever had while running. This could cause a situation where the script has several kilobytes of memory free, but "thinks" it only has a few bytes available. Other than to suggest that the script is redesigned in such a way that this can't happen, there isn't a workaround for this situation.

LSL scripts are each currently allocated 16KB of memory, in which the compiled script's bytecode and the above-mentioned stack and heap are stored.

Errors | Memory


Statement

A statement is a line of code that is terminated by a semicolon. All statements in a program are separated from one another by semicolons (the compiler ignores whitespace). These rules make up LSL syntax.

Examples:
integer answer = 42;

llSay(0,"The answer to life, the universe and everything: " + (string)answer);
The first and third line of this example are statements.

Script


States

See State.


status

Status, besides being an ego thing, is another term for the state of something. Editors have status panes (a user interface element) and there are some functions related to getting and setting the status of objects, scripts, avatars, simulators, etc.

Functions

llGetStatus
llSetStatus

Links

- SL History Wiki: status

user interface | state


Step

A step can refer to different things. Texture animation can "step" through frames. Prim rotation and position changes are done in steps (except for llTargetOmega which is smooth).

Animation | Rotation | Position


Stride

In lists, strides are blocks of related data.

Strides are used by llListSort and llList2ListStrided. See the llListSort entry for a better description.

Lists


Strings

See string.


subroutine

See Functions and UserDefinedFunctions.


Suggestions

Got a suggestion or feature request for the wiki? Post it here, along with your name and the date. Read about implemented suggestions on LSLWiki.

Post to the suggestions forum! Friday, 2004-04-23 by CatherineOmega

__Make__ [[WikiFormatting]] (or ''at least some'') //work// ##in## headers.

strikethrough

I can add a strikethrough tag, like so:

--monkey-- renders as monkey.

But we'd have to change every page where people used -- before. Do we want this?

Do people use strikethrough enough to warrant adding wiki markup for it? And if it is warranted, is it really necessary to use --? Other characters might be less intuitive but at least they wouldn't require modification of old pages.

would be nice... but not sure what the format you would use for it as -- is a bad set of characters to use. - BW

separate SpeedTweaks page

Maybe create a seperate SpeedTweaks page instead of lopping them on the StyleGuide. -Chris 4/24/04

Debug section

How about a Debug section that details EVERYTHING like... what? :), in which LL employees could come and contribute for us. -Dave Zeeman 4/30/04
Actually, I think there might be an official combination user manual and tech support wiki in the near future. -- Cat
Really? That would be neat. My question is though, would there be as much user input if it was 'official'? Or would people be more afraid to edit? -- Ama
I think it wouldn't be as freeform as the LSL Wiki Mirror 7-7-7 is, but that it would let users edit the pages that mattered. -- Cat

messy homepage

I feel the HomePage is becoming a mess. There's too much going on. When I first visited two months ago, it was much simpler and more accessible. Now, I worry about whether new visitors will be turned off. The use of bold link names seems arbitrary and I feel a number of the links shouldn't be on the HomePage, but rather somewhere else. Also, I don't see a need for Debug to have it's own table heading.

-BuckminsterHamilton, 5/2/04

Agreed. I'm working on a better system. -- Cat

Well, the reason Debug had it's own table heading was because it didn't fit in with anything else, but is a huge chunk of information which may or may not take up a lot of Wiki space depending on the evolution/popularity of the section. Otherwise, the only way it would be accessable is by a random browser of the PageIndex who happens to just trip upon it. -- Dave

I'm waiting for email back about a better place for it and some of the other stuff. -- Cat.

in-depth texture overview

It would be nice to have an in-depth overview of texturing. There's plenty of technical information here but the number of texture misconceptions in SL forum posts tells me something covering the whys and hows of format, creation, and use would be helpful. Then again, this is outside of the LSL sphere so maybe it's just off-topic? -- Julian 5/3/04

It's about as off topic as my debug menu, so I can see it fitting in somewhere. -- Dave

There is some planning going on for references and tutorials on textures, building, etc, to be folded into the rest of badgeometry - keep your eyes open. -- bbc

comment-page text conversion

How about folding in-line questions and comments back into the regular text after they've been there for a while? I can see how keeping the thread of the thought process is good for the short term till people have a chance to review things, but seems like we should rewrite a page to concisely represent new information once it gels a bit.

I try to do this when it seems obvious. If you decide to do it, just make sure it's as easy to understand. -- Catherine

extend ACLs to give certain people "moderation" privileges

Quote taken from the ToDo page:
Extend ACLs to give certain people "moderation" privileges, i.e. editing/deleting other peoples pages/comments as well as taking over ownership. -EzharFairlight
Reposted to Suggestions by ChristopherOmega on 6/6/04.

I thought that the suggestions page would be a more proper place for something like this. It is extremely controversial, at least in my opinion, since it violates a principle of Wiki (see the "People are editing my pages! How do I make them stop!?" question) . -Chris

You take over my pages over my de-rezzed corpse. --MoleculorSatyr

The idea here was simply to be able to delete some orphaned pages whose owners weren't around any longer, as well as cleaning up some comments that no longer applied (like the mess on the homepage), not playing Wiki FBI. -Ez

italics syntax correction

Is there some fuzzy logic-y way for the wiki to differentiate between the // commonly used in URLs and the // markup? I find that an incredible number of italization errors occur because people post the full URL for a website to a comment (e.g. http://secondlife.com/badgeo/wakka.php?wakka=Suggestions) without first putting it in double-double quotes, inadvertantly opening and never closing an <i> tag.
-Chris on 8/15/05

Talk/Discussion Pages

Since WakkaWiki doesn't support talk/discussion pages (like MediaWiki does), I suggest using the page name syntax of TalkRegularPageName so my talk page would be TalkEepQuirk, the homepage's would be TalkHomePage, etc. What do you think? Sure would reduce the amount of comments on pages... -EepQuirk, 6/16/6 (now if there can just be signature syntax...)

Don't comments serve this purpose? Before creating new talk pages, I think we should take a serious look at how the comment system works. Using both a comments system and talk page would likely confuse readers and may lead to discussion decoherance (parts on the page's talk page, parts in the page's comments). Instead, I propose that comments be deleted automatically after a set number of hours/days/weeks. Of course, lots of useful info will need to be transferred from comments to the page itself beforehand. -ChristopherOmega 6/16/06

WikiFormatting | ToDo | LSLWiki


SWF

SWF Files

An SWF, or Shockwave Flash file, is a form of animation and games common on the internet. Non-interactive flash animations are supported via the Second Life Quicktime plugin.


Syntax

Syntax is "the rules governing the formation of statements in a programming language". (source)

statement


SyntaxError

See Errors and debugging.


tangent

Tangent is a trigonometric function, llTan, when given an angle to an object will return the opposite side (y position) over the adjacent side (x position).

The inverse function of tangent is arctangent.

sine | cosine


Target

Target is what a script defines as the basis for the next function or event.

Examples include llMoveToTarget and llTarget, both of which define a positional target. However, in real terms, target can define anything from an archery target to the goal of a script.

Functions

llMoveToTarget
llStopMoveToTarget
llTarget
llTargetRemove
llRotTarget
llRotTargetRemove
llTargetOmega

Events

at_target
not_at_target
at_rot_target
not_at_rot_target
The next functions take place before or after the functions llMoveToTarget and llStopMoveToTarget
moving_start
moving_end


task

This word is often used as shorthand in the (*cough* poorly written *cough*) LSL documentation and basically refers to either:

The object this script is running on, the script itself, the object's script that called the function, or the object containing the script calling the function. (The latter is the most likely definition.)

It's used like this:

llGetInventoryNumber(integer INVENTORY_CONSTANT); returns the number of inventory items contained in task.
or:
llGetNumberOfSides(); returns the task's number of sides.


tau

In LSL tau is a greek symbol representing time for damping functions.

time | damp


Telehub

When teleporting across the Life grid, you may arrive at the telehub nearest to your destination rather than teleporting directly to it. For example, if you want to teleport to the sim Dore, you first teleport to the telehub in Ahern then fly to Dore. Teleportation begins at any point in the world but ends only at a telehub (except when you accept a point-to-point teleport offer from another player). You do not need to be at a telehub to initiate teleportation.

Telehubs were created to encourage directed community growth. According to Linden theory, telehubs will become population centers and population density will decrease with distance from the hub. Eventually "urban" and "rural" areas will develop. This theory has yet to be proved or disproved and likely will remain undecided for some time because of the slow process of settlement and resettlement of pre-telehub properties.

Trivia

During beta, and for some time after launch, teleportation cost a fee based on distance and was point-to-point.

There is nothing special about a telehub. Typically, the mechanism is a cube prim with a descriptive name and no script. You can find the single prim that is the actual telehub in some of the new private island sims that do not yet have telehub structures. Apparantly, the system is hard-coded and the UUID (key) of the prim used to determine the telehub location.

Teleport | Grid


Temporary on Rez

Objects set to "temporary on rez" (temp-on-rez) automatically die without triggering any events after a short time and do not count against the prim allotment for the land parcel beneath them. Otherwise, they're the same as an ordinary prim: visible to all, able to be scripted, interacted with, etc. Temp-on-rez objects do not make assets when they are deleted, so the deletion events are not a load on the asset server.

An object's temporary on rez property can be set with llSetPrimitiveParams, or by checking the "Temporary" checkbox in the building editor in the SL client. llGetPrimitiveParams can be used to determine if an object has been set to temporary on rez.

Performance Advisory
Always use temp-on-rez when rezzing a large number of "disposable" objects such as bullets or other projectiles. Doing this allows them to automatically be cleaned up, should they break or encounter no-script land. This is both good scripting practice, as well as good for your sim and neighbours. It also keeps you and your customers happy by not getting complaints sent after them.

Temp-on-rez prim limits

According to Andrew Linden, as of SL 1.11, there is a limit on the number of temp-on-rez prims allowed on a parcel based on the parcel's prim limit.

The formula is currently "temp_prim_limit = 0.5 * regular_prim_limit + 20", or 500 temp-on-rez prims, whichever is smaller. Any rezzing above this limit will cause older temp-on-rez objects to immediately be garbage collected until the limit is reached again. As the aforementioned forum thread indicates, the limit may be tweaked in later versions.

Lifespan of Temporary Prims

According to Andrew Linden, the current system is a per-frame (1/45th of a second) tally correction for any parcel that has gained or lost objects. If the parcel is over its temp-on-rez limit, it aggresively deletes older temporary objects until it is within its limit. Otherwise, if it has any temporary objects at all, that parcel is scanned for old ( > 60 seconds) temporary objects every 10 seconds.

In practical terms, expect the lifespan of temp-on-rez objects to range between 60 and 70 seconds.

Observations

WARNING! The following behaviour was observed in 1.12.0 (2006-08-23). Creating an object and then setting the "temporary on rez" property DOES make the object temporary (eligible for garbage collection) directly, even without being taken and re-rezzed. Setting the "temporary on rez" property on an existing object DOES decrease the number of objects the simulator thinks are on the parcel.

Taking an object with the "temporary on rez" property set into inventory and rezzing it gives a copy of the object which is temporary, and which does not count towards the number of objects the simulator thinks are on the parcel. This is the normal case.

If you remove the "temporary on rez" property from an already-rezzed temporary object, that object will no longer be temporary and will not be garbage collected. However, it will then count against the number of objects on the parcel.

Although rezzing a new non-temp-on-rez object on a parcel isn't allowed if it would take you over the parcel limit, it does appear to be possible to remove the "temporary on rez" property from a temporary object sitting on a full parcel. This puts you in a situation where the "About Land" dialog says something like "235 out of 234 (1 will be deleted)" and a little while later said prim will be removed after all. Note that such an object is not deleted outright as a dying temp-on-rez object would be, but is sent back to your "Lost and Found" folder instead.

Primitive | Object | Rez


TextureConsole

This is a list of all textures being rendered by the client (perhaps stored in memory too). If the "select texture" edit option is selected and a texture on a prim has been chosen, then a yellow highlight will come across the information about that texture on the debug list.

The far left shows the texture key, but doesn't display the full UUID (only the first 16 characters). This could be because of space restrictions or because displaying the full key makes it easier for someone to 'hijack' that texture and display it without actually owning it (yes, it can be done).

2nd, 3rd, 4th column, I have no idea.

5th (the purple and black bars) column is most likely a representation of the client's download status of the texture. I believe purple means it is data already loaded into memory, blue means it is being downloaded, and black means it simply isn't in memory but is ready to be downloaded.

6th column, I believe, to be stored size/file size (as it seems to correspond with the resolution).

7th column is the current LOD (level of detail) of the texture for the client. 0 is highest LOD (closest) and 5 is lowest. This one is very important for people who are seeing ATI video card texture glitches. It helps LL support out greatly if you notice there is a bug here.

8th I believe is which LODs are being stored in memory/have already been loaded. Ex: 0 2 45 would mean LODs 0 2 4 and 5 have been loaded.

9th and 10th columns I have no idea but 9th can be pretty when loading new textures.

11th column is the current texture resolution.
- If LOD is 0 this is the actual resolution of the image stored on the servers
- If LOD is not 0 then this will be less than the full resolution and is the resolution of the texture as it is displayed on the screen

Debug


TextureResorces

Texture Resources | url
Violent Nation | http://www.violentnation.com/visuals/downtown/index.php
Strange Breed | http://www.strangebreed.ca/photos/photography/index.html
Unknown | ftp://ftp.cs.tu-berlin.de/pub/sci/virtual-reality/graphics/texture/tile/
3DKingdom | http://www.3dkingdom.org/
3dTronic's Textures | http://3dtronic.webbied.com/
Image*After | http://www.imageafter.com/
3dvalley | http://www.3dvalley.com/textures.shtml
ANIMAX | http://www.animax.it/animax/Textures.htm
XSI Base | http://www.xsibase.com/tools/textures.php
Center for Bioscience | http://bio.ltsn.ac.uk/imagebank/default.aspx


TextureResources

Texture Resources | url
Shapeless Mass | http://www.shapelessmass.com/visuals/index.html
Strange Breed | http://www.strangebreed.ca/photos/photography/index.html
Unknown | ftp://ftp.cs.tu-berlin.de/pub/sci/virtual-reality/graphics/texture/tile/
3DKingdom | http://www.3dkingdom.org/
Image*After | http://www.imageafter.com/
3dvalley | http://www.3dvalley.com/textures.shtml
ANIMAX | http://www.animax.it/animax/Textures.htm
XSI Base | http://www.xsibase.com/tools/textures.php
Center for Bioscience | http://bio.ltsn.ac.uk/imagebank/default.aspx
The Centre for Astrophysics and Supercomputing | http://astronomy.swin.edu.au/~pbourke/texture/
GUG | http://gug.sunsite.dk/texture.php
LHE computing centre | http://sunhe.jinr.dubna.su/textures/
SCM Animation Studio | http://smorigin.scm.cityu.edu.hk/cgi-bin/imagelib/imagelib_toc.cgi


Textures

See Texture.


texturing_sides

Texturing sides/faces (particularly on hollowed, advanced-cut prims) so they all look the same can be annoying (and is possibly a bug).

Be sure "Stretch Textures" is checked in the edit object dialog box or the UV repeats will get screwed up whenever the prim is resized or its hollow amount changed.

Rotated textures will require different UV offsets. Perhaps a formula can be derived for finding these exact repeats/offsets.

Primitive | Hollow Amount | Hollow Type | Texture Settings
Box | X | default/Square | U Repeat: 4 / X U Offset: Varies (.5@50/25, .606@95, .939@45)
Torus | 95 | default/Circle | U Repeat: 2.1 - U Offset: .55
Torus | 70 | Square | U Repeat: 2.87 - U Offset: .93
Cylinder | 95 | default/Circle | U Repeat: 1.05 - U Offset: 0
textures | side


texturing sides

Texturing sides/faces (particularly on hollowed, advanced-cut prims) so they all look the same can be annoying (and is probably a bug).

Be sure "Stretch Textures" is checked in the edit object dialog box or the UV repeats will get screwed up whenever the prim is resized or its hollow amount changed.

Rotated textures will require different UV offsets. Perhaps a formula can be derived for finding these exact repeats/offsets.

 |  |  |  | hollowed side texture
 | hollow |  |  | repeats |  | offset
prim | amount | type | advanced cut | U | V | U | V | comments
box | 95 | default/square | - | 4.2 | - | .606 | - | to get U repeat: divide 4 (# sides) by float (95 = .95) hollow amount
box | 50 | default/square | - | 8 | - | .5 | - | 4/.5 = 8
box | 45 | default/square | - | 8.89 | - | -.061 | - | 4/.45 = 8.888
box | 25 | default/square | - | 16 | - | .5 | - | 4/.25 = 16
torus | 95 | default/circle | yes | 2.1 | - | .55 | -
torus | 70 | square | yes | 2.87 | - | .93 | -
"-" = unchanged/unavailable

texture | side


TGA

Developed by Truevision, Inc., Targa, aka TGA, is a bitmapped graphics format, usually uncompressed, that supports an alpha channel. Second Life can import both 24-bit (no alpha) and 32-bit (alpha) TGA files. This format is the graphical workhorse of the SL garment industry.

Avoid using the 32-bit format if the texture doesn't have/need any transparency; otherwise there might be unwanted visual effects.

Note that SL doesn't use TGA files, but actually JPEG2000. TGA files are converted to JPEG2000 and uploaded in JPEG2000 format to the asset server. Texture data is downloaded as JPEG2000 and decompressed directly into memory on the client.

Wikipedia: TGA

texture


ThingThatIsNotaGame

"Second Life is not a game, but Second Life is a game."

Second Life is an environment for many things, including games, but is not limited to only games.

The technology that makes SL work resembles that of an online game more than anything else. Like a game, SL needs to render 3D space efficiently. Like a game, SL has a database for keeping track of accounts and records. Those are subjects that are often discussed at a game developers conference which is why LL participates.

Also, like a game, SL is a source of entertainment for most of its residents. However, a few use it for other purposes. LL's plan is to add functionality to make SL more of a platform for those who want to use it as a tool and those who want to use [it] for business. (Andrew Linden, 10/19/05)

SL was much like a game in version 1.0 but it has become less gamey since then. You can expect it to become more like a platform in the future. Unfortunately, it takes longer to make it so than to conceptualize it. (Andrew Linden, 10/19/05)

Second Life


timestamp

See time.


To-Do

These are things the LSLWiki needs improving on. See WantedPages for more.

  • Modify the function pages to use a clearer description template, example.
    • SomeFunction( theType myParam)

    Parameter:
    • theType myParam
      • myParam is used to pass any value you like in

Returns:
  • -Integer-- : Returns a number between 1 and 5

Comments:
Though the value passed to myParam in this function appears to have no affected, it does make the function go FOO, which of course causes it to spit a number between 1 and 5 out between its teeth.

Examples:
None.
  • Just thought it might be useful. By all means delete the above item if im being silly - NikEbisu
  • Doesn't sound like a bad idea. I'd wate for more comments before doing it - BW
  • I like the idea. It can be difficult to see what all the parameters are for, and what is expected. vector doesn't tell me much. - AakanaarLaSalle

    • Give everyone on the contributors page a middle name.
      • It's a Wiki, put your own name up :P -Ice
    • Write a vehicle tutorial.
      • You get Wiki bonus points for this! -?
      • The LSL Scripting Guide that is included with every upgrade has a pretty extensive section on vehicles. -VaguegirlPetty
    • What do you think of someone(s) going through and adding a page each for all the particle, vehicle, and primitive parameters?
      • :-O That really depends upon what kind of page will be posted on each of the parameters. If its only like a two-liner, I dont think its worthwhile to make a whole new page. -Chris
      • Particle and prim parameters could be defined within their pages but I could see vehicle parameters getting pretty involved. Mostly I want this because vehicle parameters consistently kick my butt. :) -JulianFate
        • Isn't this done? -EepQuirk
    • Test event behavior during delays and add results to ScriptDelay.
    • Add the ability to view all comments you have posted. MyComments?
    • Allow people to view all changes/pages other people have made, not just MyPages/MyChanges.
    • Comment deletion should redirect to the previous page (the page the comment was posted on) instead of the HomePage.
    • Turn double-click editing off by default for people who have not logged in.

    Admin

    Syntax Highlighting

    PRIM_FULLBRIGHT, PSYS_SRC_ANGLE_BEGIN, PSYS_SRC_ANGLE_END, PRIM_TEXGEN

    Requires a different wiki installation

    • Port the wakka to a wiki implementation that isn't dead, preferably MediaWiki. This would allow for much more complex layouts, uploads, LaTeX math formatting, and numerous other features. Seriously, I got Mediawiki set up on my Linux box in under an hour. -KeknehvPsaltery
      • Endorsed by: Keknehv Psaltery, WolfWings, ChristopherOmega, AaronLevy, WillMoxie, St. Psaltery, Strife Onizuka, BurnmanBedlam, StevenKramer, Chasingred3Ixtab, JippenFaddoul, StryfeLowell, SmileyBarry, MidianStreeter, JasonHashimoto (add your name?)
      • MediaWiki is incredibly easy to install, I'm currently pondering how hard it would be to create a script to port everything. My only concern would be implimenting the LSL script highlighter into it. Endorsed if possible. -FionaFielding
      • I bet it wouldn't be that hard... Ezhar knows PHP. I don't even care about a script to port everything, I'd be willing to go, by hand through the PageIndex and port everything. - KeknehvPsaltery
      • Add me onto that list of folks willing to go through by hand to move things from Wakka to MediaWiki. - WolfWings
      • St. Psaltery is also willing to transfer the wiki information by hand if necessary.
      • MediaWiki is good enough for Wikipedia, so it must be good enough for us! ;) I would also be willing to transfer information by hand if needed. - BurnmanBedlam
      • I've only been here for a short while, but I stand behind this. Besides the glitches in this wiki and its lack of maintenance, I would also like to see translation of the wiki happen. I took a quick peek at MediaWiki, and the first syntax highlighting plugin I found ran on GeSHi, so I hacked up a quick and dirty language file for it. Example and download are at my site. The MediaWiki plugin is rather simple, but works. - StevenKramer

    Requires WakkaWiki rewrite/change

    • Fix links so that they do not contain "?" where they should "&", these scripts are yet to be patched
      • actions\mychanges.php
      • actions\wantedpages.php
    • Allow wiki links to be in the form [[Constants#Rotation fubar]] so we can link easily to an anchor.
    • Make comments deletable by the owner of the page the comment is posted on.
    • Fix wakawiki so that clicking the title of the page (ex, the big link to "ToDo" at the very top of this page) directs the browser to the page, not some wierd TextSearch query.
    • Fix revisions so that we can view the actual page as seen by the revisor. (Clicking the date link next to the revision currently triggers some wierd "unknown method" error.)
    • Fix all the stuff that broke during the move. wakka.php is supposed to be using the index or 404 handler, I think, and thus, is causing slow and annoying corruption. -Ice
      • Slow and annoying corruption? Sounds scary... how is this manifesting itself currently? got any examples? -Chris
    • Make wiki-links contained in header/large-main-idea-text (double/triple equal-sign) characters work.
      [[FOO]]
      • See, doesnt make link to "FOO" wiki page. :-/ -Chris
      • Hmm, that's really weird. I'll take a look at it some time... -Catherine
      • This would require some major changes in the wakka formatter - IMHO it's not worth it. -Ez
    • I don't suppose this is possible, but... could the search function be altered in such a way as to not be limited to whole words? (I.e. a search for Pos would at least return 'llGetPos' and any page that contains 'Pos' in any form.)
      • That would be possible, but the load on the SQL server would be unpleasant, so I'd rather not do that. -Ez
          • -The load on the SQL server? What wrong with using a LIKE clause? SELECT `your`,`colums` FROM `yourpagetable` WHERE `columntosearch` LIKE '%pos%'
            • One statment and will produce the search you want, with little added load. NikEbisu
      • Could we not use Google Site Search for the site to have that functionality? Would that seriously piss people off? Google search embedded in pages -MaxCase
      • Are there any downsides to using google for site searches? (Why would it piss people off?) -Chris
      • I actually put this up on my user page. You should have a look at it. -Keknehv
      • NikEbisu is correct on that the database can do the search itself if you perfer to keep the search local. The database software was designed to do this easily with little load. AakanaarLaSalle
    • Remove pages with ".", "?", "=", "&", "\" in their titles or rewrite the wiki to better handle them (or stop their creation) because the wiki can't make links to them.
    LSLwiki | WantedPages | Suggestions


tooltip

A tooltip is the little text bubble that pops up when you hover your mouse pointer over an object of interest. In SL, tooltips appear for land, objects, on the map--everywhere! These are especially useful as a quick reference within the LSL editor.

Of course, the ones in the LSL editor tend to have mispelled words, bad grammar--oh, and the occasional lie. That's what the LSL Wiki Mirror 7-7-7 is for. :)

user interface


Torque

Torque is a physical property of rotational force measured in N-m.

Functions

llGetTorque
llSetTorque
llSetForceAndTorque

Physics | Dynamics


tree

Linden trees (and also shrubs, grasses, and seaweeds) are decorative objects that can be placed on the ground or underwater. They only count as a single prim, but some are quite detailed, with many leaves and branches.

Trees will bend in the local simulated wind, but don't otherwise do anything interesting.


trigonometric

A trigonometric function is one that describes the ratio between sides of a triangle.

Examples of trigonometric functions used in LSL are llSin, llCos, llTan, llAsin, llAcos, and llAtan2.


Troubleshooting

Troubleshooting Second Life

A note from Catherine:
Because there wasn't a proper place for this at the time, it ended up here on the LSLwiki. The LSL Wiki Mirror 7-7-7 deals with how to script. The current troubleshooting resource is the SL Wiki.

(Also see newview.)

Most technical problems with SL can be fixed or at least explained by corrupt cache, bad drivers, overheating, bad power, or a bad network connection--usually in that order. These suggestions are intended to help you track down the specific issue you're having and hopefully resolve it. Note: most of these solutions are intended for Windows users. Most of the problems Windows users have aren't issues the Mac client has to deal with.

  1. My SL client is crashing!
  2. Why is everything black? I can only see a few things.
  3. SL runs really slowly! What gives?
  4. I can't connect!
  5. General Advice

A) My SL client is crashing!

Assuming that you've rebooted your computer, try the following suggestions:

  1. Wipe your cache. The quickest and easiest way to do this is to simply delete the "cache" directory. Un-/reinstalling SL is a more drastic, and usually unnecessary, option.

  2. Check your drivers. Are you using the newest version of your video drivers, motherboard chipset drivers, BIOS, and DirectX? When you start SL, it should give you an error message if you're not up-to-date. Follow the links the error dialog box gives you.
    1. When installing a new video card driver, the best thing to do is to first download the new driver, then uninstall the old one, reboot, then install the new one and reboot again.
    2. Make sure you're using the official drivers from your video card's chipset manufacturer (usually either nVidia or ATI). OEM (original equipment manufacturer) drivers produced by the company that manufactured the card itself (Chaintech, Asus, etc.) will usually suck, as will those available from major OEMs like Dell or IBM.
    3. If you're running a desktop system with ATI video and you're using third-party drivers, such as the Omega or DNA drivers, switch back to the official ATI Catalyst ones if you can.
    4. If you're running a laptop with ATI graphics, you should first try to see if you can install ATI's official Catalyst drivers. Failing that, try either the Omega or DNA drivers. Update: This site seems to have modified Catalyst drivers that may work on your ATI-based laptop. It's my understanding that they work in the same way that Phil Metalhead's suggestion does.
    5. Are you sure you're actually running the driver you think you are? Windows can be really uncooperative at times. Check out this thread for more information on how to properly remove the Microsoft driver and install the correct nVidia or ATI one.

  3. What is your network connection like?
    1. I don't know, how do I figure that out?
      2. Try running DSL Reports' BandwidthTester to find out your connection's capacity.
    2. dial-up (56k modem / ISDN)
      3. Your connection is too slow and low-capacity for you to run SL properly. SL is intended for use with a broadband connection. You're probably crashing or freezing at the "Accept Licence Agreement" screen, right? If you used a broadband connection to get past the licence agreement, you could theoretically connect to SL, but I wouldn't expect to do things like move around or necessarily even talk to people.
    3. broadband (DSL or cable)
      4. This is good. You should check your ping times to the SL server. (Alt-1 in SL, or type "ping www.secondlife.com" at your OS's command prompt.)
    4. broadband (wireless)
      5. If you have a wireless LAN, try plugging your computer directly into the router and seeing if that helps. Some wireless networks are subject to interference. You should also check your ping times. A good Windows-based tool for determining whether or not you're getting wireless interference is NetStumbler, (try MacStumbler if you have a Mac) which allows you to see how many wireless networks are nearby. If you find that all your neighbours are on the same channel as you, try switching to a different channel. If your wireless card supports it, NetStumbler allows you to see the signal:noise ratio as well. Remember, if you have an 802.11b or 802.11g network, everything from cordless phones, to microwave ovens to Bluetooth can interfere with your LAN.
    5. "wireless dual T1"
      6. This doesn't exist. You need to find out what you're actually using. Check your bandwidth capacity and ping time.
    6. satellite (bidirectional or satellite downstream/modem upstream)
      7. Wow, that sucks. Too bad for you. It's quite possible to play SL, at least on a bidirectional connection, but the extreme latency of such a connection will make it really painful.

  4. Do other computers work on the same internet connection you're using? On the same cables and everything?

  5. Are you trying to run SL on two computers on the same internet connection? If so, what kind of hub or router are you using? Some brands are exceptionally bad at it.

  6. Does SL crash or give errors when it's the only thing running, or just when you're running other software? If so, what are they?
    1. file-sharing apps like Gnutella or BitTorrent: These are especially notorious for SL. Keep them turned off.
    2. Photoshop, Illustrator, Maya and 3DS Max, all at the same time: These are heavy-duty applications. It's unrealistic to expect that you can run them all and SL at the same time.

  7. Is your pagefile fragmented? Running Windows' Defrag utility won't get it all. Try Sysinternals' PageDefrag utility.
    1. After defragmenting your pagefile, try defragging your entire hard drive just for good measure.

  8. Are you running Windows 95/98/ME? Try upgrading to Windows XP. If your computer's fast enough to run SL, it's fast enough to run XP.
    1. If you can't get XP, but you can get 98, consider upgrading to Windows 98 over either 95 or ME. They're horrible. Yes, 98 is better than ME, even though ME came out later. Note however that SL isn't officially supported on 95/98/ME.

  9. What combination of RAM are you using in your PC? If you know what you're doing inside a computer, try removing all but one memory module and seeing if that helps. (Assuming you have SDRAM or DDR SDRAM, that is. If you have RAMBUS, you likely aren't crashing in the first place.)
    1. if it turns out that one of your memory modules is causing SL or computer to crash, try running memtest86 with only that module. It may be bad RAM.

  10. Is your computer adequately cooled? Download a CPU temperature monitor like Motherboard Monitor. Also, make sure that your CPU isn't radically overclocked. Get WCPUID to check. If your computer's only crashing when it's been turned on and running SL or a similarly video/CPU/memory-intensive application or game, it's possible that it's overheating. If your computer room is very hot, try waiting until it cools down before you connect.

  11. Is your computer's power good? Check the voltages Motherboard Monitor gives you. What capacity is your power supply, and how old is it? How many devices do you have hooked up to it? A 300W power supply with four hard drives and a GeForce FX 5950 Ultra is not going to work very well.

  12. When did your problem start happening? If the answer is, "when a new version of SL was released" or "when I got my new video card", that can be helpful in tracking down the problem.

  13. How much free disk space do you have? You should ideally have a couple gigabytes on the disk your pagefile's on, as well as the C: drive if your pagefile's on another drive. (Second Life doesn't let you specify the location of the cache files yet.) If you don't have enough disk space, it can cause SL to give you errors when trying to create or access the cache.

  14. It's unlikely, but possible that your computer has a virus or trojan that's contributing to an existing problem, or even causing your problems with SL in the first place. I recommend Trend Micro's free virus scanner and Lavasoft's Ad-Aware for dealing with viruses and spyware, respectively. Some spyware apps are notorious for affecting system stability and performance.

  15. Are you using any third-party desktop extensions or multi-monitor stuff? Stop it.

  16. If you have local lighting or bump mapping turned on in Preferences, turn them off. You don't need that stuff anyway. Note that this is more a performance tip, but there are still potentially crash issues related to them.

B) Why is everything black? I can only see a few things.

You're probably using a very old graphics card, or a new card with very old drivers (especially ATI users).

  1. In Start -> Settings -> Control Panel -> Display -> Settings, make sure you're using 32-bit colour depth. SL won't work in 16-bit. If you've been able to get into SL before and now it's suddenly all black, this is what's likely causing it.

  2. Turn off anti-aliasing in Start -> Settings -> Control Panel -> Display -> Settings -> Advanced

  3. Make sure you are using the latest graphics card and mainboard (AGP) drivers (see "Check your drivers" above).

  4. If you can see the menus and controls, but not the world, it's possible that it's "night" in SL and your computer's brightness settings are set incorrectly. You can change SL's brightness in Edit -> Preferences -> Display -> Gamma.

C) SL runs really slowly! What gives?

SL is a very CPU-intensive program. If you don't have a really fast CPU, it won't run very quickly. Even the very fastest CPUs available run SL slower than you might think. Because SL is CPU-bound, your CPU and memory bandwidth tend to be the most important parts. Having a good video card is less important than having a really fast CPU.

  1. If you have local lighting or bump mapping turned on in Preferences, turn them off. You don't need that stuff anyway. Note that this is more a performance tip, but there are still potentially crash issues related to them.

  2. Switching off ripple water also helps, as well as reducing the terrain detail.

  3. If you're running other software, turn it off. Did that help?
    1. If it was an application like Photoshop, how much RAM do you have? If it's less than 1GB, you may find that you don't want to run it and SL with less than 1GB of system RAM.

    2. If it was a filesharing application like Bittorrent, Emule, or Gnutella, it's possible you don't have enough bandwidth to use SL at the same time. If your filesharing software supports it, use a bandwidth limiter while running SL.
  4. In Preferences, turn down your draw distance. Default is 128m, which works best for most people.

  5. Also in Preferences, reduce the bandwith used for SL. Things will load more slowly, but as the client won't be as busy with processing all the incoming data, it will have more time for other tasks like rendering.

  6. Is your pagefile fragmented? Running Windows' Defrag utility won't get it all. Try Sysinternals' Pagefile Defrag utility.
    1. After defragmenting your pagefile, try defragging your entire hard drive just for good measure.

D) I can't connect!

  1. Reboot your computer.

  2. Does Sl say it can't resolve the userserver? It's possible you have a minor problem with your DNS. Try connecting again.

  3. Does SL say you need a valid Certificate Authority File? That means your computer's clock is likely off by a couple years. Set it to the proper time and SL will be able to connect. Other applications may also be affected by this problem, so it's good to keep current.

  4. Does SL say that it's down for maintenance? That means it's down for maintenance. Check the SL Announcements Forum for details.

  5. Does the SL system status page indicate SL is down? If so, you have to wait to log in.

  6. Make sure you can actually connect to the SL website in your web browser.

  7. What time is it? If it's nighttime, PST, check the New in Second Life" forum. If there's a bunch of messages along the lines of "Is the grid down?", "SL is down", "I can't connect", etc., it usually means that something has failed. The Lindens will have been paged, but as they generally prefer to sleep at night, they may not respond as quickly as you might prefer.

  8. Are you having trouble connecting to other websites? The problem is likely with your entire network connection.

  9. Open a console prompt. (Use your OS's Help files to find out how to do that.) Can you ping login.agni.lindenlab.com? If not, there's something wrong with your network.

  10. Assuming everything has checked out fine so far, you may need to open some ports on your firewall. If you've previously been able to connect, but now can't, and you haven't changed anything in your network configuration or setup, you can generally disregard this suggetion. If not, read on: SL currently uses the IP ranges of 66.150.244.0/23 (all IP addresses from 66.150.244.0 to 66.150.245.255), 69.25.104.0/23, and 72.5.12.0 - 72.5.15.255, on ports 12020 - 13050. If that port range is too large, you can safely limit it to ports 12020 - 12050 and 13000 - 13050. In practice, just opening all ports on the SL IP range is probably the best idea.

E) General advice

  1. Run Windows Update. Yes it's annoying, slow, painful, you might have to reboot your computer several times and it sends your pirated Windows serial number to Microsoft. Do it anyways, it will ensure that your Windows has the latest security fixes, drivers and will also offer you the most recent version of DirectX. Install everything in Critical Updates and Service Packs and also everything in Drivers, with the exception of the graphics driver, which you should get from the chip manufacturer, as described above. In the Windows section you should download DirectX, if a newer version is offered. Repeat this about once per month.

  2. Use a virus scanner. There are several free ones available, for example AntiVir. Make sure you run it's auto-update about once a week, so it can find the latest uninvited guests. Strange things and unexplained crashes can often be caused by a virus/worm infection. If you don't want to have a permanently running virus scanner, you should at least use an emergency tool like Stinger once in a while. It only scans for the most recent cases though.


Tutorial

This page serves as a basic tutorial for users new to Linden Scripting Language and SecondLife.

However, LSL101 and the Crash Course are far better options for both complete beginners, and people who have programmed before.

A script is an item in the inventory of an object that contains instructions that make the object do stuff. Like other inventory items, scripts can be copied to the inventories of other objects, copied to your inventory or given to other players (assuming the script has the appropriate permissions set).

Scripts are written in LSL, a programming language created for Second Life. Its structure and syntax are similar to those of C and, to a lesser extent, Java. A script is organized into states and functions. States consist of a series of events.

Any object can have as many scripts on it as memory allows. Scripts have a length limit, but it's not usually a problem. You create a new script by going to the "Content" tab of an object's "Edit" box and clicking the "New Script..." button. This will open the script editor with the example script (which makes the object say "Hello, Avatar!" on its state_entry event and says "Touched" when touched). The default state is the state that the script first enters. The state_entry event is the first event that is called in a state. So, the code you want executed when the script first starts goes where llSay(0, "Hello Avatar"); is now. You end each statement in LSL with a semicolon (;). So, to say a few things in our state_entry, we would use:

llSay(0, "This is the first thing.");
llSay(0, "This is the second thing.");

The function llSay has two arguments: a channel and a message. The channel can be any integer. The message is a string, which is a group of alphanumeric characters enclosed in quotation marks (""). You must specify both or the script editor will give you an error and keep the script from running. For simple scripts that execute one segment of code on runtime, you only need to know about state_entry. But if you want the object to react to other things, you'll need to use multiple events. See the events page for a full list of them. The events are seperate; you cannot put one event within another. Each event, state, and FlowControl call are kept seperate by using { }. For example:

default
{
    // Now we are in the default state, but not in an event.
    state_entry()
    {
        // Now we are in state_entry, in the default state.
        if (1==1)
        {
            // Now we are in an if/then logic [[block]]. The code inside the { } is executed if the logical evaluation made in the ( ) is true.
        }
        // We are in state_entry. If the "if" statement was false, then execution would have skipped over the code inside the if's { } and resumed here.
    }
    // We have now left state_entry, and are in the default state.
}
// Now we are outside of the default state, and any events that are associated with it.

NOTE: The double slashes (//) begin comments, so any text following the // on that line of code will be ignored by the LSL compiler. This is useful for placing notes about how your code works, or for adding notes to other scripters about what each block does, or for deactivating troublesome lines of code.

You define variables in LSL by giving the type of the variable (string, integer, list, etc) and then the name, followed by a semicolon (;). If you want them to be accessible in every event and every state, you define them before the "default" line. If you only want them avaliable to the function/event from which they were created, you put them in that function. For example:

string gStringName; // This is a global variable

default
{
    state_entry
    {
        string gLocalString; // This is a local variable
    }
}

If you want to use your own functions, define them at the top of the script, before the "default" state, using the standard block notation, for example:

MyFunction(integer channel, string message) // If you want the function to take inputs, put their type/name here, seperated with commas.
{
    llSay(channel, message);
    llSay((channel+1), message);
}

default
{
    state_entry
    {
        MyFunction(0, "Hello, Avatar");
    }
}

This script says "Hello, Avatar" on channel 0 and 1.
To learn more about the things you can do with LSL, look at the function list. This is intended to be a guide to the basics of LSL scripting. If someone wants to add more basic information, feel free.

Quick Note: Typecasting

Many times, you are getting a string that you want to serve as a vector or key for a particular function call. In this case, you use what programmers call typecasting. Typecasting is a method of using one variable of type X as one of type Y temporarily.
To typecast in LSL, you prefix a variable reference with the type you want to cast to in parentheses, like this:

integer gInt = 1;
llSetText((string)gInt, <0,0,0>, (float)gInt);

So, when llSetText looks for a string of text, it changes the type of gInt from integer to string. Then, it is equal to "1", instead of 1. Also, the third argument of llSetText is a float, but you can supply an integer (in this case only 0 or 1) by simply typecasting it as a float. It's important to observe the distinctions between types, because operators behave differently on different types; if you're not aware of this, you may be surprised when you add "1" and "1" and get "11".

Quick Note: Formatting

Proper indentation is the most important part of any code or script. There are a couple indentation styles for scripting and as long as you pick one and are consistant with it that is good. Random is not a style. Here are some simple rules:
  • All { and } go on their own lines. There should never be text on the same line as a { or a }
  • Never put more than one statement on a line.
  • Each line should be indented the same amount as the line above it. Two exceptions:
    • On the line after a { indent one additional tab (SL will do this for you if you hit enter immediately after typing {)
    • On the line containing a } indent one less tab (SL will do this for immediately after typing } if there's a tab already in place)

The default script follows this pattern and SL will do most of this for you if you were to type your code straight from begining to end and hit enter at the right places (after a statement, a { and a })

Why is this so important? For starters, although Ama Omega likes to help new people with their scripts; he will not help you, and may scold you, if you give him a poorly formatted script. The main reason, though, is that if your script has the proper formatting you can see what code is inside each loop, event, function, and state, including what variables are local to where. 99% of the scripts Ama Omega receives that won't compile do compile after the creator cleans up the code.


TutorialJukebox

Jukebox Tutorial

By FoolishFrost

You want a device that plays a song, and when it's done, repeats. It does not give the user the option to stop the music. First, I can say that in practice, they will always have the play and stop buttons on the bottom of the screen. <shrugs> We'll ignore this for the moment. Let's go with this design: A device that will play a song, and when done, play the next one in a list. To do this, we will need a timer, a command to play the song, and the data containing what songs to play and for how long.

The timer is needed because SL cannot tell when a song has ended. It will just stop playing the mp3 and not continue.

First, let's make the base code:

default
{
     state_entry()
     {
          //code will go here.
     }
}

Above, you can see the most basic code in a script. The object 'default' is the state that LSL will always run first, and must be the starting point of any program. This means that when a program runs, it finds DEFAULT and runs its code first.

Inside default is 'state_entry()'. This is an EVENT. An event is something triggered by the program when something happens. In this case, 'state_entry()' happens with the state is first run. Since default is the first state run, and it runs 'state_entry()' first since it has just started running it, this means any code placed in the brackets {} of 'state_entry()' will run first.

Got all that? Don't be afraid of asking questions.

Next up:

default
{
     state_entry()
     {
          llSetParcelMusicURL("http://musicsite.com/song.mp3");
     }
}

We just added our first command to our program. In this case, the llSetParcelMusicURL command. It will play the song or stream listed inside of it ("http://musicsite.com/song.mp3"). In this case, we are using a fake site: http://musicsite.com/song.mp3

This means when we run this, it loads and plays the song above when run. LSL thinks like this: RUN DEFAULT -> RUN STATE_ENTRY -> RUN llSetParcelMusicURL -> NOTHING ELSE TO DO: END

Not quite there yet. We need to add more to this.

Let's continue with adding some more code:

string song = "http://musicsite.com/song.mp3";

default
{
     state_entry()
     {
          llSetParcelMusicURL(song);
     }
}

As you can see, we just added a line OUTSIDE of the default. In this case, the line:

string song = "http://musicsite.com/song.mp3";

What this line does is sets the variable song to equal the text string http:musicsite.com/song.mp3. The word 'string' as the begining tells LSL that the variable 'song' is a type of variable that can contain text. the '=' sign tells it to put the text "http:musicsite.com/song.mp3" inside of the variable 'song'. (minuse the quotes, that just define the edges of the data)

So, when you get down to the line that says:

llSetParcelMusicURL(song);

You see the work 'song' again. Notice it's not in quotes. That means instead of looking for "song", it looks for what 'song' stands for: "http://musicsite.com/song.mp3"

Did that make sense?

Oh, this is a good time to point out that commands always end in a ';'. The semicolon tells LSL that the command line is ended, and to go to the next line. It's really nothing more than a seperator.

Still, all we did is change the way it works, but it's doing the exact same thing as before...

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
     }
}

Wow. This got a lot more complex. Did it not?

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];

What this lines does it make a variable that can store more than one piece of data. a list variable can store a series of things. This data is accessed by using the llList2String command.

If you want to get data out of a list, you can pull it out by the llList2String command. As you can see in the program, it looks like:

llList2String(songs, currentsong)

Notice the first variable 'songs' is the list that llList2String is looking to get data from. it then looks at the variable currentsong to figure out what item in the list to get.

Now, look at line:

integer currentsong = 0;

This is another variable. It means that currentsong is an integer, or in general terms, a whole number. Integers can be added, multiplied, and worked like most numbers. and integer variable (2) added to another integer variable (2) equals the sum of the two variables (4) just like in normal math.

since integer currentsong = 0, then it's looking for item '0' (zero) in the list. Lists are numbered starting with 0. This means it just found the text data 'song1.mp3' from the list and used it.

Let's look at that music player line of code again:

llSetParcelMusicURL(path + llList2String(songs, currentsong));

Notice that the variable path is added to the first item in the list songs. Both of these are strings, so instead of adding them like you do in math, it combines them like words. This means that since path = "http://musicsite.com/" and llList2String(songs, currentsong) = "song1.mp3", that the combined string = "http://musicsite.com/song1.mp3"!

Are we still following? More soon to come.

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetTimerEvent(0.01);
     }

     timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          currentsong = currentsong + 1;
          llSetTimerEvent(30.0);
     }

}

Well. That changed alot!

You now notice that a new event is present, and that the event state_entry() was changed.

Notice that integer currentsong = 0; still sets the variable currentsong to be the number 0. The lext line, though, seems to be a new command!

llSetTimerEvent(0.01);

This line tells LSL to set a timer. This timer is set in seconds, so in this case, it's saying trigger a Timer event in about .01 seconds. <grins> This is a cheap way to make LSL do something more or less immediatly.

So when it reaches the above line, it sets the timer and ends the program. Then when the timer goes off, it started running the code in the timer() event!

timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          currentsong = currentsong + 1;
          llSetTimerEvent(30.0);
     }

Above you see the llSetParcelMusicURL again. We moved it from the state_entry() event to this one so that when the timer goes off, it plays a song. The next line:

currentsong = currentsong + 1;

tells LSL to make the variable currentsong equal itself plus one. This means that currentsong was increased by one.

Last, you see a new llSetTimerEvent command. This resets the timer to go off in 30 seconds (more or less, lag can make this take longer).

And then the program ends until the timer goes off again. When it does, it runs the llSetParcelMusicURL command again. This time though, currentsong has been increased, and so it equals 1 instead of 0. This means that it will use the 2nd item in the list! So it will play: "http://musicsite.com/song2.mp3"

Whew. Are you getting this? Cause it's taking FOREVER to type out.

Wait!

We just found our first BUG! A bug is a programming error that leads to problems in the program running properly.

In this case, the variable currentsong increases by one eachtime the timer goes off, but what happens when it get's to 4? There is no 5th item in the list!

Well, even if the program does not crash from the error, it's not going to give the data from the list we want. What we need to do is teach it to start over at zero again!

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetTimerEvent(0.01);
     }

     timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          currentsong = currentsong + 1;
          if (currentsong > 3)
          {
              currentsong = 0;
          }
          llSetTimerEvent(30.0);
     }

}

Now we just added a flow control. In this case, an IF decision!

if (currentsong > 3)
          {
              currentsong = 0;
          }

So, the line if (currentsong > 3) seems simple enough. IF currentsong is greater than 3, then the variable currentsong needs to equal zero again! Notice the commands to run if the IF decision is true are held in brackets again {}.

Now, if the program increases currentsong to higher than 3, it makes it equal 0 again and starts withthe first song in the list again!

Woot!

Any of this making sense yet?

Now, on with the show:

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
list songlengths = [300,200,234,98];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetTimerEvent(0.01);
     }

     timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          llSetTimerEvent(llList2Float(songlengths, currentsong));
          currentsong = currentsong + 1;
          if (currentsong > 3)
          {
              currentsong = 0;
          }
     }

}

Well. In the above, we added a new list called 'songlengths' and changed the llSetTimerEvent in the 'timer()' event to read it.

Notice that the songlengths is a number, and in this case represents the number of seconds that the song will play. Each of the 4 entries of the list are placed to match the 4 songs in the list 'songs'.

The line:
llSetTimerEvent(llList2Float(songlengths, currentsong));

Sets the timer based on the list 'songlengths'. Note that the command to pull the date from the list is now llList2Float. This means it reads the data as a float variable. A float is like an integer, but can deal with numbers that are not whole. While an Integer might be 1, or 100, or 3920102; a float can be 1.4, or 45.1, or even 0.1

Since the llSetTimerEvent MUST use a float number, the llList2Float converts the data in the songlengths list to something it can understand and read. So when it places song 0, it will play the song 'song1.mp3' for about 300.0 seconds.

Are we all following still?

Tutorials


TutorialRobotics

Robotics

This page is currently under construction (But don't let that stop you contributing)- Tip

The objective of this page is to provide information, techniques and tips on the scripting of autonomous objects (And to give me some practice in formating a wiki page Tip). That is, objects that move on their own.

This tutorial is intended for those with a good general knowledge of LSL and programming.

The term 'robot' will be used on this page to describe such an autonomous object.

Basics

Functions and events you need to know and understand

Functions | Events | Notes
llSetPos, llSetPrimitiveParams |  | For non physical movement
llTarget, llTargetRemove | at_target, not_at_target | In order to know whether or not the robot has reached a specific location
llRotTarget, llRotTargetRemove | at_rot_target, not_at_rot_target | In order to know whether or not the robot is facing in a specific direction
 | collision, collision_start, collision_end | In order to know when the robot has hit something
llLookAt, llRotLookAt , llSetRot |  | To make a robot face a specific direction. Not recomended for vehicle based robots.
llSetForce, llSetForceAndTorque, llApplyImpulse, llMoveToTarget, llStopMoveToTarget |  | For non-vehicle, physics based movement
llSetBuoyancy |  | In order to make small physics based robots hover. Not recomended for vehicle based robots.
llSensor, llSensorRepeat | sensor, no_sensor | In order to detect what is around the the robot

Movement

You have a choice of three different principles for movement each with their own advantages and restrictions:
  • Non-physics based
  • Physics based non-vehicular
  • Vehicular

Non-physics based

A non-physical robot can be moved using llSetPos, or llSetPrimitiveParams. Rotation can be achieved using llSetRot, llLookat or llRotLookAt depending on the application. The comments for llSetPos have further information on how to get round some of the limitations for long distance movement.

llSetPos has a 10-meter range limit. There are a few ways to get around this, but the fastest is to use this bit of code:

vector target;
while(llVecDist(llGetPos(), target) > 0.01) llSetPos(target);
or so that you dont use energy
warpPos( vector destpos )
{   //R&D by Keknehv Psaltery, 05/25/2006
    //with a little pokeing by Strife, and a bit more
    //some more munging by Talarus Luan
    //Final cleanup by Keknehv Psaltery
    // Compute the number of jumps necessary
    integer jumps = (integer)(llVecDist(destpos, llGetPos()) / 10.0) + 1;
    // Try and avoid stack/heap collisions
    if (jumps > 100 )
        jumps = 100;    //  1km should be plenty
    list rules = [ PRIM_POSITION, destpos ];  //The start for the rules list
    integer count = 1;
    while ( ( count = count << 1 ) < jumps)
        rules = (rules=[]) + rules + rules;   //should tighten memory use.
    llSetPrimitiveParams( rules + llList2List( rules, (count - jumps) << 1, count) );
}

Here is an example of a non physical robot by AnorcaCalamari
//Written by Anorca Calamari on the TG
//This robot moves non-physically.
//It only turns phantom so that you can walk through it. It does not need to be phantom to go through walls.
//It randomly chooses targets within 50 meters excluding the owner.
//If noone is within 50 meters or only the owner is within 50 meters it increases the range. It continues
//increasing till it finds a target and moves there,at which point it resets to 50.

integer range=50;

default
{
    touch_start(integer total_number)
    {
        if(llDetectedKey(0) == llGetOwner())
        {
            llSetStatus(STATUS_PHANTOM,TRUE);
            llOwnerSay("ROBOT ON");
            state on;
        }
    }

}

state on
{
    state_entry()
    {
        llSensorRepeat("","",AGENT,range,PI,2.5);
    }

    sensor(integer num_detected)
    {
        integer target = (integer)llFrand((float)num_detected);

        if(llDetectedKey(target) == llGetOwner())
        {
            integer target = (integer)llFrand((float)num_detected);
        }

        if(num_detected == 0 | num_detected == 1 && llDetectedKey(0) == llGetOwner())
        {
            integer range=range+10;
        }

        while(llGetPos() != llDetectedPos(target) + <0,0,2> && llDetectedKey(target) != llGetOwner())
        {
            llSetPos(llDetectedPos(target) + <0,0,2>);
        }

        if(llGetPos() == llDetectedPos(target) + <0,0,2>)
        {
            range=50;
        }

    }

    touch_start(integer total_number)
    {
        if(llDetectedKey(0) == llGetOwner())
        {
            llOwnerSay("ROBOT OFF");
            llResetScript();
        }
    }
}

I have never used non-physics based movement, so I shall leave it to others to add additional information to this section - Tip

Physics, Non-Vehicle Based

Example
This example shows a simple ground based, hopping motion. It was created for a small, mass < 5, robot. The robot moves towards its target, the nearest avatar, by turning towards its target and then jumping forward. If it is too near its target, it jumps back. After a few seconds near an Avatar, it leaves that one and moves towards another. Note: You can also use llMoveToTarget for physics based vehicles.
integer current_food_level = 0;
integer maximum_food_level = 2;
key current_target;
float max_food_range = 4;
float min_food_range = 2;
vector kick_up = <0,0,1.5>;
float forward_kick = 3;

default
{
    state_entry()
    {
        llOwnerSay("Script reset:moving to searching state");
        llSetStatus(STATUS_PHYSICS,TRUE);
        llSetStatus(STATUS_ROTATE_X, FALSE);
        llSetStatus(STATUS_ROTATE_Y, FALSE);
        state searching;
    }
}

state searching
{
    state_entry()
    {
        llSensorRepeat("","",AGENT,50, 2 * PI, 3);
    }
    sensor(integer total)
    {
       if(total == 0)
       {
            current_target = llDetectedKey(0);
        }
        else
        {
            current_target = llDetectedKey(1);
        }
        state moving;
    }
    state_exit()
    {
        llSensorRemove();
    }
}
state moving
{
    //This state moves the robot towards or away from the selected Agent.
    state_entry()
    {
        llSensorRepeat("",current_target,AGENT,50, 2 * PI, 3);
        llSetTimerEvent(30);
    }
    no_sensor()
    {
        state searching;
    }
    state_exit()
    {
        llSensorRemove();
        llSetStatus(STATUS_PHANTOM,FALSE);
    }
    timer()
    {
        //If the target hant been reached after 30 seconds, the robot may be stuck behind something
        //So go phantom to get throught it.
        llSetStatus(STATUS_PHANTOM,TRUE);
    }
    sensor(integer total)
    {
      if(llVecDist(llGetPos(),llDetectedPos(0)) > max_food_range)
        {
           // llWhisper(0,"too far");
            llApplyImpulse((kick_up + <0,0,llFrand(1)>) * llGetMass(), FALSE);
            llApplyImpulse(-llVecNorm(llGetPos() - llDetectedPos(0)) * forward_kick * llGetMass(), FALSE);
        }
        else if(llVecDist(llGetPos(),llDetectedPos(0)) < min_food_range)
        {
            //move away from food
            llApplyImpulse((kick_up + <0,0,llFrand(1)>)  * llGetMass() , FALSE);
            llApplyImpulse(0.5 * llVecNorm(llGetPos() - llDetectedPos(0)) * forward_kick * llGetMass(), FALSE);
        }
        else
        {
            //Just right, start feeding
            state feeding;
        }
    }
}
state feeding
{
    state_entry()
    {
        llSensorRepeat("",current_target,AGENT,20, 2 * PI, 3);
    }
    no_sensor()
    {
      //  llWhisper(0,"target lost, searching");
        state searching;
    }
    state_exit()
    {
        llSensorRemove();
    }
    sensor(integer total)
    {
  //  llWhisper(0,"pong");
      if(llVecDist(llGetPos(),llDetectedPos(0)) > max_food_range)
        {
      //      llWhisper(0,"too far");
            state moving;
        }
        else if(llVecDist(llGetPos(),llDetectedPos(0)) < min_food_range)
        {
            //move away from food
            state moving;
        }
        current_food_level = current_food_level + 1;
      //  llWhisper(0,"yum");
        if(current_food_level > maximum_food_level)
        {
            current_food_level = 0;
          //  llWhisper(0,"bored");
            state searching;
        }
    }
}

Flying
To make a non-vehicle, physics based robot float in the air or water or to fly, you need to set the bouyancy to 1.0 to cancel the effect of gravity. Because of the effect of energy loss, robot's using this technique cannot have a mass greater than 90 units. Use the llGetMass function to find the mass. If the model is just over 90 units in mass, try hollowing some of the prims.

Although the Create a flying pet example is unfinished, the movement routine is a good starter for any small flying robots. This swarm example (wiki version) on the forums is similar, but with a more complex AI.

Example
This example is taken from a small, mass < 10, flying robot. It is based on the Create a flying pet example. The code requires brain script to tell it, via link messages, a waypoint, a speed and a target distance (How near to get to the waypoint). This script then moves the robot using llSetForce and llApplyImpulse to the waypoint. It sends a link message when it has reached the waypoint.

The example has simple collision detection. If it collides with an object or the ground it moves back, to the left and up.

float heartbeat = 0.5;
float z_force = 0.4;
//Go back, left and up if it collides with the land or an object
vector collision_force = <-0.25,0.25,0.75>;
vector target_position;
integer target;

//Start of code from wiki
vector AXIS_UP = <0,0,1>;
vector AXIS_LEFT = <0,1,0>;
vector AXIS_FWD = <1,0,0>;
// getRotToPointAxisAt()
// Gets the rotation to point the specified axis at the specified position.
// @param axis The axis to point. Easiest to just use an AXIS_* constant.
// @param target The target, in region-local coordinates, to point the axis at.
// @return The rotation necessary to point axis at target.
// Created by Ope Rand, modifyed by Christopher Omega rotation

rotation getRotToPointAxisAt(vector axis, vector target)
{
    return  llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}
//End of code from wiki

all_stop()
{
    llSetForceAndTorque(<0.0,0.0,0.0>, <0.0,0.0,0.0>, TRUE);
    llApplyImpulse(-llGetVel() * llGetMass(), FALSE);
    llApplyRotationalImpulse(-llGetTorque() * llGetMass(), FALSE);
}

default
{
    state_entry()
    {

        llSetBuoyancy(1);
        llSetStatus(STATUS_PHYSICS,TRUE);
        llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y, FALSE);
        llSetTimerEvent(heartbeat);
        //llVolumeDetect(TRUE);
        //llSetStatus(STATUS_PHANTOM,TRUE);
        all_stop();
    }
    link_message(integer sender_num,integer num, string data,key id)
    {
        if(num == 150)
        {
             list data = llParseStringKeepNulls( data, ["-=-"], [] );
             string Cmd = llList2String( data, 0 );
             if( Cmd == "MOVE" )
            {
                  target_position = (vector)llList2String( data, 1 );
                 float speed = (float)llList2String( data, 2 );
                 float range = (float)llList2String( data, 3 );
                llTargetRemove(target);
                target = llTarget(target_position,range);
                llSetForce(<speed,0,0> * llGetMass(),TRUE);
            }
            if(Cmd == "STOP" )
            {
                //Received a stop message
                llTargetRemove(target);
                all_stop();
            }
        }
     }
    not_at_target()
    {

        llRotLookAt(getRotToPointAxisAt(AXIS_FWD , target_position), 1, 0.5);
        vector currentpos = llGetPos();
        if(currentpos.z > target_position.z + 0.5)
        {
                //little push downwards
             llApplyImpulse(<0,0,-z_force> * llGetMass(),FALSE);
        }
        else
        {
            if(currentpos.z < target_position.z - 0.5)
            {
                //little upwards
                llApplyImpulse(<0,0,z_force> * llGetMass(),FALSE);
            }
        }
    }
    at_target(integer target_id,vector t_pos,vector o_pos)
    {
        all_stop();
        llTargetRemove(target);
        llMessageLinked(LINK_SET,150,"ATPOS","");
    }
    timer()
    {
        //Apply some friction
        llApplyImpulse(-llGetVel() * llGetMass() * 0.6,FALSE);
    }//end timer
    collision_start(integer num_detected)
    {
        //Hit something?
        //go abit higher
        llApplyImpulse((collision_force) * llGetMass(),TRUE);
    }
    land_collision_start(vector num_detected)
    {
        //Hit something?
        //go abit higher
        llApplyImpulse((collision_force) * llGetMass(),TRUE);
    }
    on_rez(integer param)
    {
        llTargetRemove(target);
        all_stop();
    }
}

state turn_to_target
{
    state_entry()
    {
        llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y, FALSE);
        llRotLookAt(getRotToPointAxisAt(AXIS_FWD , target_position), 0.1, 0.5);
        llSetTimerEvent(1);
    }
    timer()
    {
        state default;
    }
    link_message(integer sender_num,integer num, string data,key id)
    {
        if(num == 150)
        {
             list data = llParseStringKeepNulls( data, ["-=-"], [] );
             string Cmd = llList2String( data, 0 );
             if( Cmd == "MOVE" )
            {
                  target_position = (vector)llList2String( data, 1 );
                 float speed = (float)llList2String( data, 2 );
                 float range = (float)llList2String( data, 3 );
                llTargetRemove(target);
                target = llTarget(target_position,range);
                llSetForce(<speed,0,0> * llGetMass(),TRUE);
            }
        }
    }
}

Vehicle Based

Example
This is an example of a simple vehicle based 'engine'. It does nothing except move and change direction as directed by the contents of the link messages it receives. Creating functional modules like this has a number of advantages, including being able to take an 'engine' such as this and drop it into a robot with different behaviours.

This example was originally in a Star Wars droid with a single behaviour of moving from waypoint to waypoint My first vehicle - Tip. It then spent some time in a small follower style pet. The last incarnation was in a large (20 m) ship capable of navigating across void sims.

float speed_multi = 3;
float turn_multi = 1;
set_motor(vector speed,vector turn)
{
      //      llOwnerSay("speed = " + (string)(speed * speed_multi * llGetMass()) + " turn = " +(string)turn);
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, speed * speed_multi);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, turn* turn_multi);
    }
default
{
    state_entry()
    {

        integer chosen_type = VEHICLE_TYPE_CAR;

        llSetVehicleType(chosen_type);

        llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 10.0);

        llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 0.1);

        llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 0.1);

        llRemoveVehicleFlags(-1);

        llSetVehicleFlags(VEHICLE_FLAG_NO_DEFLECTION_UP | VEHICLE_FLAG_LIMIT_MOTOR_UP);
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, ZERO_VECTOR);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, ZERO_VECTOR);
    }
    on_rez(integer param)
    {
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, ZERO_VECTOR);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, ZERO_VECTOR);
    }
     link_message(integer sender,integer num,string data,key id)
    {
        list Arguments = llParseStringKeepNulls( data, ["-=-"], [] );
        string Command = llList2String( Arguments, 0 );
        if( Command == "FWD" )
        {
            llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <1,0,0>* speed_multi);
        }
        if( Command == "STRT" )
        {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,ZERO_VECTOR);
        }
        if( Command == "ROT_LFT" )
        {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0,0,PI>);
        }
        if( Command == "ROT_RGT" )
        {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0,0,PI>);
        }
        if( Command == "LFT" )
        {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0,0,PI_BY_TWO>);
        }
        if( Command == "RGT" )
        {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0,0,-PI_BY_TWO>);
        }
        if( Command == "STP" )
        {
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,ZERO_VECTOR);
        }

    }
}

Sensors

There are a couple of 'gotcha's' with sensors.
  • Only the nearest 16 objects are detected
  • Only objects whose centers fall within the sensor sweep are detected
  • A sensor sweep detecting all around an object has a sweep of PI not 2 * PI as may be expected.
  • llSensor does not detect objects in adjacent sims. llSensorRepeat has limitations when detecting objects in adjancent sims

Location

Gimbal Mounts

Putting a sensor in its own prim has a number of advantages:
  • For ground based robots a problem can occur when a robot is travelling over uneven ground. If the sensor is in the root prim of the robot, it could end up pointing into the air or ground. With the sensor in a separate prim, its rotation can be altered to keep the sensor pointing in the correct direction. This is a common technique in RL robotics.
  • Mounting the sensor as far forward as possible has advatages for collision detection.
  • The sensor can be scripted to rotate slightly in the direction the robot is turning.

Sensor Arc

As noted, an arc of PI is required to detect object/avatars in a sphere around a robot, not TWO_PI. In practice, if a complete sphere is what is required, it doesnt matter whethere PI or TWO_PI are used. But it must be taken into account if less than a complete sphere is required.

If an animal is being simulated, then the animals field of view is, in part, determined by whether it is hunting or hunted. Hunters tend to have their eyes looking forward to give them good depth perception but with a narrower field of view. The hunted have their eyes either side of their head giving them a wide field of view, so they can see hunters behind them. Think of the difference between the eye position of a cat compared with a rabbit. Take this into account when deciding on the sensor arc.

Collision Avoidance

The objective of collision avoidance is to detect a potential obstacle and then take action to avoid a collision.

Sensor Range and Repeat Rate

It is worth doing a few rough calculations when deciding on the arc, rate and range parameters of your sensor sweep.

Example How far ahead does a robot moving at 10 meters a second have to look to have enough time to spot potential obstacles?
Assumptions, a one second sensor sweep, ignore the limitations of sensors with regards over 16 objects and only centres being detected.
  • In one second, the robot travels 10 meters, therefore in the next second it will collide with any object within the 10 meters ahead. A one second sensor with a 10 meter range will pick up any hazzards.

    • If the robot is 10 meters long (I like big robots - Tip) then the front of the robot could be five meters ahead of where the sensor range is measured from. So a 15 meter sensor range is needed.

    • If the object that it is most likely to be encountered is a similar sized robot, then the target's front edge is five meters ahead of the point that will be detected. Therefore a 20 meter sensor range is needed.

    • If the object is another robot, it may be moving straight at the orginal robot at 10 meters per second. Sensor range needed is now 30 meters.

    • If the robot is vehicle based, the VEHICLE_LINEAR_MOTOR_TIMESCALE and other vehicle related parameters will cause a delay before the robot actually changes direction or speed. If this delay is around a second, then another 10 meters needs to be added to the sensor range.

    • Therefore, for a robot moving at 10 meters a second, a sensor range of 40 meters may be required. Obviously increasing the sensor repeat rate will reduce the sensor range required.

    Thinking ahead

    Not all the moving objects detected in a sensor sweep are a hazard, it depends on the direction they are moving in. Whether or not an object is on a collision course can be determined as follows:

      • Calculate the current distance between the robot and the detected object using llVecDist
      • Calculate where the object and robot will be in one second time by using llGetVel and llDetectedVel.
      • Calculate the distance between the two new positions
      • If the distance is increasing, they are not on a collision course.

      • Better: if (pos1 - pos2) * (vel1 - vel2) > 0 then the two objects are moving away from each other. -SeifertSurface

    And/Or
    • Calculate whether the object is to the left or right of the robot. Example function.
    • Calculate whether the object is moving to the right or left of the robot
    • If the object is to the left of the robot and moving to the left or vice versa, its not on a direct collision course

    Or
    • Calculate whether or not the two paths intersect.

    Faking Collision Avoidance

    Collision avoidance is tricky. In a controlled environment (hint Void sims) good results can be achieved. In the real world (err... you know what I mean), it is not easy to detect all potential hazards or decide how to avoid them. A simple but effective alternative is to fake it with a invisible bumper.

    Getting Stuck

    It's a one in a million chance, but very very occasionally a robot may get stuck. Trapped between two prims or unable to navigate around an obsticle, the robot constantly collides with the blocking object.

    In many cases, dependant on relative speeds, the robot can inter-penetrate I know there is a better word, when I'm awake I'll remember - Tip the obstacle. In this case, physics wil be turned off.

    For the creator this is another interesting problem, but for everyone else this is a Bad Thing. Constant collisions between objects is irritating especially if the noise is not suppressed. It is also a major drain on simulator resources, and may result in the Lindens tapping on your door.

    First problem that has to be solved is, how does the robot know it is stuck, A number of techniques can be used in combination depending on the scenario:
    • Has it taken too long to reach its destination? When a new movement target is selected, estimate the time it shoud take to reach the target. If after this time, plus a bit, the robot has not reached its destination, take action. This method can catch the situations where the robot isnt stuck, but is unable to manouver out of a 'trap' and ends up moving in a loop.
    • Check the robots speed. If it is not moving, and it should be, it could be stuck.
    • Has physics been turned off?
    • Constantly colliding with the same object can also be a good sign that the robot is stuck, unless the object is below the robot in which case it could be the floor.

    Like many aspects of behaviours, there is no one 'Flag' that can read to determine if a robot is stuck. Its case of experimenting, finding out what in practice causes your robot to become trapped and then figuring out how to detect it. Try having the 'is it stuck' functionality as a single script which sends out a link message when it thinks the robot is stuck.

    The action you take depends on whether you want the robot to continue onto its destination or not. If not, select a new destination, preferable in the opposite direction to the obsticle.

    If the robot still needs to get to its original destination, the action depends on how sophisticated your behaviours are. A simple trick is to turn the robot phantom for a while and let in move through the blocking object. Use the Collision event to determine when the robot has reached the other side.

    Behaviours

    External References

    CW Reynolds page on steering behaviours. Craig Reynolds created the flocking algorithm. This is a must read. The java applets are great, but there is more information in the actual paper itself.

    AI planning in the FEAR game. Top of the right hand column are some presentations describing some of the high level strategies used to develop an AI capable of planning ahead to achieve its goal.

    Functions | Physics | Vehicles


Tutorials

See LSLTutorials.


TutorialVehicle

Tutorials: Vehicles

11. Vehicles
11.1. Overview
11.2. Warnings
11.3. Definitions
11.4. Setting the Vehicle Type
11.5. Linear and Angular Deflection
11.6. Moving and Steering the Vehicle
11.7. The Linear Motor
11.8. The Angular Motor
11.9. Using the Camera to Steer
11.10. The Vertical Attractor
11.11. Banking
11.12. Friction Timescales
11.13. Buoyancy
11.14. Hover
11.15. Reference Frame
C. Constants
C.24. Vehicle Parameters
C.25. Vehicle Flags
C.26. Vehicle Types

Chapter 11. Vehicles

Custom vehicles can be constructed and controlled using the LSL. This chapter will cover the basics of how vehicles work, the terms used when describing vehicles, and a more thorough examination of the api available.

There are several ways to make scripted objects move themselves around. One way is to turn the object into a "vehicle". This feature is versatile enough to make things that slide, hover, fly, and float. Some of the behaviors that can be enabled are:

  • deflection of linear and angular velocity to preferred axis of motion
  • asymmetric linear and angular friction
  • hovering over terrain/water or at a global height
  • banking on turns
  • linear and angular motor for push and turning

11.1. Overview
Each scripted object can have one vehicle behavior that is configurable through the llSetVehicleType, llSetVehicleFloatParam, llSetVehicleVectorParam, llSetVehicleRotationParam, llSetVehicleFlags, and llRemoveVehicleFlags library calls.

These script calls are described in more detail below, but the important thing to notice here is that the vehicle behavior has several parameters that can be adjusted to change how the vehicle handles. Depending on the values chosen the vehicle can veer like a boat in water, or ride like a sled on rails.

Setting the vehicle flags allow you to make exceptions to some default behaviors. Some of these flags only have an effect when certain behaviors are enabled. For example, the VEHICLE_FLAG_HOVER_WATER_ONLY will make the vehicle ignore the height of the terrain, however it only makes a difference if the vehicle is hovering.

11.2. Warnings
Vehicles are a work in progress and will likely experience changes in future versions of Second Life. Some of the details of vehicle behavior may be changed as necessary to ensure stability and user safety. In particular, many of the limits and defaults described in the appendices will probably change and should not be relied upon in the long term.

It is not recommended that you mix vehicle behavior with some of the other script calls that provide impulse and forces to the object, especially llSetBuoyancy, llSetForce, llSetTorque, and llSetHoverHeight.

While the following methods probably don't cause any instabilities, their behavior may conflict with vehicles and cause undesired and/or inconsistent results, so use llLookAt, llRotLookAt, llMoveToTarget, and llTargetOmega at your own risk.

If you think you have found a bug relating to how vehicle's work, one way to submit the problem is to give a copy of the vehicle and script to Andrew Linden with comments or a notecard describing the problem. Please name all submissions "Bugged Vehicle XX" where XX are your Second Life initials. The vehicle and script will be examined at the earliest convenience.

11.3. Definitions
The terms "roll", "pitch", and "yaw" are often used to describe the modes of rotations that can happen to a airplane or boat. They correspond to rotations about the local x-, y-, and z-axis respectively.

z-axis  .
      yaw-axis /|\
                |     __. y-axis
   ._        ___|      /| pitch-axis
  _||\       \\ |\.   /
  \|| \_______\_|__\_/_______
   | _ _   o o o o o o o    |\_  ______\ x-axis
   // ./_______,----,__________)       / roll-axis
  /_,/        //  ./
             /__,/

The right-hand-rule, often introduced in beginning physics courses, is used to define the direction of positive rotation about any axis. As an example of how to use the right hand rule, consider a positive rotation about the roll axis. To help visualize how such a rotation would move the airplane, place your right thumb parallel to the plane's roll-axis such that the thumb points in the positive x-direction, then curl the four fingers into a fist. Your fingers will be pointing in the direction that the plane will spin.

.-.--.--.--.              __
   / /  /  /  _ \            /  \
  (-(- (- (- (   | _________|______\ axis of
   \.\._\._\._)  |          |      / rotation
    |           \:__,---.  \|/
    |                    |  + positive
     \           .,_.___.'    rotation
      \_ ^ `.__,/
      |      /
      |      |

Many of the parameters that control a vehicle's behavior are of the form: VEHICLE_BEHAVIOR_TIMESCALE. A behavior's "timescale" can usually be understood as the time for the behavior to push, twist, or otherwise affect the vehicle such that the difference between what it is doing, and what it is supposed to be doing, has been reduced to 1e of what it was, where e is the natural exponent (approximately 2.718281828). In other words, it is the timescale for exponential decay toward full compliance to the desired behavior. When you want the vehicle to be very responsive use a short timescale of one second or less, and if you want to disable a behavior then set the timescale to a very large number like 300 (5 minutes) or more. Note, for stability reasons, there is usually a limit to how small a timescale is allowed to be, and is usually on the order of a tenth of a second. Setting a timescale to zero is safe and is always equivalent to setting it to its minimum. Any feature with a timescale can be effectively disabled by setting the timescale so large that it would take them all day to have any effect.

11.4. Setting the Vehicle Type
Before any vehicle parameters can be set the vehicle behavior must first be enabled. It is enabled by calling llSetVehicleType with any VEHICLE_TYPE_*, except VEHICLE_TYPE_NONE which will disable the vehicle. See the vehicle type constants section for currently available types. More types will be available soon.

Setting the vehicle type is necessary for enabling the vehicle behavior and sets all of the parameters to its default values. For each vehicle type listed we provide the corresponding equivalent code in long format. It is important to realize that the defaults are not the optimal settings for any of these vehicle types and that they will definitely be changed in the future. Do not rely on these values to be constant until specified.

Should you want to make a unique or experimental vehicle you will still have to enable the vehicle behavior with one of the default types first, after which you will be able to change any of the parameters or flags within the allowed ranges.

Setting the vehicle type does not automatically take controls or otherwise move the object. However should you enable the vehicle behavior while the object is free to move and parked on a hill then it may start to slide away.

We're looking for new and better default vehicle types. If you think you've found a set of parameters that make a better car, boat, or any other default type of vehicle then you may submit your proposed list of settings to Andrew Linden via a script or notecard.

11.5. Linear and Angular Deflection
A common feature of real vehicles is their tendency to move along "preferred axes of motion". That is, due to their wheels, wings, shape, or method of propulsion they tend to push or redirect themselves along axes that are static in the vehicle's local frame. This general feature defines a class of vehicles and included in this category a common dart is a "vehicle": it has fins in the back such that if it were to tumble in the air it would eventually align itself to move point-forward -- we'll call this alignment effect angular deflection.

A wheeled craft exhibits a different effect: when a skateboard is pushed in some direction it will tend to redirect the resultant motion along that which it is free to roll -- we'll call this effect linear deflection.

So a typical Second Life vehicle is an object that exhibits linear and/or angular deflection along the "preferential axes of motion". The default preferential axes of motion are the local x- (at), y- (left), and z- (up) axes of the local frame of the vehicle's root primitive. The deflection behaviors relate to the x-axis (at): linear deflection will tend to rotate its velocity until it points along it's positive local x-axis while the angular deflection will tend to reorient the vehicle such that it's x-axis points in the direction that it is moving. The other axes are relevant to vehicle behaviors that are described later, such as the vertical attractor which tries to keep a vehicle's local z-axis pointed toward the world z-axis (up). The vehicle axes can be rotated relative to the object's actual local axes by using the VEHICLE_REFERENCE_FRAME parameter, however that is an advanced feature and is covered in detail in a later section of these documents.

Depending on the vehicle it might be desirable to have lots of linear and/or angular deflection or not. The speed of the deflections are controlled by setting the relevant parameters using the llSetVehicleFloatParam script call. Each variety of deflection has a "timescale" parameter that determines how quickly a full deflection happens. Basically the timescale it the time coefficient for exponential decay toward full deflection. So, a vehicle that deflects quickly should have a small timescale. For instance, a typical dart might have a angular deflection timescale of a couple of seconds but a linear deflection of several seconds; it will tend to reorient itself before it changes direction. To set the deflection timescales of a dart you might use the lines below:

llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 2.0);
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 6.0);

Each variety of deflection has an "efficiency" parameter that is a slider between 0.0 and 1.0. Unlike the other efficiency parameters of other vehicle behaviors, the deflection efficiencies do not slide between "bouncy" and "damped", but instead slide from "no deflection whatsoever" (0.0) to "maximum deflection" (1.0). That is, they behave much like the deflection timescales, however they are normalized to the range between 0.0 and 1.0.

11.6. Moving and Steering the Vehicle
Once enabled, a vehicle can be pushed and rotated by external forces and/or from script calls such as llApplyImpulse, however linear and angular motors have been built in to make motion smoother and easier to control. Their directions can be set using the llSetVehicleVectorParam call. For example, to make the vehicle try to move at 5 meters/second along its local x-axis (the default look-at direction) you would put the following line in your script:

llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<5, 0, 0>);

The motor strength is not the full story, since you can also control how fast the motor engages (VEHICLE_LINEAR_MOTOR_TIMESCALE) and there is a parameter that causes the motor's effectiveness to decay over time (VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE).

Steering the vehicle involves setting the VEHICLE_ANGULAR_MOTOR_DIRECTION and related parameters. It is also possible to set some flags that allow the angular motor slave to your camera view when in mouselook.

For more details about the vehicle motors read the sections on the linear and angular motors below.

11.7. The Linear Motor
The parameters that control the linear motor are:

- VEHICLE_LINEAR_MOTOR_DIRECTION

A vector. It is the velocity (meters/sec) that the vehicle will try to attain. It points in the vehicle's local frame, and has a maximum length of 40.

- VEHICLE_LINEAR_MOTOR_OFFSET

A vector. It is the offset point from the vehicle's center of mass at which the linear motor's impulse is applied. This allows the linear motor to also cause rotational torque. It is in the vehicle's local frame and its maximum length is 100 meters! No need to worry about stability -- if the vehicle starts to spin too fast (greater than about 4*PI radians per second) then angular velocity damping will kick in. The reason the offset is allowed to be so large is so that it can compete with the other vehicle behaviors such as angular deflection and the vertical attractor. Some of the other vehicle behaviors may drastically reduce the effective torque from the linear motor offset, in which case a longer leverage arm may help.

- VEHICLE_LINEAR_MOTOR_TIMESCALE

A float. Determines how long it takes for the motor to push the vehicle to full speed. Its minimum value is approximately 0.06 seconds.

- VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE

A float. The effectiveness of the motor will exponentially decay over this timescale, but the effectiveness will be reset whenever the motor's value is explicitly set. The maximum value of this decay timescale is 120 seconds, and this timescale is always in effect.

The flags that affect the linear motor are:

- VEHICLE_FLAG_LIMIT_DEFLECTION_UP

Useful for "ground vehicles". Setting this flag will clamp the z-component of the linear motor (in world frame) to prevent it from defeating gravity.

Setting the motor speed is not enough to enable all interesting vehicles. For example, some will want a car that immediately gets up to the speed they want, while others will want a boat that slowly climbs up to its maximum velocity. To control this effect the VEHICLE_LINEAR_MOTOR_TIMESCALE parameter can be used. Basically the "timescale" of a motor is the time constant for the vehicle to exponentially accelerate toward its full speed.

What would happen if you were to accidentally set the vehicle's linear velocity to maximum possible speed and then let go? It would run away and never stop, right? Not necessarily: an automatic "motor decay" has been built in such that all motors will gradually decrease their effectiveness after being set.

Each time the linear motor's vector is set its "grip" immediately starts to decay exponentially with a timescale determined by the VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, such that after enough time the motor ceases to have any effect. This decay timescale serves two purposes. First, since it cannot be set longer than 120 seconds, and is always enabled it guarantees that a vehicle will not push itself about forever in the absence of active control (from keyboard commands or some logic loop in the script). Second, it can be used to push some vehicles around using a simple impulse model. That is, rather than setting the motor "on" or "off" depending on whether a particular key is pressed "down" or "up" the decay timescale can be set short and the motor can be set "on" whenever the key transitions from "up" to "down" and allowed to automatically decay.

Since the motor's effectiveness is reset whenever the motor's vector is set, then setting it to a vector of length zero is different from allowing it to decay completely. The first case will cause the vehicle to try to reach zero velocity, while the second will leave the motor impotent.

The two motor timescales have very similar names, but have different effects, so try not to get them confused. VEHICLE_LINEAR_MOTOR_TIMESCALE is the time for motor to "win", and VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE is the time for the motor's "effectiveness" to decay toward zero. If you set one when you think you are changing the other you will have frustrating results. Also, if the motor's decay timescale is shorter than the regular timescale, then the effective magnitude of the motor vector will be diminished.

11.8. The Angular Motor
The parameters that control the angular motor are:

- VEHICLE_ANGULAR_MOTOR_DIRECTION

A vector. It is the angular velocity (radians/sec) that the vehicle will try to rotate. It points in the vehicle's local frame, and has a maximum value of 4*PI (two revolutions per second).

- VEHICLE_ANGULAR_MOTOR_TIMESCALE

A float. Determines how long it takes for the motor to spin the vehicle to full speed. Its minimum value is approximately 0.06 seconds.

- VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE

A float. The effectiveness of the motor will exponentially decay over this timescale, but the effectiveness will be reset whenever the motor's value is explicitly set. The maximum value of this decay timescale is 120 seconds, and this timescale is always in effect.

Like the linear motor the angular motor can be set explicitly, and has magnitude/direction, a timescale, and a decay timescale.

When it comes to actually steering a vehicle there are several ways to do it. One way would be for the script to grab keyboard input and to explicitly turn the motor on/off based on which keys are pressed. When steering this way you probably don't want it to turn very far or for very long. One way to do it using the angular motor would be to leave the decay timescale long, enable a significant amount of angular friction (to quickly slow the vehicle down when the motor is turned off) then set the angular motor to a large vector on a key press, and set it to zero when the key is released. That has the effect of making the vehicle unresponsive to external collisions, due to the angular friction.

Another way to do it is to set the VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE to a short value and push the vehicle about with a more impulsive method that sets the motor fast on a key press down (and optionally setting the motor to zero on a key up) relying on the automatic exponential decay of the motor's effectiveness rather than a constant angular friction.

Finally, it may be possible to discard the angular motor entirely and use the VEHICLE_LINEAR_MOTOR_OFFSET. Whenever the offset has a component that is perpendicular to the direction of the linear motor the vehicle will rotate as it travels. Note, with the incorrect values for offset and strength the linear motor effect can easily cause the vehicle to tumble and spin uncontrollably, so experiement with small offsets first!.

Setting the angular motor to zero magnitude is different from allowing it to decay. When the motor completely decays it no longer affects the motion of the vehicle, however setting it to zero will reset the "grip" of the vehicle and will make the vehicle try to achieve zero angular velocity.

Many real vehicles bank (roll about their forward axis) to effect a turn, such as motorcycles and airplanes. To make it easier to build banking vehicles there is banking behavior available which can be controlled by setting other parameters and is described in more detail here.

It is also possible to make a vehicle turn in response to changing the camera view (right now this only works in mouselook).

11.9. Using the Camera to Steer
The vehicle can be instructed to rotate its forward axis to point in the same direction as the camera view. This is achieved by setting some flags that change how the VEHICLE_ANGULAR_MOTOR_DIRECTION is interpreted. When used properly this feature has the advantage of being able to provide simple and stable steering that is resilient to bad render frame rates on the client.

The flags that affect the angular motor are:

- VEHICLE_FLAG_MOUSELOOK_STEER

Steer the vehicle using the mouse. Use this flag to make the angular motor try to make the vehicle turn such that its local x-axis points in the same direction as the client-side camera.

- VEHICLE_FLAG_MOUSELOOK_BANK

Same as above, but relies on banking. It remaps left-right motions of the client camera (also known as "yaw") to rotations about the vehicle's local x-axis (also known as "roll").

- VEHICLE_FLAG_CAMERA_DECOUPLED

Makes mouselook camera rotate independently of the vehicle. By default the client mouselook camera will rotate about with the vehicle, however when this flag is set the camera direction is independent of the vehicle's rotation.

When using the VEHICLE_FLAG_MOUSELOOK_STEER (or VEHICLE_FLAG_MOUSELOOK_BANK) the meaning of the VEHICLE_ANGULAR_MOTOR_DIRECTION parameter subtly changes. Instead of representing the "angular velocity" of the motor the components of the parameter scale the "measured angular velocity" (as determined by the rotation between the client's camera view direction and the forward-axis of the vehicle) to compute the "final angular velocity". That is, suppose you set the angular motor to <0, 0, 5>, then moved the camera view to be PI/4 radians to the left of the vehicle's forward axis, and down PI/8 toward the ground. The measured angular velocity would be <0, -PI/8, PI/4> radians/second, but the final velocity would be <0, 0, 5*PI/4>: the vehicle will turn left, but will not dip its nose down. Thus, by setting a component of the VEHICLE_ANGULAR_MOTOR_DIRECTION to zero, one can negate the pitch or yaw response of the motor, or even scale one to be much more responsive than the other.

The VEHICLE_ANGULAR_MOTOR_TIMESCALE still has an effect when using mouselook control, and scales the global responsiveness of the angular motor. The VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, on the other hand, is ignored when using mouselook controls.

11.10. The Vertical Attractor
Some vehicles, like boats, should always keep their up-side up. This can be done by enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency, and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an efficiency of 1.0 will cause the spring to reach it's equilibrium with exponential decay.

llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 4.0);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5);

The vertical attractor is disabled by setting its timescale to anything larger than 300 seconds.

Note that by default the vertical attractor will prevent the vehicle from diving and climbing. So, if you wanted to make a airplane you would probably want to unlock the attractor around the pitch axis by setting the VEHICLE_FLAG_LIMIT_ROLL_ONLY bit:

llSetVehicleFlags(VEHICLE_FLAG_LIMIT_ROLL_ONLY);

11.11. Banking
The vertical attractor feature must be enabled in order for the banking behavior to function. The way banking works is this: a rotation around the vehicle's roll-axis will produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude of the yaw effect will be proportional to the VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's velocity along it's preferred axis of motion.

The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it's positive then any positive rotation (by the right-hand rule) about the roll-axis will effect a (negative) torque around the yaw-axis, making it turn to the right -- that is the vehicle will lean into the turn, which is how real airplanes and motorcycle's work. Negating the banking coefficient will make it so that the vehicle leans to the outside of the turn (not very "physical" but might allow interesting vehicles so why not?).

The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making banking vehicles do what you want rather than what the laws of physics allow. For example, consider a real motorcycle... it must be moving forward in order for it to turn while banking, however video-game motorcycles are often configured to turn in place when at a dead stop -- because they're often easier to control that way using the limited interface of the keyboard or game controller. The VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic banking by functioning as a slider between a banking that is correspondingly totally static (0.0) and totally dynamic (1.0). By "static" we mean that the banking effect depends only on the vehicle's rotation about it's roll-axis compared to "dynamic" where the banking is also proportional to it's velocity along it's roll-axis. Finding the best value of the "mixture" will probably require trial and error.

The time it takes for the banking behavior to defeat a pre-existing angular velocity about the world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to bank quickly then give it a banking timescale of about a second or less, otherwise you can make a sluggish vehicle by giving it a timescale of several seconds.

11.12. Friction Timescales
VEHICLE_LINEAR_FRICTION_TIMESCALE is a vector parameter that defines the timescales for the vehicle to come to a complete stop along the three local axes of the reference frame. The timescale along each axis is independent of the others. For example, a sliding ground car would probably have very little friction along its x- and z-axes (so it can easily slide forward and fall down) while there would usually significant friction along its y-axis:

llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1000, 3, 1000>);

Remember that a longer timescale corresponds to a weaker friction, hence to effectively disable all linear friction you would set all of the timescales to large values.

Setting the linear friction as a scalar is allowed, and has the effect of setting all of the timescales to the same value. Both code snippets below are equivalent, and both make friction negligible:

set all linear friction timescales to 1000
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1000, 1000, 1000>);

same as above, but fewer characters
llSetVehicleFloatParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, 1000);

VEHICLE_ANGULAR_FRICTION_TIMESCALE is also a vector parameter that defines the timescales for the vehicle to stop rotating about the x-, y-, and z-axes, and are set and disabled in the same way as the linear friction.

11.13. Buoyancy
The vehicle has a built-in buoyancy feature that is independent of the llSetBuoyancy call. It is recommended that the two buoyancies do not mix! To make a vehicle buoyant, set the VEHICLE_BUOYANCY parameter to something between -1.0 (extra gravity) to 1.0 (full anti-gravity).

The buoyancy behavior is independent of hover, however in order for hover to work without a large offset of the VEHICLE_HOVER_HEIGHT, the VEHICLE_BUOYANCY should be set to 1.0.

It is not recommended that you mix vehicle buoyancy with the llSetBuoyancy script call. It would probably cause the object to float up into space.

11.14. Hover
The hover behavior is enabled by setting the VEHICLE_HOVER_TIMESCALE to a value less than 300 seconds; larger timescales totally disable it. Most vehicles will work best with short hover timescales of a few seconds or less. The shorter the timescale, the faster the vehicle will slave to is target height. Note, that if the values of VEHICLE_LINEAR_FRICTION_TIMESCALE may affect the speed of the hover.

Hover is independent of buoyancy, however the VEHICLE_BUOYANCY should be set to 1.0, otherwise the vehicle will not lift itself off of the ground until the VEHICLE_HOVER_HEIGHT is made large enough to counter the acceleration of gravity, and the vehicle will never float all the way to its target height.

The VEHICLE_HOVER_EFFICIENCY can be thought of as a slider between bouncy (0.0) and smoothed (1.0). When in the bouncy range the vehicle will tend to hover a little lower than its target height and the VEHICLE_HOVER_TIMESCALE will be approximately the oscillation period of the bounce (the real period will tend to be a little longer than the timescale).

For performance reasons, until improvements are made to the Second Life physics engine the vehicles can only hover over the terrain and water, so they will not be able to hover above objects made out of primitives, such as bridges and houses. By default the hover behavior will float over terrain and water, however this can be changed by setting some flags:

If you wanted to make a boat you should set the VEHICLE_HOVER_WATER_ONLY flag, or if you wanted to drive a hover tank under water you would use the VEHICLE_HOVER_TERRAIN_ONLY flag instead. Finally, if you wanted to make a submarine or a balloon you would use the VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT. Note that the flags are independent of each other and that setting two contradictory flags will have undefined behavior. The flags are set using the script call llSetVehicleFlags.

The VEHICLE_HOVER_HEIGHT determines how high the vehicle will hover over the terrain and/or water, or the global height, and has a maximum value of 100 meters and a minimum of just above 0. Values of 0 or less have no effect. Note that for hovering purposes the "center" of the vehicle is its "center of mass" which is not always obvious to the untrained eye, and it changes when avatar's sit on the vehicle. Note: in my testing, it appears that this is incorrect, and the height value is based off the prim-center of the root prim. -- Lex Neva

11.15. Reference Frame
The vehicle relies on the x- (at), y- (left), and z- (up) axes in order to figure out which way it prefers to move and which end is up. By default these axes are identical to the local axes of the root primitive of the object, however this means that the vehicle's root primitive must, by default, be oriented to agree with the designed at, left, and up axes of the vehicle. But, what if the vehicle object was already pre-built with the root primitive in some non-trivial orientation relative to where the vehicle as a whole should move? This is where the VEHICLE_REFERENCE_FRAME parameter becomes useful; the vehicle's axes can be arbitrarily reoriented by setting this parameter.

As an example, suppose you had built a rocket out of a big cylinder, a cone for the nose, and some stretched cut boxes for the fins, then linked them all together with the cylinder as the root primitive. Ideally the rocket would move nose-first, however the cylinder's axis of symmetry is its local z-axis while the default "at-axis" of the vehicle, the axis it will want to deflect to forward under angular deflection, is the local x-axis and points out from the curved surface of the cylinder. The script code below will rotate the vehicle's axes such that the local z-axis becomes the "at-axis" and the local negative x-axis becomes the "up-axis":

rotate the vehicle frame -PI/2 about the local y-axis (left-axis)
rotation rot = llEuler2Rot(<0, PI/2, 0>);
llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, rot);

Another example of how the reference frame parameter could be used is to consider flying craft that uses the vertical attractor for stability during flying but wants to use VTOL (vertical takeoff and landing). During flight the craft's dorsal axis should point up, but during landing its nose-axis should be up. To land the vehicle: while the vertical attractor is in effect, rotate the existing VEHICLE_REFERENCE_FRAME by +PI/2 about the left-axis, then the vehicle will pitch up such that it's nose points toward the sky. The vehicle could be allowed to fall to the landing pad under friction, or a decreasing hover effect.

C.24. Vehicle Parameters
Parameters

- VEHICLE_LINEAR_FRICTION_TIMESCALE
A vector of timescales for exponential decay of the vehicle's linear velocity along its preferred axes of motion (at, left, up). Range = [0.07, inf) seconds for each element of the vector.

- VEHICLE_ANGULAR_FRICTION_TIMESCALE
A vector of timescales for exponential decay of the vehicle's angular velocity about its preferred axes of motion (at, left, up). Range = [0.07, inf) seconds for each element of the vector.

- VEHICLE_LINEAR_MOTOR_DIRECTION
The direction and magnitude (in preferred frame) of the vehicle's linear motor. The vehicle will accelerate (or decelerate if necessary) to match its velocity to its motor. Range of magnitude = [0, 30] meters/second.

- VEHICLE_LINEAR_MOTOR_OFFSET
The offset point from the vehicle's center of mass at which the linear motor's impulse is applied. This allows the linear motor to also cause rotational torque. Range of magnitude = [0, 100] meters.

- VEHICLE_LINEAR_MOTOR_TIMESCALE
The timescale for exponential approach to full linear motor velocity.

- VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE
The timescale for exponential decay of the linear motor's magnitude.

- VEHICLE_ANGULAR_MOTOR_DIRECTION
The direction and magnitude (in preferred frame) of the vehicle's angular motor.The vehicle will accelerate (or decelerate if necessary) to match its velocity to its motor.

- VEHICLE_ANGULAR_MOTOR_TIMESCALE
The timescale for exponential approach to full angular motor velocity.

- VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE
The timescale for exponential decay of the angular motor's magnitude.

- VEHICLE_HOVER_HEIGHT
The height (above the terrain or water, or global) at which the vehicle will try to hover.

- VEHICLE_HOVER_EFFICIENCY
A slider between minimum (0.0 = bouncy) and maximum (1.0 = fast as possible) damped motion of the hover behavior.

- VEHICLE_HOVER_TIMESCALE
The period of bounce (or timescale of exponential approach, depending on the hover efficiency) for the vehicle to hover to the proper height.

- VEHICLE_BUOYANCY
A slider between minimum (0.0) and maximum anti-gravity (1.0).

- VEHICLE_LINEAR_DEFLECTION_EFFICIENCY
A slider between minimum (0.0) and maximum (1.0) deflection of linear velocity. That is, it's a simple scalar for modulating the strength of linear deflection.

- VEHICLE_LINEAR_DEFLECTION_TIMESCALE
The timescale for exponential success of linear deflection. It is another way to specify how much time it takes for the vehicle's linear velocity to be redirected to it's preferred axis of motion.

- VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY
A slider between minimum (0.0) and maximum (1.0) deflection of angular orientation. That is, it's a simple scalar for modulating the strength of angular deflection such that the vehicle's preferred axis of motion points toward it's real velocity.

- VEHICLE_ANGULAR_DEFLECTION_TIMESCALE
The timescale for exponential success of angular deflection. It's another way to specify the strength of the vehicle's tendency to reorient itself so that it's preferred axis of motion agrees with it's true velocity.

- VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY
A slider between minimum (0.0 = wobbly) and maximum (1.0 = firm as possible) stability of the vehicle to keep itself upright.

- VEHICLE_VERTICAL_ATTRACTION_TIMESCALE
The period of wobble, or timescale for exponential approach, of the vehicle to rotate such that it's preferred "up" axis is oriented along the world's "up" axis.

- VEHICLE_BANKING_EFFICIENCY
A slider between anti (-1.0), none (0.0), and maximum (1.0) banking strength.

- VEHICLE_BANKING_MIX
A slider between static (0.0) and dynamic (1.0) banking. "Static" means the banking scales only with the angle of roll, whereas "dynamic" is a term that also scales with the vehicle's linear speed.

- VEHICLE_BANKING_TIMESCALE
The timescale for banking to exponentially approach it's maximum effect. This is another way to scale the strength of the banking effect, however it affects the term that is proportional to the difference between what the banking behavior is trying to do, and what the vehicle is actually doing.

- VEHICLE_REFERENCE_FRAME
A rotation of the vehicle's preferred axes of motion and orientation (at, left, up) with respect to the vehicle's local frame (x, y, z).

Vehicle Flags
Constant | Description | Value
VEHICLE_FLAG_NO_DEFLECTION_UP | This flag prevents linear deflection parallel to world z-axis. This is useful for preventing ground vehicles with large linear deflection, like bumper cars, from climbing their linear deflection into the sky. | 1
VEHICLE_FLAG_LIMIT_ROLL_ONLY | For vehicles with vertical attractor that want to be able to climb/dive, for instance, airplanes that want to use the banking feature. | 2
VEHICLE_FLAG_HOVER_WATER_ONLY | Ignore terrain height when hovering. | 4
VEHICLE_FLAG_HOVER_TERRAIN_ONLY | Ignore water height when hovering. | 8
VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT | Hover at global height instead of height above ground or water. | 16
VEHICLE_FLAG_HOVER_UP_ONLY | Hover doesn't push down. Use this flag for hovering vehicles that should be able to jump above their hover height. | 32
VEHICLE_FLAG_LIMIT_MOTOR_UP | Prevents ground vehicles from motoring into the sky. This flag has a subtle effect when used with conjunction with banking: the strength of the banking will decay when the vehicle no longer experiences collisions. The decay timescale is the same as VEHICLE_BANKING_TIMESCALE . This is to help prevent ground vehicles from steering when they are in mid jump. | 64
VEHICLE_FLAG_MOUSELOOK_STEER | Steer the vehicle using the mouse. Use this flag to make the angular motor try to make the vehicle turn such that its local x-axis points in the same direction as the client-side camera. | 128
VEHICLE_FLAG_MOUSELOOK_BANK | Same as above, but relies on banking. It remaps left-right motions of the client camera (also known as "yaw") to rotations about the vehicle's local x-axis. | 256
VEHICLE_FLAG_CAMERA_DECOUPLED | Makes mouselook camera rotate independently of the vehicle. By default the client mouselook camera will rotate about with the vehicle, however when this flag is set the camera direction is independent of the vehicle's rotation. | 512

VEHICLE_TYPE_SLED

Simple vehicle that bumps along the ground, and likes to move along it's local X-axis.

// most friction for left-right, least for up-down
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <30, 1, 1000> );

// no angular friction
llSetVehicleVectorParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, <1000, 1000, 1000> );

// no linear motor
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 120 );

// no angular motor
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 120 );

// no hover (but with timescale of 10 sec if enabled)
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 10 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 10 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 0 );

// maximum linear deflection with timescale of 1 second
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 1 );

// no angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 10 );

// no vertical attractor (doesn't mind flipping over)
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 1000 );

// no banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 1 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 10 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT
                      | VEHICLE_FLAG_HOVER_UP_ONLY );

// set these flags (the limit_roll flag will have no effect
// until banking is enabled, if ever)
llSetVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                   | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                   | VEHICLE_FLAG_LIMIT_MOTOR_UP );

VEHICLE_TYPE_CAR

Another vehicle that bounces along the ground but needs the motors to be driver from external controls of a timer event.

// most friction for left-right, least for up-down
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <100, 2, 1000> );

// no angular friction
llSetVehicleVectorParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, <1000, 1000, 1000> );

// linear motor wins after about a second, decays after about a minute
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 1 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor wins after a second, decays in less time than that
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 1 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.8 );

// no hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 0 );

// maximum linear deflection with timescale of 2 seconds
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 2 );

// no angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 10 );

// critically damped vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 10 );

// weak negative critically damped banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, -0.2 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 1 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 1 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);

// set these flags
llSetVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                   | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                   | VEHICLE_FLAG_HOVER_UP_ONLY
                   | VEHICLE_FLAG_LIMIT_MOTOR_UP );

VEHICLE_TYPE_BOAT

Hovers over water with lots of friction and some angular deflection.

// least for forward-back, most friction for up-down
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <10, 3, 2> );

// uniform angular friction (setting it as a scalar rather than a vector)
llSetVehicleFloatParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, 10 );

// linear motor wins after about five seconds, decays after about a minute
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor wins after four seconds, decays in same amount of time
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 4 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 4 );

// hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 2.0 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 1 );

// halfway linear deflection with timescale of 3 seconds
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 3 );

// angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 5 );

// somewhat bouncy vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 5 );

// weak negative damped banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, -0.3 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 0.8 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 1 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);

// set these flags
llSetVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                   | VEHICLE_FLAG_HOVER_WATER_ONLY
                   | VEHICLE_FLAG_HOVER_UP_ONLY
                   | VEHICLE_FLAG_LIMIT_MOTOR_UP );

VEHICLE_TYPE_AIRPLANE

Uses linear deflection for lift, no hover, and banking to turn.

// very little friction along forward-back axis
llSetVehicleVectorParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, <200, 10, 5> );

// uniform angular friction
llSetVehicleFloatParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, 20 );

// linear motor
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 2 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 4 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 8 );

// no hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 1000 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 0 );

// linear deflection
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 0.5 );

// angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 1.0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 2.0 );

// vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.9 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 2 );

// banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 0.7 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 2 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                      | VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT
                      | VEHICLE_FLAG_HOVER_UP_ONLY
                      | VEHICLE_FLAG_LIMIT_MOTOR_UP );

// set these flags
llSetVehicleFlags( VEHICLE_FLAG_LIMIT_ROLL_ONLY );

VEHICLE_TYPE_BALLOON

Hover, and friction, but no deflection.

// uniform linear friction
llSetVehicleFloatParam( VEHICLE_LINEAR_FRICTION_TIMESCALE, 5 );

// uniform angular friction
llSetVehicleFloatParam( VEHICLE_ANGULAR_FRICTION_TIMESCALE, 10 );

// linear motor
llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_TIMESCALE, 5 );
llSetVehicleFloatParam( VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 60 );

// angular motor
llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0> );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, 6 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 10 );

// hover
llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 5 );
llSetVehicleFloatParam( VEHICLE_HOVER_EFFICIENCY, 0.8 );
llSetVehicleFloatParam( VEHICLE_HOVER_TIMESCALE, 10 );
llSetVehicleFloatParam( VEHICLE_BUOYANCY, 1 );

// no linear deflection
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 5 );

// no angular deflection
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 5 );

// no vertical attractor
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 1 );
llSetVehicleFloatParam( VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 1000 );

// no banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 0 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 0.7 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 5 );

// default rotation of local frame
llSetVehicleRotationParam( VEHICLE_REFERENCE_FRAME, <0, 0, 0, 1> );

// remove all flags
llRemoveVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
                      | VEHICLE_FLAG_HOVER_WATER_ONLY
                      | VEHICLE_FLAG_LIMIT_ROLL_ONLY
                      | VEHICLE_FLAG_HOVER_TERRAIN_ONLY
                      | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT
                      | VEHICLE_FLAG_HOVER_UP_ONLY
                      | VEHICLE_FLAG_LIMIT_MOTOR_UP );


TwosComplement

Two's Complement is the way most computers store negative numbers.

To find the negative of a number, write it in binary, then flip all the bits and add 1.

Example (in 8 bits):
.              19 = 0001 0011

flips all bits      1110 1100
add one           + 0000 0001
.                   __________
.             -19 = 1110 1101

Example 2:
.               1 = 0000 0001

flip all bits       1111 1110
add one           + 0000 0001
.                   __________
.              -1 = 1111 1111

Some less useful information follows.

Why is two's complement notation so common? Well, it has this really neat property:
In eight bits...

 10 = 00001010  ==>  -10 = 11110110

  8 = 00001000  ==>   -8 = 11111000

And then

  10   00001010
+  8  +00001000
----  ---------
  18   00010010  ==>  -18 = 11101110

 -10   11110110
+ -8  +11111000
----  ---------
 -18   11101110  ==>   18 = 00010010

As you can see, under at least addition and multiplication the computer doesn't have to care at all whether a number is positive or negative. This makes computer hardware much easier (and therefore cheaper) to produce: you only need one set of ADD instructions, not one for unsigned (all positive) integers and another one for signed (some positive, some negative) numbers. Imagine having to test whether a number is negative or positive before being able to decide how to add it to another number!

There is, however, a small disadvantage to two's complement notation. Since zero is considered a positive number (as its leading bit is, unsurprisingly, 0) there is one more negative number than positive numbers. See here:
In eight bits...

+-+-+-+-+-+-+-+---- (These are the values of the bits in 8 bit two's complement, by the way)
| | | | | | | |
v v v v v v v v

-
1
2 6 3 1
8 4 2 6 8 4 2 1

0 1 1 1 1 1 1 1 = 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127, the most positive number.

1 0 0 0 0 0 0 0 = -128, the most negative number.

Suppose we try to negate -128 to make +128 in eight bits.  Let's try it.
Invert the bits and add one:

   01111111
 + 00000001
   10000000  <-- oops!  -(-128) is -128.

Actually, it's 010000000, but we don't have enough bits to show that.

Note that LSL uses 32-bit integers and not 8-bit as in these examples.

BooleanLogic


type

See Types.


Typecast

Typecasting

Typecasting allows conversion between a value of one type to another.

Most typecasting in LSL actually involves converting values from one type to another. For example, when a string is cast to an integer, the string value is converted into a new integer value. The exception is casting between string and key, because a key is merely a specialized form of string.

We can do typecasting in one of two ways: Explicit Typecasting | Implicit Typecasting. In both cases, note that typecasting never modifies a value stored in a variable or list; rather, the result of typecasting the value is used in the rest of the expression.

Explicit Typecasting

Explicit typecasting is the process of converting between variable types by casting them in the form: (newtype)variable.

For example, to say an integer:
integer x = 5;
llSay(0, (string)x);

When converting a string to an integer, leading spaces are ignored, as are any characters after the run of digits. For example, all of the following convert to 42:
"42"
" 42" // leading spaces are ignored
"42 is the answer" // text after the number is ignored
"42,123,456" // only the first digit sequence is interpreted

All other strings convert to the value zero.

If you use explicit typecasting, the typecast will show up in the LSL bytecode. So (integer)((integer)a), will result in bytecode typecasting an integer to an integer. Which wastes cpu time and memory; which is detrimental to your script. -- BW

Implicit Typecasting

Implicit typecasts are like explicit typecasts except that the (type) notation is not actually seen; the compiler does the casting. Implicit typecasting also happens when a value is copied from a list. However, implicit typecasting can lead to unexpected behavior with mathematical operators.

Explicit typecasting is preferred over implicit typecasting, since it is often easier to see what is actually happening during the type conversion. Plus, explicit typecasting is prone to less compiler bugginess.

LSL supports two implicit typecasts:

integer to float
string to key

Note: as of SL 1.12.3 (6), I could also typecast (string) to (vector), and vise-versa. -ziphren

Examples:
float myFloat = 1; // 1 is an integer.
integer myInt = 3;
myFloat = myInt; // No typecasting needed.

string myString = "foobar";
key myKey = myString;
// ^^^ No typecasting needed, but bad coding practice (key variables should always contain UUID values).

// Without implicit typecasting, this is necessary:
float myFloat = (float)1; // or float myFloat = 1.0;
integer myInt = 4;
myFloat = (float)myInt;

// With integer -> float conversion, explicit typecasting is unnecessary since precision is never lost.

Any string can be used where a key value is required, such as storing a value into a variable of type key. Note that keys can only be explicitly typecast to and from strings. To cast a value from any other type to a key, first cast it to a string.

  • Many function calls will not compile if (string)my_key isn't explicitly typecast, while others will accept my_key on its own.
  • Obviously, a script will not work correctly if it's expecting a key in the form "66864f3c-e095-d9c8-058d-d6575e6ed1b8" and instead gets "hello".

It is important to note that some string values can be interpreted in odd ways when typecasting. One thing that catches a lot of people is an empty string ("") or a blank space (" "); both cast to the value 0 when converting from a string to an integer. Many other programming languagues have a NULL value that would be used instead, but LSL does not.

Implicit typecasting does not work with lists because lists can hold any type of value (except other lists). If a specific type of value is required in a list, it must be explicitly cast to that type or already be of that type. LSL functions (llParticleSystem and llSetPrimitiveParams) that read lists and expect specific types will not implicitly cast the list entries. This is why functions that use lists will fail to compile when passed an integer instead of a float.

Types


Unicode

What it is
Computers primarily deal with numbers. Any character or graphic that you see is represented by an internal numeric code. The set of codes that represents a given set of characters is referred to as a character-set. Computers use the codes to find the correct symbol in a font, for display.

Traditional computer character sets comprised only 127 different codes (including some that had special meaning and did not directly translate to a symbol). This was fine for the display terminals at the time, but internationalisation required more. ISO-8859-1 (aka Latin-1) expanded on the available symbols, but human languages comprise a set of literally tens of thousands of different symbols.

Unicode was originally standardised in 1991 (by Unicode, Inc.), and has undergone several revisions since then. At last count it supports more than 90,000 codes, with more being added. While several standards currently compete for technical, historical and political reasons, Unicode is the most complete character code representation available at the present time. For a more indepth history of Unicode try here.

What does it mean to me?
You may have noticed some of the SL residents chat in languages other than english. Some of our Japanese residents, for example are chatting in their native language, with their native character sets. If you have the proper font installed you will see the characters in their native japanese character set. If you do not, you'll see a series of small grey blocks in your chat history (your operating system's way of telling you that it cannot find a font which contains symbols that match the character codes).

Not every part of SL is happy with unicode. While some years old now, Unicode adoption has been slow. Generally you will find that unicode functions with Chat, IM, and the email/IM gateway. Other parts, like the names of objects, inventory search and such may not understand characters typed that are not Latin-1

How Unicode is implemented in SL
The encoding standard that SL uses is UTF-8, raw UTF-8 strings can be built with the use of llUnescapeURL. SL isn't fully compliant with UTF-8 yet.

Functions
These two functions will make you wish you were never born.
integer UTF8ToUnicodeInteger(string character)
returns the unicode value of the character. If the character is not part of the utf-8 character set or is invalid the function returns the first byte and sets the negative bit.
string UnicodeIntegerToUTF8(integer unicode)
returns the utf-8 encoded version of the integer unicode. Standard Compliant.
integer UTF8ToUnicodeInteger(string a)
{
    integer b;
    if(a = llEscapeURL(llGetSubString(a,0,0)))
    {
        if(1 == b = llStringLength(a))
        {
            if(48 == (b = 48 + (integer)a))
                b = (!!b << 4) + 48 + b = llSubStringIndex("0ABCDEFGHIJKLMNOPQRSTUVWXYZ      abcdefghijklmnopqrstuvwxyz",a);
        }
        else if(!(b % 3))
        {
            a = (string)llParseString2List(a,["%"],[]);
            if(b == 3)
                b = (integer)("0x"+a);
            else
            {
                b = ((b & 0x3f) |
                    ((b & 0x3f00) >> 2) |
                    ((b & 0x3f0000) >> 4) |
                    (((b = (integer)("0x"+llGetSubString(a,-8,-1))) & 0x3f000000) >> 6) |
                    ((b & 0x3f) << 24) |
                    (((b = (integer)("0x"+llDeleteSubString(a,-8,-1))) & 0x100) << 22)) &
                    (0x7FFFFFFF >> (30 - (5 * (b/3))));
            }
        }
    }
    return b;
}

string byte2hex(integer x)
{
    string hexc="0123456789ABCDEF";
    return llGetSubString(hexc, x = ((x >> 4) & 0xF), x) + llGetSubString(hexc, (x & 0xF), (x & 0xF));
}

string UnicodeIntegerToUTF8(integer a)
{
    if(a <= 0) return "";//unicode & utf8 only support 2^31 characters, not 2^32; so no negitives.
    integer b = (a >= 0x80) + (a >= 0x800) + (a >= 0x10000) + (a >= 0x200000) + (a >= 0x4000000);
    string c = "%" + byte2hex((a >> (6 * b)) | ((0x7F80 >> b) << !b));
    while(b)
        c += "%" + byte2hex((((a >> (6 * (b=~-b))) | 0x80) & 0xBF));
    return llUnescapeURL(c);
}

These functions are attempts at obtaining the number of bytes in a string:
I'm pretty sure you cannot scan for that many tokens all at once; not to mention that llEscapeURL is borked. -BW
// Memory intensive, but fast.
// TODO: Overcome llParseStringKeepNulls/llParseString2List's seperators/spacers limitations.
// TODO: Check for off by 1 errors.
integer stringBytes(string n) {
    n = llEscapeURL(n);
    // tokens list contains all combinations of %XX that llEscapeURL can output.
    list tokens = ["%01","%02","%03","%04","%05","%06","%07","%08","%09","%0A","%0B","%0C","%0D","%0E","%0F","%10","%11","%12","%13","%14","%15","%16","%17","%18","%19","%1A","%1B","%1C","%1D","%1E","%1F","%20","%21","%22","%23","%24","%25","%26","%27","%28","%29","%2A","%2B","%2C","%2D","%2E","%2F","%3A","%3B","%3C","%3D","%3E","%3F","%40","%5B","%5C","%5D","%5E","%5F","%60","%7B","%7C","%7D","%7E","%7F","%80"]+["%81","%82","%83","%84","%85","%86","%87","%88","%89","%8A","%8B","%8C","%8D","%8E","%8F","%90","%91","%92","%93","%94","%95","%96","%97","%98","%99","%9A","%9B","%9C","%9D","%9E","%9F","%A0","%A1","%A2","%A3","%A4","%A5","%A6","%A7","%A8","%A9","%AA","%AB","%AC","%AD","%AE","%AF","%B0","%B1","%B2","%B3","%B4","%B5","%B6","%B7","%B8","%B9","%BA","%BB","%BC","%BD","%BE","%BF","%C0","%C1"]+["%C2","%C3","%C4","%C5","%C6","%C7","%C8","%C9","%CA","%CB","%CC","%CD","%CE","%CF","%D0","%D1","%D2","%D3","%D4","%D5","%D6","%D7","%D8","%D9","%DA","%DB","%DC","%DD","%DE","%DF","%E0","%E1","%E2","%E3","%E4","%E5","%E6","%E7","%E8","%E9","%EA","%EB","%EC","%ED","%EE","%EF","%F0","%F1","%F2","%F3","%F4","%F5","%F6","%F7","%F8","%F9","%FA","%FB","%FC","%FD","%FE","%FF"];
    // The null strings in this list represent the number of %XX tokens that were parsed out.
    integer withNullLength = llGetListLength(llParseStringKeepNulls(n, tokens, []));
    list noNulls = llParseString2List(n, tokens, []);
    return llStringLength((string)noNulls) // The 1-byte chars that weren't escaped
    + withNullLength - llGetListLength(noNulls); // Number of bytes in escaped chars.
}

Here's another test, showing that ASCII chars take 1 byte each, Latin-1 2 bytes each.

// Change this to 1 for ASCII, 2 for Latin-1.
integer TEST = 1;

test1() {
    integer i;
    string s1;
    integer before1;
    integer after1;

    before1 = llGetFreeMemory();
    for (i = 0; i < 100; ++i) {
        s1 = (s1 = "") + s1 + "1";
    }
    after1 = llGetFreeMemory();

    llOwnerSay((string)llStringLength(s1)+" ASCII = " + (string)(before1 - after1) + " bytes");
}

test2() {
    integer i;
    string s2;
    integer before2;
    integer after2;

    before2 = llGetFreeMemory();
    for (i = 0; i < 100; ++i) {
        s2 = (s2 = "") + s2 + "?";
    }
    after2 = llGetFreeMemory();

    llOwnerSay((string)llStringLength(s2)+" Latin-1 = " + (string)(before2 - after2) + " bytes");
}

default
{
    state_entry()
    {
        llSay(0, "Touch to start test "+(string)TEST);
    }

    touch_start(integer total_number)
    {
        if (TEST == 1) {
            test1();
        } else if (TEST == 2) {
            test2();
        }
    }
}


Units

SL (arguably) follows the International System of Units (SI) system for fundamental units:

Quantity | Unit | Abbreviation
length | meter | m
angle | radian | r
mass | lindogram | Lg (not kg)
time | second | s

Other units are combinations of these fundamental units. For instance, velocity is measured in m/s, force is in Newtons, i.e. kg-m/s², etc.

Note: energy (given by llGetEnergy) is a dimensionless constant between 0.0 and 1.0, and is not closely related to RL energy.

If you live in a country that uses other measurement units, this site can convert between furlongs, cubits, and what-have-you.

Physics | Gravity


Unit Vector

A unit vector, also known as a normalized vector, is a vector that returns an llVecMag (vector magnitude) of 1 (it points 1 unit in a direction).

Unit vectors are found using llVecNorm.

Functions | Vector


unlinked

Unlinked Set

An unlinked set is a collection of multiple objects that have been shift-selected in edit mode (but not all linked together into one object) and then taken into inventory, where they are stored as one inventory item.

To unlink an object, select it and press Ctrl-Shift-L.

Link


UnsignedInteger

An unsigned integer is an integer without a sign -- IE, it doesn't have negative values and thus can provide twice as many positive values as a signed integer with the same number of bits.

LSL doesn't feature unsigned integers, only signed ones.

Types


URL

Uniform Resource Locator

A Uniform Resource Locator (URL) is a standardized string of characters that represents the location of a resource (such as a document or an image) on the Internet. For example, the URL of this page is:
http://secondlife.com/badgeo/wakka.php?wakka=URL

An "HTTP URL", commonly called a "web address", is usually shown in the address bar of a web browser.

Functions

llEscapeURL
llUnescapeURL
llLoadURL
llHTTPRequest
llParcelMediaCommandList
llSetParcelMusicURL

string | Land | HTTP


User

A user ("resident") is the piece-of-meat-based hardware (and the software running on it) that attempts to operate the computer on which the Second Life client runs. A user is represented on the server as an agent and within the world of SL by an avatar.

Second Life | Lindens | Agent/Avatar


UserDefinedFunction

User-Defined Functions

User-defined functions are blocks of code that can be executed just like a built-in "ll" function. They are used to improve readability and to reduce the amount of redundant code in a script. If you copy and paste a section of code more then once in a script, that code should be put into a function.

Functions can return (give) a value back to the caller that is any valid LSL type. The return keyword is used to do this.

Functions are declared (defined, created) above the default state, either before or after global variables.
The format for declaring a function is as follows:

type functionName(type parameter1, type parameter2, type parameter3) {
// code goes here:
}

Where type is any valid LSL type or nothing (which specifies that the function returns nothing), functionName is any combination of letters and numbers that starts with a letter (A-Z, a-z), $ or _. The parenthesis after the function name are where the parameters of the function are defined. Parameters follow the same naming conventions variables do, and are seperated by commas. Parameters are variables with a scope encompassing the entire function; they can be used just like variables anywhere inside the function. Functions without parameters simply have nothing between the open and closed parenthesis (the parenthesis are still required).

Example usage of a function:
float getAbsoluteValueOf(float value) {
    if (value < 0)
        return value * -1;
    return value;
}

default {
    state_entry() {
        float myFloat = 4.392;
        float absFloat = getAbsoluteValueOf(myFloat);
        llSay(0, "The absolute value of " + (string) myFloat
            + " is " + (string) absFloat);
    }
}

getAbsoluteValueOf has the same behavior as the function llFabs has, and can be used in the same way.

Here's an example of a function without parameters or a return value:
saySomething() {
    llSay(0, "Something");
}

default {
    state_entry() {
        saySomething();
    }
}
This script makes the object say "Something" out loud, on channel 0. As you can see, functions can call other functions; saySomething can call llSay.

User-defined functions can call other user-defined functions as well:
sayOutloud(string message) {
    llSay(0, message);
}

saySomething() {
    sayOutloud("Something");
}

default {
    state_entry() {
        saySomething();
    }
}

User-defined functions can also call themselves. This is a special programming aspect called recursion.
subtractTillTwo(integer value) {
    if (value > 2) {
        subtractTillTwo(value - 1);
    } else if (value == 2) {
        llSay(0, "Value is two!");
    }
}

default {
    state_entry() {
        subtractTillTwo(35);
    }
}

llPizza, added originally as a joke, is another practical example of a user-defined function.

Functions


User Interface

A user interface (UI, or GUI: graphical user interface) is how you, the user, controls software. A user interface can include dialog boxes, windows (like the script editor's), buttons (push, radio, spinner), checkboxes, menus (pull-down, pie menus), tooltips, etc.

These functions communicate information to the user by using the SL client's features itself (not the world's) or via chat/IM:

Functions

Function | Description
llDialog | Produces a dialog box with text and up to 12 buttons.
llSetSitText | Changes the "Sit Here" text on the right-click pie menu.
llSetTouchText | Changes the "Touch" text on the right-click pie menu.

Heads-Up Display

Introduced in SL 1.7, HUDs or Heads-Up Displays allow the creation of custom user interface elements. See HUD.

Links

  • SL History Wiki: User Interface

Chat | Dialog | Editor


UTC

Universal Time (UTC); another name for GMT.

Navy UTC FAQ

To get the current time in UTC, use llGetGMTclock or llGetTimestamp.

time


UUID

UUID (Universal Unique IDentifier) are 128-bit numbers assigned to any asset in SecondLife and are guaranteed to be unique inside their scope.

The LSL type used to store UUIDs is a key which can be represented as a string of hex-numbers in the format "00000000-0000-0000-0000-000000000000" (or "66864f3c-e095-d9c8-058d-d6575e6ed1b8").

Many functions that take an inventory item name as input also work with a UUID (like llSetTexture).

Function
PRIM_TEXTURE
llSetTexture
llSound et al.
llGetNotecardLine
llGetNumberOfNotecardLines

For more, see the official UUID specification.
LL does not follow the official specification; some permanent description data can be proven to exist on the third section of numbers.

types | key


UV

UV are texture coordinates that specify how a texture is mapped to a prim's side. The U is horizontal and V is vertical.

  • Q: Why aren't they "HV"? -EepQuirk
  • A: The 3D graphics standards use XYZ for positioning, and the three preceding letters, UVW, for the texture coordinates associated with them: U=X, V=Y, W=Z. Yes, there is such a thing as a standard for 3-dimensional textures, and most modern graphics cards support them, but almost nobody uses them compared to 2-dimensional textures which, also by 'accepted practices' means the last axis, Z, is the one dropped from the texture-coordinate specification, meaning that W is also dropped, leaving U and V. That help? - WolfWings
    • Z/W is irrelevant since polygons are flat, 2D planes. Using H for horizontal makes more sense. -EepQuirk
    • UVW is industry standard, going back maybe as far as 20 years, first appearing in software written by "Wavefront Technologies" who are now known as Autodesk, makers of Maya and connected historical with many other products that shaped the industry. W corresponds to depth not Z; how this differs i cannot say, as i have yet to find any documentation on the topic. Personally i believe it is used for lighting. The only explination as to why it is UVW, like XYZ, they are adjacent; and i have yet to find any documentation giving a reason (and i have been looking). The answer to this question probably has more to do with the temperament of some programer in the 1980's then anything else. -BW

texture | side


value

In LSL, values are used and returned by operators and functions.

Each value has an associated type that constrains how it may be used (these constraints are purely in the compiler and not part of LSL). To convert one value type into another, a typecast is required. Typecasting a value may have irreversable effects to its value.

New values are created in scripts using literal expressions or by executing expressions containing operators or value-returning function calls. Values may be assigned to (or stored in) variables, retrieved from variables, used as operands in expressions, and result from applying operators. Values are also passed into function calls as parameters and returned from function calls.

Many, but not all, values can be represented with literal text in code. In the following example, the literal text representation of 5, "Hello", and <1, 2, 3> are all referred to as literal values.

integer x = 5;
string greeting = "Hello";
vector pos = <1, 2, 3>;

LSL has a limitation on the values that can be in a list.

Those curious as to how literals are parsed can take a look at the lex file.

script | variable | type


Variables

See Variable.


vehicle

See vehicles.


VehicleTutorial

VehicleTutorial ? i dont see one....

You should make one...

Well, what about the one at the slightly less aptly named TutorialVehicle?


Velocity

The speed at which something is moving.

In SL, this is really a vector indicating the speed at which a physical object is moving, in meters per second (m/s).

llGetVel: get the velocity of the containing object
llDetectedVel: for detected objects

dynamics


Vendor

A scripted object used to sell items.

LibraryVendor


vertex

A vertex (plural: vertices), or vert, is a single point (dot/corner) used in a grouping of at least 3 to create a polygon.

SL lighting is vertex-based.

primitive | polygon | coordinate


Virtual Machine

A virtual machine is a machine (usually a computer) completely defined and implemented in software rather than hardware. It is often referred to as a "runtime environment"; code compiled for such a machine is typically called bytecode.


void

A keyword used in the official LSL documentation but not a keyword in LSL itself (unlike C). It means that a function has no parameters, or returns no value.

LSL


Volume

volume is the float argument to a number of sound functions. Valid values range from 0.0 through 1.0; anything higher is treated as 1.0. Sound playback at a volume of 1.0 can potentially be heard from a great distance away, so courtesy dictates that it should be done with considerable reluctance and caution, if at all.

Depending on one's distance/rolloff factor audio SL preferences, a normalized sound clip played at volume 1.0 can be heard in all directions up to about 128m from the source task, an area of over 51000m^2. Indefinitely looping sounds at a volume of 1.0 is potentially griefing because it is likely to be heard throughout most of a sim in an unsolicited manner. Audio played at a volume of 0.05 is audible for about 7m from the source task, or over an area of about 150m^3.

Volume-related audible ranges work differently with llTriggerSoundLimited, where the audible range is explicitly specified with bounding position coordinates. llTriggerSound is also slightly different in that the sound is fixed at the task's position at the time of playback start, regardless of whether or not the task subsequently moves while the sound continues to play.

A volume is also another, though rarely used, name for a prim. (Prims are referred to as "volume" in the debug menu.)

Volume is also used in the llVolumeDetect function, referring to volume - an avatar or object - entering the task.

Sound


VolumeDetect

See llVolumeDetect


Vorbis

Vorbis is the name of a specific audio compression scheme that's designed to be contained in Ogg. Note that other formats are capable of being embedded in Ogg such as FLAC and Speex.

As described on the Tiki Wiki, Ogg Vorbis can be used to stream music into Second Life, but at the current time it cannot be used to upload audio as an inventory item.

Compare with MP3.


wakka.php

Welcome to the LSL Wiki Mirror 7-7-7, an effort to document the LSL scripting language of Second Life. This wiki is intended as an open documentation resource to help scripters of all skill levels. Anyone can contribute, so feel free to jump in.

Looking for information about Second Life in general? Try Linden Lab's official Second Life Wiki.
Are you new to LSL scripting? Try our LSL Tutorials.

There are other forks of the LSL Wiki Mirror 7-7-7 in various states of development. See the 'Link to Useful Resources' at Linden Lab's LSL Portal.

Getting Started: LSL Reference: Function Categories:
Whether you're new to scripting or an experienced programmer, these pages should help you learn to use LSL.

Introduction
Search the Wiki
Tutorials
How Do I...
Glossary
Humor

Scripting Mentors
Scripting Groups
Scripting Teachers

Alternate Editors
Errors
Hacks
Known Bugs & Debugging Tips
Annoyances/Gotchas
Lag
Revisions - What's new in LSL?
LSL Style Guide

Script Library
Examples
Protocol Exchange 		Already know how to script, and just need the reference?

Constants
Events
Flow Control:
for, do-while, if-else, jump, return, state, while
Functions (see categories to right)
Operators:
assignment, binary, bitwise, boolean, unary
States
Types:
integer, float, vector, rotation, key, string, list
Variables

Page Jump:
  • Agent/Avatar
  • Camera
  • Collision
  • Color
  • Communications
  • Controls
  • Dataserver
  • Detection
  • Dynamics/Movement/Physics
  • Group
  • Inventory
  • Link
  • Land (Ground/Terrain)
  • Light
  • List
  • Math
  • Particle (llParticleSystem)
  • Primitive/Object (llSetPrimitiveParams)
  • Script
  • Sensor
  • Simulator (Environment/World)
  • Sound
  • String (Text/Name)
  • Teleport
  • Texture
  • Time
  • Transformation:
    Rotation/Scaling/Translation
  • Vehicle
  • Video
  • Weather
  • XML-RPC
The LSL Wiki Mirror 7-7-7 is an independent resource and is not affiliated with Linden Lab. Second Life and LSL are the property of Linden Lab. Portions of this site are copyright © Linden Lab.
By posting here, you agree to allow other users to copy, reproduce and alter your contribution for non-profit use. For additional information on the LSL Wiki Mirror 7-7-7's copyright policy, please see LSL Wiki Mirror 7-7-7 Copyright.


WakkaWiki

WakkaWiki is the Wiki implementation that the LSL Wiki Mirror 7-7-7 runs on. The official WakkaWiki project is now apparently dead. Their old URL (http://www.wakkawiki.com) redirects to a link farm. (You can, however, access the last avaliable version of their site on The Wayback Machine.) There are several Wakka forks; two of them are http://wikka.jsnx.com and http://www.wakkawiki.de.

Q: This site has some features not yet in the main Wakka branch, such as support for tables. Are you using additional code or a Wakka variant, such as Wacko?
I'm assuming that there's some sort of customization ability in terms of conversion between wiki syntax and html.

A: Nope, we're using some stuff that I came up with and some stuff I picked out of PHP that others posted to the WakkaWiki site. Note that the official WakkaWiki software does not include such awesome features as tables and... uh... something else! (Good work, Cat.) EzharFairlight added some more extras, including Wiki markup for anchors and acronyms, a LSL syntax highlighter, visual improvements (hopefully) and some minor changes/fixes.

WG sez: Would it be possible to add a page-count or MB-count to the front page? Lots of contributing going on now, and it'd be kinda cool to see it grow
Hmm, maybe. I'm afraid it could cause stupidity, like forum post counters do. -Ez


Water

Simulators have a static water level. If land is raised above this level, water is covered. If land is lowered below this level, the water becomes visible. Water level is not the same for every simulator, which allows for a diverse set of terraforming possibilities per simulator.

The water level can be determined by using the llWater function. Whether or not the water is visible on a point on the ground can be determined by comparing the result from llWater on the point and llGround on the point; if the former result is greater, then water is visible.

The "ripple water" setting in SL's preferences is purely a client-side visual effect and does not influence the sim's water height. Also note that it does not work for all video cards, and that it will make vehicles that float on the water look goofy.

Water doesn't influence the movement of objects or avatars. At some point, possibly around SL 1.2 or 1.3, water began to muffle sound.

World | Simulator | Land


Whitespace

Whitespace is simply the empty space in between anything in a programming language, such as LSL. Whitespace is completely ignored by the LSL compiler, so different formatting styles can arise. Unfortunately, this also allows scripters to write code that neglects whitespace formatting standards, often confusing readers.

All of the following examples have the same effect and efficiency, even though their whitespace is different. Note how much easier it is to read the first example.

Example 1:
default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

Example 2:
default
{
    state_entry() { llSay(0, "Hello, Avatar!"); }

    touch_start(integer total_number) { llSay(0, "Touched."); }
}

Example 3:
default{state_entry(){llSay(0, "Hello, Avatar!");
}touch_start(integer total_number){llSay(0, "Touched.");}}

PLEASE, FOR THE LOVE OF ALL THAT IS HOLY, DO NOT WRITE CODE LIKE THIS.

Example 4:
default
{
state_entry   ()
                                        {   llSay(0,"Hello, Avatar!"          )
;    }
touch_start   ( integer     total_number )                             {
llSay (   0, "Touched.")
;}}
Styleguide


Wiki

A wiki is a special kind of website that the user can edit. Wikis are useful for creating collaborative documentation projects like this one. The LSL Wiki Mirror 7-7-7 runs on a modified version of WakkaWiki.

LSLWiki


Wiki Etiquette

What can I do on the LSL Wiki Mirror 7-7-7?

Feel free to contribute wherever you see something that could use some improvement. Got an example script? Know how to word something better? Jump in and do it. Completing items listed in ToDo would be especially helpful.

Don't be afraid to contribute to the wiki. Anyone's allowed to do it, just follow this page and the WikiStyleGuide. Try to keep your pages consistent with the rest of the site. (In other words, changing the colour scheme and making up new naming conventions would be stupid.) Don't be afraid to make a mistake, though. Other users will correct it if you do.

What CAN'T I do on the LSL Wiki Mirror 7-7-7?

Don't advertise, don't bash people, and stay on topic. This resource is intended for things that pertain to LSL and scripting. A note about your cat is not relevant. Don't deface the wiki. It's a minor inconvenience for us, and is a really childish thing to do.

What should I avoid?

Don't edit major pages (like the HomePage) without reason. If you see something you want added, leave Catherine a note on her page or post a note on the relevant pages.
Do not create pages with ".", "?", "=", "&", "\", "-" in their titles; the wiki will not make proper wiki links to them. It is improper to create pages that start "ll*" (double L) as these are reserved for Linden functions.

I have an example script. What should I do?

Before you post it, make sure it actually compiles. Then make sure it works. Then show it to someone else and see if they see any problems. Then, if you're sure it's functional and actually demonstrates what you want to show, go to ExampleScripts and put it up, following the guidelines on that page.

I found an error. What should I do?

If it's a spelling or grammatical error, make sure that it's not just a UK/Canadian-ism. Not everyone spells "colour" the same as Americans do. If it's something really stupid like "ur" instead of "your", or "your" instead of "you're", go ahead and change it. Just make sure you know what you're doing. We don't need the Spelling Police going around unarmed. ;)

If it's a technical error, and you're unsure of how to correct it, or if you have a question, just leave a note and let someone else deal with organizing it better. When leaving notes, remember to include the current SL version number. This allows other users to see how long ago you noticed the error.

Example of a note:
As of SL 1.6.2, this function does not return a value.

Should I leave a note or a comment?

A note would probably be best, unless it's in reply to a comment. If it's to make a suggestion or clarify something, notes let us change the page. After the fix has been made, pages with comments on them saying things like, "line 65 is wrong" really don't make a lot of sense. There's not currently an easy way for CommentDeletion to work, though there is PageDeletion.

I want to leave a message for someone. Where do I do that?

If they're a registered user, (and why wouldn't they be if you want to talk to them here?) leave a message for them on their page. They'll get to it.

People are editing my pages! How do I make them stop!?

It's a wiki, that's the point. Everything here is effectively GPLed. If you don't want your words edited, don't post them here. It's as simple as that.

I have a suggestion. Who do I talk to about getting it implemented?

If you're unsure about whether or not it would be a welcome addition, leave a note on the Suggestions page. Otherwise, either add it to ToDo, or just put it together yourself. If its an LSL function suggestion see this page: LSLFunctionRequest.

LSLwiki / WikiStyleGuide


WikiFormatting

Introduction

The general rule of thumb with a wiki is that all formatting commands are double characters. Text formatting on the LSL Wiki Mirror 7-7-7 slightly differs from other wikis. Anything between two sets of double quotes ("") is ignored and presented exactly as typed.

You can experiment with this in the WikiSandbox.

Basic Formatting

**bold text** | //italicized text// | __underlined__ text | ##monospace (10) text## | ''note''
bold text | italicized text | underlined text | monospace 10 text | note

Remember! Those are apostrophes (''), not quotation marks (") for creating note text.

Links

To create a link to another WikiPage, use this: [[HomePage]] which renders: HomePage

To create a new WikiPage, simply create a link to it. Clicking on that link will then send you to the new page, from where you can edit the page.

To create an anchor (a link to a section on the same page), use this: =#thisisananchor#= - (this doesn't produce anything visible)
Then link to it using a link that contains a #, like this: [[#thisisananchor]] - thisisananchor

Write: | [[HomePage]] | [[HomePage Foo]] | [[http://www.secondlife.com/]] | [[http://www.secondlife.com/ Second Life]]
Renders as: | HomePage | Foo | http://www.secondlife.com/ | Second Life

Code

Long code examples can be placed in a scrollable text box. However, you usually do not want to use these because they chew up a bunch of screen space and interrupt the flow of the document. Monospace text works better for code examples. See the Wiki Style Guide for further explanation.

%% Embed Code Here %%

default
{
    state_entry()
    {
        llSay(0,"Hello Avatar!");
    }

    touch_start(integer touched)
    {
        llSay(0,"Touched");
    }
}

You can specify the type of syntax highlighting by placing it in parentheses ( () ) after the initial %%. Currently, three types are supported, code (the default), php and lsl (still experimental).

PHP Example:
%%(php) // php code here %%

<?php
  echo 'Hello, World!'; //PHP code
?>

LSL Example:
%%(lsl) // lsl code here %%

default
{
    state_entry()
    {
        llSay(0,"Hello Avatar!");
    }

    touch_start(integer touched)
    {
        llSay(0,"Touched");
    }
}

Acronyms

Write (?HHGG==Hitchhikers Guide to the Galaxy?) to get HHGG.
I'm confused as to what this does and what the point of it is, can someone explain? - Haz
Haz - this makes some browsers pop a tooltip up, expanding the acronym. - JillianCallahan

Headers

====== Really big header ======

Really big header

===== Rather big header =====

Rather big header

==== Medium header ====

Medium header

=== Not-so-big header ===

Not-so-big header

== Smallish header ==
Smallish header

Tables

The exception to the "always two characters" rule, tables are opened by three pipes (|||) followed by "0" for no border and/or "c" for center-justified columns. Table cells are seperated by two pipes (||). Tables are closed by three pipes again (|||).

border, left-justified:
||| **First Name** || **Last Name** || **Sim** || **Coordinates** ||
|| Catherine || Omega || Chartreuse || 90, 198 ||
|| Ryan || Linden || Bonifacio || 235, 250 |||

First Name | Last Name | Sim | Coordinates
Catherine | Omega | Chartreuse | 90, 198
Ryan | Linden | Bonifacio | 235, 250

no border, left-justified:
|||0 **First Name** || **Last Name** || **Sim** || **Coordinates** ||
|| Catherine || Omega || Chartreuse || 90, 198 ||
|| Ryan || Linden || Bonifacio || 235, 250 |||

First Name | Last Name | Sim | Coordinates
Catherine | Omega | Chartreuse | 90, 198
Ryan | Linden | Bonifacio | 235, 250

border, center-justified:
|||c **First Name** || **Last Name** || **Sim** || **Coordinates** ||
|| Catherine || Omega || Chartreuse || 90, 198 ||
|| Ryan || Linden || Bonifacio || 235, 250 |||

First Name | Last Name | Sim | Coordinates
Catherine | Omega | Chartreuse | 90, 198
Ryan | Linden | Bonifacio | 235, 250

no border, center-justified:
|||0c **First Name** || **Last Name** || **Sim** || **Coordinates** ||
|| Catherine || Omega || Chartreuse || 90, 198 ||
|| Ryan || Linden || Bonifacio || 235, 250 |||

First Name | Last Name | Sim | Coordinates
Catherine | Omega | Chartreuse | 90, 198
Ryan | Linden | Bonifacio | 235, 250

Remember to put a space after the closing " |" or the next line after the table will get cut off. Currently, nested tables are not something the LSL Wiki Mirror 7-7-7 can do. If there's a need for it, it will be added.
To make a cell blank in a table and still have the table borders, put "&nbsp;" in the middle of four double quotes.

Horizontal Separators

----
Note: you must place this at the end of the last line before the separation (if you want to retain indentation) or the very beginning of the new line (if you want the separator to be as wide as the whole page). Otherwise, it results in a bullet and a line-break.

Use Full-Width Horizontal Line Separators Sparingly

Try to use the "----" full-width horizontal line separator (also called a linerule) only once per page to separate the bottom links from the rest of the page. Too many linerules are confusing and disorientating--like that used to be on this page. If in doubt, use this HTML to specify specific-width linerules:

"<hr width=50%>":

Forced Line Break (not visible)

---

Indents and Lists

Indent text by typing two tildes: "~~" or, if using Internet Explorer (IE) on Windows hit the Tab key. (Windows' IE is the only browser known where Tab indents in a textarea field.)

Indented, bulleted/numbered lists:

- bulleted list

  • bulleted list
  • Line two
  • line 3

1) numbered list

  1. numbered list
  2. Line two

A) Using uppercase characters

  1. Using uppercase characters
  2. Line two

a) Using lowercase characters

  1. Using lowercase characters
  2. Line two

i) using Latin numbers

  1. using Latin numbers
  2. Line two

Entity Literals

Entering &amp;lt; into the wiki actually displays the character &lt;. To show the literal string &amp;lt;, use &amp;amp;lt;. Likewise for similar entities such as &amp;gt; and so on.

FAQ

Question: How do you delete a page or a comment?
Answer: See PageDeletion and CommentDeletion.

Question: How do you unwikiname a word?
Answer: Add two pair of double-quotes around the word.
""WikiName""
WikiName

Question: How do you get a pair of double-quotes (without any text between them) to display properly?
Answer: Escape them by placing an empty tag between them:
"////"
""

Question: How does WakkaWiki know to what URL to send a visitor to if it wasn't specified?
Answer: The link is to a forced WikiPage. That means a link to a page in this wiki is generated.

Question: How do you get advanced tables or images to show up in WakkaWiki?
Question: Is there a way to use scripting languages such as JavaScript in a Wiki page?
Answer: If you really think you need it, you can enter HTML directly by entering a pair of double-quotes (&quot;&quot;) around the desired text like so:

""<img src="foo.gif">""

There isn't currently a mechanism for uploading images to the wiki, though. If you need images, I recommend ASCII art. :-)
Answer: Yes you can use JavaScript for certain things. Me and Dolus used JavaScript for expand/collapse functions. (Actually I naffed it off Dolus but oh well...) - Haz

Question: How do you color text, other than a note?
Answer: There is a way. See the question above reguarding putting HTML tags directly in Wiki pages.
Nota bene: Use color sparingly. Use notes, italics and bold when you want to add emphasis.

LSL Wiki Mirror 7-7-7 | Wiki Etiquette | Wiki Style Guide


WikiName

A WikiName is 'bumpy' (CamelCase).

It consists of non-consecutive capital letters, for instance - "BuhBuhCuh" is a WikiName, while "buhbuhcuh" is not.

A WikiName will automatically link to a page about it. If a page has not been created yet, the WikiName will be shown in orange and with a dashed underline, inviting editing.

A word in CamelCase can be un-WikiNamed by enclosing it in double double quotes: ""IDontWantThisToBeAWikiName""

WikiPage / WikiFormatting


WikiNames

See WikiName.


WikiPage

A page on a Wiki. To create a WikiPage, just link to it. Once you link to a WikiPage, it automatically creates the page.
[[HomePage]] becomes... HomePage.

WikiFormatting


WikiQuestions

This page was created to make it easier to find questions that need to be answered throughout the wiki.
Since the wiki is getting quite big, it was best to make one location where questions can be reposted then answered on their own pages.

General formatting:
PageWhereQuestionWasFound
Question

For example:
Variable
Is there a maximum length to a variable's name?

Note that this is not a place to post answers to these questions. Rather, post the answer to the wiki page that's linked to before the question and remove the question from here.
If you see a question on a wiki page that needs answering and can't answer it, post it here.
WantedPages
Is there any wasy we can prevent user pages from showing up on the wanted pages? =\ It seems unecessary, and prevents the wanted page from being used for its intended purpose.
StyleGuide
Has anyone tested if declaring variables outside of loops helps speed things up?
llGetForce
Is this in local or global coordinates? It's in whichever form you used when you called llSetForce. Yes, we know that it's occasionally annoying. --KeknehvPsaltery
llSetTorque
Anyone have more detail on the rate of impulse?
llSetForceAndTorque
Do not use with vehicles. Why not? What happens?
llRequestSimulatorData
Does DATA_SIM_RATING actually work? If I look at the key, I'm getting back all zeroes, which gets eaten by dataserver, rather than returning a definitive string of UNKNOWN... anyone know?
Sorry it only works in preview (i'll mark it as such)


WikiRenamingPages

WakkaWiki does not support renaming pages. The closest that can be done is a page delete and page create.

Say you wanted to rename WikiRenamingPages to ILikeCandy. First you would edit WikiRenamingPages and copy it's wiki source. Then you would create the page ILikeCandy and paste that source into it and save. You would then go back and delete WikiRenamingPages

WikiRenamingPages/edit
ILikeCandy/edit
WikiRenamingPages/delete


Wiki Style Guide

Were you looking for the LSL styleguide?

Edit this page! If you see something that's not done, needs clarification, has changed or is just plain wrong, fix it.

Page Creation/Editing

  • The SecondLife Terms Of Service and Community Standards apply to the wiki.
  • The wiki is PG (as defined in the Community Standards).
  • Capitalize the page's title and use 5 "=" signs to indicate it's a title.
  • Use 3 "=" for subtitles and capitalize the first letter: Functions, Events, Constants, etc.
  • Creating a new page: write the title using CamelCase. "WikiStyleGuide", for example. The actual page title can be written according to normal English conventions.
  • Naming a page for a function, event, or constant: name it and link to it as the element appears in the code itself. "llSetScale", not "LLSetScale" or anything else.
  • Linking to another page from text within the body of a page: linking in CamelCase is unnecessary (especially in titles/headers). For example:
    • Good: "LSL contains many functions and events. llGetScale is one of them."
    Bad: "LSL contains many Functions and Events. llGetScale is one of them."
  • Expressing a short element of code, such as a function or variable name: use the monospaced tags. (##monospaced tags##). When embedding a larger script, contain it within code tags. See more at WikiFormatting.
  • Make sure your new page links back to the relevant sections, and only the relevant sections. You don't need to place a link at the bottom of llSetColor linking it to the money() event.
  • Spell check the page. IE: IESpell, Firefox: SpellBound
  • Check to make sure any changes do not break pre-existing formatting on the page.
  • Similarly, only place a link to the new page on relevant pages. There doesn't need to be a link to "ExampleFrogScript" on a texturing page.
  • Contributing to the Examples or Library: name the new page in the form "ExampleFrogScript", or "LibraryFrogScript". This allows us to easily sort pages into categories.
  • If in doubt about what fits where, find a similar page and copy the layout.
  • Do not create pages for/as feature suggestions. There are proper places for feature suggestions, see: LSLFunctionRequest.
  • If a feature suggestion is to be included on a wiki page it should be at the bottom of the page, in it's own box. In the box, text at the top declaring it a feature suggestion with Vote or Forum threads discussion it and finaly a simple explination as to how it would work. If the box has more content then the rest of the page (excluding comments and bottom links) the page will be considered as having been created for/as a feature suggestion and in violation of the previous rule.
  • Pages for functions that do not exist should be clearly marked at the top, such marking should be devoid of comments (and opinions).
  • Do not post content on a page to which there is little or no relevence.
  • Do not put syntax for functions that do not exist on the wiki (this will only lead to confusion).
  • Do not revert a page if you disagree with an edit, unless the edit entirely conflicts with one or more of the editing guide lines found here, WikiFormatting or WikiEtiquette.
  • Stylesheet overriding should only be done on user pages.
  • If an author wishes to implement a new style, they must first add it to this page (or to the page for the style guidelines for the group); unless it only effects a small subset of the pages in a specific group on the wiki and does not conflict with existing style guidelines. An example group is the ScriptLibrary. +
  • Don't get mad if people edit your page; that's the point of a wiki.

Function/Event Format

integer llDoSomething(vector where, string what, integer why, integer really)

This function does something. You can define where, what and why to do it.
The boolean parameter really determines if it will be executed (TRUE) or not (FALSE).

It returns a boolean value that determines if something was successfully done (TRUE) or not (FALSE).

Example:
llDoSomething(<1.0, 1.0, 1.0>, "hey ya", 23, TRUE)
The above code does something really interesting.

Functions | (other sections of the Wiki this relates to)

Q: It seems 'other sections' is currently interpreted in various ways, sometimes referring to general areas (e.g. references to functions, rotation, constants), related concepts (e.g. the big list on rotation), other times as an 'see also these pages' (e.g. the links on and towards LIFO). Was this meant purely as categories or is this essentially up for 'what makes sense for you?'

Q: There indeed seems to be a style difference, using either | or / to split these. Looking at the some of them, such as the list on llRequestAgentData, it seems these are not meant as trees, which / suggests to me - and of course, categories will sometimes work out as rough trees, which can be nice. But is there a central idea to this, or is this an as-you-see-fit thing too?

Code Example Text Boxes

Text boxes should be used for code examples. For LSL code, use %%(lsl), for PHP code use %%(php), and for everything else use just %%. This saves you from the horrors of escaping parts of the code that the Wiki would interpret as formatting instructions and keeps pages easy to edit.

Examples

Keep examples short and to the point. You probably don't need to include things like variable declarations and the such for most snippets.

Single Wiki Name Linking

Link the same WikiName only once per entry. Example:
The object with the script does move objects and can also link your object with other objects.
Instead of:
The object with the script does move objects and can also link your object with other objects.

I think this should read: Link the same WikiName only once per entry per screen of information. As it makes for easier for the reader as they don't have to page up to find a link for the key word. So it would be OK to have 3 links to "object" as long as you couldn't see any two of the links at any one time. I remember reading this on another wiki as an acceptable linking standard.

It's a nice theory but how does one know what screen resolution, font size, etc the viewer has? 1024x768 resolution and Trebuchet MS font will be different than 1280x1024 and Times New Roman font. A better rule might be to reuse a link after x number of characters/words/paragraphs--perhaps an average "screenful" interval could be determined. Regardless, upkeeping such formatting would be more annoying than only linking to a term once per entry. -EepQuirk

I believe fonts are specified by the meta data and tags. Overriding these would be in conflict with the standard. So your example of different fonts is irrelevant. It is safe to assume the average user uses 1024x768 as there resolution. More importantly it doesn't have to be perfect. -BW

I override this site's stylesheets because I don't like it. Standards are relative; relative to MY standard, this site's "meta data and tags" (stylesheets) conflict with my tastes. This site needs alternate styles. -EepQuirk

Push for support for MediaWiki. As has been stated in the past, WakkaWiki, the internal engine running this wiki, sucks. This style guide is for writing pages tailored to the original style sheet layout of this wiki. If you override the original wiki style sheets to better your viewing experince, please don't make modifications to pages to custom tailor them to your particular style sheet layout. Everyone else will see them using the wiki's original style sheets. -Chris

Self-Linking

Don't link a page to itself. If a page is named WikiStyleGuide, it shouldn't have any links that lead to WikiStyleGuide. Put CamelCase words in double quotes to avoid automatic linking where it's not wanted.

Monospace Variable Names

Use the monospace font (##) for writing variable/event/function names. It helps to separate the actual code from the rest of the text.

Incorrect: llSetRot
Correct: llSetRot

In some cases, the combination of bad markup and poor choices when naming variables can be somewhat...confusing:

Bad: "The variable the is an integer. and is a float."
Better: "The variable the is an integer. and is a float."

Also, stick to a consistent style of markup. If an entry uses bold (**) text in addition to monospace for its variables, stick to that layout. It keeps the page readable and consistent.

Singular Wiki Names

Always use singular for wiki names; never plural. Yes, we already f*cked that up on many pages, but there's no need to carry on that bad habit. Why do it? It makes formatting easier ([[function]]s is better than [[functions function]]). If all entries are singular, you won't have to guess anymore if the page you want to link to was [[types]] or [[type]].

Notes

If you want to comment about something on a page that you want added or changed, edit the page and leave a note, (see WikiFormatting) not a comment, as CommentDeletion is currently not very comfortable. It is probably a good idea to indicate who is leaving the note. If you "fix" a page as a note requests, it is probably a good idea to delete the note.

There doesn't seem to be a clear cut answer to using plural links to singular pages. [[function]]s vs. [[function functions]] obviously it's wrong to doctor LSL keyword link names.
I dont think it matters much, IMO I like seeing [[function functions]] more then [[function]]s because it just... looks better :) The only confusion I can see occuring is if someone does something like [[if while]]-Chris

Note: notes are not the same as notes about things that require special attention--like this note.

Note:
  • Multiple notes can be formatted like this.
  • If stylesheets are disabled, notes won't appear any different than normal text. This should be changed... -EepQuirk

Use Line Separators Sparingly

Try to use the "----" full-width line separator (also called linerules) only once per page to separate the bottom links from the rest of the page. Too many linerules are confusing and disorientating--like that used to be on this page. If in doubt, use this HTML to specify specific-width linerules:

"<hr width=50%>":

I don't jive with this one, i think full breaks should stay. On this page i don't think the 50%'s give enough seperation between the sections -BW
Well, you can always set the % greater if you want, but any wider and it will detract from the bottom linerule even more... -EepQuirk

Seperate Relevant Sections with Whitespace

People tend to be overwhelmed if they're presented with a BIG glob of text, especially one that doesn't even respect paragraph rules. Group information together, and seperate it into 3-5 line chunks.

Direct Linden Quotes

Direct linden quotes are highlighted in sienna, as this color is the default used by lindens in the SecondLife forum.

To highlight text in sienna, place "<font color=sienna>" before it and "</font>" after it.
Wiki Formatting | LSLWiki


Wiki Templates

Wakka supports a few different "actions" by default. Here is a list.

Name | Description
{{BackLinks}} | backlinks
{{FindPage}} | ?
{{Footer}} | ?
{{Header}} | ?
{{InterWikiList}} | ?
{{MyChanges}} | MyChanges
{{MyPages}} | MyPages
{{PageIndex}} | RealPageIndex
{{RecentChanges}} | RecentChanges
{{RecentComments}} | ?
{{RecentlyCommented}} | RecentlyCommented (why not "RecentComments"?)
{{Test}} | ?
{{TextSearch}} | TextSearch
{{UserSettings}} | UserSettings
{{WantedPages}} | WantedPages
?: doesn't work
LSLWiki


WikiUsers

Registered Wiki Users

A complete list (as of 2-2-06) of all wiki users with accounts, aka contributors.

User | Backlinks
AaronLevy | Backlinks
AbdielBrodsky | Backlinks
AceCassidy | Backlinks
AceDuport | Backlinks
AdamMarker | Backlinks
AdamZ | Backlinks
AdamZaius | Backlinks
AeolusWaves | Backlinks
AfirZ | Backlinks
AgentCase | Backlinks
AimeeW | Backlinks
AislinWallaby | Backlinks
AjDasilva | Backlinks
AkiMontale | Backlinks
AlanKiesler | Backlinks
AlbertFool | Backlinks
AlecFleming | Backlinks
AlendrilMullen | Backlinks
AlexanderDaguerre | Backlinks
AlexEdo | Backlinks
AlhelorMaginot | Backlinks
AlkenanK | Backlinks
AllarKamloops | Backlinks
AlleleSemple | Backlinks
AlmareaLumiere | Backlinks
AlondriaLeFay | Backlinks
AlphaZaius | Backlinks
AmaOmega | Backlinks
AmethystRosencrans | Backlinks
AndrewLinden | Backlinks
AndreylSatyr | Backlinks
AnneDroid | Backlinks
AnorTabla | Backlinks
AnotherFool | Backlinks
AnuStatosky | Backlinks
ApotheusSilverman | Backlinks
AppleUnit01 | Backlinks
ArbusFahid | Backlinks
AresDesmoulins | Backlinks
ArgentSmith | Backlinks
ArgentStonecutter | Backlinks
ArgusStravinsky | Backlinks
AritoCotton | Backlinks
ArkLehane | Backlinks
ArmitageNilsson | Backlinks
AshByrne | Backlinks
AsterLardner | Backlinks
AtwaHadlee | Backlinks
AvaGanache | Backlinks
AyAn4m1Noir | Backlinks
BabaYamamoto | Backlinks
BabbageLinden | Backlinks
BeatfoxXevious | Backlinks
BelMuse | Backlinks
BenBacon | Backlinks
BensonGrommet | Backlinks
BernardoRockwell | Backlinks
BigJohn | Backlinks
BigJohnJade | Backlinks
BinoArbuckle | Backlinks
BlackCheetah | Backlinks
BlackRaven | Backlinks
BlayneUk | Backlinks
BlindWanderer | Backlinks
BlOOdWiZE | Backlinks
BluemanSteele | Backlinks
BluMunky | Backlinks
BobBailly | Backlinks
BobBauhaus | Backlinks
BobBrightwillow | Backlinks
BondHarrrington | Backlinks
BosozokuKato | Backlinks
BrianLivingston | Backlinks
BrianMifflin | Backlinks
BriggiBard | Backlinks
BronYAurStomp | Backlinks
BuckminsterHamilton | Backlinks
BuhBuhCuh | Backlinks
BurntPage | Backlinks
BusterPeel | Backlinks
ButcherCassidy | Backlinks
ButtonPusherJones | Backlinks
CadroeMurphy | Backlinks
CamperDave | Backlinks
CandyPeart | Backlinks
CaoimheArmitage | Backlinks
CaptainBarmy | Backlinks
CarlDeElsweyre | Backlinks
CashMere | Backlinks
CatFart | Backlinks
CatfartGrayson | Backlinks
CatherineOmega | Backlinks
CazzjBrearly | Backlinks
CenjiNeutra | Backlinks
ChadStatosky | Backlinks
ChageMcCoy | Backlinks
ChandraPage | Backlinks
CharlesFauna | Backlinks
ChimanFassbinder | Backlinks
ChrisAltman | Backlinks
ChrisAshton84 | Backlinks
ChrisKnox | Backlinks
ChristopherOmega | Backlinks
ChromalBrodsky | Backlinks
ChuckBeckett | Backlinks
CidJacobs | Backlinks
CiennaRand | Backlinks
CirrMarat | Backlinks
CodesForAvacados | Backlinks
CorisonsNino | Backlinks
CorSter | Backlinks
CorsterMousehold | Backlinks
CortoMaltese | Backlinks
CorvaRaven | Backlinks
CorvidaePeregrine | Backlinks
CreaTine | Backlinks
CrossLament | Backlinks
CrossQuartermass | Backlinks
CrystalshardFoo | Backlinks
CuaCurie | Backlinks
CubeosBoffin | Backlinks
CubeyTerra | Backlinks
DakotaCallahan | Backlinks
DallasWilliamson | Backlinks
DamaniosThetan | Backlinks
DamienRutherford | Backlinks
DancinPanther | Backlinks
DanielBenton | Backlinks
DanielLuchador | Backlinks
DanLinden | Backlinks
DanMedici | Backlinks
DaRiK | Backlinks
DarkDeciple | Backlinks
DarkHarbinger | Backlinks
DarkJustice | Backlinks
DarzusStrutt | Backlinks
DavanCamus | Backlinks
DaveZeeman | Backlinks
DavidGall | Backlinks
DavoHuber | Backlinks
DedricMauriac | Backlinks
DelimachusSachertorte | Backlinks
DelphBunny | Backlinks
DeltaCzukor | Backlinks
DeneriaSholokhov | Backlinks
DennisPadar | Backlinks
DerekJones | Backlinks
DerekTesla | Backlinks
DevanSieyes | Backlinks
DevonCassidy | Backlinks
DivadWheeling | Backlinks
DjinnFizz | Backlinks
DmitriDaguerre | Backlinks
DnateMars | Backlinks
DoLpHpUn | Backlinks
DolusNaumova | Backlinks
DoodusMoose | Backlinks
DoopsLomax | Backlinks
DoozerSurface | Backlinks
DorMan | Backlinks
DoteDoteEdison | Backlinks
DougLinden | Backlinks
DraGonLord | Backlinks
DraXen | Backlinks
DriftwoodNomad | Backlinks
DuffleBunnysFromMars | Backlinks
DustinQuinn | Backlinks
DyneTalamasca | Backlinks
DysonDion | Backlinks
EataKitty | Backlinks
EchoOmega | Backlinks
EepQuirk | Backlinks
EggyLippmann | Backlinks
EllePollack | Backlinks
EllsWort | Backlinks
EloisePasteur | Backlinks
ElsbethPadar | Backlinks
ElteeStatosky | Backlinks
EnjiaPenguin | Backlinks
EscortDeFarge | Backlinks
EternusGupte | Backlinks
EvanOud | Backlinks
EzharFairlight | Backlinks
FairgeKinsella | Backlinks
FanZjie | Backlinks
FateKnox | Backlinks
FaustHarbinger | Backlinks
FearlessLeader | Backlinks
FeemLomax | Backlinks
FelixMcCoy | Backlinks
FionaFielding | Backlinks
FlipperPA | Backlinks
FlyingTakoTips | Backlinks
FolcoBoffin | Backlinks
FoxdieGhia | Backlinks
FoxDiller | Backlinks
FrAncis | Backlinks
FrancisC | Backlinks
FrancisChung | Backlinks
FredExtraordinaire | Backlinks
FreqoutManray | Backlinks
FrimkronPascal | Backlinks
FroglegHornpipe | Backlinks
GabrielSpinnaker | Backlinks
GaneshBling | Backlinks
GarrukHamsun | Backlinks
GarthFairlight | Backlinks
GenePoole | Backlinks
GenLevy | Backlinks
GentlespiritPanther | Backlinks
GeorgeFlan | Backlinks
GilesCela | Backlinks
GinaJane | Backlinks
GinnyPoppy | Backlinks
GordonPrior | Backlinks
GoshuaLament | Backlinks
GrazelCosmo | Backlinks
GreenFate | Backlinks
GretchenMann | Backlinks
GriffinOud | Backlinks
GrimLupis | Backlinks
GrimmyMoonflower | Backlinks
GunzourYellowknife | Backlinks
GuzarFonzarelli | Backlinks
GwynethLlewelyn | Backlinks
HadenMarshall | Backlinks
HadHod | Backlinks
HankRamos | Backlinks
HansBaldwin | Backlinks
HaravikkMistral | Backlinks
HeidiPinkney | Backlinks
HenriLemieux | Backlinks
HillaryMelville | Backlinks
HiroPendrag0n | Backlinks
HiroPendragon | Backlinks
HiroYamamoto | Backlinks
HoochAltman | Backlinks
HotTempura | Backlinks
HughPerkins | Backlinks
HunsValen | Backlinks
HuntingHare | Backlinks
HydroNots | Backlinks
HyMe | Backlinks
IanLinden | Backlinks
IceBrodie | Backlinks
IceBrodie2 | Backlinks
IconSerpentine | Backlinks
IntobeHamsun | Backlinks
IntrovertPetunia | Backlinks
IoneLameth | Backlinks
JaapBergman | Backlinks
JaclynMaltese | Backlinks
JadeLily | Backlinks
JafeMoxie | Backlinks
JagHarker | Backlinks
JakeBailly | Backlinks
JamesLinden | Backlinks
JarhynWilde | Backlinks
JassinPain | Backlinks
JavierPuff | Backlinks
JayDTorgeson | Backlinks
JeffCaldera | Backlinks
JeffreyGomez | Backlinks
JesradSeraph | Backlinks
JesterKnox | Backlinks
JiffiesManimal | Backlinks
JillianCallahan | Backlinks
JimBunderfeld | Backlinks
JimLinden | Backlinks
JimmyLoveless | Backlinks
JippenFaddoul | Backlinks
JohnPallister | Backlinks
JohntronThirty | Backlinks
JonasBakalava | Backlinks
JonathanShaftoe | Backlinks
JopsyPendragon | Backlinks
JordanCotton | Backlinks
JpatrickMaltese | Backlinks
JulianFate | Backlinks
JulianPoppy | Backlinks
JuneTate | Backlinks
JuranJumador | Backlinks
JusticeMonde | Backlinks
KafkaSolvang | Backlinks
KaiNyak | Backlinks
KairaOverdrive | Backlinks
KalemikaDougall | Backlinks
KankerGreenacre | Backlinks
KarizonHatfield | Backlinks
KarizonHetfield | Backlinks
KayinZugzwang | Backlinks
KaylaStonecutter | Backlinks
KeedonPollack | Backlinks
KeilaronTomba | Backlinks
KeknehvPsaltery | Backlinks
KellyLinden | Backlinks
KendraBancroft | Backlinks
KenzingtonFairlight | Backlinks
KevinLomax | Backlinks
KexGodel | Backlinks
KimmieLoveless | Backlinks
KithylinPerth | Backlinks
KitProudfoot | Backlinks
KlawsDusk | Backlinks
KonigmannLippmann | Backlinks
KoyukiMichabo | Backlinks
KralPlayfair | Backlinks
KristianMing | Backlinks
KurtZidane | Backlinks
KyrahAbattoir | Backlinks
KzPasteur | Backlinks
KzzchPrefect | Backlinks
LaenTeazle | Backlinks
LambdaMoose | Backlinks
LanceLeFay | Backlinks
LanceMirabeau | Backlinks
LaserPascal | Backlinks
LasivianLeandros | Backlinks
LavanyaHartnell | Backlinks
LazarusLong | Backlinks
LazarusLumiere | Backlinks
LazinkMaeterlinck | Backlinks
LeafAlbion | Backlinks
LeeLinden | Backlinks
LeopardLoveless | Backlinks
LeptonLeandros | Backlinks
LesWhite | Backlinks
LexNeva | Backlinks
LeynaBachman | Backlinks
LittlesuzeAlexander | Backlinks
LordJason | Backlinks
LordKaos | Backlinks
LucAubret | Backlinks
LuccaKitty | Backlinks
LucifTias | Backlinks
LukeRamona | Backlinks
LumpyTapioca | Backlinks
LurSachs | Backlinks
MacLifer | Backlinks
MadasaHornet | Backlinks
MaerouCzukor | Backlinks
MafluenceKilian | Backlinks
MagicianThorn | Backlinks
MaiglynaGoodliffe | Backlinks
MajorTomHebert | Backlinks
MalachiPetunia | Backlinks
MalarthiBehemoth | Backlinks
ManyBiggles | Backlinks
MarcEisenberg | Backlinks
MarcusEnnui | Backlinks
MarkovBrodsky | Backlinks
MarkSmith | Backlinks
MarsramGretzky | Backlinks
MasakazuKojima | Backlinks
MaxCase | Backlinks
McGrawDillon | Backlinks
MeatwadExtraordinaire | Backlinks
MechaniqueThirty | Backlinks
MeghanMagpie | Backlinks
MeiyoSojourner | Backlinks
MemeticDivision | Backlinks
MichaelFirefly | Backlinks
MichaelSmall | Backlinks
MikeMos | Backlinks
MikeyD | Backlinks
MingChen | Backlinks
MiniCooper | Backlinks
MinskOud | Backlinks
MiseryMoonlight | Backlinks
MisnomerJones | Backlinks
MoleculorSatyr | Backlinks
MonahanWake | Backlinks
MooshGoodliffe | Backlinks
MoriashMoreau | Backlinks
MotorHappy | Backlinks
MsMarcel | Backlinks
MsoLambert | Backlinks
NadaEpoch | Backlinks
NadaEpochs | Backlinks
NargusAsturias | Backlinks
NashBaldwin | Backlinks
NataruOdets | Backlinks
NathanStewart | Backlinks
NcaRalph | Backlinks
NealNomad | Backlinks
NebSvarog | Backlinks
NelvaynaCyr | Backlinks
NeoRebus | Backlinks
NepenthesIxchel | Backlinks
NewfiePendragon | Backlinks
NexusNash | Backlinks
NickiamSolvang | Backlinks
NicolaEscher | Backlinks
NightSpy | Backlinks
NikolaiWeber | Backlinks
NoelMarlowe | Backlinks
NoeticEidolon | Backlinks
NonnuxWhite | Backlinks
NornAlbion | Backlinks
Nxxx23Wallaby | Backlinks
ObsidianStormwind | Backlinks
OlmySeraph | Backlinks
OmeiTurnbull | Backlinks
OoPsGalatea | Backlinks
OzSpade | Backlinks
PadraigStygian | Backlinks
PandaronCodesmith | Backlinks
PantharOrlowski | Backlinks
PappyPopinjay | Backlinks
PastaMann | Backlinks
PatchLamington | Backlinks
PepplarSklar | Backlinks
PeriMuir | Backlinks
PersialHebert | Backlinks
PeterLameth | Backlinks
PlutoniumDragonfly | Backlinks
PolAris | Backlinks
PrakCurie | Backlinks
PrioSerpentine | Backlinks
PsykePhaeton | Backlinks
QarlFizz | Backlinks
QubiusQuinn | Backlinks
RadeGalatea | Backlinks
RaemusPatel | Backlinks
RaffaelePirandello | Backlinks
RalanGall | Backlinks
RalphDoctorow | Backlinks
RavenRaide | Backlinks
ReadyJack | Backlinks
RebeliahMartinez | Backlinks
ReleeBaysklef | Backlinks
ResunaOddfellow | Backlinks
RhaegysNyak | Backlinks
RiannMaltese | Backlinks
RichardPinkerton | Backlinks
RichardSolvang | Backlinks
RickardRoentgen | Backlinks
RickL | Backlinks
RobertaDalek | Backlinks
RobertLuchador | Backlinks
RobinsonArgonaut | Backlinks
RobotMann | Backlinks
RocketRoentgen | Backlinks
RodrickHarrington | Backlinks
RyAgent | Backlinks
RyanDayton | Backlinks
RyanDayton2 | Backlinks
RyanKHart | Backlinks
RyeDinMeiji | Backlinks
RysidianRubio | Backlinks
SaiHarbinger | Backlinks
SamhainBroom | Backlinks
SatchmoPrototype | Backlinks
SatorCanetti | Backlinks
ScalarTardis | Backlinks
SchitsoMonkey | Backlinks
SchwartzGuillaume | Backlinks
ScopiPascal | Backlinks
Scott69 | Backlinks
ScXx | Backlinks
SeagelNeville | Backlinks
SeanGorham | Backlinks
SeanHadlee | Backlinks
SeanHeavy | Backlinks
SeifertSurface | Backlinks
SeronisZagato | Backlinks
SevenEightTwo | Backlinks
SgeoNewbie | Backlinks
ShadowBailly | Backlinks
ShadowGretzky | Backlinks
ShadowWeaver | Backlinks
ShadusStonebender | Backlinks
ShallonThereian | Backlinks
SiGiSBarmy | Backlinks
SilarsisLioncourt | Backlinks
SiljeRussell | Backlinks
SiobhanTaylor | Backlinks
SionZaius | Backlinks
SiRiSAsturias | Backlinks
SirlorStonecutter | Backlinks
SiroMfume | Backlinks
SkyHoney | Backlinks
SlitchNilsson | Backlinks
SmittyJensen | Backlinks
Sn0rph | Backlinks
SniperUmm | Backlinks
SodCutter | Backlinks
SophiaMirabeau | Backlinks
SophiaPertwee | Backlinks
SpideyCela | Backlinks
SpinnerPoutine | Backlinks
Splat1Edison | Backlinks
Splat1Edsion | Backlinks
SpudsMilk | Backlinks
StaraxStatosky | Backlinks
StefanNilsson | Backlinks
SteveSteed | Backlinks
StickMan | Backlinks
StinkyQueso | Backlinks
StoneMason | Backlinks
StoneSelf | Backlinks
StPsaltery | Backlinks
StrifeOnizuka | Backlinks
SuezanneCBaskerville | Backlinks
SummerShatner | Backlinks
SwiftusGlitterbuck | Backlinks
SwitchSpectre | Backlinks
SylvarGould | Backlinks
SynergyBelvedere | Backlinks
TalilaLiu | Backlinks
TarasBalderdash | Backlinks
TasmanPerth | Backlinks
TateruNino | Backlinks
TauNino | Backlinks
TemplarBaphomet | Backlinks
TesterOmega | Backlinks
TestingBard | Backlinks
TheaBlanc | Backlinks
TheGrue | Backlinks
ThenarOddfellow | Backlinks
TheodorePolonsky | Backlinks
TheRayven | Backlinks
TheWeasel | Backlinks
ThrixxEnnui | Backlinks
TiffanyStrauss | Backlinks
TigerCrossing | Backlinks
TigseN | Backlinks
TimelessPrototype | Backlinks
TimeToTime | Backlinks
TinkerLaFollette | Backlinks
TinkerThetan | Backlinks
TiPBaKeR | Backlinks
TjGillies | Backlinks
TonelessTomba | Backlinks
TonyTigereye | Backlinks
ToY | Backlinks
TreadWhiplash | Backlinks
TrentMondrian | Backlinks
TrepCosmo | Backlinks
TreyKawabata | Backlinks
TrimdaHedges | Backlinks
TrimmingHedges | Backlinks
TroyWesterburg | Backlinks
TupacWallace | Backlinks
TwistZaius | Backlinks
UniqueNerd | Backlinks
UsherCaldera | Backlinks
UshuaiaTokugawa | Backlinks
VaLeNtE | Backlinks
VanhalMcGettigan | Backlinks
VanlerEverett | Backlinks
VanlerOdets | Backlinks
VektorLinden | Backlinks
VelosoLippmann | Backlinks
VeloxSeverine | Backlinks
VeritasKennedy | Backlinks
VershanaAmarula | Backlinks
ViceroLambert | Backlinks
VxShaw | Backlinks
WednesdayGrimm | Backlinks
WenNomad | Backlinks
WheelFizz | Backlinks
WikiAlex | Backlinks
WikiSpoon | Backlinks
WikiTrevor | Backlinks
WilBury | Backlinks
WillMoxie | Backlinks
WinterGullwing | Backlinks
WinterSilverman | Backlinks
WolfEnfield | Backlinks
WolfWings | Backlinks
XaiPierce | Backlinks
XenonLinden | Backlinks
XuguMadison | Backlinks
XylorBaysklef | Backlinks
YbNormalMan | Backlinks
YuuByou | Backlinks
ZacharyZamboni | Backlinks
ZachZamboni | Backlinks
ZacWolf | Backlinks
ZangetsuOe | Backlinks
ZarfVantongerloo | Backlinks
ZelMing | Backlinks
ZenoConcord | Backlinks
ZindorfYossarian | Backlinks
ZingoMars | Backlinks
ZobeidZuma | Backlinks
ZodiakosAbsolute | Backlinks
ZonaxD | Backlinks
ZonaxDelorean | Backlinks
LSLWiki


WinAmp

WinAmp is a popular audio player for Windows.

It can be downloaed at www.winamp.com It is available in paid and free versions.

There are also many alternatives, such as foobar2000.


wind

See llWind.


World

Another name for grid.

Simulator


WorldFunctions

See Simulator.


Wrong Page Setup

Pages listed here have problems that cannot be corrected properly by the LSL Wiki Mirror 7-7-7 community due to major problems in procedure for this Wiki.

Note, this page's ACL varies because it contains sensitive information, also users with more than 3 locked pages will be ACL blocked for violation of ACL standards. Addition of a third ACL violation, should be acompanied by an IM to Ice Brodie inworld to effect ACL revision. ACL revision will be made with inspection to asure accurate information.

When changing ACL's on the wiki be sure to submit the page with Western and not Unicode encoding

(Unicode is default !!!).

ACL of this page is currently:

Read | Write | Comment
? | ? | ?
Jul 5 2006, ACL updated per sub 3 error policy listed below, thank you contributors! - Ice

Standard ACL setup:

Read | Write | Comment
* | ? | ?

Individuals listed in ACL bans will be removed when ACL flaw count goes below 3, excluding owner pages. Ideally no users should be banned from this page as everyone should be doing the right thing.
If a ban needs to be imposed or lifted, Ice Brodie can be IMed inworld to effect it.

ACLs with incorrect setup

Due to wiki spam recieved in the past, all pages must be locked to allow registered users of the Wiki to edit.
"square box" is correct in the write field, * is not.
Page Name | Date Added | Owner | Problem
LslHomePage | 6/Jun/06 | JimLinden | Needs registered-user-only lockdown to prevent spamming.
Detected | 5/Aug/06 | CatherineOmega | Needs registered-user-only lockdown to prevent spamming.

ACLs with incorrect setup, denying editing by registered members

All pages in this WIKI are to conform to GPL guidelines of open editing by members, this is a group documentation project, not a personal web page. The Wiki is here thanks to LL, let's not waste their server resources with pages people can't contribute to, shall we?
Page Name | Backlinks | Owner | Date Added | Comments
texturingSides | Backlinks | EepQuirk | 2005-09-04 | Re-route to editable version located at texturing_sides
GrimmyMoonflower | Backlinks | EepQuirk | 2005-09-04 | This page is a personal attack against another SL user.
Links in this section are to appear as external links, as this page should not be reducing a page's possiblity of being orphaned.

User Pages section removed, User Pages don't count against misconfigurations and should not be listed, as there's a minor debate to the exact settings for user pages. - Ice

AboutACLs


XML

XML (Extensible Markup Language) is a simple, very flexible text format derived from SGML. It is used as in XMLRPC, which you can use in LSL.


XMLRPCDiscussion

XML-RPC is working, but with the restriction that the script cannot initiate a remote procedure call - it can only answer them. This means, you cannot use XML-RPC to communicate directly between objects. According to a posting from Mark Linden, this will remain the case until they have figured out a way to let LSL scripts do XML-RPCalls without having malicious scripters abuse the feature.

Ezhar thinks they could probably safely enable it for connecting to their own hosts, not the rest of the net, to allow object-object communication within SL. -- Ezhar

Chris thinks that if LindenLab wants to call the functionality they give us "XML-RPC", they should give us a full implementation, not some crippled parts of a full implementation. Malicious scripters? Please post the scenario where fully implementing XML-RPC will somehow cause grief.

Several: Portscanning using XML-RPC requests. Denial of service attacks against any kind of TCP-based service (webservers, mailservers etc.). Any of these things will cause a) people to get very angry at LL and b) people to block LLs IP range. So lets say someone from SL will rez a few hundred objects that each send XML-RPC requests as fast as they can to a webserver hosted by the british telecom. Webserver craps out, BT people get pissed, probably tell LL to stop it immediately and meanwhile block LLs IP range to protect themselves. Result: Nobody using BT as their internet provider can log into SL anymore, LL facing pissed BT users, and possibly legal trouble. Banning the offender from SL will not influence the outcome of this - it's too late, the damage is done. Worse yet, if this happens two or three times (not unlikely), LL can easily loose their credibility on the net and end up having many more providers block their IP range as preventive measure. -- EzharFairlight

I stand corrected. --Chris

Proposed solution to this problem: Check the servers response for the correct message-id:

If the server is not responding/timing out, responding with an invalid XML-RPC reply or responding with the wrong/missing message id (from llSendRemoteData) an error should be returned to the script that initiated the XML-RPCall. The next XML-RPCall from the same script would execute only after a delay (10 - 30 seconds is not unreasonable). Such an error could be sent to the script via a remote_data event with a new constant, for example REMOTE_DATA_ERROR - this way the script can handle an error without delay and then retry the call (and only now the delay kicks in, preventing flooding a service with requests).

In the case of a well-formed reply with the correct message-id, the next call would not be delayed. This would ensure that a server unaware of llRemoteData XML-RPCalls could not be flooded with requests while a server that handles them and returns the correct message-id (and thus can be expected to be a valid target for such requests) can still communicate with the script without delay.

This solution would prevent the abuse of LSL's XML-RPC client as denial of service mechanism, with the exception of attacks on valid XML-RPC/llRemoteData servers. A fairly acceptable risk that could be further lowered by including the scripts owners UUID in each XML-RPC call and thus making tracing the offender very easy.

-- EzharFairlight

maybe keep track of ip's of XML-RPC/llRemoteData servers and delay any request that is sent to close to another request; have each ip have a 10 request buffer, and if the buffer fills, throw an error. Then any time data comes back from that server send 2 requests from the buffer if they are present ignoring delay period but not ignoring some global cap. That way you could never exceed the capacity of the server or flood a server that doesn't send responces. Also would need the sims to tell each other which ip's they were hosing so someone doesn't leverage all 210+ sims resources (maybe a good idea to do the same thing for email). -- BW

Here's Testing Bard's opinion on the subject:
"This problem has already been addressed and solved by Macromedia.

To prevent this same security issue and potential absue, Flash Player 7 changed its security model to only allow TCP and HTTP connections back to the exact address (host.domain) serving the application (SWF). -- Except -- If you specify a host/domain outside the serving address, it will check for a security policy file, crossdomain.xml in the website root.

e.g.

http://foreign.host.domain/crossdomain.xml

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*.secondlife.com" />
</cross-domain-policy>

Before an XML-RPC connection is opened to a foreign server, the policy file on the corresponding web server is checked. That policy can then be cached by SL for 1 hour, 1 day, or whatever they decide. Therefore there is NO DoS potential for any site unless they explicitly allow XML-RPC connections from SecondLife.

For servers that do allow connections from SL, it is unlikely anyone but the server-admin & script author would know the address, unless they make it available for public use. The security file could also be extended to only allow connections from objects owned by a particular owner, or particular sim.

<cross-domain-policy>
<allow-access-from domain="sim05.secondlife.com" />
<allow-access-from domain="EzharFairlight.secondlife.com" />
</cross-domain-policy>

The other piece prevent denial-of-service attacks (against another site or the SL sim server resources itself) is to simply limit the number of concurrent XML-RPC calls a given object can make. Also, have a finite XML-RPC object pool on each server limiting the number of concurrent XML-RPC calls the entire sim can make, insuring the integrity of server resources.

Polling the servers from outside poses a much higher and unmanageable server load risk than does initiate connections in-world. " [Testing Bard]

Here's Chris' responce to Testing Bard's suggestion:
"I would love to see outbound RPC calls enabled, and this seems like a plausable solution. The only problem I have with it is how it can be actually implemented.

Ideally, to request security policy information, I'd like the linden server itself to make an XML-RPC request to the destination domain.

Something like, calling a remote function named "isRpcConnectionAvailableFor" with a String argument containing the hostname of secondlife's XML-RPC server ("xmlrpc.secondlife.com" or something like that. The Linden domain and the function the Linden server calls to check its permission have to be known published values). isRpcConnectionAvailableFor will then return a String (or int value) containing "true" or "false" (or 1 or 0, if you go with ints). If the xml-rpc destination address doesn't recognize (or doesnt respond to) "isRpcConnectionFor", then the Linden server would assume the responce is "false". The Linden server would then store the destination domain's responce and purge it every x amount of <minutes, seconds, hours, days>.

That way, people on the Linden end and us SL scripters dont have to jump through hoops or make huge modifications to implement this secure system." [Christopher Omega]

Here's Xugu's thoughts:
"Would it be possible for the system to block outgoing requests to hosts from which it has not yet received XML-RPC requests? Ideally, have a specific command hosts could send to tell the Linden severs that they're willing to accept XML-RPC requests?" [Xugu Madison]

Grim's thoughts
I'm pretty much against Strife's recommendation, and even Testing Bard's solution has some problems. Xugu's idea still wouldn't prevent DoS attacks against VALID XML-RPC servers that are intended for use with SL.

I'm working on setting up a remote database site for storage and retrieval of data for use in SL objects. Problem is, I need ways to ensure that only "subscribers" are accessing the database. I think I could work around Testing Bard's solution if it involved the object-owner level security. It would really suck if somebody got pissed off and DoS?'ed my data server, and I had no way to lock out that user's requests without locking out ALL of my users in the process.

I don't want it throttled or capped at all, especially globally. Sure, a global cap may work if I have 10 subscribers, but what happens when I have 100? 1000? [GrimLupis]

Grim, where's Strife's recommendation? What about my idea? :) -Chris

Umm, I don't remember what that comment was in response to. Oops. The potential problem with your idea, Chris, is the timeout period. What happens if there's an internet hiccup, or I'm rebooting my server, and a request comes in? How long will it be before my address will be cleared from the blacklist? And we have to keep in mind the side-effects that we'll get if there's a minor glitch. How hard is it going to be to code fault-tolerance into our little 16k scripts? Also, I'm keen on the idea of being able to allow/deny access to services based on user name/key. Otherwise there's no way for me to ban a bonehead without hurting my legit users in the process. [GrimLupis]

See my comment about Bard's "username.secondlight.com" (or firstname.lastname.secondlight.com)... that would solve the users problem... just add the users to the permissions file when they sign up. [ResunaOddfellow]

They did a nice job making the order of the integer and string values inconsistent in the various functions too ;-p

Here are some Secondlife.com forum posts about XML-RPC:
Title | Description
XML-RPC Implementations | A post about XML-RPC clients in different programming languages.
Not another XML-RPC Thread!?! | A post discussing the crippling of XML-RPC and possible solutions.
XML-RPC

22-07-2004 Questions?!?! I don't know this stuff. Do XML/RPC, SOAP and CORBA all do essentially the same thing? Is this a subset or a superset of what is accomplished by COM, COM+ and DCOM on Microsoft Machines but this stuff is cross platform? Am I close or am I way behind and way off here? Could someone comment on the differences and similarities of these technologies for ignorant people like me? Also, which technologies are taking root now and which are being scrapped? What should I spend my precisous time learning and what is no longer worth the trouble? Thank you everyone. -- TinkerThetan

I just posted the following into the Feature Suggestions forum; I'll copy it here also.

One possible way to avoid [spamming external sites] would be to have a potential out-calling site register itself with SL, which would then allow scripts to make XML-RPC calls to that site. For example:

My fictitious site, neorebus.com, sends an XML-RPC request to second life, using a specific "registration" channel (e.g. 01234567-baad-c0de-d00d-0123456789ab). The IntValue of the request would be the number of seconds for which the registration is valid, possibly with a maximum (say 86400 seconds, 1 day). The StringValue of the request would be a security code which would be used to allow only authorized objects to talk to the XML-RPC server.

The registration process would record the expiration time of the registration, the security code, and the IP address from which the registration request was received.

After the site is registered, objects within SL could start sending XML-RPC requests to the site. The "channel" of the llSendRemoteData call would have to be the security code listed in the registration, and the hostname portion of the URL for "dest" would have to resolve to the same IP address that was recorded for the registration; if either of these requirements is not met, the XML-RPC request would fail [silently or otherwise].

By restricting access to sites that have already registered themselves with SL, you prevent random DoS attacks, and by including a "security code", you prevent unauthorized access to private XML-RPC servers. (public servers could use a null security code to allow general access).

This was a quick thought this morning, and so there are quite possibly security flaws in this model that I haven't considered; if you think of any, please discuss them here

- Neo

HuntingHare was once a network admin (for 8 years) in his first life. Must point a few things out:

1: Places that create XML-RPC resources are already vulnerable to XML-RPC DoS. How they deal with that is their own site-specific policy.
2: Linden Labs acts as an internet service provider in this case -- they can't be responsible for what their users do with the tools, as long as they're responsive to complaints. Since they have positive economic user identification (including a credit card!) for their players, they can be responsive. (block certain credit cards from using XML-RPC, as an example, since there's a finite universe of credit cards -- and LL can certainly log all usage of the XML-RPC function, and make the number of XML-RPC connections per hour a world-wide shared pool, limiting it by the number of primitives that the script creator has, or by the number of instantiated objects that were created by the script creator in the event of a widely-merchandized script, or by an XML-RPC tax for the script creator, or...)
3: Authentication mechanisms are properly placed on the resource itself, not mechanisms for accessing the resource. (I can understand the concept of wanting to minimize the abuse of a 'proxy' system -- but sheesh, this is supposed to be a functional economy, and it would be a good thing to help reduce the amount of money in the world, if they need it.)

The proper way to look at this isn't as a technical problem, but as an economic problem. XML-RPC requests are a precious resource -- and it's in LL's (and the players') best interests to reduce the abuse of them, else they become less precious as more sites block SL's servers.

Since users of XML-RPC already have to authenticate to the XML-RPC servers they're trying to use, forcing a proxy authentication system (or any kind of system that increases the load on LL or the XML-RPC site owner) just doesn't work well. Putting a L$ pricetag on a certain number of XML-RPC requests would be a start, but I'm not sure that that would be useful either. Minimizing the number of objects per owner that can be accessing a certain data source at a time would be another good thing. (though why LL doesn't implement SOAP requests as well -- for compatibility with such things as Amazon and Google -- is beyond me... unless I just answered my own question.)

Anyway. I've been up for about 15 hours, and I'm heavily caffeinated, so these are just some rambling thoughts -- but these are thoughts designed to help put the problem in the correct domain, instead of a heavy-overhead technical concept. (I must admit, I would very much like to be able to post to my blog from within SL. Write up a notecard, ship it off to my custom blogpost script, and make it easier to do things in-world.)

How about simply proxying the XML-RPC calls through the client? That way only that client is affected if they try to DOS another server.

- Noodles

I, OrangeE, concurr with HuntingHare probably because of having a similar background in network administration. I've been considering making data in the real world represented in SL. For example, I am working on a 'mood ball' that could change colors based on changes in financial markets or other parameters. I was working on making a nice script that would not make RPC requests unless an avi was nearby, but because I can only do XML responses I'll have to push data into the ball from the outside world frequently without knowing if it's worth updating or not. I suppose my point is that without a working llSendRemoteData Second Life is creating an inbound-spam problem of lots of inbound RPC requests for objects that do not need updates.

BTW, can't users already send E-Mail out through the system? Has there been any problem there with spamming?


XMLRPCImplementations

Here you'll find some XML-RPC sample implementations in various languages:

Language: | Modules/Libraries Used:
ASP | aspxmlrpc
C# | System.Web
C++ Example 1 | XmlRpc++ (Modified)
C++ Example 2 | KDE 3.x (Linux)
Java | Apache XML-RPC
JavaScript | JavaScript O Lait
Perl | RPC::XML
PHP Example 1 | XML-RPC
PHP Example 2 | XML-RPC
Python Example 1 | xmlrpclib
Python Example 2 | python-xmlrpc
Ruby | xmlrpc4r

FYI: XML assumes UTF-8 as the encoding type if not otherwise speficied (like in XML-RPC), in your software you should expect UTF-8 strings when parsing results. SL uses UTF-8 for it's encoding of strings.

ASP

This ASP example uses the aspxmlrpc http://aspxmlrpc.sourceforge.net/ library by David Carter-Tod, et. al..

<%

Dim paramList(0)
Set paramList(0) = CreateObject("Scripting.Dictionary")
paramList(0).Add "Channel","0d58c21c-b56f-57df-745f-9ccd0e06df49"
paramList(0).Add "IntValue",CByte(17)
paramList(0).Add "StringValue","Hello, Alexander!"

Dim myresp
on error resume next
Set myresp = xmlRPC("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi", "llRemoteData", paramList)
on error goto 0

if err.number <>0 then
    response.write("Error number: " & err.number & "<P>")
    response.write("Error description: " & err.description & "<P>")
else
    response.write("Channel: " & myresp("Channel") & vbCrLf)
    response.write("IntValue: " & myresp("IntValue") & vbCrLf)
    response.write("StringValue: " & myresp("StringValue") & vbCrLf)
end if

%>

C#

C# Implementation by DedricMauriac. Make sure to add a reference to System.Web in your project.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Web;

/*
 * XmlRpc rpc = new XmlRpc(
 *      new Guid("ba70440d-7d8b-cd1e-db1c-198d563bae55"),
 *      "This is some text",
 *      42);
 * rpc.llRemoteData();
 * Debug.WriteLine(rpc.channel, "channel");
 * Debug.WriteLine(rpc.stringValue, "string");
 * Debug.WriteLine(rpc.intValue, "int");
 */

public class XmlRpc
{
    private const string MethodCallTemplate = @"<?xml version=""1.0""?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct><member><name>Channel</name><value><string>{0}</string></value></member><member><name>IntValue</name><value><int>{1}</int></value></member><member><name>StringValue</name><value><string>{2}</string></value></member></struct></value></param></params></methodCall>";

    public Guid channel;
    public string stringValue;
    public int intValue;
    public bool TimedOut;

    public XmlRpc()
    {
    }
    public XmlRpc(Guid channel, string stringValue, int intValue)
    {
        this.channel = channel;
        this.stringValue = stringValue;
        this.intValue = intValue;
    }

    public void llRemoteData()
    {
        string methodCall = string.Format(MethodCallTemplate,
            this.channel,
            this.intValue,
            HttpUtility.HtmlEncode(this.stringValue)
            );

        Uri requestUri = new Uri("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");

        string methodResponse = Post(requestUri, methodCall);

        ParseResponse(methodResponse);
    }

    private void ParseResponse(string methodResponse)
    {
        if (methodResponse.IndexOf("<fault>") != -1)
        {
            string message = Regex.Match(methodResponse,
                "<value><string>([^>]+)</string></value>")
                .Groups[1].Value;
            throw new ApplicationException(message);
        }

        MatchCollection matches = Regex.Matches(methodResponse, @"<name>([^<]+)</name>\s*<value>\s*<(string|int)>([^<]+)</\2>\s*</value>");
        foreach (Match match in matches)
        {
            string name = match.Groups[1].Value;
            string value = match.Groups[3].Value;
            switch (name)
            {
                case "Channel":
                    this.channel = new Guid(value);
                    break;
                case "StringValue":
                    this.stringValue = HttpUtility.HtmlDecode(value);
                    break;
                case "IntValue":
                    this.intValue = int.Parse(value);
                    break;
            }
        }
    }
    private string Post(Uri requestUri, string data)
    {
        TimedOut = false;
        byte[] postData = ASCIIEncoding.Default.GetBytes(data);
        HttpWebRequest request = null;
        Stream writer = null;
        Stream reader = null;
        WebResponse response = null;
        try
        {
            request = HttpWebRequest.Create(requestUri) as HttpWebRequest;

            request.Method = "POST";
            request.KeepAlive = false;
            request.ContentType = "application/x-www-form-urlencoded";
            request.Timeout = 10000;

            request.ContentLength = postData.Length;

            writer = request.GetRequestStream();
            writer.Write(postData, 0, postData.Length);
            writer.Close();

            response = request.GetResponse();

            reader = response.GetResponseStream();
            byte[] postResponse = new byte[response.ContentLength];
            reader.Read(postResponse, 0, postResponse.Length);

            return ASCIIEncoding.Default.GetString(postResponse);
        }
        catch (WebException ex)
        {
            if (ex.Status == WebExceptionStatus.Timeout)
            {
                TimedOut = true;
                return "";
            }
            else
                throw;
        }
        finally
        {
            if (writer != null)
            {
                writer.Close();
                writer.Dispose();
            }
            if (reader != null)
            {
                reader.Close();
                reader.Dispose();
            }
            if (response != null)
            {
                response.Close();
            }
        }
    }
}

C++

This C++ example uses the XmlRpc++ implementation, with some modifications listed below

Modified XmlRpcValue.cpp (v.07)
#include "XmlRpcValue.h"
#include "XmlRpcException.h"
#include "XmlRpcUtil.h"
#include "base64.h"

#ifndef MAKEDEPEND
# include <iostream>
# include <ostream>
# include <stdlib.h>
# include <stdio.h>
#endif

namespace XmlRpc {

  static const char VALUE_TAG[]     = "<value>";
  static const char VALUE_ETAG[]    = "</value>";

  static const char BOOLEAN_TAG[]   = "<boolean>";
  static const char BOOLEAN_ETAG[]  = "</boolean>";
  static const char DOUBLE_TAG[]    = "<double>";
  static const char DOUBLE_ETAG[]   = "</double>";
  static const char INT_TAG[]       = "<int>";
  static const char I4_TAG[]        = "<int>"; /* Was i4 */
  static const char I4_ETAG[]       = "</int>"; /* Was i4 */
  static const char STRING_TAG[]    = "<string>";
  static const char STRING_ETAG[]   = "</string>";
  static const char DATETIME_TAG[]  = "<dateTime.iso8601>";
  static const char DATETIME_ETAG[] = "</dateTime.iso8601>";
  static const char BASE64_TAG[]    = "<base64>";
  static const char BASE64_ETAG[]   = "</base64>";

  static const char ARRAY_TAG[]     = "<array>";
  static const char DATA_TAG[]      = "<data>";
  static const char DATA_ETAG[]     = "</data>";
  static const char ARRAY_ETAG[]    = "</array>";

  static const char STRUCT_TAG[]    = "<struct>";
  static const char MEMBER_TAG[]    = "<member>";
  static const char NAME_TAG[]      = "<name>";
  static const char NAME_ETAG[]     = "</name>";
  static const char MEMBER_ETAG[]   = "</member>";
  static const char STRUCT_ETAG[]   = "</struct>";

  // Format strings
  std::string XmlRpcValue::_doubleFormat("%f");

  // Clean up
  void XmlRpcValue::invalidate()
  {
    switch (_type) {
      case TypeString:    delete _value.asString; break;
      case TypeDateTime:  delete _value.asTime;   break;
      case TypeBase64:    delete _value.asBinary; break;
      case TypeArray:     delete _value.asArray;  break;
      case TypeStruct:    delete _value.asStruct; break;
      default: break;
    }
    _type = TypeInvalid;
    _value.asBinary = 0;
  }

  // Type checking
  void XmlRpcValue::assertTypeOrInvalid(Type t)
  {
    if (_type == TypeInvalid)
    {
      _type = t;
      switch (_type) {    // Ensure there is a valid value for the type
        case TypeString:   _value.asString = new std::string(); break;
        case TypeDateTime: _value.asTime = new struct tm();     break;
        case TypeBase64:   _value.asBinary = new BinaryData();  break;
        case TypeArray:    _value.asArray = new ValueArray();   break;
        case TypeStruct:   _value.asStruct = new ValueStruct(); break;
        default:           _value.asBinary = 0; break;
      }
    }
    else if (_type != t)
      throw XmlRpcException("type error");
  }

  void XmlRpcValue::assertArray(int size) const
  {
    if (_type != TypeArray)
      throw XmlRpcException("type error: expected an array");
    else if (int(_value.asArray->size()) < size)
      throw XmlRpcException("range error: array index too large");
  }

  void XmlRpcValue::assertArray(int size)
  {
    if (_type == TypeInvalid) {
      _type = TypeArray;
      _value.asArray = new ValueArray(size);
    } else if (_type == TypeArray) {
      if (int(_value.asArray->size()) < size)
        _value.asArray->resize(size);
    } else
      throw XmlRpcException("type error: expected an array");
  }

  void XmlRpcValue::assertStruct()
  {
    if (_type == TypeInvalid) {
      _type = TypeStruct;
      _value.asStruct = new ValueStruct();
    } else if (_type != TypeStruct)
      throw XmlRpcException("type error: expected a struct");
  }

  // Operators
  XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs)
  {
    if (this != &rhs)
    {
      invalidate();
      _type = rhs._type;
      switch (_type) {
        case TypeBoolean:  _value.asBool = rhs._value.asBool; break;
        case TypeInt:      _value.asInt = rhs._value.asInt; break;
        case TypeDouble:   _value.asDouble = rhs._value.asDouble; break;
        case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break;
        case TypeString:   _value.asString = new std::string(*rhs._value.asString); break;
        case TypeBase64:   _value.asBinary = new BinaryData(*rhs._value.asBinary); break;
        case TypeArray:    _value.asArray = new ValueArray(*rhs._value.asArray); break;
        case TypeStruct:   _value.asStruct = new ValueStruct(*rhs._value.asStruct); break;
        default:           _value.asBinary = 0; break;
      }
    }
    return *this;
  }

  // Predicate for tm equality
  static bool tmEq(struct tm const& t1, struct tm const& t2) {
    return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min &&
            t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday &&
            t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year;
  }

  bool XmlRpcValue::operator==(XmlRpcValue const& other) const
  {
    if (_type != other._type)
      return false;

    switch (_type) {
      case TypeBoolean:  return ( !_value.asBool && !other._value.asBool) ||
                                ( _value.asBool && other._value.asBool);
      case TypeInt:      return _value.asInt == other._value.asInt;
      case TypeDouble:   return _value.asDouble == other._value.asDouble;
      case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime);
      case TypeString:   return *_value.asString == *other._value.asString;
      case TypeBase64:   return *_value.asBinary == *other._value.asBinary;
      case TypeArray:    return *_value.asArray == *other._value.asArray;

      // The map<>::operator== requires the definition of value< for kcc
      case TypeStruct:   //return *_value.asStruct == *other._value.asStruct;
        {
          if (_value.asStruct->size() != other._value.asStruct->size())
            return false;

          ValueStruct::const_iterator it1=_value.asStruct->begin();
          ValueStruct::const_iterator it2=other._value.asStruct->begin();
          while (it1 != _value.asStruct->end()) {
            const XmlRpcValue& v1 = it1->second;
            const XmlRpcValue& v2 = it2->second;
            if ( ! (v1 == v2))
              return false;
            it1++;
            it2++;
          }
          return true;
        }
      default: break;
    }
    return true;    // Both invalid values ...
  }

  bool XmlRpcValue::operator!=(XmlRpcValue const& other) const
  {
    return !(*this == other);
  }

  // Works for strings, binary data, arrays, and structs.
  int XmlRpcValue::size() const
  {
    switch (_type) {
      case TypeString: return int(_value.asString->size());
      case TypeBase64: return int(_value.asBinary->size());
      case TypeArray:  return int(_value.asArray->size());
      case TypeStruct: return int(_value.asStruct->size());
      default: break;
    }

    throw XmlRpcException("type error");
  }

  // Checks for existence of struct member
  bool XmlRpcValue::hasMember(const std::string& name) const
  {
    return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end();
  }

  // Set the value from xml. The chars at *offset into valueXml
  // should be the start of a <value> tag. Destroys any existing value.
  bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset)
  {
    int savedOffset = *offset;

    invalidate();
    if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset))
      return false;       // Not a value, offset not updated

 int afterValueOffset = *offset;
    std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset);
    bool result = false;
    if (typeTag == BOOLEAN_TAG)
      result = boolFromXml(valueXml, offset);
    else if (typeTag == I4_TAG || typeTag == INT_TAG)
      result = intFromXml(valueXml, offset);
    else if (typeTag == DOUBLE_TAG)
      result = doubleFromXml(valueXml, offset);
    else if (typeTag.empty() || typeTag == STRING_TAG)
      result = stringFromXml(valueXml, offset);
    else if (typeTag == DATETIME_TAG)
      result = timeFromXml(valueXml, offset);
    else if (typeTag == BASE64_TAG)
      result = binaryFromXml(valueXml, offset);
    else if (typeTag == ARRAY_TAG)
      result = arrayFromXml(valueXml, offset);
    else if (typeTag == STRUCT_TAG)
      result = structFromXml(valueXml, offset);
    // Watch for empty/blank strings with no <string>tag
    else if (typeTag == VALUE_ETAG)
    {
      *offset = afterValueOffset;   // back up & try again
      result = stringFromXml(valueXml, offset);
    }

    if (result)  // Skip over the </value> tag
      XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset);
    else        // Unrecognized tag after <value>
      *offset = savedOffset;

    return result;
  }

  // Encode the Value in xml
  std::string XmlRpcValue::toXml() const
  {
    switch (_type) {
      case TypeBoolean:  return boolToXml();
      case TypeInt:      return intToXml();
      case TypeDouble:   return doubleToXml();
      case TypeString:   return stringToXml();
      case TypeDateTime: return timeToXml();
      case TypeBase64:   return binaryToXml();
      case TypeArray:    return arrayToXml();
      case TypeStruct:   return structToXml();
      default: break;
    }
    return std::string();   // Invalid value
  }

  // Boolean
  bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset)
  {
    const char* valueStart = valueXml.c_str() + *offset;
    char* valueEnd;
    long ivalue = strtol(valueStart, &valueEnd, 10);
    if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1))
      return false;

    _type = TypeBoolean;
    _value.asBool = (ivalue == 1);
    *offset += int(valueEnd - valueStart);
    return true;
  }

  std::string XmlRpcValue::boolToXml() const
  {
    std::string xml = VALUE_TAG;
    xml += BOOLEAN_TAG;
    xml += (_value.asBool ? "1" : "0");
    xml += BOOLEAN_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // Int
  bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset)
  {
    const char* valueStart = valueXml.c_str() + *offset;
    char* valueEnd;
    long ivalue = strtol(valueStart, &valueEnd, 10);
    if (valueEnd == valueStart)
      return false;

    _type = TypeInt;
    _value.asInt = int(ivalue);
    *offset += int(valueEnd - valueStart);
    return true;
  }

  std::string XmlRpcValue::intToXml() const
  {
    char buf[256];
    snprintf(buf, sizeof(buf)-1, "%d", _value.asInt);
    buf[sizeof(buf)-1] = 0;
    std::string xml = VALUE_TAG;
    xml += I4_TAG;
    xml += buf;
    xml += I4_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // Double
  bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset)
  {
    const char* valueStart = valueXml.c_str() + *offset;
    char* valueEnd;
    double dvalue = strtod(valueStart, &valueEnd);
    if (valueEnd == valueStart)
      return false;

    _type = TypeDouble;
    _value.asDouble = dvalue;
    *offset += int(valueEnd - valueStart);
    return true;
  }

  std::string XmlRpcValue::doubleToXml() const
  {
    char buf[256];
    snprintf(buf, sizeof(buf)-1, getDoubleFormat().c_str(), _value.asDouble);
    buf[sizeof(buf)-1] = 0;

    std::string xml = VALUE_TAG;
    xml += DOUBLE_TAG;
    xml += buf;
    xml += DOUBLE_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // String
  bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset)
  {
    size_t valueEnd = valueXml.find('<', *offset);
    if (valueEnd == std::string::npos)
      return false;     // No end tag;

    _type = TypeString;
    _value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset)));
    *offset += int(_value.asString->length());
    return true;
  }

  std::string XmlRpcValue::stringToXml() const
  {
    std::string xml = VALUE_TAG;
    xml += STRING_TAG;// optional
    xml += XmlRpcUtil::xmlEncode(*_value.asString);
    xml += STRING_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // DateTime (stored as a struct tm)
  bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset)
  {
    size_t valueEnd = valueXml.find('<', *offset);
    if (valueEnd == std::string::npos)
      return false;     // No end tag;

    std::string stime = valueXml.substr(*offset, valueEnd-*offset);

    struct tm t;
    if (sscanf(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6)
      return false;

    t.tm_isdst = -1;
    _type = TypeDateTime;
    _value.asTime = new struct tm(t);
    *offset += int(stime.length());
    return true;
  }

  std::string XmlRpcValue::timeToXml() const
  {
    struct tm* t = _value.asTime;
    char buf[20];
    snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
      t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
    buf[sizeof(buf)-1] = 0;

    std::string xml = VALUE_TAG;
    xml += DATETIME_TAG;
    xml += buf;
    xml += DATETIME_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // Base64
  bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset)
  {
    size_t valueEnd = valueXml.find('<', *offset);
    if (valueEnd == std::string::npos)
      return false;     // No end tag;

    _type = TypeBase64;
    std::string asString = valueXml.substr(*offset, valueEnd-*offset);
    _value.asBinary = new BinaryData();
    // check whether base64 encodings can contain chars xml encodes...

    // convert from base64 to binary
    int iostatus = 0;
   base64<char> decoder;
    std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary));
  decoder.get(asString.begin(), asString.end(), ins, iostatus);

    *offset += int(asString.length());
    return true;
  }

  std::string XmlRpcValue::binaryToXml() const
  {
    // convert to base64
    std::vector<char> base64data;
    int iostatus = 0;
   base64<char> encoder;
    std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data);
  encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf());

    // Wrap with xml
    std::string xml = VALUE_TAG;
    xml += BASE64_TAG;
    xml.append(base64data.begin(), base64data.end());
    xml += BASE64_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // Array
  bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset)
  {
    if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset))
      return false;

    _type = TypeArray;
    _value.asArray = new ValueArray;
    XmlRpcValue v;
    while (v.fromXml(valueXml, offset))
      _value.asArray->push_back(v);       // copy...

    // Skip the trailing </data>
    (void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset);
    return true;
  }

  // In general, its preferable to generate the xml of each element of the
  // array as it is needed rather than glomming up one big string.
  std::string XmlRpcValue::arrayToXml() const
  {
    std::string xml = VALUE_TAG;
    xml += ARRAY_TAG;
    xml += DATA_TAG;

    int s = int(_value.asArray->size());
    for (int i=0; i<s; ++i)
       xml += _value.asArray->at(i).toXml();

    xml += DATA_ETAG;
    xml += ARRAY_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // Struct
  bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset)
  {
    _type = TypeStruct;
    _value.asStruct = new ValueStruct;

    while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) {
      // name
      const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset);
      // value
      XmlRpcValue val(valueXml, offset);
      if ( ! val.valid()) {
        invalidate();
        return false;
      }
      const std::pair<const std::string, XmlRpcValue> p(name, val);
      _value.asStruct->insert(p);

      (void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset);
    }
    return true;
  }

  // In general, its preferable to generate the xml of each element
  // as it is needed rather than glomming up one big string.
  std::string XmlRpcValue::structToXml() const
  {
    std::string xml = VALUE_TAG;
    xml += STRUCT_TAG;

    ValueStruct::const_iterator it;
    for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) {
      xml += MEMBER_TAG;
      xml += NAME_TAG;
      xml += XmlRpcUtil::xmlEncode(it->first);
      xml += NAME_ETAG;
      xml += it->second.toXml();
      xml += MEMBER_ETAG;
    }

    xml += STRUCT_ETAG;
    xml += VALUE_ETAG;
    return xml;
  }

  // Write the value without xml encoding it
  std::ostream& XmlRpcValue::write(std::ostream& os) const {
    switch (_type) {
      default:           break;
      case TypeBoolean:  os << _value.asBool; break;
      case TypeInt:      os << _value.asInt; break;
      case TypeDouble:   os << _value.asDouble; break;
      case TypeString:   os << *_value.asString; break;
      case TypeDateTime:
        {
          struct tm* t = _value.asTime;
          char buf[20];
          snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
            t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
          buf[sizeof(buf)-1] = 0;
          os << buf;
          break;
        }
      case TypeBase64:
        {
          int iostatus = 0;
          std::ostreambuf_iterator<char> out(os);
          base64<char> encoder;
          encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf());
          break;
        }
      case TypeArray:
        {
          int s = int(_value.asArray->size());
          os << '{';
          for (int i=0; i<s; ++i)
          {
            if (i > 0) os << ',';
            _value.asArray->at(i).write(os);
          }
          os << '}';
          break;
        }
      case TypeStruct:
        {
          os << '[';
          ValueStruct::const_iterator it;
          for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it)
          {
            if (it!=_value.asStruct->begin()) os << ',';
            os << it->first << ':';
            it->second.write(os);
          }
          os << ']';
          break;
        }

    }

    return os;
  }

} // namespace XmlRpc

// ostream
std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v)
{
  // If you want to output in xml format:
  //return os << v.toXml();
  return v.write(os);
}

Test.c
#include <iostream>
#include "src/XmlRpc.h"

using namespace XmlRpc;

int main(int argc, char* argv[])
{

   XmlRpcClient c("xmlrpc.secondlife.com", 80, "/cgi-bin/xmlrpc.cgi");

   XmlRpcValue testLSL,result;

   testLSL["Channel"] = "83af2b8d-80fa-1db6-b35a-b9c0c2dc8670";
   testLSL["IntValue"] = 50;
   testLSL["StringValue"] = "Hello World!";

   if (c.execute("llRemoteData", testLSL, result))
     {
 std::cout << "Returned:\n";
 std::cout << "\t" << result["IntValue"] << "\n\t" << result["StringValue"] << "\n\n";
     }
   else
     {
 std::cout << "Error calling 'llRemoteData'\n\n";
     }

   return 0;
}

C++ Example 2

This C++ example uses the KDE 3.x libraries (Linux) and reads the XML data from a file.

test.h
#include <kio/job.h>

class Test : public QObject
{
    Q_OBJECT

    public:
        Test();

    protected slots:
        void requestData
            (KIO::Job *job, const QByteArray &data);
        void requestResult
            (KIO::Job *job);
};

test.cpp
#include "test.moc"

#include <kapp.h>
#include <kaboutdata.h>
#include <kcmdlineargs.h>

#include <kfile.h>
#include <stream.h>

int main(int argc, char **argv)
{
    KAboutData about("test", "Test", "0.1",
        "A test of communication with SL", KAboutData::License_GPL,
               "Copyright (c) 2007 LSL Wiki Mirror 7-7-7");
    KCmdLineArgs::init(argc, argv, &about);
    KApplication application(false, false);

    Test *test = new Test();

    return application.exec();
}

Test::Test()
    : QObject()
{
    KIO::SimpleJob *job;
    KURL address("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");
    QFile testFile("test.xml");
    QByteArray toSend;

    if (!testFile.open(IO_ReadOnly))
    {
        cerr << "Failed to open XML file";
        return;
    }
    toSend = testFile.readAll();
    testFile.close();

    job = KIO::http_post(address, toSend, false);
    job->addMetaData("accept", "text/xml, */*");
    job->addMetaData("content-type", "Content-type: application/x-www-form-urlencoded");

    connect(job,
        SIGNAL(data(KIO::Job *, const QByteArray &)),
        this,
        SLOT(requestData(KIO::Job *, const QByteArray &)));
    connect(job,
        SIGNAL(result(KIO::Job *)),
        this,
        SLOT(requestResult(KIO::Job *)));
}

void Test::requestData(KIO::Job *job, const QByteArray &data)
{
    cout << data;
}

void Test::requestResult(KIO::Job *job)
{
    if (job->error())
        cerr << "Failure";
           else cout << "Transfer succeeded";
}

test.moc

Is generated automatically using Qt's "moc" utility.

Java

This Java example uses the XML-RPC implementation available from the Apache Web Services Project. This example uses an outdated version of the library... does anyone want to fix it? -Fixed using v3.0 of apache. Revised code below. DeleriumHannibal

import java.net.URL;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

public class RpcTest {

    public static void main(String[] args) {
        /*
         * Put the arguments into an XML-RPC struct, represented by
         * a Java Hashtable.
         */
        Hashtable s = new Hashtable();
        s.put("Channel", "5b90c3ad-a641-fc2c-55e1-dfcc3eefc7cd");
        s.put("IntValue", new Integer(17));
        s.put("StringValue", "Hello, Alexander!");

        /*
         * Argument list for the call is a vector containing the struct.
         */
        Vector v = new Vector();
        v.add(s);

        try {
            XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
            config.setServerURL(new URL("http://xmlrpc.secondlife.com:80/cgi-bin/xmlrpc.cgi"));
            XmlRpcClient client = new XmlRpcClient();
            client.setConfig(config);
            Object result = client.execute("llRemoteData", v);
            if (result != null)
                System.out.println("Result class: " + result.getClass().getName());
        } catch(Exception e) {
            System.out.println("Failed: " + e);
        }
    }

}

JavaScript

This JavaScript example uses the JavaScript O Lait http://jsolait.net/index.xhtml library by Jan-Klaas Kollhof. However you should note that the default settings on most browsers do not allow XML-RPC to external websites.

<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/init.js"></script>  <!--- You can replace these with local copies (download from http://jsolait.net/doc/index.xhtml) --->
<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/lib/urllib.js"></script>
<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/lib/xml.js"></script>
<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/lib/xmlrpc.js"></script>
<script type="text/javascript" >
var xmlrpc=null;
try{
    var xmlrpc = importModule("xmlrpc");
}catch(e){
    reportException(e);
    throw "importing of xmlrpc module failed.";
}

function send_rpc(){
    var addr = "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi";

    var sChannel = "0d58c21c-b56f-57df-745f-9ccd0e06df49";  // Add your ChannelID and arguments (can be pulled from textboxes)
    var iValue = 17;
    var sString = "Hello, Alexander!";
    var evalStr = {"Channel" : sChannel, "IntValue" : iValue, "StringValue" : sString};

    try{
        var service = new xmlrpc.ServiceProxy(addr);
        var rslt = service.llRemoteData(evalStr);  // Result object

        alert(rslt["StringValue"]);  // Returns String Value
        alert(rslt["IntValue"]);  // Returns Int Value
    }catch(e){
        var em;
        if(e.toTraceString){
            em=e.toTraceString();
        }else{
            em = e.message;
        }
        alert("Error trace: \n\n" + em);
    }
    return false;
}
</script>

Perl

Here's an example in Perl. It uses the RPC::XML module that is available from CPAN.
The values for $key, $ival and $sval need to be valid of course.
require RPC::XML;
require RPC::XML::Client;

my $client = RPC::XML::Client->new('http://xmlrpc.secondlife.com:80/cgi-bin/xmlrpc.cgi');

my $request = RPC::XML::request->new('llRemoteData', RPC::XML::struct->new(
   'Channel'   => RPC::XML::string->new($key),
   'IntValue' => RPC::XML::int->new($ival),
   'StringValue' => RPC::XML::string->new($sval)
   )
);

my $response = $client->simple_request($request);
if (!$response) {
   print $RPC::XML::ERROR, "\n";
} else {
   print $response->{StringValue}, "\n";
   print $response->{IntValue}, "\n";
}

PHP Example 1

Here's an implementation in PHP, using PHP's XML-RPC library. Note that you may need to compile it into your PHP build. Users of the PHP Windows binaries just need to add extension=php_xmlrpc.dll into their php.ini.
<?php
 function llRemoteData($channel, $intvalue, $stringvalue) {
  $host = "xmlrpc.secondlife.com";
  $port = 80;
  $uri = "/cgi-bin/xmlrpc.cgi";
  $request = xmlrpc_encode_request('llRemoteData',
   array( 'Channel' => $channel,
    'IntValue' => $intvalue,
    'StringValue' => $stringvalue));
  $fp = fsockopen($host, $port, $errno, $errstr);
  $query = "POST " . $uri . " HTTP/1.0\n" .
    "User_Agent: PHP XML-RPC Interface\n" .
    "Host: " . $host . "\n" .
    "Content-Type: text/xml\n" .
    "Content-Length: " . strlen($request) . "\n\n" .
    $request . "\n";

  if (!fputs($fp, $query, strlen($query)))
   return NULL; // some sort of write error occured

  $xmlresp = "";

//I replaced some code that was here but did not work. This should work just as well, in less code and quicker ;p
//read the maximum amount (if you have more bytes than 8192, this method will not work)
 $response = fread($fp, 8192);
//grab the XML
 $xmlresp = (substr($response, strpos($response, "\r\n\r\n")+4));

  fclose($fp);
  if ($xmlresp == "")
   return NULL;
  else
   return xmlrpc_decode($xmlresp);
 }
?>

PHP Example 2

Here is a way to send RPC without the need of special includes
<?php
  function postToHost($host, $path, $data_to_send) {
    $fp = fsockopen($host, 80);
    fputs($fp, "POST $path HTTP/1.1\r\n");
    fputs($fp, "Host: $host\r\n");
    fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
    fputs($fp, "Content-length: ". strlen($data_to_send) ."\r\n");
    fputs($fp, "Connection: close\r\n\r\n");
    fputs($fp, $data_to_send);
    $res = "";
    while(!feof($fp)) {
        $res .= fgets($fp, 128);
    }
    fclose($fp);
    return substr($res, strpos($res, "\r\n\r\n"));;
  }

  function parseResponse($response) {
    $result = array();
    if (preg_match_all('#<name>(.+)</name><value><(string|int)>(.*)</\2></value>#U', $response, $regs, PREG_SET_ORDER)) {
      foreach($regs as $key=>$val) {
        $result[$val[1]] = $val[3];
      }
    }
    return $result;
  }

  function sendRequest($channel, $intValue, $stringValue) {
    $channel = htmlspecialchars($channel);
    $int = (int)$intValue;
    $string = htmlspecialchars($stringValue);

    $data = '<?xml version="1.0"?>';
    $data .= '<methodCall>';
    $data .= '<methodName>llRemoteData</methodName>';
    $data .= '<params><param><value><struct>';
    $data .= '<member><name>Channel</name><value><string>'.$channel.'</string></value></member>';
    $data .= '<member><name>IntValue</name><value><int>'.$int.'</int></value></member>';
    $data .= '<member><name>StringValue</name><value><string>'.$string.'</string></value></member>';
    $data .= '</struct></value></param></params></methodCall>';

    return parseResponse(postToHost("xmlrpc.secondlife.com","/cgi-bin/xmlrpc.cgi", $data));
  }
?>

Python Example 1

Client example in python using xmlrpclib (included in Python 2.2+):
#!/usr/local/bin/python

from xmlrpclib import ServerProxy;
import sys;

def llRemoteData(Channel, Int, String):
        client = ServerProxy("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");
        return client.llRemoteData({"Channel" : Channel, "IntValue" : Int, "StringValue" : String});

reply = llRemoteData("9307b83e-0739-63d5-86a5-f510aded12f4", 0, "Hello from " + sys.platform);

for name, value in reply.iteritems():
        print name, value;

Python Example 2

Another python example using the python-xmlrpc module which comes as a nice debian package:
#!/usr/bin/python
import xmlrpc
client = xmlrpc.client("xmlrpc.secondlife.com", 80, "/cgi-bin/xmlrpc.cgi")
print client.execute("llRemoteData", [{"Channel": "2ac9fac6-6194-8e2a-2415-06257dea4a72", "IntValue": 0, "StringValue": "yourmessage"}])

Python Example 3

Another python example using twisted:
from twisted.web.xmlrpc import Proxy
from twisted.internet import reactor

def printValue(value):
    print repr(value)
    reactor.stop()

def printError(error):
    print 'error', error
    reactor.stop()

proxy = Proxy('http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi')
proxy.callRemote('llRemoteData', {"Channel": "a9c9b2e5-2b91-3e3e-ef98-a26b83d21fcd", "IntValue": 0, "StringValue": "yourmessage"}).addCallbacks(printValue, printError)
reactor.run()

Ruby

Here is a Ruby example using xmlrpc4r:
#!/usr/bin/ruby

require "xmlrpc/client"

server = XMLRPC::Client.new("xmlrpc.secondlife.com","/cgi-bin/xmlrpc.cgi")

begin
        result = server.call("llRemoteData", {
                "Channel"     => channel,
                "StringValue" => sdata,
                "IntValue"    => idata
        })

        puts "Channel:     #{result['Channel']}"
        puts "StringValue: #{result['StringValue']}"
        puts "IntValue:    #{result['IntValue']}"

rescue XMLRPC::FaultException => e
        puts "Error: #{e.faultCode} (#{e.faultString})"
end

XMLRPC / XMLRPCDiscussion


XOR

Stands for eXclusive OR. Also called an exclusive disjunction. The operation returns TRUE when one, and only one, of its operands is TRUE.

Foo | Bar | Result:
FALSE | FALSE | FALSE
TRUE | FALSE | TRUE
FALSE | TRUE | TRUE
TRUE | TRUE | FALSE

See llXorBase64Strings.

The correct pronounciation of XOR is "ex-or", not "zor".

BooleanLogic / Bitwise


yaw

Angle of rotation around the Z-axis. Compare with pitch and roll.

Vehicle


Test1

LSL-Editor

LSL-Editor is a standalone LSL (Secondlife) script editor and run-time environment for Windows and compiles and executes LSL scripts. No SecondLife viewer is needed nor connection to the SecondLife grid.

The program is updated on regular intervals. When the project emerges, more and more code can be compiled, debugged, and tested outside SecondLife world.

Current release LSL-Editor 2.10 featuring:

Code highlighting (including Brackets matching)
Code auto completion (Like Visual Studio)
Auto insert events
Argument information on functions (typing)
Tabbed or MDI interface
LSLWiki or SecondLife color scheme
Code editing ( Copy / Cut / Paste / Delete / Undo / Redo / Find / Replace / Clipboard function)
Block commenting / uncommenting
Tooltip on keywords (LSL-Editor contains a complete LSL guide!)
Auto-indent (using tabs or SecondLife indent scheme)
Help, on every keyword, uses www.LSLwiki.NET information
Compiling, Execution and Debugging outside SecondLife world
llDialog input screen
Printing
XML-RPC
Solution Explorer
You don't have to install (!) the program, unzip en use it.

Runs on Windows XP , Windows 2000 , Windows 2003, Windows Vista
.NET Framework 2.0 has to be installed (standard on most Windows machines)
The LSL-Editor is free to use and does not contain viruses,
ad-ware, or other bad things we all do not like.

LSL-Editor 2.6 and above has implemented an XML-RPC interface.
For testing XmlRpc, download the test application XmlRpcLSLEditor
This application can also be used to do InWorld object testing.


Test1

LSL-Editor


Test1

Compiler


Test1

Solution Explorer