User:Fred Gandt/Tuition

From Second Life Wiki
Jump to navigation Jump to search
I get in-world very rarely these days. The knock-on effect of this, is that I am not in the best position to keep these scripts updated. Please message me in-world (forwarded to email) if you discover any bugs (that can't be simply fixed), and I'll do my best to find a solution. Please also message me in-world if you need help understanding LSL, and I'll do my best to help. Please though, bare in mind that I have a real life and may not be able to respond immediately.

Some Complete Scripts for Use and Abuse

YouTube Hosted LSL Tutorials

Full Tutorial Playlist on YouTube

Introduction to Linden Scripting Language (LSL), the programming language used within Second Life. Absolutely no previous experience with reading or writing any programming language is required. This tutorial (in however many parts it eventually is) is for absolute beginners and, will lead the pupil through from learning how to use the scripting UI (the actual tool we use to write/edit scripts) to them being totally proficient; fully able to design, write, test, develop and complete their own projects.

If you have a little experience with LSL and are looking to learn more (rather than having no idea about it at all) and want to skip ahead to later parts in the tutorial you may find that it will be difficult to follow since the series will not be in self contained 10 minute "lessons" but, rather will form a continuous lesson from part 1 to part (?). It would do you no harm to watch from part 1 (even if it seems too simple for you) and you never know, I may show you something you didn't already know.

Parts 1 to 3: What is a "Script"?

Before learning to write LSL code you need to first learn how to use the tool that is the scripting UI and know a little about the asset we call a script. The first three parts of this tutorial deal almost exclusively with the UI menus and settings although I also explain a little about how the asset fits into the world too. This may seem like dull stuff but knowing how to use the tool is essential and importantly if I don't start here and expect you to figure it out yourself the future parts of this tutorial may be less simple to understand. However much you may want to jump ahead, I strongly recommend that you don't.

Welcome aboard the LSL Express!! Next stop...Scripter!

Part 1

<videoflash>Qbx6Di7hjLY</videoflash>

Part 2

<videoflash>IbpBy0WWtNg</videoflash>

Part 3

<videoflash>E4gr43wbO74</videoflash>

For further tuition follow the link at the top of this section to YouTube. That way you can view as a playlist and in full-screen (also seems pointless to post them all here).

Terms Used in Scripting

Bracket Types

Bracket Types
Name Example Description
Parentheses ( ) Used for wrapping various info and making smilies.
Brackets [ ] Used for wrapping lists.
Braces { } Used for wrapping scopes.
Angle brackets < > Used for wrapping vectors and rotations

Angle brackets are also used as the operators "<" (less than), ">" (more than) and "<<" (left) or ">>" (right) bit-shifting.

Data Types

All Data used in/by an lsl script is of one of 7 types.

Data Types
Type Example Description
string "The quick brown fox jumps over the lazy dog." Textualized data.
key "c6622a52-103d-02e3-0e12-5c241c091fdb" (Also known as a UUID) Used to reference an asset or agent.
integer 1 Whole number values between −2147483648 and +2147483647
float 1.375 Floating point values (even zero should be "0.0" if used where a float is expected).
vector <1.0, 1.0, 1.0> Representing X,Y,Z (in degrees) or R,G,B values.
rotation <0.0, 0.0, 0.0, 1.0> Representing X,Y,Z values (in radians) and a normalizer called S.
list ["List entry.", "c6622a52-103d-02e3-0e12-5c241c091fdb", 1, 1.375, <1.0, 1.0, 1.0>, <0.0, 0.0, 0.0, 1.0>] Lists cannot contain lists. Entries are separated by commas.

Although lists cannot contain lists, they can contain CSV strings (Comma Separated Values). By using the functions llList2String and llCSV2List (to extract the string from a list and use it as a list) AND llList2CSV (to convert a list into a CSV to store as a string in a list) a list can kinda contain lists.

There is a drawback to this insofar that the stored types will HAVE to be cast to suit from string to whatever type is required.

Various Scripting Examples for Tuition

Over time I will be creating a full course starting here to teach the basics of LSL scripting. I will be accompanying the posted examples with text explanation, videos and in-world tuition. The course will take quite some time to produce fully since I enjoy scripting for my own amusement most of the time I am logged in so, unless I can invent an LSL powered time machine I will be creating this course in between projects.

User Created Functions

LSL allows us to create functions to run by calling those functions within the script runtime. They are an extremely useful way to help organize a script, keep the memory use down and simplify code. Some simple ways to create and use this ability are shown below...

This first example shows the most basic syntax used to create a function.

Function() // This is the name we choose for our function. We use the name to call its use.
{
    ;
}

default
{
    state_entry()
    {
        Function(); // This is how we call for the function to run.
    }
    touch_start(integer nd)
    {
        Function(); // From anywhere the function can be called.
    }
}

So we add the actions we desire within the function braces.

Function()
{
    llOwnerSay("I worked!!");
}

default
{
    state_entry()
    {
        Function();
    }
    touch_start(integer nd)
    {
        Function();
    }
}

We can add conditions inside the function and pass to it any type of information.

Function(integer i) // Now the function carries information to be processed.
{
    if(i) // We can check the information as the function runs.
    llOwnerSay("I worked!!");
    else
    llOwnerSay("I still worked!!");
}

default
{
    state_entry()
    {
        Function(TRUE); // Now when we call the function we MUST provide the information the function NEEDS to run
    }                   // (even if the function doesn't always use it).
    touch_start(integer nd)
    {
        Function(FALSE);
    }
}

We can give the function a type and have it return the result dependent on what we feed it.

string Function(integer i, integer q)
{
    integer r = (i + q);
    return ((string)r); // This style MUST return the same type of info as the function type.
}

default
{
    state_entry()
    {  // Now the function can be used in-line as it returns the type of info we want to use directly to where it is called.
        llOwnerSay(Function(5, 5));
    }
    touch_start(integer nd)
    {
        integer i = 5;
        integer q = (i*5);
        llOwnerSay(Function(i, q));
    }
}

Scopes and Variables

This is to demonstrate where a variable may and may not be referenced within a script. It also demonstrates a basic view of what dictates a scope. A scope is an area within a script that is a parent and/or a child of another area. There will be more in-depth explanation as time goes on.

integer global_variable = TRUE; // This variable can be referenced throughout the script (in any state).

default
{ // No variable can be created to exist throughout the state (use a global variable).
    state_entry()
    {
        integer event_variable = TRUE; // Events can have local variables.
        if(global_variable) // A global variable can be referenced in any child scope in the script.
        if(event_variable) // The event variable can be referenced anywhere within the event it is created in.
        {
            integer conditional_variable_1 = TRUE; // If the event has child scopes new local variables can be created for them.
            if(global_variable)
            if(event_variable)
            if(conditional_variable_1)
            {
                integer conditional_variable_2 = TRUE; // Each local scope can have its own unique variables.
                if(global_variable)
                if(event_variable)
                if(conditional_variable_1)
                if(conditional_variable_2)
                {
                    integer conditional_variable_3 = TRUE;
                    if(global_variable)
                    if(event_variable)
                    if(conditional_variable_1)
                    if(conditional_variable_2)
                    if(conditional_variable_3)
                    {
                        integer conditional_variable_4 = TRUE;
                        if(global_variable)
                        if(event_variable)
                        if(conditional_variable_1)
                        if(conditional_variable_2)
                        if(conditional_variable_3)
                        if(conditional_variable_4)
                        llOwnerSay("Yikes!"); // We made it through!!
                    }
                }
            }
        } // No variable can be referenced outside the scope it was created in.
    }
}

Notes on Order of Operations in Scripts

As a script runs it uses information we give it and information it gets for us. If we instruct the script to get information that we want to use immediately we can use the requesting function in-line like this -

llSetText(llKey2Name(llGetOwner()), <1.0,1.0,1.0>, 1.0);

However, some requested information is not acquired at the calling of the function but during the triggering of an associated event. These events include -

dataserver

http_response

run_time_permissions

...amongst others. When using this type of data requesting function and event setup we need to be careful how we structure our script.

NONE OF THESE EXAMPLES DEAL WITH HOW TO STOP THE ANIMATION AND ARE NOT MEANT FOR IN-WORLD USE

The following is an example of a typical mistake made in scripts (I have fixed scripts SOLD with this error in them).

// This script compiles without error but is badly written. It attempts to animate the owner of the script...
// ...when the object it is in is touched. But written this way it will fail (the first time it is touched).

integer perms; // Used to store the boolean value TRUE (1) or FALSE (0).
// If the script has been granted permissions the value of perms will be 1. Else it will be zero.
key owner; // Used to store the UUID (key) of the owner of the script.

string anim = "Dance"; // The name of the anim we want to use.

default // Create a default state.
{
    state_entry() // On entering the state...
    {
        owner = llGetOwner(); // Establish the owners UUID and store it.
    }
    touch_start(integer nd) // When touched...
    {
        if(llDetectedKey(0) == owner) // Check if the person who touched us is the owner by comparing the UUIDs.
        {
            llRequestPermissions(owner, PERMISSION_TRIGGER_ANIMATION); // Request permission to animate the owner.
            if(perms) // If we have permission...
            llStartAnimation(anim); // Start the animation.
        }
    } // So what is all this stuff for?
    run_time_permissions(integer perm) // This event is triggered as a result of requesting permissions.
    { // We can use a condition like this to check that we have the perms we asked for.
        if(perm & PERMISSION_TRIGGER_ANIMATION)
        perms = TRUE; // Then we can store the condition of the permissions to a global variable (for checks).
    }
}// So if asking for permissions triggers run_time_permissions with the result we need to wait...
// ...until run_time_permissions is triggered before we can know one way or another...
// ...if our request for perms was granted. The problem with this script then is that we attempt to start the animation...
// during the run time of the triggered touch_start event and while that is running the triggering of...
// ...run_time_permissions has to wait. So the value of perms cannot be established until after the last code in...
// ...the touch_start event has run.

// The lesson is that llRequestPermissions does what it says. It requests the perms. It doesn't always get them.
// run_time_permissions is where we check what perms we have got and act accordingly.

So what we need to do is change the order of the function calls and events to something like this -

integer perms;

key owner;

string anim = "Dance";

default
{
    state_entry()
    {
        owner = llGetOwner(); // Since this script will only ever attempt to animate the owner...
        llRequestPermissions(owner, PERMISSION_TRIGGER_ANIMATION); // ...we can request the perms on state_entry.
    }
    touch_start(integer nd)
    {
        if(llDetectedKey(0) == owner) // Only react to the owners touch.
        {
            if(perms) // If we have perms...
                llStartAnimation(anim); // ...start the animation.
        }
    }
    run_time_permissions(integer perm) // Triggered as a result of the request in state_entry.
    {
        if(perm & PERMISSION_TRIGGER_ANIMATION) // Did we get the perms we asked for?
            perms = TRUE; // Yup. Store the result.
    }
}

Or this -

string anim = "Dance";

default
{
    touch_start(integer nd)
    { // We can request perms directly for whoever comes along.
        llRequestPermissions(llDetectedKey(0), PERMISSION_TRIGGER_ANIMATION);
    }
    run_time_permissions(integer perm)
    {
        if(perm & PERMISSION_TRIGGER_ANIMATION) // If we get the perms we asked for...
            llStartAnimation(anim); // The animation can begin immediately.
    }
}

Getting things in the right order is extremely important. I hope this helps.