Abi's Scripting Cookbook

This is just a quick lsl scripting cheat sheet I put together for my own use. I’m hosting it here for ease of access and in the hope it maybe of use to the community. It is not intended as a full guide on how to script, just an aid for scripting but it might be helpful those still learning.

Scripting Limits


add next: https://wiki.secondlife.com/wiki/LSL_Script_Memory

Random Channel


Method 1a

Method 1 Random number based on object uuid. Useful for menu channels. Found in an NS Drone script for light effects.
default
{
	touch_start(integer total_number)
	{
		key id = llDetectedKey(0);
		
		integer chan = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -7, -1) );
		
		llRegionSayTo(id, 0, "Random Channel: "+(string)chan);
	}
}

Method 1

Method 1 Random number based on object uuid. Useful for menu channels. Found in an NS Drone script for light effects.
default
{
	touch_start(integer total_number)
	{
		key id = llDetectedKey(0);
		
		integer chan = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -7, -1) );
		
		llRegionSayTo(id, 0, "Random Channel: "+(string)chan);
	}
}
Snippet
iChannelLights = -1 - (integer)("0x" + llGetSubString( (string) kOwner, -7, -1) ) + 106;

Method 2

Random integer. Useful for fixed channels, temp channels, talking to rezzed objects, ect.
default
{
	touch_start(integer total_number)
	{
		key id = llDetectedKey(0);
		
		integer chan = 0x80000000 | (integer)llFrand(65536) | ((integer)llFrand(65536) << 16);
		
		llRegionSayTo(id, 0, "Random Channel: "+(string)chan);
	}
}

Method 3

Method devised by Claire M (primerib1)
default
{
	touch_start(integer total_number)
	{
		key id = llDetectedKey(0);
		
		integer chan = (integer)("0x" + (string)id) | 0xD0000000;
		
		llRegionSayTo(id, 0, "Random Channel: "+(string)chan);
	}
}
Dchan = (integer)("0x" + (string)llGetOwner()) | 0xD0000000;

Memory Limit


Method 1

Sets the memory limit to 20kb

lllSetMemoryLimit(20000);

Method 2

Sets the memory limit to used memory + 5kb

lllSetMemoryLimit(llGetUsedMemory()+5000);

Method 3

Sets the memory limit to used memory + 4096 bytes. Hexadecimal number 0x1000 = 4096

llSetMemoryLimit(llGetUsedMemory()+0x1000);

Memory Free


llOwnerSay("\n::: "+llGetScriptName()+" :::\nFree Memory: "+(string)llGetFreeMemory());
llOwnerSay("\n::: "+llGetScriptName()+" :::\nFree Memory: "+(string)llGetFreeMemory()+" of "+(string)llGetMemoryLimit());

Owner Check


Touch

default
{
	touch_start(integer num_detected)
	{
		key id = llDetectedKey(0);
		
		//owner check (touch) prevents others from access this script.
		if ( id != llGetOwner() ) {return;}
		
		// send a message to the chat window of the avatar touching
		llRegionSayTo(id, 0, "You touched this!");
	}
}

Listen

default
{
	listen(integer channel, string name, key id, string msg)
	{
		//owner check (listen)	
		if ( llGetOwner() != llGetOwnerKey(id) ){return;}
		
		llOwnerSay(msg);
	}
}

Listen


Basic Listener

integer chan;
default
{
	state_entry()
	{
		chan = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -7, -1) );
		
		llListen(chan, "", "", "");
	}
	listen(integer channel, string name, key id, string msg)
	{
		//owner check (listen)
		if ( llGetOwner() != llGetOwnerKey(id) ){return;}
		
		llOwnerSay(msg);
	}
}

Global Listener

integer listener;
integer chan;
default
{

	state_entry()

	{

		chan = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -7, -1) );
		
		listener = llListen(chan, "", "", "");
	}
	
	listen(integer channel, string name, key id, string msg)
	{
		//owner check (listen)	
		if ( llGetOwner() != llGetOwnerKey(id) ){return;}
		
		llOwnerSay(msg);
		llListenRemove(listener);
	}
}

Say


default
{
	touch_start(integer num_detected)
	{
		key id = llDetectedKey(0);
		
		// send a message to the chat window of the avatar touching
		llRegionSayTo(id, 0, "You touched this!");
 
		// send a message to the attachments of the avatar touching
		// example channel: -12345
		llRegionSayTo(id, -12345, "Hello there attachments!");
		
		//full region range (note cannot be channel 0)
		llRegionSay(25,"This is an incredibly useless program." );
		
		//100 meter range
		llShout(0, "I scream icecream!");
		
		//20 meter range
		llSay(0, "Hello, Avatar!");
		
		//10 meter range
		llWhisper(0, "This is an incredibly useless program.");
		
		//gride wide
		llInstantMessage( id, "Someone touched me!" );
		
		//owner only
		llOwnerSay("Ouch!");
	}
}

