This script allows you to store data in
lists as you would in a C++ or Java multidimensional array. It is a bit more flexible than those language's arrays, since it allows you to dynamically resize each dimension; You don't have to define the size of the array before you use it. If you ever ran into the "Runtime Error: Lists cannot contain lists" message and really needed a list within another, this is what you can use.
For example, say you had a chessboard with each square a different color. Your script can refer to each color using a two-dimensional array; one number for the row another for the column of the particular color.
default {
state_entry() {
integer SQUARES_ON_SIDE = 12;
// Randomly assign colors to each square:
list colorArray;
integer row;
integer col;
for (row = 0; row < SQUARES_ON_SIDE; ++row) {
for (col = 0; col < SQUARES_ON_SIDE; ++col) {
vector color = <llFrand(1), llFrand(1), llFrand(1)>;
colorArray = setArray(colorArray, [row, col], [color]);
}
}
// Get the color of the square at row 1, column 2:
vector squareColor = (vector) llList2String(getArray(colorArray, [0, 1]), 0); // Note that arrays, like LSL's lists, are 0-based; they start counting at 0, not 1.
// ...
// Get the colors of all the squares in row 4:
list rowColors = getArray(colorArray, [3]);
// ...
}
}
Getting an element at index [1, 5, 2, 9], in an array "A" translates to:
Get list at index 1 in A, call this B,
Get list at index 5 in B, call this C,
Get list at index 2 in C, call this D,
Return element at index 9 in D.
And here are the functions :)
// Generates a random string of length len
// from the characters passed to it.
string randomSeperator(integer len) {
integer firstChar = (integer)llFrand(60) + 20; // Range of printable chars = 0x20 to 0x7E
if (len <= 1)
return llUnescapeURL("%"+(string)firstChar);
integer lastChar;
do { // Last char must not equal first char.
lastChar = (integer)llFrand(60) + 20;
} while (lastChar == firstChar);
string ret = llUnescapeURL("%"+(string)firstChar);
for (len -= 2; len > 0; --len) {
integer val;
do {
val = (integer)llFrand(60) + 20;
} while (val == firstChar || val == lastChar);
ret += llUnescapeURL("%" + (string)val);
}
return ret + llUnescapeURL("%"+(string)lastChar);
}
integer SEPERATOR_LEN = 3;
string dumpList2String(list src) {
// Generate a seperator not present in any of the
// elements in the list.
string chars = (string) src; // Squashes all elements together.
string seperator;
do {
seperator = randomSeperator(SEPERATOR_LEN);
} while (llSubStringIndex(chars, seperator) != -1);
return seperator + llDumpList2String(src, seperator);
}
list parseStringKeepNulls(string src) {
// The seperator should be the first SEPERATOR_LEN
// characters in the string.
return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
[llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}
list setListElement(list dest, list src, integer index) {
if (src == []) {
return llListReplaceList(src, [""], index, index);
} else {
integer len = llGetListLength(dest);
for (; len < index; ++len) {
dest += "";
}
return llListReplaceList(dest, src, index, index + llGetListLength(src) - 1);
}
}
list getArray(list array, list index){
if (index == [])
return array;
integer numIndicies = llGetListLength(index);
list src = array;
integer i;
for (i = 0; i < numIndicies - 1; ++i) {
integer listIndex = llList2Integer(index, i);
string element = llList2String(src, listIndex);
if (llGetSubString(element, 0, 0) == "l")
element = llDeleteSubString(element, 0, 0);
src = parseStringKeepNulls(element);
}
string element = llList2String(src, llList2Integer(index, -1));
if (llGetSubString(element, 0, 0) == "l") {
// Caller is retreiving a dimension.
return parseStringKeepNulls(llDeleteSubString(element, 0, 0));
} else {
return [element];
}
}
// To set an element, we need to extract
// each list starting from where exactly the
// new data will be. In a set of russian dolls, this
// would be equivelant to grabbing the littlest one
// first. then gradually adding back on the layers over it.
// I think its easiest to do this recursively.
list setArray(list array, list index, list data) {
string element;
if (llGetListLength(data) > 1) {
// Data is a new dimension:
element = "l" + dumpList2String(data);
} else {
// Data is a single element
element = llList2String(data, 0);
}
if (llGetListLength(index) > 1) {
// index is in the form [a,b,c,d]
// here, we grab the list that d is in.
list containerIndex = llDeleteSubList(index, -1, -1);
list dest = getArray(array, containerIndex);
integer listIndex = llList2Integer(index, -1); // Grab d
dest = setListElement(dest, [element], listIndex); // replace the element in d's list.
// Make sure the recursion treats the container like a list, not an element.
if (llGetListLength(dest) == 1)
dest += "";
return setArray(array, containerIndex, dest);
} else {
return setListElement(array, [element], llList2Integer(index, 0));
}
}
Indicies are referred to like this: [1, 2, 3, 4], instead of the standard C++/Java style [1][2][3][4]. It saves the script parsing work :)
This work is licensed under a
Creative Commons Public Domain License.