Written by Kahiro Watanabe

 

Go to part 1

While in the first part we’ve seen some generic examples of menus, submenus and how to make them browsable, the scripts were not useful at all. In this second and last part we are going to make an useful menu script that controls the light property of a prim and can limit the access to the menu. So, lets get to work!

The main menu has two buttons:

Light -> On, Off, << Back
Access -> Owner, Group, Anyone, << Back

The Light menu is very simple: it just turns on/off the light property of the prim and it has a << Back button to go back to the main menu
The Access menu limits the access to certain levels: Owner only, Group members (same group as setted in prim) and Anyone (anyone can use the menu)

Lets take a look at the script:

 

// Example made by Kahiro Watanabe, visit modernclix.info for lsl and php/mysql tutorials
// You can share this script with anyone

integer listener;  // Listener for handling different channels
integer mainMenuChannel;
integer lightChannel;
integer accessChannel;

vector green = <0.0,1.0,0.0>;

string access = "Owner"; // by default only owner can access the menu

// Function that returns a random number (used for generating a random channel)
integer randomNumber()
{
    return((integer)(llFrand(99999.0)*-1));
}

menu(key id, integer channel, string title, list buttons) // Generic menu creator
{
    llListenRemove(listener);
    listener = llListen(channel,"",id,"");
    llDialog(id,title,buttons,channel);
    llSetTimerEvent(10.0);   // if no menu button is pressed in 10 seconds the timer is triggered to kill the listener
}

// These function call the menu creator
mainMenu(key id)
{
    mainMenuChannel = randomNumber();
    menu(id,mainMenuChannel,"Select an option",["Light","Access"]);
}

lightMenu(key id)
{
    lightChannel = randomNumber();
    menu(id,lightChannel,"Select an option",["On","Off","<< Back"]);
}

accessMenu(key id)
{
    accessChannel = randomNumber();
    menu(id,accessChannel,"Select an option",["Owner","Group","Anyone","<< Back"]);
}

lightOn(vector color, float intensity, float radius, float falloff)
{
    llSetPrimitiveParams([PRIM_POINT_LIGHT, TRUE, color, intensity, radius, falloff]);
}

lightOff()
{
    llSetPrimitiveParams([PRIM_POINT_LIGHT,FALSE,ZERO_VECTOR,0.0,0.0,0.0]);
}

lightController(string message) // Function that control lights function
{
    if (message == "On")
    {
        lightOn(green,1.0,10.0,0.75);
    }
    else
    {
        lightOff();
    }
}

// Function that checks if user has permissions to use the menu
integer allowed(key id)
{
    return (id == llGetOwner() || (llDetectedGroup(0) && (access == "Group")) || (access == "Anyone"));
}

default
{

    touch_start(integer num)
    {
        key toucher = llDetectedKey(0);
        if (allowed(toucher)) // function that checks if use has permissions to access the menu
        {
            mainMenu(toucher);
        }
    }

    listen (integer channel, string name, key id, string message)
    {
        if (message == "<< Back") // lets go back to the main menu
        {
            mainMenu(id);
            return; //event stops here
        }

        // Now we need to know what channel is being listened
        if (channel == mainMenuChannel)
        {
            if (message == "Light")
            {
                lightMenu(id);
            }
            else if (message == "Access")
            {
                if (id == llGetOwner()) // Only owner cann use the "Access" menu
                {
                    accessMenu(id);
                }
                else // Show a message and show Main Menu again
                {
                    llWhisper(0,"Sorry, only owner can access to this menu");
                    mainMenu(id);
                }
            }
        }
        else if (channel == lightChannel)
        {
            lightController(message);
            lightMenu(id);
        }
        else if (channel == accessChannel)
        {
            access = message;
            llOwnerSay("Access level: " + access);
            accessMenu(id);
        }
    }

    timer()
    {
        llListenRemove(listener); // kill the listener
        llSetTimerEvent(0.0); // stop the timer
    }
}

 

As you can see, it’s very similar to the previous example, two buttons and one submenu each. The only difference is the functions triggered in the listen event. In this case the “lightController” function receives a message (“On”/”Off”) and according to that messages turns on/off the light thanks to the functions “lightOn()“/”lightOff().

There’s also a function called “allowed()” used in the touch_start event. This function returns only TRUE if:

1) Toucher is owner
OR
2) Toucher is a group member (same group as the one setted in the prim) and Acess level is “Group”
OR
3) Acess level is “Anyone” (simply let anyone use the menu)


LSL is an event oriented programming language. Unfortunately you can’t use the Object Oriented paradigm here or design an application using a Model View Controller pattern to separate the logic (functions) from the representation (menu). But in this example if you check the listen event, that is where all the menu work is done, it only does what it has to: check what channel is listening and trigerring functions, nothing else. I didn’t add the logic to turn on/off the light inside the menu logic, there’s a separate function for controlling light. The menu simply sends the message, doesn’t even check if it’s On or Off, the “lightController” function does that.