Difference between revisions of "LSL Protocol/Cool Hud Protocol"

From Second Life Wiki
Jump to navigation Jump to search
 
(34 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Back to [[LSL Protocol]]
==What is the Cool Hud ?==
==What is the Cool Hud ?==


The Cool Hud is a small, unobstrusive HUD which was designed by {{User|Henri Beauchamp}} to trigger the menus of his Cool Products.
The Cool Hud is a small, unobstrusive HUD which was designed by {{User|Henri Beauchamp}} to trigger the menus of his Cool Products.


It can be used to control up to 5 different scripted items, each being associated with one of its buttons. When pressing a button of the HUD, the blue menu of the corresponding item is pulled down.
It can be used to control up to 15 (for v3.20) different scripted items, each being associated with one of its buttons. When pressing a button of the HUD, the menu of the corresponding item is pulled down.
The Cool Hud was originally designed to control scripted attachments, but nothing prevents you from using it with items rezed in-world (note however that the avatar wearing the HUD will then have to be within 20m of the item in order to be able to control the latter via the HUD).
The Cool Hud was originally designed to control scripted attachments, but nothing prevents you from using it with items rezed in-world (note however that the avatar wearing the HUD will then have to be within 20m of the item in order to be able to control the latter via the HUD).


Unlike specialized HUDs, the Cool Hud may work with any scripted object which accepts commands on a private channel, provided the specifications of the protocol described here are followed.
Unlike specialized HUDs, the Cool Hud may work with any scripted object which accepts commands on a private channel, provided the specifications of the protocol described here are followed.


The Cool Hud itself is free and freely redistributable (it got the copy-ok and transfer-ok permissions). You can get a Cool Hud from the [http://slurl.com/secondlife/Ahndang/245/137/49/?img=http%3A//sldev.free.fr/CoolProductsBurningIceSmall.png&title=Cool%20Shop&msg=Cool%20Products%20home Cool Shop], in the freebies corner.
The Cool Hud itself is free (like in "free beer") and freely redistributable (it got the copy-ok and transfer-ok permissions). You can get a Cool Hud from the [http://slurl.com/secondlife/Hunburgh/61/153/54/?img=http%3A//sldev.free.fr/CoolProductsBurningIceSmall.png&title=Cool%20Shop&msg=Cool%20Products%20home Cool Shop], in the freebies corner, or from the [https://marketplace.secondlife.com/p/Cool-Hud-v391/1508192 Marketplace]. Reselling the Cool Hud is forbidden, please contact {{User|Henri Beauchamp}} if someone attempts to or did sell a Cool Hud to you.


Alternatively and using the specifications below, you may design your own, compatible HUD.
Alternatively and using the specifications below, you may design your own, compatible HUD.


Over time, the Cool Hud functionalities were expanded, and the v2.00 specifications described here provide a relay messaging system between items declared in the Cool Hud.
Over time, the Cool Hud functionalities were expanded, and the v2.00 specifications described here provide a relay messaging system between items declared in the Cool Hud.
The Cool Hud v2.00 also includes a RestrainedLife relay (see [[LSL_Protocol/Restrained_Life_Relay/Specification]]), for which the v2.00 protocol also provides a command to declare trusted dominants.
The Cool Hud v2.xx also includes a RestrainedLove relay (see [[LSL_Protocol/Restrained_Love_Relay/Specification]]), for which the v2.00 protocol also provides a command to declare trusted dominants.


==Requirements==
==Requirements==


The items designed to be compatible with the Cool Hud must:
The items designed to be compatible with the Cool Hud must:
* Permanently listen to a private command channel.
* Permanently listen to a private command channel. The channel number may be any positive or negative integer.
* Provide a command to pull down their main menu.
* Provide a command to pull down their main menu via this command channel.
* Provide a texture UUID for use in the associated button of the Cool Hud. The texture shall be a square one and does not need to be larger than 128x128 pixels. It is better if it does not use any transparency (i.e. if it got no alpha channel).
* Provide a texture UUID for use in the associated button of the Cool Hud. The texture shall be a square one and does not need to be larger than 128x128 pixels. It is better if it does not use any transparency (i.e. if it got no alpha channel).
* Declare themselves to the Cool Hud on rezing and on initialization of their scripts (see below).
* Declare themselves to the Cool Hud on rezing and on initialization of their scripts (see below).
* Either recognize or silently reject relayed commands from the Cool Hud.
* Either recognize or silently reject relayed commands from the Cool Hud.
 


==Communications==
==Communications==
Line 36: Line 37:
==Basic Protocol==
==Basic Protocol==


* On rezing (on_rez event) and on initializing (state_entry), the compatible item must declare itself to the Cool Hud. It does so by sending the following parameters on the Cool Hud channel as a CSV list: Id name, Texture UUID, Command channel, Command to send to pull down the menu
* On rezing (on_rez event) and on initializing (state_entry), the compatible item must declare itself to the Cool Hud. It does so by sending the following parameters on the Cool Hud channel as a CSV list: Id name (*), Texture UUID, Command channel, Command to send to pull down the menu
* Whenever the Cool Hud receives such a declaration message, it first checks to see if a corresponding button already exists, by comparing the Id name with the ones it already registered. If the name already exists, then the Cool Hud refreshes the associated data (texture, command channel and command) with the newly received one. If the name was unknown so far, and provided it still got free slots (unaffected buttons), the Cool Hud "adds" the corresponding button, using the texture which UUID was passed in the declaration as the button texture and storing the associated data for later use.
(*) Please note that for the Id name parameter, all names starting with "Cool " and ending with " Hud" (i.e. "Cool Whatever Here Hud") are reserved for Cool Products (the Cool Hud may use different, proprietary protocols with devices registering themselves with this form of Id name)
* Whenever the Cool Hud receives such a declaration message, it first checks to see if a corresponding button already exists, by comparing the Id name with the ones it already registered. If the name already exists, then the Cool Hud refreshes the associated data (texture, command channel and menu pull down command) with the newly received one. If the name was unknown so far, and provided it still got free slots (unaffected buttons), the Cool Hud "adds" (renders visible) the corresponding button, using the texture which UUID was passed in the declaration as the button texture and storing the associated data for later use.
* Whenever a button of the Cool Hud is pressed, the Cool Hud renames itself to take the associated Id name, then it emits (with a [[llSay]]()) the associated command on the associated command channel. Once this is done, it renames itself back to its original name.
* Whenever a button of the Cool Hud is pressed, the Cool Hud renames itself to take the associated Id name, then it emits (with a [[llSay]]()) the associated command on the associated command channel. Once this is done, it renames itself back to its original name.
* On receiving the command on its command channel, the compatible item should check the name of the sender and compare it with the id name, then if equal, it should take the appropriate action for the command it receives.
* On receiving the command on its command channel, the compatible item should check that the sender belongs to the same owner as itself, then if equal, it should take the appropriate action for the command it receives (it could also check for the name of the sender which is the Id Name it registered with the Cool Hud).


===Implementation example for a compatible item===
===Implementation example for a compatible item===
<lsl>
<source lang="lsl2">
integer MyPrivateChannel = 42;
integer MyPrivateChannel = 42;
string IdName = "Kilroy Was Here HUD";
string IdName = "Kilroy Was Here HUD";
Line 84: Line 86:


     listen(integer chan, string name, key id, string message) {
     listen(integer chan, string name, key id, string message) {
         if (llGetOwnerKey(id) != llGetOwner()) {
         if (llGetOwnerKey(id) == llGetOwner()) {
            // if the object/agent does not belong to us, ignore.
             // Parse commands sent by objects/agents belonging
            return;
             // to us only.
        }
        if (id == llGetOwner() || name == IdName) {
             // if the command was sent by our avatar or an object
             // with the same name as the IdName we declared to the
            // Cool Hud, then accept the command and parse it.
             Parse(message);
             Parse(message);
         }
         }
     }
     }
}
}
</lsl>
</source>
&nbsp;


==Extended Protocol==
==Extended Protocol==
Line 116: Line 113:
===Dominants declaration===
===Dominants declaration===


Your scripted objects may declare trusted dominants to the Cool Hud for use with its built-in RestrainedLife relay. The objects owned by the trusted dominants are also trusted by the RestrainedLife relay when the latter is in "Auto-accept" mode, which means that in this mode no confirmation will be asked by the relay to its user when interacting with these trusted objects.
Your scripted objects may declare trusted dominants to the Cool Hud for use with its built-in RestrainedLove relay. The objects owned by the trusted dominants are also trusted by the RestrainedLove relay when the latter is in "Auto-accept" mode, which means that in this mode no confirmation will be asked by the relay to its user when interacting with these trusted objects.


The general syntax for this command is:
The general syntax for this command is:
Line 123: Line 120:


Example:
Example:
<lsl>
<source lang="lsl2">
key MyPrimaryDominant;
key MyPrimaryDominant;
list MySecondaryDominants; // Must contains a list of keys (avatar UUIDs)
list MySecondaryDominants; // Must contain a list of keys (avatar UUIDs)


RegsisterPrimaryDominant() {
RegisterPrimaryDominant() {
     llWhisper(-888888, "%dominant;" + (string)MyPrimaryDominant);
     llWhisper(-888888, "%dominant;" + (string)MyPrimaryDominant);
}
}


RegsisterSecondaryDominants() {
RegisterSecondaryDominants() {
     llWhisper(-888888, "%dominant;" + llList2CSV(MySecondaryDominants));
     llWhisper(-888888, "%dominant;" + llList2CSV(MySecondaryDominants));
}
}
</lsl>
</source>


By calling RegsisterPrimaryDominant(), the item will register the primary dominant, while by calling RegsisterSecondaryDominants() it will register all the secondary dominants to the Cool Hud.
By calling RegisterPrimaryDominant(), the item will register the primary dominant, while by calling RegisterSecondaryDominants() it will register all the secondary dominants to the Cool Hud.


Note that the Cool Hud will not make any difference between the two kinds of dominants in the above example: it simply considers all of them as trusted persons.
Note that the Cool Hud will not make any difference between the two kinds of dominants in the above example: it simply considers all of them as trusted persons.


===Messages relaying===
===Messages relaying===
Line 160: Line 156:
* arousal 2: avatar highly aroused.
* arousal 2: avatar highly aroused.
* arousal 3: orgasm reached.
* arousal 3: orgasm reached.
The "hide" and "show" commands are also implemented on some attachments so to allow hiding or showing them, depending on what other attachment is worn or showing (for example, a chastity belt will use a "hide genitals" command to force the hiding of any worn genitals). Examples:
* hide genitals
* show genitals
* hide nipples
* show nipples
Finally, the "status" command is also used to query the current state of a particular type of attachment and to allow syncing them with each others. Examples:
* status genitals
* status nipples
* status chastity
* status diaper
to which the attachments shall reply with either their state of arousal or with the last hide/show command they emitted.
&nbsp;


So, for a scripted attachment sensitive to arousal (let's say nipples, for example), you could use some code like:
So, for a scripted attachment sensitive to arousal (let's say nipples, for example), you could use some code like:


<lsl>
<source lang="lsl2">
integer MyPrivateChannel = 42;
integer MyPrivateChannel = 42;
string IdName = "My Nipples HUD";
string IdName = "My Nipples HUD";
Line 173: Line 183:
     // We send the data to the Cool Hud.
     // We send the data to the Cool Hud.
     llWhisper(-888888, llList2CSV([ IdName, HudButtonTexture, MyPrivateChannel, MenuCommand ]));
     llWhisper(-888888, llList2CSV([ IdName, HudButtonTexture, MyPrivateChannel, MenuCommand ]));
    // Let's query the status of any worn genitals attachments.
    llWhisper(-888888, "%relay;status genitals");
}
}


Line 209: Line 221:
Parse(string command) {
Parse(string command) {
     command = llToLower(command);
     command = llToLower(command);
     if (command == "normal") {
     if (command == MenuCommand) {
        Menu();
    } else if (command == "normal") {
         Normal(TRUE);
         Normal(TRUE);
     } else if (command == "stiff") {
     } else if (command == "stiff") {
Line 220: Line 234:
         }
         }
         string message = llList2String(tokens, 1);
         string message = llList2String(tokens, 1);
        if (llSubStringIndex(message, "arousal") != 0) {
            // Not an arousal message... just ignore it then
            return;
        }
         if (message == "arousal 0") {
         if (message == "arousal 0") {
             // Not aroused. Shrink the nipples down.
             // Not aroused. Shrink the nipples down.
             Normal(FALSE);
             Normal(FALSE);
         } else {
         } else if (llSubStringIndex(message, "arousal") == 0) {
             // All the other arousal messages mean we are aroused,
             // All the other arousal messages mean we are aroused,
             // so make the nipples stiff.
             // so make the nipples stiff.
             Stiff(FALSE);
             Stiff(FALSE);
        } else if (message == "hide nipples") {
            // Another attachment wants to hide us.
            llSetLinkAlpha(LINK_SET, 0.0, ALL_SIDES);
        } else if (message == "show nipples") {
            // Another attachment wants to show us.
            llSetLinkAlpha(LINK_SET, 1.0, ALL_SIDES);
        } else if (message == "status nipples") {
            // Report our current status of arousal.
            message = "%relay;arousal ";
            if (Hard) {
                message += "1";
            } else {
                message += "0";
            }
            llWhisper(-888888, message);
         }
         }
     }
     }
Line 250: Line 275:
     touch_start(integer n) {
     touch_start(integer n) {
         if (llDetectedKey(0) == llGetOwner()) {
         if (llDetectedKey(0) == llGetOwner()) {
            // Pull down the menu on touch for our owner only.
             Menu();
             Menu();
         }
         }
Line 255: Line 281:


     listen(integer chan, string name, key id, string message) {
     listen(integer chan, string name, key id, string message) {
         if (llGetOwnerKey(id) != llGetOwner()) {
         if (llGetOwnerKey(id) == llGetOwner()) {
             // if the object/agent does not belong to us, ignore.
             // Parse messages sent by objects/agents belonging to us.
            return;
        }
        if (name == IdName && message == MenuCommand) {
            // This is the Cool Hud asking to pull down the menu.
            Menu();
        } else {
             Parse(message);
             Parse(message);
         }
         }
     }
     }
}
}
</lsl>
</source>

Latest revision as of 14:12, 3 March 2022

Back to LSL Protocol

What is the Cool Hud ?

The Cool Hud is a small, unobstrusive HUD which was designed by Henri Beauchamp to trigger the menus of his Cool Products.

It can be used to control up to 15 (for v3.20) different scripted items, each being associated with one of its buttons. When pressing a button of the HUD, the menu of the corresponding item is pulled down. The Cool Hud was originally designed to control scripted attachments, but nothing prevents you from using it with items rezed in-world (note however that the avatar wearing the HUD will then have to be within 20m of the item in order to be able to control the latter via the HUD).

Unlike specialized HUDs, the Cool Hud may work with any scripted object which accepts commands on a private channel, provided the specifications of the protocol described here are followed.

The Cool Hud itself is free (like in "free beer") and freely redistributable (it got the copy-ok and transfer-ok permissions). You can get a Cool Hud from the Cool Shop, in the freebies corner, or from the Marketplace. Reselling the Cool Hud is forbidden, please contact Henri Beauchamp if someone attempts to or did sell a Cool Hud to you.

Alternatively and using the specifications below, you may design your own, compatible HUD.

Over time, the Cool Hud functionalities were expanded, and the v2.00 specifications described here provide a relay messaging system between items declared in the Cool Hud. The Cool Hud v2.xx also includes a RestrainedLove relay (see LSL_Protocol/Restrained_Love_Relay/Specification), for which the v2.00 protocol also provides a command to declare trusted dominants.

Requirements

The items designed to be compatible with the Cool Hud must:

  • Permanently listen to a private command channel. The channel number may be any positive or negative integer.
  • Provide a command to pull down their main menu via this command channel.
  • Provide a texture UUID for use in the associated button of the Cool Hud. The texture shall be a square one and does not need to be larger than 128x128 pixels. It is better if it does not use any transparency (i.e. if it got no alpha channel).
  • Declare themselves to the Cool Hud on rezing and on initialization of their scripts (see below).
  • Either recognize or silently reject relayed commands from the Cool Hud.

 

Communications

They are bidirectional but performed on distinct channels.

  • The Cool Hud permanently listens on channel -888888 and receives commands on this channel only.
  • The compatible item may listen on any private channel number of its own, and must declare this channel to the Cool Hud (see below). The Cool Hud will then use this private channel to send commands to the item.


Basic Protocol

  • On rezing (on_rez event) and on initializing (state_entry), the compatible item must declare itself to the Cool Hud. It does so by sending the following parameters on the Cool Hud channel as a CSV list: Id name (*), Texture UUID, Command channel, Command to send to pull down the menu

(*) Please note that for the Id name parameter, all names starting with "Cool " and ending with " Hud" (i.e. "Cool Whatever Here Hud") are reserved for Cool Products (the Cool Hud may use different, proprietary protocols with devices registering themselves with this form of Id name)

  • Whenever the Cool Hud receives such a declaration message, it first checks to see if a corresponding button already exists, by comparing the Id name with the ones it already registered. If the name already exists, then the Cool Hud refreshes the associated data (texture, command channel and menu pull down command) with the newly received one. If the name was unknown so far, and provided it still got free slots (unaffected buttons), the Cool Hud "adds" (renders visible) the corresponding button, using the texture which UUID was passed in the declaration as the button texture and storing the associated data for later use.
  • Whenever a button of the Cool Hud is pressed, the Cool Hud renames itself to take the associated Id name, then it emits (with a llSay()) the associated command on the associated command channel. Once this is done, it renames itself back to its original name.
  • On receiving the command on its command channel, the compatible item should check that the sender belongs to the same owner as itself, then if equal, it should take the appropriate action for the command it receives (it could also check for the name of the sender which is the Id Name it registered with the Cool Hud).

Implementation example for a compatible item

integer MyPrivateChannel = 42;
string IdName = "Kilroy Was Here HUD";
string MenuCommand = "menu";
key HudButtonTexture = "d0aff1a0-c6b2-be47-1c61-f32d18d45f0c";

RegisterHud() {
    // We send the data to the Cool Hud. Note that llWhisper is suitable for attachments, but
    // not really for in-world items.
    llWhisper(-888888, llList2CSV([ IdName, HudButtonTexture, MyPrivateChannel, MenuCommand ]));
}

Menu() {
    llDialog(llGetOwner(), "Kilroy Was Here !", [ "OK" ], MyPrivateChannel);
}

Parse(string command) {
    if (command == MenuCommand) {
        // Pull down the menu.
        Menu();
    } else if (command == "reset" || command == "init") {
        // Just an example of another, user command
        // (here: /42reset or /42init to reset the script).
        llResetScript();
    }
}

default {
    state_entry() {
        // Open my private command channel.
        llListen(MyPrivateChannel, "", NULL_KEY, "");
        // Register ourselves with the Cool Hud
        RegisterHud();
        llOwnerSay("Ready.");
    }

    on_rez(integer param) {
        // Register ourselves with the Cool Hud on rezing.
        RegisterHud();
    }

    listen(integer chan, string name, key id, string message) {
        if (llGetOwnerKey(id) == llGetOwner()) {
            // Parse commands sent by objects/agents belonging
            // to us only.
            Parse(message);
        }
    }
}

 

Extended Protocol

Over time, some extensions have been added to the above protocol, in the form of commands sent to the Cool Hud on its private channel (-888888). These commands are to be sent in the form:

%command;parameters

with:

  • "command": the name of the Cool Hud command.
  • "parameters": the parameters for this command, which must not contain any semi-colon (";").

Note also that there shall be no space between the command and the semi-colon (i.e. "%command ;parameters" is not valid).

It is not necessary for the item issuing these commands to be registered with the Cool Hud, but they however must belong to the same owner as the Cool Hud to be taken into account and acted upon.

As of its v2.00, the Cool Hud accepts the following extended commands:

Dominants declaration

Your scripted objects may declare trusted dominants to the Cool Hud for use with its built-in RestrainedLove relay. The objects owned by the trusted dominants are also trusted by the RestrainedLove relay when the latter is in "Auto-accept" mode, which means that in this mode no confirmation will be asked by the relay to its user when interacting with these trusted objects.

The general syntax for this command is:

%dominant;avatar1-uuid[,avatar2-uuid[,avatar3-uuid ...]]

Example:

key MyPrimaryDominant;
list MySecondaryDominants; // Must contain a list of keys (avatar UUIDs)

RegisterPrimaryDominant() {
     llWhisper(-888888, "%dominant;" + (string)MyPrimaryDominant);
}

RegisterSecondaryDominants() {
     llWhisper(-888888, "%dominant;" + llList2CSV(MySecondaryDominants));
}

By calling RegisterPrimaryDominant(), the item will register the primary dominant, while by calling RegisterSecondaryDominants() it will register all the secondary dominants to the Cool Hud.

Note that the Cool Hud will not make any difference between the two kinds of dominants in the above example: it simply considers all of them as trusted persons.

Messages relaying

It is possible for the Cool Hud to relay messages to the items which are registered with it. The advantage of using the Cool Hud for such a relay, is that any item can broadcast a message to other items for which it ignores what are their command channel. It avoids having to use one more permanent listener on a fixed private channel in all the items. The generic syntax for this extended command is:

%relay;message

where "message" must not contain any semi-colon.

On receiving such a command, the Cool Hud relays it to all the items it got registered, on their respective private command channels, and in the following form:

%relay;message;originator object UUID

Note however that the Cool Hud does not rename itself (with the Id name) before relaying the command to each item like it does when one of its buttons is pressed.

This extended command is so far used by the Cool Products to relay arousal messages (for example between scripted genitals and scripted nipples), with the following conventions for the messages:

  • arousal 0: avatar not aroused.
  • arousal 1: avatar lightly aroused.
  • arousal 2: avatar highly aroused.
  • arousal 3: orgasm reached.

The "hide" and "show" commands are also implemented on some attachments so to allow hiding or showing them, depending on what other attachment is worn or showing (for example, a chastity belt will use a "hide genitals" command to force the hiding of any worn genitals). Examples:

  • hide genitals
  • show genitals
  • hide nipples
  • show nipples

Finally, the "status" command is also used to query the current state of a particular type of attachment and to allow syncing them with each others. Examples:

  • status genitals
  • status nipples
  • status chastity
  • status diaper

to which the attachments shall reply with either their state of arousal or with the last hide/show command they emitted.  

So, for a scripted attachment sensitive to arousal (let's say nipples, for example), you could use some code like:

integer MyPrivateChannel = 42;
string IdName = "My Nipples HUD";
string MenuCommand = "menu";
key HudButtonTexture = "e0aff1a0-c6b2-be47-1c61-f32d18145f0c"; // This is a fake key, use your own
integer Hard = FALSE;

RegisterHud() {
    // We send the data to the Cool Hud.
    llWhisper(-888888, llList2CSV([ IdName, HudButtonTexture, MyPrivateChannel, MenuCommand ]));
    // Let's query the status of any worn genitals attachments.
    llWhisper(-888888, "%relay;status genitals");
}

Normal(integer relay) {
    if (!Hard) {
        // No need to continue if the nipples are not stiff.
        return;
    }
    if (relay) {
         // Only relay arousal level when the state change is not
         // the result of an already relayed arousal message...
         llWhisper(-888888, "%relay;arousal 0");
    }
    // Insert the code to make the nipples shrink here
    Hard = FALSE;
    llOwnerSay("Your nipples shrink.");
}

Stiff(integer relay) {
    if (Hard) {
        // No need to continue if the nipples are already stiff.
        return;
    }
    if (relay) {
         llWhisper(-888888, "%relay;arousal 1");
    }
    // Insert the code to make the nipples stiffen here
    Hard = TRUE;
    llOwnerSay("Your nipples stiffen.");
}

Menu() {
    llDialog(llGetOwner(), "\nNipples state", [ "Stiff", "Normal" ], MyPrivateChannel);
}

Parse(string command) {
    command = llToLower(command);
    if (command == MenuCommand) {
        Menu();
    } else if (command == "normal") {
        Normal(TRUE);
    } else if (command == "stiff") {
        Stiff(TRUE);
    } else if (llSubStringIndex(command, "%relay;") == 0) {
        list tokens = llParseStringKeepNulls(command, [ ";" ], [ ]);
        if (llList2Key(tokens, 2) == llGetKey()) {
            // Ignore messages we relayed ourselves.
            return;
        }
        string message = llList2String(tokens, 1);
        if (message == "arousal 0") {
            // Not aroused. Shrink the nipples down.
            Normal(FALSE);
        } else if (llSubStringIndex(message, "arousal") == 0) {
            // All the other arousal messages mean we are aroused,
            // so make the nipples stiff.
            Stiff(FALSE);
        } else if (message == "hide nipples") {
            // Another attachment wants to hide us.
            llSetLinkAlpha(LINK_SET, 0.0, ALL_SIDES);
        } else if (message == "show nipples") {
            // Another attachment wants to show us.
            llSetLinkAlpha(LINK_SET, 1.0, ALL_SIDES);
        } else if (message == "status nipples") {
            // Report our current status of arousal.
            message = "%relay;arousal ";
            if (Hard) {
                message += "1";
            } else {
                message += "0";
            }
            llWhisper(-888888, message);
        }
    }
}

default {
    state_entry() {
        // Open my private command channel.
        llListen(MyPrivateChannel, "", NULL_KEY, "");
        // Register ourselves with the Cool Hud
        RegisterHud();
    }

    on_rez(integer param) {
        // Register ourselves with the Cool Hud on rezing.
        RegisterHud();
    }

    touch_start(integer n) {
        if (llDetectedKey(0) == llGetOwner()) {
            // Pull down the menu on touch for our owner only.
            Menu();
        }
    }

    listen(integer chan, string name, key id, string message) {
        if (llGetOwnerKey(id) == llGetOwner()) {
            // Parse messages sent by objects/agents belonging to us.
            Parse(message);
        }
    }
}