Difference between revisions of "Dialog Menus"

From Second Life Wiki
Jump to: navigation, search
m (added category: tutorials to link this page up the category chain)
m (some readability improvements)
Line 55: Line 55:
 
llDialog is picky about the lists it is fed. It will shout an error if it is unhappy about anything of the following things in a list:
 
llDialog is picky about the lists it is fed. It will shout an error if it is unhappy about anything of the following things in a list:
  
#If any element in the list is anything other than a string;<blockquote>BAD example # 1:<lsl>[1, "Green", <0,0,0>];</lsl>Only "Green" is a string in the list.</blockquote>
+
#If any element in the list is anything other than a string;<blockquote>BAD example # 1:<lsl>[1, "Green", <0.0, 0.0, 0.0>];</lsl>Only "Green" is a string in the list.</blockquote>
 
#If you attempt to feed it a list all at once with more than 12 items in the list;
 
#If you attempt to feed it a list all at once with more than 12 items in the list;
 
#If any item in the list is a blank string;<blockquote>BAD example # 2:<lsl>["", "Green", "Yellow"];</lsl></blockquote>
 
#If any item in the list is a blank string;<blockquote>BAD example # 2:<lsl>["", "Green", "Yellow"];</lsl></blockquote>
Line 83: Line 83:
 
You can ensure that all of your scripted objects have a unique chat channel with this small snippet of code:
 
You can ensure that all of your scripted objects have a unique chat channel with this small snippet of code:
  
<lsl>integer channel_dialog; // top of script in variables
+
<lsl>
 +
// global variables
 +
integer channel_dialog;
  
  channel_dialog = ( -1 * (integer)("0x" + llGetSubString((string)llGetKey(),-7,-1)) );
+
 
  //put this in state_entry() or somewhere like that.
+
//  main body of script
}
+
//  default
 +
//  {
 +
//      state_entry()
 +
//      {
 +
            channel_dialog = ( -1 * (integer)("0x" + llGetSubString((string)llGetKey(),-7,-1)) );
 +
//     }
 +
//  }
 
</lsl>
 
</lsl>
  
Line 101: Line 109:
  
 
<lsl>
 
<lsl>
  touch_start(integer total_number) {
+
    touch_start(integer num_detected)
      ToucherID = llDetectedKey(0);
+
    {
  }
+
        ToucherID = llDetectedKey(0);
 +
    }
 
</lsl>
 
</lsl>
  
Line 123: Line 132:
  
 
<lsl>
 
<lsl>
list colourchoices = ["-", "Red", "Green", "Yellow"];
+
list colorChoices = ["-", "Red", "Green", "Yellow"];
string msg = "Please make a choice.";
+
string message = "Please make a choice.";
  
 
key ToucherID;
 
key ToucherID;
integer channel_dialog;
+
integer channelDialog;
  
default {
+
default
  state_entry() {
+
{
     channel_dialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
+
    state_entry()
  }
+
     {
 
+
        channelDialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
  touch_start(integer total_number) {
+
    }
     ToucherID = llDetectedKey(0);
+
 
    llDialog(ToucherID, msg, colourchoices, channel_dialog);
+
    touch_start(integer num_detected)
  }
+
     {
 +
        ToucherID = llDetectedKey(0);
 +
 
 +
        llDialog(ToucherID, message, colorChoices, channelDialog);
 +
    }
 
}
 
}
 
</lsl>
 
</lsl>
Line 152: Line 165:
  
 
<lsl>
 
<lsl>
llListen(channel_dialog, "", ToucherID, "");
+
llListen(channelDialog, "", ToucherID, "");
 
</lsl>
 
</lsl>
  
Line 162: Line 175:
  
 
<lsl>
 
<lsl>
listen_id = llListen( channel_dialog, "", ToucherID, "");
+
    listen_id = llListen(channelDialog, "", ToucherID, "");
 
</lsl>
 
</lsl>
  
Line 170: Line 183:
  
 
<lsl>
 
<lsl>
list colourchoices = ["-", "Red", "Green", "Yellow"];
+
list colorChoices = ["-", "Red", "Green", "Yellow"];
string msg = "Please make a choice.";
+
string message = "Please make a choice.";
 +
 
 
key ToucherID;
 
