Pages

Saturday, October 20, 2012

Scripts



Today I’m going to discuss a topic that I should not…because I know almost nothing about it. (When has that ever stopped you before, Linnie?  Oh, hush.)  That topic is…scripts.
Scripts are what bring life to the world of Second Life.  Scripts make doors open, planes fly, Animation Overriders work, and shoes change color at a touch.  Scripts make rivers flow, fish swim, birds sing, and dogs wag their tails.  They are the magic of Second Life, and the people who understand how to create them are wizards indeed.
A script is a computer program.  In SL, scripts are written in a unique software language called LSL – Linden Scripting Language.  LSL is, my programmer friends tell me, related to C, or C+, or C++…they seem to think the number of plus signs is important, anyway.  I wouldn’t know…the computer languages with which I have even a passing familiarity are now nearly obsolete…BASIC and FORTRAN.  So this article is not going to go into ANY of the details of LSL.  Instead, my intention is to give those of you who have no ambition to become scripting wizards a basic understanding of scripts, just enough so that you don’t tremble in fear when someone whispers the word. 
Well, a bit more than that...even if you can't write scripts, you should be able to open one and make some minor tweaks to its variables.
Here is an example of a script, the simplest possible.  You will see this if you right click in your inventory and select “New Script”.
default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}
What does it do?  Nothing, yet.  Scripts don’t do anything until you put them into a prim.  So, rez a cube and select Edit.  Click on the Content tab of the Edit window.  Now drag your script to the edit window and drop it into the prim’s Content area.  Close the Edit window.  Or use the alternate method...just drop your script on the object itself.
Now touch the prim by left clicking it.  Wow!  Your prim spoke, didn’t it?  Isn’t that amazing?  A talking prim!  Let's make our prim just a little smarter.  Remove the first script from the prim and drop this one in...or edit the initial script and replace the text with this.


default
{
    state_entry()
    {
    }
   
    touch_start(integer a)
    {
            string sName = llKey2Name(llDetectedKey(0));
            llSay(0, "Hello, " + sName);
    }
}
Now touch the prim again.  Hey, it knows your name!
Here is a script that I used a lot, before LL made the transparency box in the texture window go all the way up to 100%.  If you drop this script into a prim, it will do two things…turn the prim completely transparent, and then delete itself.
default
{
    state_entry()
    {
        llSetAlpha(0.0, ALL_SIDES);
        llRemoveInventory(llGetScriptName());
    }
}
Now have a look at a more complex script.  This one is for a sliding door. 
key owner;
// Will be used to retrieve owner's key.

integer iChan = 1000;
// Channel door will listen on if other doors are touched;
// also the channel this door will broadcast on.

integer iSteps = 3;
// How many steps the door will open in, used to provide the
// illusion of sliding. Fewer steps means it opens faster,
// but more steps will make it "slide" smoother.

vector vOffset = <0.0, 2, 0.0>;
// Indicates how far the door will move with each step.
// Multiply by iSteps to calculate the total distance the
// door will move.

vector vBase;
// Used to "un-stick" the door if something blocks it.
// Not sure if this is needed since 0.5.1, objects don't
// seem to block the door any more. Leaving it in just
// in case, though. I think attempting to edit the door
// while it's moving may make it stick. This will solve
// that problem as well.

float fOpenTime = 25;
// How long the door stays open

string sSKeyword = "open1";
// Keyword door broadcasts when it's touched, to make
// other doors open. You can chain these to make multiple
// doors open when any one is touched.
// NEVER make sSKeyword and sRKeyword the same, or you may
// get some doors stuck in an infinite loop, continuously
// re-triggering each other.

string sRKeyword = "open2";
// Keyword door listens for from other doors. Will open
// when it "hears" this keyword.
// Again, NEVER make sSKeyword and sRKeyword the same.

integer bMove = FALSE;
// Is the door moving?

integer bLock = FALSE;
// Is the door locked?

integer bVerbose = FALSE;
// Confirm when owner locks/unlocks the door.

