Using the Linden Scripting Language

The Linden Scripting Language is where Second Life begins.


February 23, 2007
URL:http://drdobbs.com/parallel/using-the-linden-scripting-language/197008520

The authors also wrote Second Life: The Official Guide, on which this article is based. Courtesy John Wiley & Sons, 2007.


The Linden Scripting Language (LSL) lets you add behaviors and interactivity to objects inside Second Life. Scripting is just another word for programming, so in learning about LSL you will end up learning about programming as well. Do not be afraid, though -- between this article and the many resources available both online and in-world, you'll be up and scripting in no time. You do not need to write scripts to have fun in Second Life, but scripting drives the magic, from vehicles and guns to vendors and HTTP requests.

This article walks you through creating your first script and covers the LSL syntax and more advanced language features. It also teaches you how LSL scripts can sense and communicate with the rest of SL, and how LSL can apply physical forces and move scripted objects onto your screen as heads-up display (HUD) attachments. Finally, it covers the many resources available for when you want to learn more.

The Origins of LSL

So, where did LSL come from? When Second Life was still called LindenWorld and had small space ships and eyeballs flying around instead of avatars, it did not have a scripting language. Instead, anything created in-world was built via static creations and physics. Thanks to rigid body dynamics, objects in Second Life act more or less like real-world objects, colliding with each other, falling under the effect of gravity, etc. This enabled a wide variety of creations, but many types were missing.