key ToucherID;
integer channel_dialog;
+
integer channelDialog;
integer listen_id; // OUR NEW HANDLE
+
  
default{
+
integer listenId; // OUR NEW HANDLE
  state_entry() {
+
 
     channel_dialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
+
default
  }
+
{
 
+
    state_entry()
  touch_start(integer total_number) {
+
     {
     ToucherID = llDetectedKey(0);
+
        channelDialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
    llDialog(ToucherID, msg, colourchoices, channel_dialog);
+
    }
    listen_id = llListen( channel_dialog, "", ToucherID, ""); // OUR NEW LISTEN
+
 
  }
+
    touch_start(integer num_detected)
 +
     {
 +
        ToucherID = llDetectedKey(0);
 +
        llDialog(ToucherID, message, colorChoices, channelDialog);
 +
        listenId = llListen(channelDialog, "", ToucherID, "");// OUR NEW LISTEN
 +
    }
 
}
 
}
 
</lsl>
 
</lsl>
Line 196: Line 214:
  
 
<lsl>
 
<lsl>
  listen(integer channel, string name, key id, string choice) {
+
    listen(integer channel, string name, key id, string message)
     if (choice == "-") {
+
     {
     llDialog(ToucherID, msg, colourchoices, channel_dialog);  
+
        if (message == "-")
    }
+
//     {
    else if (choice == "Red") {
+
            llDialog(ToucherID, info, colorChoices, channelDialog);
    //do something
+
//      }
    }
+
        else if (message == "Red")
    else if (choice == "Green") {
+
//      {
    //do something
+
            ;//do something
    }
+
//      }
    else {
+
        else if (message == "Green")
    //do something else.
+
//      {
 +
            ;//do something
 +
//      }
 +
        else
 +
//      {
 +
            ;//do something else.
 +
//      }
 
     }
 
     }
  }
 
 
</lsl>
 
</lsl>
  
Line 225: Line 248:
  
 
<lsl>
 
<lsl>
list colourchoices = ["-", "Red", "Green", "Yellow"];
+
list buttons = ["-", "Red", "Green", "Yellow"];
string msg = "Please make a choice.";
+
string dialogInfo = "Please make a choice.";
 +
 
 
key ToucherID;
 
key ToucherID;
integer channel_dialog;
+
integer dialogChannel;
integer listen_id;
+
integer listenHandle;
  
default{
+
default
  state_entry() {
+
{
    channel_dialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
+
    state_entry()
  }
+
    {
 
+
        dialogChannel = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
  touch_start(integer total_number) {
+
     }
     ToucherID = llDetectedKey(0);
+
    llDialog(ToucherID, msg, colourchoices, channel_dialog);
+
    listen_id = llListen( channel_dialog, "", ToucherID, "");
+
  }
+
  
     //HERE'S OUR NEWLY ADDED LISTEN EVENT
+
     touch_start(integer num_detected)
  listen(integer channel, string name, key id, string choice) {
+
     {
     if (choice == "-") {
+
        ToucherID = llDetectedKey(0);
    llDialog(ToucherID, msg, colourchoices, channel_dialog);  
+
 
 +
        llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
 +
        listenHandle = llListen(dialogChannel, "", ToucherID, "");
 
     }
 
     }
     else if (choice == "Red") {
+
 
    //do something
+
     //HERE'S OUR NEWLY ADDED LISTEN EVENT
    }
+
    listen(integer channel, string name, key id, string message)
    else if (choice == "Green") {
+
    {
    //do something
+
        if (message == "-")
    }
+
//      {
    else {
+
            llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
    //do something else.
+
//      }
 +
        else if (message == "Red")
 +
//      {
 +
            ;//do something
 +
//      }
 +
        else if (message == "Green")
 +
//      {
 +
            ;//do something
 +
//      }
 +
        else
 +
//      {
 +
            ;//do something else.
 +
//      }
 
     }
 
     }
  }
 
 
}
 
}
 
</lsl>
 
</lsl>
Line 269: Line 302:
  
 
<lsl>
 
<lsl>
llListenRemove(listen_id);
+
    llListenRemove(listen_id);
 
</lsl>
 
</lsl>
  
Line 275: Line 308:
  
 
<lsl>
 