Distance


Touch

-

default
{
    touch_start(integer total_number)
    {
        key id = llDetectedKey(0);
        
        //distance
        vector pos = llDetectedPos(0);
        float distance = llVecDist(pos, llGetPos() );
        
        //range
        float touchRange = 20;
        if ( distance > touchRange ){return;}
        
        llRegionSayTo(id, 0, "You touched this!");
    }
}

Listen

-

default
{
    listen(integer channel, string name, key id, string msg)
    {
        //distance
        vector pos = llList2Vector(llGetObjectDetails(id,[OBJECT_POS]),0);
        float distance = llVecDist(pos, llGetPos() );
        
        //range
        float touchRange = 20;
        if ( distance > touchRange ){return;}
        
        llSay(0, msg);
    }
}

Toggle


Method 1

This is the basic way to do it. toggles from false to true when touched. It works but it's more time consuming to code if you need more than one switch, and increases the likelyhood of bugs due to complexitity.

//ON-OFF Toggle
integer o;

default
{
	state_entry()
	{
		llSetText("OFF",<1,1,1>,1);
	}

	touch_start(integer total_number)
	{
		if ( o == TRUE )//off
		{
			o = FALSE;
			llSetText("OFF",<1,1,1>,1);
		}
		else//on
		{
			o = TRUE;
			llSetText("ON",<1,1,1>,1);
		}
	}
}

Method 2

More advanced. I found this method looking at a script for a light switch. It toggles from -1 to 0 rather than false to true which makes it difficult to work with.

//ON-OFF Toggle
integer o = -1;

default
{
	state_entry()
	{
		llSetText("OFF",<1,1,1>,1);
	}
	
	touch_start(integer total_number)
	{
		if(o = ~o)//off
		{
			llSetText("OFF",<1,1,1>,1);
		}
		else//on
		{
			llSetText("ON",<1,1,1>,1);
		}
	}
}

Method 3

As above but toggles from false to true. I use this for most scripts requring a toggle switch.

//ON-OFF Toggle
integer o;

default
{
	state_entry()
	{
		llSetText("OFF",<1,1,1>,1);
	}
	
	touch_start(integer total_number)
	{
		if(o = !o)//on
		{
			llSetText("ON",<1,1,1>,1);
		}
		else//off
		{
			llSetText("OFF",<1,1,1>,1);
		}
	}
}

Hold Touch



integer touched;  

default
{
    touch_start(integer num_detected)
    {
        touched = FALSE;
        llResetTime();
    }
    
    touch(integer num_detected)
    {
        if (llGetTime() > 1.0 && touched == FALSE)
        {
            touched = TRUE; 
            llSay(0, "Held click.");
        }
    }
    
    touch_end(integer num_detected)
    {
        if (llGetTime() <= 1.0)
        {
            llSay(0, "Normal click");
        }
    }
}

-


list menuButtons(integer page, list buttons)
{
    page--;
    
    integer index = (page * 9);
    list tokens = llList2List(buttons, index, index + 8 );
    integer length = llGetListLength(tokens);
    
    if ( length < 9 )
    {
        length = 9 - length;
        //llSay(0,(string)length);
        
        integer i;
        for (i = 0; i < length; i++)
        {
            tokens += "-";
        }
            
    }
    
    return tokens;
}

-


example list for testing scripts.

list greekLetters = ["Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega"];

-


string str;
        
        integer i;
        integer length = llGetListLength(tokens);
        
        for (i = 0; i < length; ++i)
        {
            str = llList2String(tokens,i);
            
            llSay(0, (string)i+" - "+str);
        }

loops


F



integer i;
    integer end = llGetListLength(tokens);

    for (i = 0; i < end; ++i)

For Loop


string str;
integer i;
integer end = llGetListLength(tokens);
for (i = 0; i < end; ++i)
{
	str = llList2String(tokens,i);
	
	llSay(0, (string)i+" - "+str);
}

while(i < end)
{
	str = llList2String(tokens,i);
	
	llSay(0, (string)i+" - "+str);
	
	i++;
}
		
		

While

Do While

LSL Golf: Most efficient loop. (Not really my invention, but interesting)

integer i = -(lVar != []);
string elem;
// If lVar is guaranteed to never be empty, can drop the `if (i)` part
if (i) do {
    elem = llList2String(lVar, i);
    // do something with elem
} while(++i);

llSubStringIndex


default
{
    touch_start(integer total_number)
    {
        string str;
        
        string message = "Abi Wolfy";
        
        if ( ~llSubStringIndex(message,"Wolfy") )
        {
            llSay(0, message);
        }
    }
}

Odd & Even


Odd or even number check

#1


integer num;

