The list Type
A list is an ordered collection of values. Unlike a variable that holds one thing, a list can hold many — and they can even be different types.
default
{
state_entry()
{
// A list of strings
list colours = ["red", "green", "blue", "gold"];
// A list of integers
list scores = [10, 25, 50, 100];
// A mixed list (valid in LSL!)
list mixed = ["Blackthorn", 42, 3.14, TRUE];
llOwnerSay("Colours list created with " +
(string)llGetListLength(colours) + " items.");
}
}
Lists are stored in script memory. Each item uses roughly 16–64 bytes depending on type. For scripts with strict memory limits, keep lists small and delete items you no longer need.
llGetListLength
llGetListLength(list) returns the number of items in a list. List indices in LSL are zero-based — the first item is at index 0, the last is at index length - 1.
default
{
state_entry()
{
list names = ["Alice", "Bob", "Carol", "Dave"];
integer len = llGetListLength(names); // 4
string first = llList2String(names, 0); // "Alice"
string last = llList2String(names, len - 1); // "Dave"
llOwnerSay("Count: " + (string)len);
llOwnerSay("First: " + first);
llOwnerSay("Last: " + last);
}
}
Extracting Values — llList2*
Because lists can hold mixed types, you use typed extraction functions to pull values out. Using the wrong function returns a default value silently, so use the right one:
- llList2String(list, index)
- Get item as a string
- llList2Integer(list, index)
- Get item as an integer
- llList2Float(list, index)
- Get item as a float
- llList2Key(list, index)
- Get item as a key (UUID)
- llList2Vector(list, index)
- Get item as a vector
default
{
state_entry()
{
list scores = [10, 25, 50, 100];
integer i;
for (i = 0; i < llGetListLength(scores); i++)
{
integer score = llList2Integer(scores, i);
llOwnerSay("Score " + (string)i + ": " + (string)score);
}
}
}
Modifying Lists
Lists in LSL are immutable values — you can't change a single item in-place. Instead, you build a new list and assign it back. This sounds awkward but becomes natural quickly.
default
{
state_entry()
{
list items = ["apple", "banana", "cherry"];
// Add an item to the end
items = items + ["date"];
llOwnerSay("After add: " + (string)llGetListLength(items) + " items");
// Remove item at index 1 ("banana") using llDeleteSubList
items = llDeleteSubList(items, 1, 1);
llOwnerSay("After remove: " + llList2String(items, 0) +
", " + llList2String(items, 1) +
", " + llList2String(items, 2));
// apple, cherry, date
}
}
llListFindList
llListFindList(haystack, needle) searches for a sub-list within a list and returns the starting index, or -1 if not found. This is your membership check.
list vipList = []; // Filled with owner keys at state_entry
default
{
state_entry()
{
// Populate the VIP list with key strings
vipList = [(string)llGetOwner(), "fake-key-example-here"];
}
touch_start(integer num_detected)
{
string toucherKey = (string)llDetectedKey(0);
// Search for the toucher's key in the list
integer idx = llListFindList(vipList, [toucherKey]);
if (idx != -1)
{
llSay(0, "Welcome, VIP!");
}
else
{
llSay(0, "Sorry, VIPs only.");
}
}
}
Note that the needle argument is a list too — wrap the value you're searching for in square brackets: llListFindList(myList, [searchValue]).
llStringLength & String Tests
llStringLength(string) returns the number of characters in a string. Use it to validate input or test for an empty string:
default
{
listen(integer channel, string name, key id, string message)
{
if (llStringLength(message) == 0)
{
llOwnerSay("Empty message received — ignoring.");
return;
}
if (llStringLength(message) > 100)
{
llOwnerSay("Message too long: " + (string)llStringLength(message) + " chars.");
return;
}
llOwnerSay("Got message: " + message);
}
}
llToUpper, llToLower, llStringTrim
These three functions handle case conversion and whitespace trimming — essential for comparing user input reliably.
default
{
listen(integer channel, string name, key id, string message)
{
// Trim leading/trailing spaces and convert to lowercase
// STRING_TRIM removes both leading and trailing whitespace
string clean = llStringTrim(llToLower(message), STRING_TRIM);
// Now comparisons work regardless of case or extra spaces
if (clean == "hello")
{
llSay(0, "Hello back, " + name + "!");
}
else if (clean == "bye")
{
llSay(0, "Goodbye, " + name + "!");
}
else
{
llOwnerSay("Unknown: '" + clean + "'");
}
}
state_entry()
{
llListen(0, "", NULL_KEY, "");
}
}
- STRING_TRIM
- Remove whitespace from both ends
- STRING_TRIM_HEAD
- Remove whitespace from the start only
- STRING_TRIM_TAIL
- Remove whitespace from the end only
llParseString2List — Splitting Commands
llParseString2List splits a string into a list by one or more separator characters. This is the standard way to parse chat commands.
For example, if an avatar types /42 colour red, you split on spaces to get ["colour", "red"], then act on the parts.
integer listenHandle;
default
{
state_entry()
{
listenHandle = llListen(42, "", llGetOwner(), "");
llOwnerSay("Ready. Try: /42 colour red");
}
listen(integer channel, string name, key id, string message)
{
// Split message on spaces; empty strings discarded
list parts = llParseString2List(message, [" "], []);
string command = llToLower(llList2String(parts, 0));
if (command == "colour" && llGetListLength(parts) >= 2)
{
string colour = llToLower(llList2String(parts, 1));
if (colour == "red")
llSetColor(<1.0, 0.0, 0.0>, ALL_SIDES);
else if (colour == "green")
llSetColor(<0.0, 1.0, 0.0>, ALL_SIDES);
else if (colour == "blue")
llSetColor(<0.0, 0.0, 1.0>, ALL_SIDES);
else
llOwnerSay("Unknown colour: " + colour);
}
else
{
llOwnerSay("Usage: /42 colour [red|green|blue]");
}
}
}
You're now ready for Class 8 — the practical project where you'll combine everything you've learned into one complete, working script.