Cylindrian's Tip/Info/Vending Sign (Part 5)
This is part five in a series of howto entries.
Dialog Menus
To review I'll repost Cylindrian's original list of items she wanted condensed into a single sign:
Donations/Tips -- OK, that one has been taken care of so far, done.
SecondTunes Vendor -- OK, we also took care of this one by working with Bobby a bit and by ensuring that our root prim does not respond to the money() event, done.
The other items:
"Pay for CD using PayPal?
Link to MySpace
Link to iTunes
Link to CD Baby
Link to Cylindrian Blog
Link to Google Calendar"
What do they have in common? Well, they're all web links of course. We could possibly break them down into a few categories as well. Some are links to web pages that sell music, some are links to information about Cylindrian, etc.
There was one other category added as well. When Cylindrian found the poster at the Soft Shadows Club in Starax she liked the "Join group" option. As I discussed in part one there is currently no way to add a person to a group programmatically. The best one could do is either provide instructions to the user on how to self-add or message an officer of the group (IM or Email, etc) the name of the person interested in joining.
After thinking about this a bit I decided that we basically had two categories to work with. First, provide the user with a URL link. Second, give the user an item from inventory. One could quickly come up with hard coded scripts to do these specific tasks and be done with it. There are, however, some users who don't feel comfortable editing scripts directly. That would make the object a one off and the scripts would not be terribly useful for others with a similar need. What if, instead, there was a script to read in a simple preference file and perform a basic task customized to those preferences? Then anyone with a similar need would just have to edit a notecard instead of a script. Great idea, of course, because I thought of it.
The Dialog Menu Script
Below is the script to read a notecard preference file and build a custom menu of options from it.
// Dialog Menu from Preference Card
// By Dolmere Talamasca
// Donated as open source to Second Life community - use it in good health.
// Improve it and share your changes please.
// For a complete write-up on how to modify the preference card please see the README file in the prim where you found this script.
// Quick note on preference card formatting:
//Preference notecard should be in the format where first line is a comment, second line is the text of the dialog box to be displayed
// 3rd through 14th lines represent the 12 possible buttons
// formatted
// BUTTON_NAME|TYPE|TARGET(inv name or url)
integer CHANNEL = 1000;
integer lisHandle;
// Vars used for reading from the preference notecard
integer TYPE_INV = 0;
integer TYPE_URL = 1;
string prefCard = "Pref:Dialog"; //radio station card - name of a notecard in the object's inventory to read in for radio stations
integer cline = 1; //start on line one when reset, line 0 is reserved for a comment
key prefKey; //the inventory key of our radiostation preference file
key cardQ; // id used to identify notecard line read dataserver queries
string dialog_text = "";
list BUTTON_NAMES = [];
list BUTTON_TYPES = [];
list BUTTON_TARGETS = [];
integer list_setup = 0;
default
{
state_entry()
{
llListenRemove(lisHandle);
lisHandle = llListen(CHANNEL,"", "","");
if (list_setup == 0) {
state READ_NOTECARD;
}
}
on_rez(integer ignored) {
list_setup = 0;
llResetScript();
}
touch_start(integer total_number)
{
llDialog(llDetectedKey(0),dialog_text,BUTTON_NAMES,CHANNEL);
}
listen(integer chan, string name, key speaker, string msg)
{
integer rsl = llListFindList(BUTTON_NAMES, [msg]);
if (rsl != -1) {
if (llList2Integer(BUTTON_TYPES, rsl) == 0) {
// Inventory was requested
llGiveInventory( speaker, llList2String(BUTTON_TARGETS, rsl) );
} else {
// A URL was requested
llLoadURL( speaker, "View the selected website.", llList2String(BUTTON_TARGETS, rsl) );
}
}
}
}
state READ_NOTECARD
{
state_entry()
{
BUTTON_NAMES = [];
BUTTON_TYPES = [];
BUTTON_TARGETS = [];
prefKey = llGetInventoryKey(prefCard);
if (prefKey == NULL_KEY)
{
llOwnerSay("There appears to be no notecard with the name '"+prefCard+". Please check this prim's inventory and try again.");
}
cardQ = llGetNotecardLine(prefCard, cline); // start requesting lines
}
dataserver(key query_id, string data) {
if (query_id == cardQ && data != EOF && cline == 1) {
//a special line was read from the preference card :)
if (data == "") {
dialog_text = "Missing Message";
} else {
dialog_text = data;
}
cline = cline + 1;
cardQ = llGetNotecardLine(prefCard, cline); // request next notecard line
}
else if (query_id == cardQ && data != EOF ) {
// A line was read form our preference notecard
list line = llParseString2List(data, ["|"], []);
string name = llList2String(line, 0);
string type = llList2String(line, 1);
string target = llList2String(line, 2);
if (name == "") {
// llOwnerSay("Could not add a notecard line - missing name, probably incorrectly formatted line: "+data);
cline = cline + 1;
cardQ = llGetNotecardLine(prefCard, cline); // do not record this line, request next notecard line
return;
}
if (type != "0" && type != "1") {
llOwnerSay("Could not add a notecard line - incorrect type, probably incorrectly formatted line: "+data);
cline = cline + 1;
cardQ = llGetNotecardLine(prefCard, cline); // do not record this line, request next notecard line
return;
}
if (type == "1") {
//some error checking on the URL
if (llSubStringIndex(target, "http://") == -1) {
llOwnerSay("Could not add a notecard line - URL not formatted, must start with http:// protocol: "+data);
cline = cline + 1;
cardQ = llGetNotecardLine(prefCard, cline); // start requesting lines
return;
}
}
// This line is useful for debugging, it'll show you what it has read in as it attempts to load the preference notecard. Commented out to reduce spammage when rezzed in normal use.
//llOwnerSay("Line #"+(string)cline+": "+data);
BUTTON_NAMES = llListInsertList( BUTTON_NAMES, [name], llGetListLength(BUTTON_NAMES) );
BUTTON_TYPES = llListInsertList( BUTTON_TYPES, [type], llGetListLength(BUTTON_TYPES) );
BUTTON_TARGETS = llListInsertList( BUTTON_TARGETS, [target], llGetListLength(BUTTON_TARGETS) );
cline = cline + 1;
cardQ = llGetNotecardLine(prefCard, cline); // request next notecard line
} else if (query_id == cardQ && data == EOF) {
//we're done reading from the notecard. we could reset cline here, but just notify the owner for now
// This line is useful for debugging, it'll note that it hit the end as it attempts to load the preference notecard. Commented out to reduce spammage when rezzed in normal use.
// llOwnerSay("Reached end of dialog preference notecard. Cline: "+(string)cline);
//return to default state to continue
list_setup = 1;
state default;
}
}
}
Take a copy of this code and put it in "Child Prim". Name it "Dialog Menu (Open Source)".
README File
I mention a README file in the comments of the scipt. You can create a new notecard and drag it into the "Child Prim" contents alongside the dialog script. The README notecard describes how the preference notecard should be formatted. Here is the body of the notecard:
The Pref:Dialog file
The first line is ignored - it's a little comment line informing you to come here for more information. Do not remove this line from your file or the script will misbehave.
The second line in the file is the text to be used as a message in the dialog box that will be displayed. This message must not exceed 512 bytes and must not be empty.
The remaining lines are used to define the buttons that will be offered as options in the dialog menu.
There are 12 potential buttons for a dialog, any lines beyond 12 will be ignored. If a line is empty there will be no button for that line and a slot will remain available.
The format for the dialog option lines is the button name immediately followed by a vertical bar, immediately followed by one of the types immediately followed by another vertical bar, finally immediately followed by the target. Like this:
BUTTON_NAME|TYPE|TARGET
BUTTON_NAME:
This is the text that will appear on the button itself. Note: Button names must be unique or the second instance of a button will never be used. Button text cannot be longer then 24 bytes per button. Otherwise the script will shout an error on DEBUG_CHANNEL.
TYPES:
0 - Type zero is for an item in inventory.
1 - Type one is for a webpage URL you would like to direct someone to.
TARGET:
The target is either the name of the item in this prim's inventory that you would like to give or the URL (starting with http://) you would like to direct the user to.
The Preference Notecard
It's time to populate our prim with the actual preference file. Create a new notecard and name it "Pref:Dialog". Drag a copy of the notecard into your prim.
Just to be clear - at this point you probably want to put the dialog menu script, README notecard, and one preference notecard into the "Child Prim" object. Later you'll unlink the child prim and make copies by shift clicking and dragging a directional arrow. You can go back and link together all the needed child prims to the root prim object. This way the base files needed are duplicated with the prims and you'll just need to customize the preference files and/or add inventory as needed.
Following are some example notecards taken straight from Cylindrian's tip sign.
//See README file for details on this file
Places to purchase my music and merchandise.
CDBaby|1|http://cdbaby.com/cd/gracebuford
iTunes|1|http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewAlbum?playListId=210652752
Napster|1|http://www.napster.com/view/artist/index.html?id=12231237
MusicIsHere|1|http://musicishere.com/artists/Grace_Buford/Living_Stories
Cafe Press|1|http://www.cafepress.com/gfolky
The above preference file is for our music purchase prim. The very first line is in the format of a comment line in code to make it clear that it has no purpose other than to inform the casual reader what this file is all about. The second line is a message line - this sentence will be displayed in the dialog menu presented to the user. After that are web links. We know this because the second of three items separated by vertical pipes ("|") are all "1" meaning web link. The first item is the name of the button to be presented in a dialog menu. By clicking on each one the user would be offered a different web page where they could purchase some Cylindrian Rutabaga / Grace Buford merchandise.