<lsl>
list colourchoices = ["-", "Red", "Green", "Yellow"];
+
list buttons = ["-", "Red", "Green", "Yellow"];
string msg = "Please make a choice.";
+
string dialogInfo = "Please make a choice.";
 +
 
 
key ToucherID;
 
key ToucherID;
integer channel_dialog;
+
integer dialogChannel;
integer listen_id;
+
integer listenHandle;
  
default{
+
default
  state_entry() {
+
{
    channel_dialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
+
    state_entry()
  }
+
    {
 
+
        dialogChannel = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
  touch_start(integer total_number) {
+
     }
     ToucherID = llDetectedKey(0);
+
    llDialog(ToucherID, msg, colourchoices, channel_dialog);
+
  listen_id = llListen( channel_dialog, "", ToucherID, "");
+
  }
+
  
  listen(integer channel, string name, key id, string choice) {
+
    touch_start(integer num_detected)
     if (choice == "-") {
+
     {
    llDialog(ToucherID, msg, colourchoices, channel_dialog);  
+
        ToucherID = llDetectedKey(0);
 +
 
 +
        llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
 +
        listenHandle = llListen(dialogChannel, "", ToucherID, "");
 
     }
 
     }
     else if (choice == "Red") {
+
 
         //do something
+
     //HERE'S OUR NEWLY ADDED LISTEN EVENT
         llListenRemove(listen_id); //HERE WE ARE BEING RESPONSIBLE
+
    listen(integer channel, string name, key id, string message)
    }
+
    {
    else if (choice == "Green") {
+
        if (message == "-")
        //do something
+
        {
         llListenRemove(listen_id); //HERE WE ARE BEING RESPONSIBLE
+
            llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
    }
+
         // in case we re-open the dialog, return now before removing the listener
    else {
+
            return;
//do something else.
+
        }
        llListenRemove(listen_id); //HERE WE ARE BEING RESPONSIBLE
+
 
 +
         llListenRemove(listenHandle);
 +
        listenHandle = FALSE;
 +
 
 +
        if (message == "Red")
 +
//      {
 +
            ;//do something
 +
//      }
 +
         else if (message == "Green")
 +
//      {
 +
            ;//do something
 +
//      }
 +
        else
 +
//      {
 +
            ;//do something else.
 +
//     }
 
     }
 
     }
  }
 
 
}
 
}
 
</lsl>
 
</lsl>
Line 323: Line 370:
  
 
<lsl>
 
<lsl>
llSetTimerEvent(60);
+
    llSetTimerEvent(60.0);
 
</lsl>
 
</lsl>
  
 
And a timer event that will kick in when those 60 seconds are up:
 
And a timer event that will kick in when those 60 seconds are up:
 
<lsl>
 
<lsl>
  timer() { //TIME’S UP!
+
    timer()
    llListenRemove(listen_id);
+
    {
    llWhisper(0, "Sorry. You snooze; you lose.");
+
    // stop timer
     llSetTimerEvent(0.0); //Stop the timer from being called repeatedly
+
        llSetTimerEvent((float)FALSE);
  }
+
 
 +
        llListenRemove(listenHandle);
 +
        llWhisper(PUBLIC_CHANNEL, "Sorry. You snooze; you lose.");
 +
     }
 
</lsl>
 
</lsl>
  
Line 343: Line 393:
  
 
<lsl>
 
<lsl>
list colourchoices = ["-", "Red", "Green", "Yellow"];
+
list buttons = ["-", "Red", "Green", "Yellow"];
string msg = "Please make a choice.";
+
string dialogInfo = "Please make a choice.";
 +
 
 
key ToucherID;
 
key ToucherID;
integer channel_dialog;
+
integer dialogChannel;
integer listen_id;
+
integer listenHandle;
  