//*********************************************
// open() -- the meat and taters of the code,
// makes the door actually move.
//*********************************************
open()
{
bMove = TRUE;
integer i;
vector basepos = llGetPos();
for (i = 0; i < iSteps; i++)
{
llSetPos(basepos + i*vOffset);
}
vOffset *= -1;
llSleep(fOpenTime);
basepos = llGetPos();
for (i = 0; i < iSteps; i++)
{
llSetPos(basepos + i*vOffset);
}
vOffset *= -1;
if (llGetPos() != vBase) {
llSetTimerEvent(5);
} else {
bMove = FALSE;
}
}

default
{
//************************************************** *
// state_entry() -- set up our global variables and
// initialize the listen events.
//************************************************** *
state_entry()
{
vBase = llGetPos();
owner = llGetOwner();
llListen(0,"",owner,"");
llListen(iChan,"",NULL_KEY,sRKeyword);
}

//************************************************** *
// listen() -- listen for other doors opening, and
// if owner wants to lock/unlock doors.
//************************************************** *
listen(integer chan, string name, key id, string msg)
{
if (chan == iChan && msg == sRKeyword) {
if (!bMove && !bLock) open();
if (bLock && bVerbose) llSay(0,"Locked!");
}
if (chan == 0 && id == owner && msg == "lock") {
bLock = TRUE;
if (bVerbose) llWhisper(0,"Locked!");
}
if (chan == 0 && id == owner && msg == "unlock") {
bLock = FALSE;
if (bVerbose) llWhisper(0,"Unlocked!");
}
}

//********************************************
// touch_start() -- what to do when someone
// touches the door.
//********************************************
touch_start(integer count)
{
if (bLock) {
llSay(0,"Locked!");
} else {
if (!bMove) {
llWhisper(iChan,sSKeyword);
open();
}
}
}

//************************************************** **
// timer() -- this is only used to un-stick the door
// (see vBase definition above).
//************************************************** **
timer()
{
llSetPos(vBase);
if(llGetPos() == vBase) {
llSetTimerEvent(0);
bMove = FALSE;
}
}
}
(NOTE: The above script is courtesy of my friend Qie Niangao.  It may not be re-sold as a script, but may be included as a part of your builds for resale.  If you use it for any purpose, a donation to Qie would be courteous.)