default
{
	touch_start(integer num_detected)
	{
		num++;
		
		string str;
		
		if ( (num % 2) == 0 )
		{
			str = "even";
		}
		else
		{
			str = "odd";
		}
		
		llSay(0, (string)num+" is "+str);
	}
}

meth 2


integer num;

default
{
	touch_start(integer num_detected)
	{
		num++;
		
		string str;
		
		if ( num % 2 )
		{
			str = "odd";
		}
		else
		{
			str = "even";
		}
		
		llSay(0, (string)num+" is "+str);
	}
}

bitwise



integer num;

default
{
	touch_start(integer num_detected)
	{
		num++;
		
		string str;
		
		if (num & 1)
		{
			str = "odd";
		}
		else
		{
			str = "even";
		}
		
		llSay(0, (string)num+" is "+str);
	}
}


Reverse Number



integer num = 7;
integer end = 8;

num = end - num;// 8 - 7 = 1

Colour




//Converting RGB colours in hexadecimal to LSL colour vectors
vector hex2lsl(string hex)
{
    if ( llGetSubString(hex, 0, 0) == "#" ){hex = llDeleteSubString(hex, 0, 0);}
    
    integer i = (integer)("0x" + hex);
    return <(i >> 16) & 0xFF, (i >> 8) & 0xFF, (i & 0xFF)> / 255;
}


//Function made by me
string lsl2Hex( vector colour )
{
    string str;
    string hex = "0123456789abcdef";
    integer i;
    integer num;
    
    //integer2Hex((integer)(255*colour.x))
    num = (integer)(255*colour.x);
    i = (num / 16);
    num = (num % 16);
    str += llGetSubString(hex, i, i)+llGetSubString(hex, num, num);
    
    //integer2Hex((integer)(255*colour.y))
    num = (integer)(255*colour.y);
    i = (num / 16);
    num = (num % 16);
    str += llGetSubString(hex, i, i)+llGetSubString(hex, num, num);
    
    //integer2Hex((integer)(255*colour.z)
    num = (integer)(255*colour.z);
    i = (num / 16);
    num = (num % 16);
    str += llGetSubString(hex, i, i)+llGetSubString(hex, num, num);
    
    
    return "#"+str;
}


//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
}

//Converting RGB colours in hexadecimal to LSL colour vectors
vector hex2lsl(string hex)
{
    integer i = (integer)("0x" + hex);
    return <(i >> 16) & 0xFF, (i >> 8) & 0xFF, (i & 0xFF)> / 255;
}

percentage



integer percentageTest( integer total, integer value )
{
   return (integer)((float)value / total * 100.0);
}

Lists


Tokens


list dataList = ["one","two","three"];
string data = llList2CSV(dataList);

list tokens = llParseString2List(data, [","], []);

string message = llToUpper(llList2String(tokens,0));

if ( message = "ONE" ){}

Notecard Reader



string gNotecard = ".notecard";
integer gNcLine;
key gNcQuery;
integer gRetry;

default
{
    state_entry()
    {
        if (llGetInventoryType(gNotecard) != INVENTORY_NONE)
        {
            gNcQuery = llGetNumberOfNotecardLines(gNotecard);
        }
    }
	
	dataserver( key query, string data )
    {
        if ( query == gNcQuery )
        {
            llResetTime();
            
            integer end = (integer)data;
            integer i = gNcLine;
            
            for (; i <= end; ++i)
            {
                string line = llGetNotecardLineSync(gNotecard, i);
                
                if ( line == NAK )
                {
                    llOwnerSay("Cache interrupted on line "+(string)(i+1)+"! Retry attempt: "+(string)gRetry);
                    if ( gRetry++ < 3 )
                    {
                        gNcLine = i;
                        gNcQuery = llGetNumberOfNotecardLines(gNotecard);
                    }
                    return;
                }
                else if ( line == EOF || line == "END" )
                {
                    llOwnerSay("Debug: EOF\nTime taken: "+(string)llGetTime());
                    gNcLine = 0;
                    gRetry = 0;
                    llSetText("--- EOF ---"+"\nTime: "+(string)llGetTime(),<1,1,1>,1);
                    return;
                }
                else if ( llGetSubString (line, 0, 0) != "#" && llGetSubString (line, 0, 1) != "\\\\" )
                {
                    integer e = llSubStringIndex( line, "=" );
                    
                    if (~e)
                    {
                        string name = llStringTrim(llToUpper(llGetSubString( line, 0, e-1 )), STRING_TRIM);
                        string value = llStringTrim(llGetSubString( line, e+1, -1 ), STRING_TRIM);
                        
                        llOwnerSay("Name: '"+name+"'\nValue: '"+value+"'");
                    }
                }
                
                llSetText("Line: "+(string)(i+1)+ " of "+(string)end+"\nTime: "+(string)llGetTime(),<1,1,1>,1);
                
            }
        }
    }
}