default{
+
default
  state_entry() {
+
{
    channel_dialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
+
    state_entry()
  }
+
    {
 
+
        dialogChannel = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
  touch_start(integer total_number) {
+
    }
    ToucherID = llDetectedKey(0);
+
 
    llDialog(ToucherID, msg, colourchoices, channel_dialog);
+
    touch_start(integer num_detected)
    listen_id = llListen( channel_dialog, "", ToucherID, "");
+
    {
    llSetTimerEvent(60); //HERE WE SET A TIME LIMIT
+
        ToucherID = llDetectedKey(0);
  }
+
 
 +
        llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
 +
        listenHandle = llListen(dialogChannel, "", ToucherID, "");
  
  listen(integer channel, string name, key id, string choice) {
+
        llSetTimerEvent(60.0); //HERE WE SET A TIME LIMIT
    if (choice == "-") {
+
    llDialog(ToucherID, msg, colourchoices, channel_dialog);  
+
 
     }
 
     }
     else if (choice == "Red") {
+
 
         //do something
+
     listen(integer channel, string name, key id, string message)
         llListenRemove(listen_id); //HERE WE ARE BEING RESPONSIBLE
+
    {
    }
+
        if (message == "-")
    else if (choice == "Green") {
+
        {
        //do something
+
            llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
        llListenRemove(listen_id); //HERE WE ARE BEING RESPONSIBLE
+
            return;
    }
+
         }
    else {
+
 
        //do something else.
+
         llListenRemove(listenHandle);
        llListenRemove(listen_id); //HERE WE ARE BEING RESPONSIBLE
+
        listenHandle = FALSE;
 +
 
 +
        if (message == "Red")
 +
//     {
 +
            ;
 +
//      }
 +
        else if (message == "Green")
 +
//     {
 +
            ;
 +
//     }
 +
        else
 +
//     {
 +
            ;
 +
//     }
 
     }
 
     }
  }
 
  
  timer() { //TIME’S UP!
+
    timer()
    llListenRemove(listen_id);
+
    {
    llWhisper(0, "Sorry. You snooze; you lose.");
+
    // stop timer
     llSetTimerEvent(0.0); //Stop the timer from being called repeatedly
+
        llSetTimerEvent((float)FALSE);
  }
+
 
 +
        llListenRemove(listenHandle);
 +
        llWhisper(PUBLIC_CHANNEL, "Sorry. You snooze; you lose.");
 +
     }
 
}
 
}
 
</lsl>
 
</lsl>

Revision as of 15:40, 30 October 2012

In Second Life, a dialog menu is a dialog box that appears on the corner of your screen [1] when a ScriptDialog message is received.

Note: Technically the box that people get is a "Dialog Box", not a menu, but for the purpose of simplicity in this article we will refer to it as a menu.

The box has on it a message and choice buttons, as well as an ignore button.

When you press a button, the script that generated the menu acts on the choice and performs the operation that the user chose by sending a ScriptDialogReply message.

There is no way to change the actual size of the menu box, nor change its color.

In this article, we are going to build a dialog menu step by step. Advanced scripters will consider this primitive, but this article is not for them :} and it will do the job while illustrating all the key principles involved.


Components Involved in Generating a Menu

At its most basic, you have to draw on the following tools to generate a menu, and make it work:

  1. A message to appear on the dialog box;
  2. A list of menu choices;
  3. A Channel that the menu communication will happen on;
  4. The llDetectedKey() function to determine whom to present the menu to;
  5. The llDialog function to present the menu to that user;
  6. A llListen to hear what choice the user made;
  7. A Listen Event to Capture the Listen;
  8. The llListenRemove function to eventually remove the listen.
  9. A timer to make sure the listen does eventually get removed.

The Message

The message must be less than 512 bytes [2] and must not be empty. That is to say, "" won't work. If it is empty, llDialog will shout "llDialog: must supply a message" on the DEBUG_CHANNEL. If it is greater than or equal to 512 bytes, it shouts (again on the debug channel): "llDialog: message too long, must be less than 512 characters". In both instances, the dialog box will not be created for avatar.

If the text of your message requires more than eight lines to display it, a vertical scroll bar will appear in the dialog box.

The message text can be formatted somewhat using "\n" (for newline) and "\t" (for tab). You can do nothing, though, to influence the font face, size or weight.


The List of Choices

An ignore button is generated automatically at the bottom-right hand corner of the menu; you do nothing to generate it, and can do nothing to make it NOT appear.

The dialog box can only present a maximum of 12 buttons to the user.

Note: it's okay to have more than 12 choices you want to give the user; you just need a way to give them MORE and BACK buttons. We'll cover options for this later.

You feed the choices to the dialog menu as a list.

Example: <lsl> list colourchoices = ["Red", "Green", "Yellow"]; </lsl>