As I said, I’m not going to go into details of commands and syntax.  But I am going to mention a few things that will, I hope, help you find your way around a bit.
First, all brackets and parentheses must have their proper counterpart.  Each { must have its corresponding }, or the script will give you an error message.
Second, any line preceded by a double slash, //, is a comment.  It’s intended to tell a human reader something about the script…what it does, who it belongs to, what parameters can be changed, and so on.  You can also use the // to turn off a line of code (by turning it into a comment), or enable a line of code by removing the slashes ("uncommenting".).
Third, every time you make a change to a script, no matter how small, the script will not run until it is saved.  When you hit the Save button, a “Compile Successful!” message will tell you that you have obeyed all the laws for writing a viable script.  It may not do what you want it to do, but at least it is not broken so badly that it won't run at all.
While you can write scripts directly in a Second Life script window, if you are doing anything that’s at all complex, it’s useful to write it in a script editor.  You can get a free LSL editor at Source Forge and several other places.
Note that our door script has places where you can tweak values to change the way it operates.  You can select the axis and direction and speed of the slide, just by changing a few numbers.
A lot of complex scripts use a separate notecard to set up their various properties, rather than have you mucking about inside their actual code.  In this case, your item will come with such a notecard, probably called “Configuration” or some such.  For example, the popular ZHAO II animation overrider comes with three parts:  The actual HUD object, the script, and a Configuration notecard.  Both the script and the notecard are dropped into the HUD’s Content tab.  You then add your own animations to the AO, again by dropping them into the HUD’s Content.  Finally, you edit the Configuration notecard to mention each animation by name and assign it to a category…walking, standing, sitting, and so on.
At the bottom of the script window is a button that allows you to compile the script “normally”, or in “Mono”.  Mono is an innovation that Linden Lab added about four years ago.  It increases the efficiency of how SL handles scripts, at least in certain ways.  This is getting close to the area of my ignorance, but as I understand it, go ahead and compile the script in Mono if it is going to be moving from place to place…in a vehicle, for example, or worn as part of an avatar attachment.  If it is going to be at a fixed location, compile it under the old method.  [EDIT:  This is bad advice from your ignorant hostess.  Please see Comments 5&6 below for better information.  Short answer:  Use Mono for everything, you'll be right more often than not.]
I’ve mentioned particles (see "Fireworks!") before in these spaces.  Particles are used to make lightning, fountains, rain, smoke, tall grass, fireworks, bling, and many other effects.  ALL particles are controlled by only ONE all-purpose particle script.  How you change the many parameters in this script determines what your particles will look like and how they will behave.  I’m not going to reproduce the script here, but you can get a copy of it, and a bunch of tutorials that show you how to obtain various effects from it, at The Particle Laboratory .
Sometimes a script will not operate.  If that happens to you, first look at your top menu bar to see if you are in a No Script area.  If you are, fly up about 50 meters or so.  The scripts will begin working again, and will remain working when you fly back down.  You can’t, however, change the way they are working with any HUD controls they may have.
If you are a land owner and have problems with griefers, you may want to (temporarily, we hope) disable script operation on your land.  This can be done in About Land/Options. 

 If you are in a script-enabled area and the script still isn’t working, you can sometimes correct the problem by selecting the scripted object, then go to the Build menu on your top bar and choose Scripts/Reset Scripts.
Somebody has probably written a script to do just about anything you want, and many of them are free.  You can find a number of collections of LSL scripts with a simple Google search.  Here are some links:
You can find a complete run-down on the various LSL commands and what they do in the Linden Lab LSL Portal But it’s mighty dry technical reading, and not always easy to understand.
You can also get help from fellow scripters on the Second Life website Scripting Forum.
If you want more detailed instruction, check out the College of Scripting Music and Science  If you want to become a true scripting wizard a few visits to this place will be worth it.
Most scripts except flat-out freebies are No Modify…because you have to see it to change it, and if you can see it, you could copy it into a new window, then give it away to all your friends.  This is another reason scripts use Configuration notecards to modify their parameters.  But there is one bit of good news:  scripts, even with full permissions, are not susceptible to theft via copybot software.
One parting bit of advice:  If you are not interested in learning the complexities of LSL yourself, make friends with a scripter or two.  Their talents can come in VERY handy!  But remember Gandalf’s advice:  “Meddle not in the affairs of wizards, for they are subtle and quick to anger.”

7 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Thank you for the clear explanations. I think that learning to script is like learning another language. Thank goodness for the scripters who are willing to teach and share.

    ReplyDelete
  3. If you want to make your friendly prim use your display name and not say "Hello so-and-so Resident", you can use

    llSay(0,"Hello, "+llGetDisplayName(llDetectedKey(0)));

    If, for whatever reason, you only want the person you're greeting to receive the message, then try

    key k = llDetectedKey(0);
    llRegionSayTo(k,0,"Hello, "+llGetDisplayName(llDetectedKey(0)));

    ReplyDelete
  4. As for Mono vs "Normal", you do in fact have it the wrong way around. Mono is *hugely* more efficient than the old LSO, to an order of magnitudes, *but* it causes a heavier hit when entering a sim.

    So "old wisdom" is to uncheck Mono when doing vehicles or attachments (unless you need the 64kb memory Mono offers), but use Mono otherwise.

    There has been a lot of work on region crossings, though, and it is to my knowledge still ongoing. So Mono is not as bad at crossings as it initially was, but I haven't seen any definitive comparisons recently.

    ReplyDelete
    Replies
    1. See, this is the trouble with writing about stuff you don't understand. Thank you Tali, I stand corrected. In fact, I did a bit more research, and this comment by Qie Niangao in a forum post may be useful to both of us:

      "Yeah, there used to be an actual bug that caused sims to freeze-up when Mono scripts came and went, and that's fixed.

      "I believe there will always remain some slight additional overhead in moving Mono scripts from sim to sim, compared with LSO, but I too have decided to simply quit worrying about it, and use whichever compilation uses less memory (or, more often, just stick with Mono and forget about it)."

      Delete
    2. I agree. Apart from that bug being fixed, Mono also has some possible server optimizations which are not obvious as seen from the scripter's side. (Such as dynamic memory allocation and shared bytecode).
      So I tend to take the philosophy "use Mono, and let the servers reap the benefits as they are upgraded".

      Delete

Note: Only a member of this blog may post a comment.