Take airplanes, for example. In a few more years (thanks to Moore's Law) we will be able to simulate a wing in real time, solving the many complex equations needed to properly model the interactions of the wing, turbulence, fluid flow, Bernoulli's Principle, etc. However, computers are not yet powerful enough to do that. So full simulation is not the answer. Instead, LSL allows residents to create content that can't be simulated currently within SL's physics system.

Technically, the scripting language that you will be playing with is LSL2, as the language added to LindenWorld in August of 2002 was LSL. However, since only a few early alpha users ever had to build using the original LSL, the current language is simply referred to as LSL. The current language is far more powerful and easier to use than the original, so be glad that you never had to play with the first one!

Your First Script

OK, so you are ready to take the first step. Great! Fire up Second Life and go somewhere you can build, such as a sandbox or land you own. Create a box on the ground and select it. Select the Content tab of the Build window and click on the New Script button. The Script Editing window will pop open with the default "Hello, Avatar!" script (Figure 8.1). We'll break down the script in a moment, but for now click the Save button on the lower right of the window. Two lines will appear in the bottom of the window -- first "Compile successful, saving..." and then "Save complete." The box then chats "Hello, Avatar!" at you. If you close the Build window and right-click on the box, the script will respond with "Touched."

Congratulations! You have created your first script within Second Life! So, what have you done? Let's break down the script. Right-click the box to pull up the pie menu and select Edit. Again, click on the Content tab. The box will now contain one item, called New Script. Double-clicking on New Script will reopen the Script Editing window. Now we can look at the script:

default
{
   state_entry()
   {
      llSay(0, "Hello, Avatar!");
   }
   touch_start(integer total_number)
   {
   llSay(0, "Touched.");
   }
}

Even if you've programmed before, few of the keywords will be familiar you to you, so let's break them down one at a time.

The keyword default indicates which state the LSL program will begin executing in. You will learn what states are and that LSL programs can have multiple states later in this article, but for now you need only know that every LSL program must have at least one state and that it is labeled default.

Figure 8.1: The "Hello, Avatar!" script

The curly braces, { and }, that follow default encapsulate the event handlers within that state -- in this case state_entry and touch_start. The state_entry event is triggered whenever execution enters that state, so in our example as soon as you clicked Save to upload the script to the simulator and attach it to the object the LSL program began to execute and entered the default state. This triggered the state_entry event, meaning that any code sitting within the state_entry was run.

In our example, the only code was the library function llSay. llSay allows a script to chat text, much like an avatar, onto a channel of its choosing. Channel 0 is the channel that all avatars chat onto and listen to, so by saying "Hello, Avatar!" onto channel 0, the script ensures that any avatars nearby can hear it. What llSay does is controlled by the arguments within the parenthesis that follow llSay, in this case the integer 0 and the string Hello, Avatar. We'll talk about arguments more later.

Of course, what if you forget what the arguments for llSay are? One option is to visit the LSL Wiki (http://lslwiki.com/lslwiki/wakka.php?wakka=HomePage) or search "llSay" on Google. Fortunately, there is an even easier way, which is to hover your cursor over the word "llSay" in the Script Editor window. This will pop up the following tooltip and remind you what llSay does and is looking for:

llSay(integer channel, string msg)
Say msg on channel

The second event handler is touch_start. Recall that when we clicked on the box, it chatted "Touched." in response? This is what the touch_start event handler does. It is triggered when an avatar begins clicking on the object. Again, the only code that exists within the event handler is the llSay library function, which we covered a moment ago.

Now let's start making this script our own. If you don't have the Script Editor up, open it by selecting the box, going to the Contents tab, and double-clicking on New Script. Clicking anywhere within the window allows you to edit the text of the script, so go ahead and change "Avatar" in "Hello, Avatar!" to your name. For me, that would be, "Hello, Cory!" As soon as you change the text, the Save button lights up to indicate that the script in the text window does not match the script that has been loaded up to your box. Click the Save button to recompile and save the script.

You should see the same sequence of "Compile successful, saving..." and then "Save complete." displayed in the bottom of the editor and then the box should chat your name at you. Well done! But what if you wanted to go back to "Hello, Avatar!"? Fortunately, the text editor supports both undo and redo. Click in the text-editor window and hit Control-Z to undo your change. You'll see your name replaced by the text "Avatar" and the Save button lights up since the script has again changed. If you want to redo the changes, use Control-Y.

Remember that until you hit Save, your changes exist only in the text editor and haven't actually been uploaded into Second Life. When working on really complicated or critical scripts, it is often a good idea to use a text editor outside of SL and then cut and paste the text into SL, since that way you'll always have backups. The LSL Wiki has a list of external editors that have syntax highlighting for LSL (http://lslwiki.com/lslwiki/wakka.php?wakka=AlternativeEditors).

It is also important to realize that once you have added a script to an object, the script will remain on the object, even if you derez and rez the object into and out of your inventory. If you want to remove the script permanently, the best way is to delete it from the object's inventory.

To fully understand the connection between the text you type and what actually runs on Second Life, you need to dig a little deeper into LSL.

Deeper Into LSL

Now we'll focus on compilation, uploading, and execution (Figure 8.2). LSL is a scripting language that runs server-side, on a piece of software called the simulator. The simulator does just what it's name implies -- it simulates the virtual world of Second Life. Each simulator runs everything for 16 acres of virtual land -- buildings, physics, and of course, scripts. While you manipulate the script text in a form that is somewhat easy to read, the actual code that runs on the simulator is compiled. A compiler is a piece of software that takes the text version of the script and converts it into something that can actually run. In the case of LSL, the compiler exists within the Second Life viewer itself. In the future, it is likely that the compiler will move from the viewer into the Second Life simulators, but where the code is compiled isn't very important. What matters is that the text is converted into a form that can run on the simulators.

Figure 8.2: A bunch of more-advanced scripts.

Compilers also serve the function of detecting errors in the code you've just written. Although compilers can't detect all errors, it can detect common mistakes like syntax errors (Figure 8.3). Let's return to our "Hello, Avatar!" script. We can introduce a syntax error in many ways, but one example would be to remove the trailing brace, }, from the end of the state_entry event. Since we have modified the text, the Save button lights up; click on it to attempt to compile the text. Rather than the "Compile successful, saving_" and then "Save complete." we've become accustomed to, we instead see "(7, 4) : ERROR : Syntax error" and the cursor moves to the first letter of the touch_ start event.

Figure 8.3: ERROR: Syntax Error

What does all this mean? The "(7, 4)" tells us that the error is at or near row 7, column 4 of the script, which we also know because the cursor has been positioned there. The "Syntax error" tells us that we've probably made a relatively simple typing error. If we examine our script, we see that the braces don't match. The compiler, upon reaching the touch_start event, was expecting a trailing brace, so it flagged this as an error and stopped compiling the script. Notice something else. If you exit the Build tool and click on the box, it fails to respond with "Touched." When a script fails to compile, Second Life stops executing the script on the target object. This is to reduce confusion and prevent broken or mismatched scripts from continuing to operate with Second Life. Simply add the trailing brace back in and save the change to both fix the error and to recompile the script.

When a script properly compiles, it generates LSL bytecode. Bytecode is a simple form that is relatively easy to execute. In the future, LSL may compile to a different form for execution, but that won't change how you write scripts. It will simply change how Second Life handles things under the hood. Why would we be contemplating these types of changes? Performance, primarily. LSL currently executes quite slowly, so we will continue to make changes to improve what you can do within LSL.

Either way, once the compiled code is on a simulator, it can actually be executed. Execution is simply the process of checking to see whether the script needs to do anything. In our example script, when an avatar clicks on the box, Second Life checks to see if any touch event handlers exist. Since one does (touch_start), the code within the handler executes. Once that code is complete, the script goes to sleep until it needs to do something else.

This process of doing work only when required is key to both how LSL is structured and to writing good LSL scripts. It requires you to understand states and events, however. We'll discuss states first. As was covered earlier, all LSL scripts need to have at least one state, denoted by the default keyword. A state is a collection of code between the opening and closing braces of the state. In its simplest form, a script has a single state and all of its code lives there.

For example, imagine that you are creating a script to manage a very simple door that you touch to open or close. Using states can clarify what your code does:

// default state is closed
default
{
   touch_start(integer tnum)
   {
      // insert code to open the door here
      // . . .
      state open;
   }
}

state open
{
   touch_start(integer tnum)
   {
      // insert code to close the door here
      // . . .
      state default;
   }
}

Note that this is different from the traditional way of writing this, where you would maintain whether the door was open or closed in a global variable, something like this:

// default state is closed
integer is_closed = TRUE;
default
{
   touch_start(integer tnum)
   {
      if (is_closed == TRUE)
      {
         // insert code to open the door here
         // . . .
         is_closed = FALSE;
      }
      else
      {
      // insert code to close the door here
      // . . .
      is_closed = TRUE;
      }
   }
}

In this simple case, it may look like the global variable option is easier, but when other behavior also has to change between the open and closed state, splitting the code into multiple states is much simpler than having to wrap everything in if statements.

These code samples illustrate some other basics of LSL syntax. First, the "//" denotes a comment. A comment allows you add text to your script to remind you of why you wrote the code a particular way, to lay groundwork for later coding (such as "insert code to open the door here"), or to make it easier for someone else to follow your work. Note that only the text to the right of the "//" is part of the comment, allowing comments to be added to a line that contains code. For example, in the line

state open // this is entered when the door opens 
"// this is entered when the door opens" is the comment.

Second, we introduce global variables and the variable type "integer." Global variables, like is_closed, are available anywhere in the code. They are similar to global variables in other programming languages, such as C, and they can be any of the types available to LSL (see the section "What Are Types?"). LSL is a statically and strongly typed language, meaning that variables are given types when created and generally require typecasting to convert between types.

=======Sidebar=======

Animations are the personality of an avatar. They're the finishing touches after you've gotten your hair, outfit, and body just right. Sure, you could use the standard animations to get your point across, but with patience, software, and a bit of luck, you can bring your favorite real-world movements into your Second Life experience.

The first thing needed is software to create the animation outside of Second Life. There are several free options; for example, Avimator, Slat, DAZ Studio, Blender, and QAvimator, as well as commercial software like Poser, Maya, and Posemaker. Each system has its own advantages, but all make superb animations. SL uses the BVH motion format, so be sure to export this way.

The jargon related to animations is the same regardless of the software. A key frame is a frame in the animation sequence with a specified joint location, and the program will fill in all the frames between key frames to create a smooth motion. A spline is the set of equations the program uses to generate this smooth motion; if you find that your animation is moving past the key frame or in other odd ways, breaking the spline on the key frames often fixes the problem. Inverse kinematics (IK) is an option that allows you to move the entire arm or leg by moving the hand or foot along the three axes, but if you turn this option on and off, it is important to check your key-frame splines again. Playing with IK can create amazing motions, but it can also have difficult-to-predict consequences, so always save before changing the IK settings.

Now that you have the software, check that the avatar loaded into it is compatible with SL. SL cannot accept information from finger, toe, genitalia, or facial joints, so you may have to use an imported or outdated avatar. You can also edit the key frames after creating the animation to delete the information from these joints.

Let's animate! All joints used in your animation must be in a different position between the first frame and the second frame. The second frame is the first frame you will see in SL, and a good first frame often twists the avatar into a ball. If you want to exclude a body part from the animation so that only part of the SL movements are overridden, the excluded joints should be in the same position in both the first and second frames.

In the second frame, the avatar should be in the starting pose for the animation. Depending on your software, each joint can be dragged by the mouse into position or selected and then moved through parameter dials. The next key frames will be the different poses your avatar will move through to complete the animation. Remember that humans rarely coordinate moves precisely, so when moving your avatar from one pose to another, set different body parts to arrive in the target pose a few seconds within one another.

There are a few different ways you can end your animation. The most common way is with the same pose as the second frame (allowing for a seamless loop to be uploaded), or in a unique pose from which the avatar will shift back into normal SL animations. You may also create an animation with a beginning animation, a section with matching key frames at each end that can be used as a loop, and a leading-out animation. If you plan on creating this type of animation, write down the numbers the loop begins and ends at.

Once you have your animation done, export it as a BVH motion and log into SL. SL's uploading process allows you to finish customizing the animation. Here you set a priority level for your animation. The higher the number, the higher the priority. The default animations are a two, so to completely override the SL default, upload a three or four.

Timing your loop can make or break an animation. Avoid a jerky animation with a bit of math. Each end of the looped section should have the exact same key frame, and dividing the key-frame number by the total number of frames will give you the in and out percents within a degree or two. Preview the animation a few times from different in and out percents to check the loop, and don't be afraid to make the matching key frames into two or three static frames to give yourself more leeway.

Hand poses and facial expressions can be set at this stage, and beyond that, ease-in and ease-out settings define how fast your avatar will transition between animations. Now you can upload and begin testing your animation. The test server is a great place to practice. There you won't have to spend your hard earned Lindens getting that hand motion just right.

There are some limits. Subtle movements aren't always picked up by SL, and animations are limited to 30 seconds. While you can upload an animation with 60 frames per second, users will likely only see between 15 and 20 frames per second, so animations are often best in this range. Full-body movements must come from the hip joint, not the body joint, and smaller- or larger-than-normal avatars will require custom animations to maintain the motion. The fastest way to get used to the quirks of the system is to dive right in and start testing it out for yourself. In no time, your avatar will be able to do your favorite dance move or develop a signature walk or strut as unique as the rest of you.

--Kiari Lefay and Leslie Havens ===========================================

What Are Types

A type determines what kind of data can be stored and LSL supports seven distinct types:

Integer

An integer is a whole number between -2,147,483,648 and 2,147,483,647. The following are some examples of integers in use:

integer int = -23; // in the language C, integers are called int. Don't be confused by this!
integer foo = 235632;
integer blar = 0;

Float

This is a floating point (or decimal) number with seven significant figures that can be positive or negative. The largest positive or smallest negative number that can be represented is +/- 3.4028235E38, while the smallest positive or largest negative number that can be represented is +/- 1.17549351E-38. Examples of floats are as follows:

float e = 2.718128; // the decimal point indicates that this is a float
float f = 0.f; // a trialing ".f" can also be used
float one = 1; // even though the literal "1" is an integer, this assignment will work.
integer i_one = 1; // note: if (one == i_one) is a BAD idea! More on this later.

String

A string is a collection of characters, such as the following:

string name = "Exposition Linden";
string character = "c"; // single characters in LSL are just string
string number = "1"; // note: "1" != 1

Vector

A vector is three floats representing x, y, and z components. A vector is generally used as a position, velocity, acceleration, or color. All three values can be set simultaneously, or they can be set as individuals:

vector pos = <123.3, 54.f, 32>; // vectors will promote entries into floats
vector vel;
vel.x = 12.f; // this is much
vel.y = 23.f; // slower than initializing via a
vel.z = 36.f; // vector!!

List

Since LSL doesn't have arrays or structures, the primary method for storing collections of data is lists. All the other data types may be placed in lists (but a list can't be placed in a list). There are many different ways to work with lists; this chapter will cover some of them, and the LSL Wiki (http://lslwiki.com/lslwiki/wakka.php?wakka=list) has excellent examples. More on lists later.

Rotation

A rotation is four floats representing the x, y, z, and s components of a quaternion rotation. Although quaternions are extremely complicated and often confusing, LSL allows them to be used without your having to master the underlying theory. We'll talk more on rotations later, and you can check out http://lslwiki.com/lslwiki/wakka.php?wakka=rotation. Here are some sample rotations:

rotation rot = <0.f, 0.f, 0.f, 1.f>; // Rotations in LSL are internally normalized
rotation rot = <32, 2, -9, 128>; // even if your initialization is not

Key

A UUID, or Universally Unique Identifier, is used to identify many objects within Second Life. Like rotations, keys allow you to use UUIDs without having to write a lot of code to support them. We'll go over details later, but you can also consult http://lslwiki.com/lslwiki/wakka.php?wakka=key.

// you almost never need to initialize keys with literals like this.
key object_id = "00000000-0000-0000-0000-000000000000";

Typecasting is used when variables of different types are assigned to each other. LSL supports two implicit conversions: integer to float and string to key. These allow the following statements to work correctly:

float my_float = 4; // although you really should write this as 4.f
// data between " and " can be either a string or a key.
key object_id = "00000000-0000-0000-0000-000000000000";

For any other conversions, explicit typecasting is needed. Like C, a typecast is the type you wish to cast to inside parentheses:

integer bad_pi = (integer)3.1425926; // bad_pi == 3
float good_pi = (float)"3.1415926"; // good_pi == 3.1415926

Now let's discuss the if statements. The if statement is the simplest of the conditional, or flow control, statements in LSL. If the code within the parentheses evaluates to TRUE, then the code within the braces is executed. The if statement is only type of expression in LSL for flow control. Flow control allows you to make decisions about whether pieces of code are executed (for more detail, consult http://lslwiki.com/lslwiki/wakka.php?wakka=FlowControl). LSL's flowcontrol statements are as follows:

integer expression = TRUE; // TRUE is an integer constant in LSL. TRUE == 1
if (expression)
{
   // do something if expression == TRUE
}
else
{
// do something else if expression == FALSE
}
if (expression)
   // do something in one line
else
   // do something else in one line
while(expression)
{
   // do something until expression == FALSE. FALSE == 0
}
do
{
   // do something until expression == FALSE. FALSE == 0
} while(expression);

integer i; // an iterator

for (i = 0; i < 100; i++)
{
   // do something 100 times, where i starts at 0 and counts up to 99
       // this code will exit when i == 100
     }
     @again; // this is a label
// this code will be executed forever
jump again; // move execution to the @again label

There are two additional flow-control mechanisms. The first is the state transition, which we already covered. The second is the return command, which we will cover in the section "Advanced Language Features." In all of the flow-control examples, the decision of which path to take was determined by the value of an expression. In LSL, an expression is a combination of operators and functions. Functions will be explained in more detail in the "Advanced Language Features" section. Operators are divided into several broad categories. As with other categories, the LSL Wiki covers this in great detail (http://lslwiki.com/lslwiki/wakka.php?wakka=operators).

Operator Types

Unary

Unary operators are arithmetic operators that modify one value, as in the following example:

integer count = 1; // create a new integer variable and assign it the value of 1
   count++; // the "++" operator adds 1 to "count" and assigns the result to "count"
llSay(0, (string)count); // says "2" -- note the type conversion

Binary

Binary operators are arithmetic operators that act on two values to produce a third, as shown here:

integer a = 5;
integer b = 2;
integer c = a % b; // compute a modulo b, so c = 1

Boolean

Boolean operators always generate TRUE (1) or FALSE (0) results:

integer a = 5;
integer b = 2;
integer c = a != b; // "!=" returns TRUE if its arguments are not the same.

Bitwise

Bitwise operators act on the bitfields that make up integers. Here are some examples:

integer a = 5; // 0x101 in binary
integer b = 2; // 0x010
integer c = a | b; // a or b = 0x111, so c = 7

Assignment

Finally we have the assignment operators, which take the result of an expression and assign it to a variable. In addition, LSL supports several variants of the assignment operator that perform an arithmetic operation along with assignment, as in the following example:

integer a = 5; // assigns 5 to a
a += 5; // adds 5 to a, then assigns the result, so a = 10

Advanced Language Features

Like other languages that share C-like syntax, blocks and scope are fundamental to understanding how to write good code in LSL. Generally speaking, in LSL a code block is any code set off by braces within a global function or an event handler, as shown here:

string GlobalString = "Hi!"; // this global variable is
                             // visible anywhere
                             // in the LSL program
integer factorial(integer n) // this is a user-defined
                             // function more on them
                             // below
{                            // this brace begins the code
                             // associated with
                             // the function factorial
string local = "Bye!";       // a local variable is
                             // visible in the block it is
                             // created in and any new
                             // blocks created within that
                             // block
llSay(0, GlobalString);      // global variables can be
                             // used anywhere in the LSL
                             // program
llSay(0, local);             // local is available anywhere
                             // inside the factorial
if (n < 3)
{                   // the if statement creates another code block
   float pi = 3.14; // pi is only available
                    // inside the if block
   llSay(0, local); // local is available
                    // anywhere inside factorial
                    // including inside new
                    // blocks created within it
   llSay(0, (string)pi); // this works since we're
                    // still inside the block
   return n;        // the return statement
                    // jumps back to the calling
                    // function or event handler
}                   // end of the if block
else
{                     // the else clause creates another block
   float e = 2.71828; // e is only available inside
                      // the else block
   llSay(0, (string)pi); // ERROR!! We aren't in the
                         // block that made pi
                         // so this is an error!
   return n*factorial(n - 1); // this is recursion,
                              // more later
   }                // end of the else block
   llSay(0, local); // local is available
                    // anywhere inside factorial
    }               // end of the factorial block
    default
    {              // states create scope in the sense that they
                   // determine which event handlers will
                   // be called
   state_entry()
   {                // this starts a block in state_entry()
   integer num = 1; // the variable do_something
                    // is a local
   if (num)
      num = 4;      // even though there are no braces,
                    // this line is actually a code block
   llSay(0, GlobalString); // global variables may
                           //be used anywhere in the
                           // LSL program
   llSay(0, (string)factorial(num));
                           // user-defined functions may be
                           // called form any event handler.
                           // This will say "24".
   }                       // end of the state_entry block
}                          // end of the default state

There is a lot going on in the simple example program. Although it is a rather silly bit of code, it shows where blocks begin and end, and introduces the idea of scope. For variables and functions, scope defines when they may be called or used. Within LSL, there are two levels of scope: global and local.

Global scope applies to user-defined functions and global variables. Both global variables and user-defined functions may be used anywhere in an LSL program; thus they are considered to be "globally accessible" and therefore of global scope. In the preceding example, the function factorial and the string GlobalString are globals.

Local scope applies to variables created within code blocks (there are no local functions in LSL). As the example shows, functions, event handlers, and flow control create blocks, and these blocks may have further blocks nested within them. The scope for a local variable is the code block it was created within, as well as any new blocks created within that block. Consider this example:

{             // a code block
   integer true = TRUE;
   list foo = ["I", "am", "a", "list"]; // a local list
   if (true) 
   {        // a new code block
      foo = []; // we can still see foo,
                //so assigning an empty
                // list to it
   }
}

Note that these scope rules can allow some really bad coding habits, like in the following example:

{
   float pi = 3.14;
   if (pi) // if a floating point value
                                               // is used in an
                                               // expression, it is FALSE
                                               // if it is exactly
                                               // equal to 0.f,
                                               // and TRUE for any other value                                      // This is a bad habit,
                                              // since floating point
                                              // values are often near to 0.f
                                              // but not exactly
                                              // equal to 0.f.
{                                            // start of if code block
       integer pi = 3; // this local pi has the same name
                                              // as pi from
                                              // the earlier scope and
                                              // is said to "shadow"
                                              // the other variable.
       llSay(0, (string)pi);          // Which pi will be used?
   }                                         // end of the if block
   else
   {                                        // start of the else block
       string pi = "3.1415";      // Ack! Another pi
        llSay(0, (string)pi);        // Now which pi will be
                                             // used?
   }
}

Please do not ever write code like this! Although it compiles and (if you are very fortunate) may even work, it will cause you_or anyone you share your code with -- headaches and confusion.

Now that you understand scope, let us turn to functions. In the last section we introduced global functions, the first of the two types of functions in Second Life. The second type, library functions, will be discussed shortly. User-defined functions allow you to create blocks of code that perform a specific task or calculation. They are placed above the default state, either before or after global variables, and are written as follows:

type function_name(type parameter1, type parameter2, . . .)
{
   // do something in here
}

The type is the function's return type, which means that if you want the function to return a value after it is called, you need to specify the type. For example, if you wanted to take the absolute value of a float, you could write the following function:

float fabs(float num)
{
   if (num > 0.f) // already a positive number, just return it
      return num; // the return command returns the value of
                  // the expression that follows it
   else
     return -num; // the negation operator returns the -1*num
}

This would be used as follows:

float negone = -1.f;
float posone = fabs(negone); // passes -1.f to fabs, returns 1.f
llSay(0, (string)posone); // will say "1.f"

Notice that fabs takes one parameter of type float. Functions can have any number of parameters (including zero). So, the following are all legal formats for naming a function:

do_something()
string do_something_else()
vector do_something_too(string target)
list do_something_now(integer number, rotation rot1, rotation rot2)

Two important features of LSL functions are that their parameters are passed by value and they support recursion. To understand the concept of pass by value, look at the following function:

integer add_one(integer n)
{
n = n + 1;
return n;
}
integer number = 10;
number = add_one(number);

So, what is the value in number? As you would hope, the value is 11. This is because when number is passed into add_one, it is passed in as its value, 10. Thus, any operations on n within the function are acting on a local copy, not on the variable number. Generally, this is what you want to have happen, but it means that in order for your function to return a value, you have to use the return command. If you had written add_one as follows, the number would still be 10:

add_one(integer n)
{
n = n + 1;
}
integer number = 10;
add_one(number);

User-defined functions are covered in considerable detail at http://lslwiki.com/lslwiki/wakka.php?wakka=UserDefinedFunction.

The second type of function in LSL is library functions (Figure 8.4). They are built-in functions that are there to perform common tasks or to provide functionality that would be difficult to write in LSL directly. More than 300 functions are built into LSL, and more are being added regularly.) For a comprehensive description of all of the functions, check out http://lslwiki.com/lslwiki/wakka.php?wakka=functions.) It is important to recognize that LSL functions operate just like the user-defined functions. They are available within any event handler or user-generated function, their arguments are passed by value, and they may or may not return a value. One additional aspect is that some of them have a delay value associated with them. This delay exists to protect Second Life from certain types of abuse.

Figure 8.4: Libraryfunction list

Connecting To the World

LSL's real power is in its ability to allow an object to communicate and interact with the rest of the world. To cover everything you can do with it would require a separate book, but the following should get you started.

Chat

In our first example, we showed that an object can chat to the rest of the world using the llSayfunction. This is handy for communicating to people near the object, and it can also be useful for local object-to-object communication.

default
{
   state_entry()
   {
     llListen(0, "", llGetOwner(), "");
   }
   listen(integer channel, string name, key id, string message)
{
   llSay(0, message);
}
}

The preceding script is a simple chat repeater that illustrates the basics of scripted chat. All it does is repeat everything the object's owner says. When the script starts, llListen sets up the listen event so that the object can listen for chat. llListenlets you filter what you want to listen for by chat channel, name, UUID, and message.

integer llListen(integer channel, string name, key id, string message)

In the example, the script listens on channel 0, which is the public chat channel that all avatars chat on. There are a few billion chat channels so that objects can chat to each other without fear of message collision. The name and message parameters are left blank in this case so the script will listen for all names and messages. llGetOwner returns the UUID of the owner, so in this case the script ends up listening for public chat made by the script's owner.

When the script "hears" something that matches the requirements set in llListen, the listen event will be triggered. In this case we just pass the message that was heard to llSay.

Chat sent with llSay can be heard in a radius of 20 meters; alternatively, chat can be sent with llWhisper (10 meters) or llShout (100 meters). Because there are times when you want to use chat but keep it private, there is also llOwnerSay -- a special chat that only the object's owner can hear.

IM

There are times when you want to send a message to someone who is not within Whisper, Say, or Shout radius, or you want to keep your message private. The easiest way to do this is through an instant message (IM). IMs can be "heard" anywhere in Second Life, but only by the intended recipient. To send an IM, you need to know the intended recipient's UUID.

llInstantMessage(key uuid, string message)

If the resident is offline, the message will be saved until they log in next. Objects cannot IM other objects.

Sensors

Sensors allow you to gather information about avatars and objects near your object. Setting up a sensor is somewhat like setting up a listen for chat. When llSensor is called, the parameters filter out the results, and if there are any matches a sensor event is called. The major difference is that whereas llListen is continuous, llSensor is a single request. The following script is an example using a sensor.

default
{
   state_entry()
{
   //Set up a repeating sensor, that once a second looks for any
   //avatars within a sphere with a 20 meter radius.
llSensorRepeat("", "", AGENT, 20.0, PI, 1.0);
}
sensor(integer detected) //A sensor returns the first 16 items detected.
{
   // Say the names of everyone the sensor detects
   for(i=0;i<detected;i++)
{
llSay(0, llDetectedName(i));
}
   }
   }

Pay

Scripts can also give and take L$ (Figure 8.5).

Figure 8.5: Giving and taking money.

This is handy for creating vendors, gambling games, and more. In order for an object to accept money, the script must have a money event:

default
{
   money(key giver, integer amount)
{
   llSay(0, "Thanks for the " + (string)amount + "L$ donation!");
}
}

Giving money via a scripted object is a bit more complicated. The script needs to request permission from the object's owner to debit L$ from their account.

default
{
   state_entry()
{
   llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
}
touch_start(integer number_touching)
{
   llGiveMoney(llDetectedKey(0), 1);
}
}

The preceding script gives L$1 to whomever clicks on the object that's using llGiveMoney. This script will not work unless when the llRequestPermissions function is called, the owner grants permission to let the object give money.

Inventory

An object has an inventory (Figure 8.6) -- this is where the script lives, but it can also contain most of the things a resident's Inventory can. The script can give, take, and use inventory. For instance, a gun would need llRezObject to shoot a bullet. A vendor would use llGiveInventory to give a single item, or llGiveInventoryList to give a folder. The following is an example of a drop box that shows how to give and take inventory;

Figure 8.6: Object inventory

default
{
   state_entry()
{
   //Allows anyone to drop inventory
   llAllowInventoryDrop(TRUE);
}
   touch_start(integer number_touched)
{
   //Only the owner can take it out.
   if(llDetectedKey(0) == llGetOwner)
{
   //Make a list of all the objects in the inventory
   list contents = [];
integer I;
   for(i=0;i<llGetInventoryNumber(INVENTORY_OBJECT);i++)
   {
      contents += llGetInventoryName(INVENTORY_OBJECT, i);
   }
   //Give all the objects in a folder
   llGiveInventoryList(llGetOwner(), "Drop Box", contents);
}
}

Physics and More

Second Life has a server-side physics simulation, which means objects will fall, bounce, and collide correctly. A script can change how the built-in physics affect an object. By default, an object in Second Life is "pinned." This means that if you lift it up and let go, it will not fall down. To make it fall, you can set its status to physical:

default
{
   state_entry()
   {
      llSetStatus(STATUS_PHYSICS, TRUE);
   }
}

An object can also change its collision status by becoming "phantom" with the llSetStatus function:

llSetStatus(STATUS_PHANTOM, TRUE);

Applying Forces

By changing the forces on a physical object, you can control how it moves. There are several ways to do this; the easiest way is to use llSetForce or llApplyImpulse. These work just like your high-school physics class taught you: a force is a continuous push, and an impulse is a single, instant push. A simple example:

default
{
   touch_start(integer touched)
   {
      // bounces the object up in the air.
      llApplyImpulse(<0,0,100>, FALSE);
}
}

Vehicles

For more control, there is a suite of functions that let you change over 20 parameters to alter every aspect of how a physical object moves, from buoyancy to friction (Figure 8.7). The vehicle code is much more complex but correspondingly more powerful. For more information, check out the vehicle page at http://lslwiki.com/lslwiki/wakka.php?wakka=vehicles.

Figure 8.7: A scripted airplane in flight.

Controls and Displays

You probably don't want to interact with an object via text and clicks alone. Flying a jet across a sim will be tough if you have to type "up" over and over again. Likewise, no one wants to watch your slide show if it is just text describing the pictures you put inside.

Taking Control

A scripted object can take over the normal movement controls from an avatar. This makes it easy to create guns that shoot when you click the mouse, or a car that drives when you use the arrow keys.

A script needs to request permission to take over the controls from the avatar. In the special case of attachments and vehicles, permission is granted automatically, but the script still needs to make the request. The following example is a portion of a very basic gun script:

default
{
   // If we attach the gun, request permissions; if we detach, release
   control.
   attach(key attachedAgent)
   {
      if (attachedAgent != NULL_KEY)
   {
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
   }
   else
   {
      llReleaseControls();
   }
   }
   // When permission is granted, the run_time_permissions event is triggered
   // Use this as a cue to take controls.
   run_time_permissions(integer permissions)
   {
      if (permissions == PERMISSION_TAKE_CONTROLS)
   {
   //We want to take the left mouse button in mouselook.
       llTakeControls(CONTROL_ML_LBUTTON, TRUE, FALSE);
       }
   }
   control(key name, integer levels, integer edges)
   {
       // After taking controls, if those controls are used, take the
       appropriate action.
       if ( ((edges & CONTROL_ML_LBUTTON) == CONTROL_ML_LBUTTON)
          &&((levels & CONTROL_ML_LBUTTON) == CONTROL_ML_LBUTTON) )
       {
          // If left mouse button is pressed, fire
         fire();
       }
   }
}

Display

Scripts can also control the appearance of an object. Probably the most used example of this is changing textures for things like slide shows. To change a texture, it is easiest to use a texture in the object's inventory. If you have a bunch of great snapshots, you can drop them into a box with the following script to watch them display:

integer i;
default
{
   state_entry()
   {
      // Change image every 10 seconds
      llSetTimerEvent(10.0);
   }
   timer()
   {
      if(i

How To Learn More

If this brief introduction to LSL has left your head spinning, don't worry! If you haven't programmed before, the promise and power of adding code -- of adding behavior -- to objects in Second Life can be overwhelming. Even if you are an experienced software developer, the quirks and unique aspects of LSL can take a while to wrap your brain around, but once you do you will have opened up entirely new worlds of possibilities. No matter what, you're going to be hungry for more -- more knowledge, more examples, more people to learn with. Fortunately all of these exist, both within Second Life and on the Web.

Let's begin inside Second Life. The Event Calendar is a great place to start looking for help about LSL. Every week there are many classes on LSL, ranging from Scripting 101 to Advanced Vehicles. Not only do these classes provide concrete information on the mechanics of scripting in Second Life, but they also can connect you to the community of scripters within the world. By meeting the other students and instructors, you can build a strong network of friends and fellow scripters to question, to collaborate with, and to find new challenges.

There are many different communities of scripters within Second Life, from the professional content developers doing large real-world projects to the beginners gathering to play in the sandboxes. You are certain to find one that matches your skills and interests. Like other forms of creation within Second Life, scripting is the most fun when done with others, so use the time when you're learning LSL to meet other people and groups.

Classes in Second Life are only one resource for learning about scripting. Two Second Life forums exist as resources for scripters; The Scripting Library forum (http://forums.secondlife.com/forumdisplay.php?f=15) acts as a repository for various scripts, new ideas, and the basic building blocks every scripter would otherwise have to reinvent. The Scripting Tips forum (http://forums.secondlife.com/forumdisplay.php?f=54) allows scripters to share knowledge on a daily basis.

Both forums see daily contributions by a many scripters, and both are worth reading and contributing to. They will connect you to other scripters rapidly.

A more specialized support option is the Second Life Scripters mailing list (http://secondlife.com/community/mailinglists.php). It tends to cover very specific questions about or problems with LSL, often before they show up on either the forums or the LSL Wiki. The mailing list is a good place to lurk as an intermediate scripter, but really it's best used by expert scripters looking to connect with other advanced scripters. However, like mailing lists in general, it isn't the best resource for general searches or broad questions. Fortunately, another option exists.

That resource is the LSL Wiki (http://lslwiki.com/lslwiki/), a wonderful resource for all things Second Life. Beyond the most comprehensive documentation of all LSL functions (http://lslwiki.com/lslwiki/wakka.php?wakka=functions), it also calls out known bugs (http://lslwiki.com/lslwiki/wakka.php?wakka=KnownBugs) and gotchas (http://lslwiki.com/lslwiki/wakka.php?wakka=annoyances). Moreover, it has numerous tutorials (http://lslwiki.com/lslwiki/wakka.php?wakka=LSLTutorials) and links to many of the scripting groups, mentors, and teachers (http://lslwiki.com/lslwiki/wakka.php?wakka=ScriptingMentors) in Second Life. Most importantly, the LSL Wiki is a wiki, so it is constantly being added to, always being updated by the Second Life community. As you begin to learn about LSL, you should become familiar with the LSL Wiki and contribute to it yourself. It is yet another way to meet other scripters and to become part of the LSL community within Second Life.

A final resource for LSL is the Second Life Education Wiki (http://www.simteach.com/wiki/index.php?title=Second_Life_Education_Wiki). The Second Life Education Wiki provides up-to-date information on the education and research projects going on in Second Life. Educators and researchers provide both a resource to scripters and a market for specialized scripts and builds. Campus:Second Life (http://www.simteach.com/wiki/index.php?title=Campus:Second_Life), as well as other university and education builds within SL, often brings groups into contact with LSL, either because their students are exploring LSL or because of specialized needs. As you become more confident with LSL, you will find educators to be some of the most collaborative and open scripters in Second Life. If you are looking for interesting and unusual projects to apply your budding scripting skills to, look no further than the Education Wiki.

Of course, no book can keep up with the resources constantly appearing on the Web in support of LSL, so new options will have appeared by the time this book is on shelves. Seek them out!

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.