llDialog is picky about the lists it is fed. It will shout an error if it is unhappy about anything of the following things in a list:

  1. If any element in the list is anything other than a string;
    BAD example # 1:<lsl>[1, "Green", <0.0, 0.0, 0.0>];</lsl>Only "Green" is a string in the list.
  2. If you attempt to feed it a list all at once with more than 12 items in the list;
  3. If any item in the list is a blank string;
    BAD example # 2:<lsl>["", "Green", "Yellow"];</lsl>

All that aside, it IS okay to feed the menu a completely blank list. (When fed a blank list, the menu will generate just an ["OK"] button (along with the ever present Ignore button too, of course.)

<lsl> list colourchoices = []; </lsl>

Tip! Often people like to create a place holder button to line buttons on menus up in a certain way. "-" and "(-)" are among ways that are commonly used. But don't use ""!

Let's make our sample list now:

<lsl> list colourchoices = ["-", "Red", "Green", "Yellow"]; </lsl>


Menu Communication Channel

Menus use Chat to work, and Chat uses channels. If you need to review these concepts, see the entry on Chat.

Negative channels are popular for dialog menu communications because the client is unable to chat directly on those channels.

You can ensure that all of your scripted objects have a unique chat channel with this small snippet of code:

<lsl> // global variables integer channel_dialog;


// main body of script // default // { // state_entry() // {

           channel_dialog = ( -1 * (integer)("0x" + llGetSubString((string)llGetKey(),-7,-1)) );

// } // } </lsl>

This generates a negative number from the last 7 digits of the UUID of the object.

Because this snippet draws on the unique UUID of the object to form a channel, it ensures that even if two of your products are operating side-by-side, they won't get mixed up listening to each other's messages.

Detecting the User

When presenting a menu, you obviously don't want to present it to every person who happens to be within a 10-block radius. You want to present it to the person who invoked it.

Most often, users are invited to invoke a menu via touching an object. When someone has touched an object, you can get their UUID (which is what you need for presenting the menu to them). You use the llDetectedKey() function in a touch event to do this.

<lsl>

   touch_start(integer num_detected)
   {
       ToucherID = llDetectedKey(0);
   }

</lsl>

Click here if you wish more information on the Touch_start event.

Tip! You could also get a user's UUID from when they do things such as speak in chat, or bump into something, or sit on something. And there may be times when that is appropriate. But touch is not only the most common way, but also perhaps the surest way to ensure that the person actually wanted a dialog menu from you.

Presenting the Dialog Menu

We now have all the components we need to present the menu.

This is done by the llDialog function. It builds a dialog box from the message and button choices you feed it, and presents that dialog box to the avatar UUID that you supplied to it.

Tip! Reminder to non-Americans; Dialog has to be spelt the American way.

We'll start putting our example together now:[3]


<lsl> list colorChoices = ["-", "Red", "Green", "Yellow"]; string message = "Please make a choice.";

key ToucherID; integer channelDialog;

default {

   state_entry()
   {
       channelDialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
   }
   touch_start(integer num_detected)
   {
       ToucherID = llDetectedKey(0);
       llDialog(ToucherID, message, colorChoices, channelDialog);
   }

} </lsl>

We have now presented choices to the user. But next, we need a way to know what s/he chose.

Listening to Hear the User's Choice

You know what a user chose by listening for the choice. The choice they make will be chatted on the channel that you specified as part of the llDialog parameters.

So, we'll open up a listen on that channel. And, we'll make sure that on that channel, we hear only the choice our user made.

You do that like this:

<lsl> llListen(channelDialog, "", ToucherID, ""); </lsl>

(For full specs on how to use this function, see the entry on llListen.)

This listen will listen only to that user (ToucherID) on our channel.

Now, because we're going to be responsible SL citizens later on, and remove that listen, we're going to have that listen assigned to a "handle", so that it's easy to kill it off just by killing off the handle.

<lsl>

   listen_id = llListen(channelDialog, "", ToucherID, "");

</lsl>

(Oh, and we have to make that listen handle a variable by doing this: integer listen_id; )

Here's our example now.

<lsl> list colorChoices = ["-", "Red", "Green", "Yellow"]; string message = "Please make a choice.";

key ToucherID; integer channelDialog;

integer listenId; // OUR NEW HANDLE

default {

   state_entry()
   {
       channelDialog = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
   }
   touch_start(integer num_detected)
   {
       ToucherID = llDetectedKey(0);
       llDialog(ToucherID, message, colorChoices, channelDialog);
       listenId = llListen(channelDialog, "", ToucherID, "");// OUR NEW LISTEN
   }

} </lsl>

A Listen Event to Capture the Listen

Listening isn't the same as hearing. So, we need to add a "listen" event.

In it, we start evaluating what got returned to us as a choice, and we process it.

<lsl>

   listen(integer channel, string name, key id, string message)
   {
       if (message == "-")

// {

           llDialog(ToucherID, info, colorChoices, channelDialog);

// }

       else if (message == "Red")

// {

           ;//do something

// }

       else if (message == "Green")

// {

           ;//do something

// }

       else

// {

           ;//do something else.

// }

   }

</lsl>

We just threw a lot at you above. Don't panic, though, here are some notes on it:

  • Some scripters may refer to the "string choice" seen above in the listen event as string msg, string message, etc. It doesn't matter what it's referred to -- string banana would work equally well, and you can call it what you want. Whatever you call it, it is the parameter you evaluate to see what the user chose.
  • Above, we use if / else if etc to figure out what the user chose. [4]
  • If our budding Einstein user chooses the "-" placeholder, we cycle the dialog menu back to him/her with another llDialog. Note that because we had defined all the parameters as variables, it is easy for us to keep on shooting the menu back to them until they get tired and pick something we can use.
  • Note that it's choice== NOT choice= In evaluating dialog box choices, this is one of the more common mistakes that tired fingers can make, and tired eyes can miss. In theory, the compiler should catch this when you go to save the script, but there are situations when it won't always. So if you're not picking up the answer that you think you should, check your == signs.
  • When you have a really elaborate set of choices going, you may need to step through many else if's. There is a limit, however, to how many else if's you can have. When you reach the limit and go to save your script, it will give you -- unhelpfully -- a "syntax" error that will have you tearing your hair out looking everywhere else in the script but here. Generally, you are considered safe having 23 or under chained together -- but it's not unknown for the error to occur with fewer. Watch out for this.
  • And finally, note that there is NO WAY to know if the user hit the ignore button to make the menu go away.


Now, let's update our example by bringing the above listen event into it:

<lsl> list buttons = ["-", "Red", "Green", "Yellow"]; string dialogInfo = "Please make a choice.";

key ToucherID; integer dialogChannel; integer listenHandle;

default {

   state_entry()
   {
       dialogChannel = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
   }
   touch_start(integer num_detected)
   {
       ToucherID = llDetectedKey(0);
       llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
       listenHandle = llListen(dialogChannel, "", ToucherID, "");
   }
   //HERE'S OUR NEWLY ADDED LISTEN EVENT
   listen(integer channel, string name, key id, string message)
   {
       if (message == "-")

// {

           llDialog(ToucherID, dialogInfo, buttons, dialogChannel);

// }

       else if (message == "Red")

// {

           ;//do something

// }

       else if (message == "Green")

// {

           ;//do something

// }

       else

// {

           ;//do something else.

// }

   }

} </lsl>

Remove the listen

Because you are a responsible SL scripter, you want to remove the listen when you no longer need it.

And because we were clever enough to assign it to a listen handle in advance, it's really easy to do:

Just:

<lsl>

   llListenRemove(listen_id);

</lsl>

Time to update the example by adding that into our listen event (you will see it at the end):

<lsl> list buttons = ["-", "Red", "Green", "Yellow"]; string dialogInfo = "Please make a choice.";

key ToucherID; integer dialogChannel; integer listenHandle;

default {

   state_entry()
   {
       dialogChannel = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
   }
   touch_start(integer num_detected)
   {
       ToucherID = llDetectedKey(0);
       llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
       listenHandle = llListen(dialogChannel, "", ToucherID, "");
   }
   //HERE'S OUR NEWLY ADDED LISTEN EVENT
   listen(integer channel, string name, key id, string message)
   {
       if (message == "-")
       {
           llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
       // in case we re-open the dialog, return now before removing the listener
           return;
       }
       llListenRemove(listenHandle);
       listenHandle = FALSE;
       if (message == "Red")

// {

           ;//do something

// }

       else if (message == "Green")

// {

           ;//do something

// }

       else

// {

           ;//do something else.

// }

   }

} </lsl>

The reason we have put these inside each 'else if' statement (and the 'else' one too) is because when a user presses the "-" button in the dialog, the dialog is repeated, and we don't want to kill the listen event just then otherwise when a user presses a button in the dialog again, nothing will happen as the listen has been killed.

If we were to have just buttons in here and no other dialog calls, we would put the llListenRemove function underneath all 'else if' statements.

A timer to make sure the listen does get removed

If the user hits the ignore button, or crashes while using a menu, or simply walks or tp's away, your listen could go on forever.

So let's set a time limit after which the listen will time out:

<lsl>

   llSetTimerEvent(60.0);

</lsl>

And a timer event that will kick in when those 60 seconds are up: <lsl>

   timer()
   {
   //  stop timer
       llSetTimerEvent((float)FALSE);
       llListenRemove(listenHandle);
       llWhisper(PUBLIC_CHANNEL, "Sorry. You snooze; you lose.");
   }

</lsl>

To learn more about timers, see here: Timer


Completed Example

Let's add those two timer bits to our example, to make the example complete:

<lsl> list buttons = ["-", "Red", "Green", "Yellow"]; string dialogInfo = "Please make a choice.";

key ToucherID; integer dialogChannel; integer listenHandle;

default {

   state_entry()
   {
       dialogChannel = ( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)) );
   }
   touch_start(integer num_detected)
   {
       ToucherID = llDetectedKey(0);
       llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
       listenHandle = llListen(dialogChannel, "", ToucherID, "");
       llSetTimerEvent(60.0); //HERE WE SET A TIME LIMIT
   }
   listen(integer channel, string name, key id, string message)
   {
       if (message == "-")
       {
           llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
           return;
       }
       llListenRemove(listenHandle);
       listenHandle = FALSE;
       if (message == "Red")

// {

           ;

// }

       else if (message == "Green")

// {

           ;

// }

       else

// {

           ;

// }

   }
   timer()
   {
   //  stop timer
       llSetTimerEvent((float)FALSE);
       llListenRemove(listenHandle);
       llWhisper(PUBLIC_CHANNEL, "Sorry. You snooze; you lose.");
   }

} </lsl>

