Adventures In Applications

It’s been awhile since I posted any good LSL examples to this blog, so I’ll give you a fairly complicated one. Danielle wanted a kiosk in front of the Gin Rummy that would hand out “job application” notecards for any one of a number of positions. I determined that a dialog-based interface would work for that, and decided to make it configurable. So here’s the code:

// Gin Rummy Application Kiosk Script
// Erbo Evans 10/2/2006
//=============================================
// To set this up you will need to create a notecard named "_config"
// in the same object as this script.  This notecard will contain pairs of lines.
// The first line is the button label, and the second line is the name of the
// notecard to give out when the button is pressed.  Example:
//
// DJ
// DJ Application
// Host
// Host Application
// Bartender
// Bartender Application
//
integer MY_CHANNEL = -897414;
string PROMPT = "Thank you for your interest in employment at the Gin Rummy. Please select the position you are interested in:";

list s_master = [];
list s_buttons = [];
integer s_line_count;
integer s_card_count;
integer s_cur;
key s_qid;

default
{
    state_entry()
    {
        s_qid = llGetNumberOfNotecardLines("_config");
    }
    dataserver(key qid, string data)
    {
        if (qid!=s_qid)
            return;
        s_line_count = (integer)data;
        state load2;
    }
}

state load2
{
    state_entry()
    {
        s_cur = 0;
        s_qid = llGetNotecardLine("_config",0);
    }
    dataserver(key qid, string data)
    {
        if (qid!=s_qid)
            return;
        s_master += [data];
        if (++s_cur==s_line_count)
        {
            s_card_count = s_line_count / 2;
            integer i = 0;
            do
            {
                string s = llList2String(s_master,2 * i);
                s_buttons += [s];
            } while (++i<s_card_count);
            state runtime;
        }
        s_qid = llGetNotecardLine("_config",s_cur);
    }
}

state runtime
{
    state_entry()
    {
        llListen(MY_CHANNEL,"",NULL_KEY,"");
    }
    touch_start(integer n)
    {
        integer i = 0;
        do
        {
            llDialog(llDetectedKey(i),PROMPT,s_buttons,MY_CHANNEL);
        } while (++i<n);
    }
    listen(integer chan, string name, key id, string message)
    {
        if (chan!=MY_CHANNEL)
            return;
        integer i;
        do
        {
            integer n = 2 * i;
            string s = llList2String(s_master,n);
            if (s==message)
            {
                s = llList2String(s_master,n + 1);
                llGiveInventory(id,s);
                return;
            }
        } while (++i<s_card_count);
    }
}

In order for this to work, you need to have a notecard called _config in the object inventory along with the script. This notecard should contain, on alternating lines, the name to put on a button and the name of the notecard that will be delivered when you hit that button. Remember that llDialog allows you to have up to 12 buttons.

The default and load2 states are all about loading the number of lines of text in the notecard and the notecard’s actual data; the contents of the notecard are stored in the s_master list. Once all the notecard data has been loaded, the load2 state then strips out just the button names to add to the s_buttons list, which is used by llDialog. We could recompute this array on the fly every time we throw up a dialog, but doing it this way saves some time at the cost of a little extra memory, which we can afford.

After the lists are all full, the code clicks to the runtime state, which immediately starts listening on a private channel. (Channels with a number less than 0 cannot be triggered with the “/### message” syntax in chat, making them good for internal script uses like this.) Then it’s just a matter of throwing up the dialog box whenever the kiosk is touched, and responding to a message from the dialog, i.e. the name of the button that was pressed, by linearly searching the s_master list for the right button name and using the corresponding object name as an argument to llGiveInventory.

So, what could be improved in this? Well, one thing that a lot of people do with dialog boxes is that they don’t set up the actual llListen to the chat channel until the dialog box is summoned, and make that listen go away when a button is pressed, or after a timeout interval. The reason for doing it this way is to eliminate the tiny bit of lag caused by the listen. However, this comes at a slight cost of usability (since a dialog box may “time out” if a user hasn’t made a selection within the time limit), which is not generally a good thing in an object designed to interact with newbies. Plus there simply won’t be that much lag induced by listening on a single non-0 channel, and a private one at that. I could also rig the script to be sensitive to inventory changes, so that, when the configuration is changed, it could click back to the default state and reinitialize itself. But we don’t change the contents all that often (if ever), and we can just reset the script if it comes to that.

Share and enjoy! 🙂

Advertisements

Leave a comment

Filed under Scripthackery

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s