Here is a preference notecard from a different prim, this one is all about finding Grace's web presence:
//See README file for details on this file
Visit me on the WWW
MySpace|1|http://www.myspace.com/gracefolk
Blog|1|http://cylindrian.wordpress.com/
The resulting dialog will have just two items in it, each of which would take the user to a different website.

In a separate prim containing the basic "Dialog Menu (Open Source)" script plus the following info in the Pref:Dialog notecard a user would be prompted to find Grace's online schedule of events:
//See README file for details on this file
Schedule of events
Calendar|1|http://www.google.com/calendar/embed?src=mas4erkfilvch2r9ierutrp5pc%40group.calendar.google.com

Give Inventory
Now what about that other dialog menu option? It just so happens that Grace's "Join group" prim uses the give inventory option. First you'd create a new child prim, add the "Dialog Menu (Open Source)" script, then ensure it has a copy of the inventory you want to give away (note: inventory is in the same prim as the script, not in the root prim).

Then you will need to add a preference notecard like this:
//See README file for details on this file
How To Join My Group
Instructions|0|Instructions - Join Cylindrian's Group
What this will do is show a button labeled "Instructions" where the action type is "0" meaning "give inventory" and the inventory that will be offered to the user is a file named "Instructions - Join Cylindrian's Group".

Mixing Options
In our examples I did not have a need to mix weblink options with give inventory options. It's OK to do that though - here is a fake example:
//See README file for details on this file
T-shirts, Virtual or Real
Cafe Press|1|http://www.cafepress.com/gfolky
Free T|0|Cylindrian Rocks T
In this case the first option would send the user to a web link where they could perhaps purchase a t-shirt for their meatspace avatar. The second link would give them an object from inventory "Cylindrian Rocks T" which may be a piece of clothing or a gift box containing clothing and other inventory.
We've pretty much wrapped up all of our scripting for this project. In the next, and probably last, entry in this series we will discuss ways in which we might customize the appearance of our sign to "pretty it up" a bit :)