Comments

We have completed a simple, primitive dialog menu script. There are more efficient ways to code a dialog menu, but they are far more complex and not for the level of scripter that this document is aimed at. The example above may do the job for your first few menus, until your needs start to grow.

You will eventually ask questions such as how do I have one menu button bring up another different menu; in presenting more than 12 choices on two or more dialog boxes, how do I make forward and back buttons (and process those responses,) etc.

To do these things, you may wish to consider looking at the script sample for learners here SimpleDialogMenuSystem. It is commented with straightforward usage steps, and you shouldn't run into much trouble with it if you just follow those steps.


User-created utility functions

•  SimpleDialogMenuSystem Multi-paging, next and previous pages
•  Dialog NumberPad Lets a dialog menu act as a number pad
•  Texture Menu Management Builds a list of textures in a prim, presents choices to user, and displays choices on that prim
•  Dialog Control Many advanced features — but complex
•  Dialog Menus Control Many advanced features — but complex

Footnotes

  1. ^ There are also hud prim-based items that one could call menu systems, but this article concerns itself with Second Life's native dialog system.
  2. ^ It is problematic to give you with any certainly how many characters that is, but you should be able to get at least a paragraph in, if you really have that much to say in a menu message!
  3. ^ In this example, we really don't need to capture and store the UUID of the toucher -- we could just use it right away. But as there will be many instances in which you want to pass the ToucherID along to other menus, or other scripts, we're showing you how to in this example. As well, we could just type the list of choices right into the llDialog parameters, but often you'll draw on them from other sources, such as a notecard, for instance.
  4. ^ In a listen event in a dialog menu situation, the "key id" part gives the UUID of the person who clicked a button. But, in this example, there is no need to try to further filter the listen event with "ifs" evaluating to see if that person was our ToucherID. We did that when we set up the listen. To do the further test would just waste script memory.