Using Notecards
Notecards can be a handy way to store string based information for a script to use. Notecards are an object type that can be embedded inside of any Second Life object. A note is nothing more than a collection of text. Very similar to a text file stored on your hard drive.
Scripts can easily read from notecards that are stored in the same object as the script. However, scripts cannot write to notecards. Notecards are very easy to modify. The owner of an object can simply open the contents of a object that contains a notecard. Notecards must be created in your inventory. To create a notecard open your inventory and select the notecards folder. You can right-click the notecards folder and select “New Notecard”. This will create the notecard in your inventory, as seen in Figure 5.1.
Figure 5.1: Editing a Notecard

There are many notecards in my inventory, as seen in Figure 5.1. The new notecard is named “New Note”. The notecard can now be drug to your object and renamed.
This notecard can be seen in Listing 5.1.
Listing 5.1: A Simple Notecard
Item 1:This is configuration item 1 Item 2:This is configuration item 2 Item 3:This is configuration item 3
The above notecard is a simple configuration file. There are three configuration items. The first is named “Item 1”. The script, that will be presented to read this configuration file, will look for each of these configuration and parse the data contained after the configuration item.
A script can easily read notecards by using the appropriate Linden Scripting Language functions. Listing 5.2 shows such a script.
Listing 5.2: Reading Notecards
integer index;
string notecardName;
key notecardQuery;
integer notecardIndex;
integer loaded = FALSE;
string item1;
string item2;
string item3;
default
{
state_entry()
{
if( loaded == FALSE )
{
notecardName = "Config";
state loading;
}
else
{
index = 0;
llSay(0,"Config data:");
llSay(0,"Item 1's value: " + item1);
llSay(0,"Item 2's value: " + item2);
llSay(0,"Item 3's value: " + item3);
}
}
}
state loading
{
state_entry()
{
llSay(0,"Loading configuration data...");
notecardIndex = 0;
notecardQuery = llGetNotecardLine(notecardName,notecardIndex++);
}
dataserver(key query_id, string data)
{
if ( notecardQuery == query_id)
{
// this is a line of our notecard
if (data == EOF)
{
llSay(0,"Config loaded...");
loaded = TRUE;
state default;
} else
{
integer i = llSubStringIndex(data, ":");
if( i!=-1 )
{
string name = llGetSubString(data,0,i-1);
string value = llGetSubString(data,i+1,-1);
if( name=="Item 1" )
{
item1 = value;
}
else if( name=="Item 2" )
item2 = value;
if( name=="Item 3" )
item3 = value;
}
notecardQuery = llGetNotecardLine(notecardName,notecardIndex++);
}
}
}
}
This script begins by defining several script level variables. These variables will be accessible from any of the states that the script may find itself in.
integer index; string notecardName; key notecardQuery; integer notecardIndex; integer loaded = FALSE;
The notecardIndex variable holds the current notecard line being read. The notecardName variable holds the name of the notecard to read. The notecardQuery variable holds the query being used to read the notecard. The loaded variable holds a boolean to determine whether the notecard has been read yet.
Each of the configuration items will be stored in strings named item1, item2 and item3. These variables could be replaced with any configuration items that you are modifying this script to be able to parse.
string item1; string item2; string item3;
The notecard script, as do all scripts, begins in the default state. The state begins by checking the loaded variable. If the notecard has not yet been read, the script enters the loading state.
default
{
state_entry()
{
if( loaded == FALSE )
{
notecardName = "Config";
state loading;
}If the notecard has already been loaded, display the item data.
else
{
index = 0;
llSay(0,"Config data:");
llSay(0,"Item 1's value: " + item1);
llSay(0,"Item 2's value: " + item2);
llSay(0,"Item 3's value: " + item3);
}
}
}The loading state is where the notecard is actually read. First, the notecardIndex variable is set to zero. This will begin reading the notecard at the first line. Next llGetNotecardLine is called to read the first line from the notecard. The llGetNotecardLine does not return the line immediately. Rather the dataserver event will be called as soon as the line is read.
state loading
{
state_entry()
{
llSay(0,"Loading configuration data...");
notecardIndex = 0;
notecardQuery = llGetNotecardLine(notecardName,notecardIndex++);
}As the lines are read in from the notecard, the dataserver event is called. If the query_id matches our notecard query established earlier, this is a line that should be processed.
dataserver(key query_id, string data)
{
if ( notecardQuery == query_id)
{If this line is from our query, check to see whether it is an end-of-file (EOF). If the file has ended, set the loaded variable to TRUE and return to the default state.
// this is a line of our notecard
if (data == EOF)
{
llSay(0,"Config loaded...");
loaded = TRUE;
state default;
} else
{If the line is part of the notecard, and not an end-of-file, we must determine which configuration item was just read. Configuration items begin with the name of the item, followed by a colon. The first step is to find the location of the colon. The following line of code does this.
integer i = llSubStringIndex(data, ":");
The variable i now contains the location of the first colon encountered. If no colon was encountered then the value -1 is returned.
if( i!=-1 )
{The name of the configuration item comes just before the colon. The name is then extracted into the name variable. The name occurs between position zero and one minus the position that ht colon was found at.
string name = llGetSubString(data,0,i-1);
The value occurs to the right of the colon. The value is extracted by obtaining all characters from one plus the colon to the end of the string. The value of -1 can be passed to llGetSubString to obtain the end of the string.
string value = llGetSubString(data,i+1,-1);
Now that both the name and value have been obtained it is time to see which configuration item was specified. The next few lines determine which configuration item was specified and copy the value to the appropriate script variable.
if( name=="Item 1" )
{
item1 = value;
}
else if( name=="Item 2" )
item2 = value;
if( name=="Item 3" )
item3 = value;
}Next, the next line of the notecard is read.
notecardQuery = llGetNotecardLine(notecardName,notecardIndex++);
}
}
}
}This process will continue until all lines have been read from the notecard.




