Difference between revisions of "Combined LockMeister Cuff script"

From Second Life Wiki
Jump to navigation Jump to search
(Comprehensive Lockmeister Cuff Script)
 
m (Fix the code tags)
 
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{LSL Header}}
{{LSL Header}}


== An update for LockMeister Cuffs ==
== LockMeister+ Cuffs ==


This script implements a combination of extensions to the LockMeister system protocol [[LSL_Protocol/LockMeister_System]] using them together to bring added benefits.
=== Overview ===


<lsl>///////////////////////////////////////////////////////////////////////
The [[LSL_Protocol/LockMeister_System|LockMeister system protocol]] for cuffs, collars and leash handles has been around from many years and in that time a number of extensions to the protocol have been added and have been documented.
 
LockMeister+ is an effort to bring together those extensions and define a set of expected behaviours to allow greater interoperability and functionality between devices that use the extended protocol.  The aims of LockMeister+ are:
 
*To gather up the existing extensions into a new extended protocol.
*To add functionality that can be gained from combining those extensions.
*To define the expected behaviour for a number if classes of devices.
*To be 100% backwards compatible with what has gone before.
 
=== The LockMeister+ Cuff reference script ===
 
The LockMeister+ cuff:
 
*MUST implement the basic LockMeister query/response.
*MUST implement the Sensations extensions to allow particle chains between cuffs ("target", "texture", "age", "gravity" commands).
*MUST implement the new "reset" command for stopping particle chains and setting their parameters back to the default for the device.
*MUST implement the "here" and "gone" extensions.
*MAY implement the "col" command if needed (the reference script does not).
 
In addition the LockMeister+ cuff's behaviour:
 
*MUST monitor "here" messages in order to connect to a target should it appear or move the particle chain to a more preferable target should it become available.
*MUST monitor the "gone" event to remove a particle chain should the target disappear.
*MUST send out new target requests should the current target disappear.
*SHOULD reconnect all particle chains when the AV logs on.
 
==== Implementation Notes ====
 
This script will only run correctly in the ROOT PRIM of and object.  This is because it uses an "attach" event which are not correctly handled when is child prims.  On detach the child prim's "attach" event is usually not called and instead is executed when the object is reattached.  To this end the "gone" command is allowed to come from a prim that is not the target prim and so should be handled by LockMeister name rather than UUID.  This allows the root prim of an object to handle "attach" events on behalf of child target prims.
 
 
<source lang="lsl2">
///////////////////////////////////////////////////////////////////////
//
//
// Comprehensive Lockmeister Cuff Script
// Comprehensive Lockmeister Cuff Script
// (LockMeister+ reference script)
//
//
// Tianna Violet
// Tianna Violet
Line 32: Line 65:
// from spying on the Lockmeister messages coming from the Amethyst cuffs
// from spying on the Lockmeister messages coming from the Amethyst cuffs
// set.
// set.
//
// Note that this script will only work in the ROOT PRIM ONLY as it
// uses an "attach" event which aren't handled properly in child prims.
//
// Note that the "gone" message is allowed to come from a prim other
// than the target prim to allow an object root prim to handle "attach"
// events on behalf of child targets.
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// The lockmeister name of the cuff.  See the above link for details
// The lockmeister name of the cuff.  See the above link for details
Line 40: Line 80:
string gWorkingTargets;
string gWorkingTargets;
key gCurrentTarget;
key gCurrentTarget;
string gCurrentTargetName;
float gAge;
float gAge;
float gGravity;
float gGravity;
string gTexture;
string gTexture;
 
// Start or update the particle stream
// Start or update the particle stream
updateParticles()
updateParticles()
{
{
     llParticleSystem( [PSYS_PART_START_SCALE, <0.1, 0.1, 0>, PSYS_PART_END_SCALE, <1, 1, 0>, PSYS_PART_START_COLOR, <1, 1, 1>, PSYS_PART_END_COLOR, <1, 1, 1>, PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 1.0, PSYS_SRC_TEXTURE, gTexture, PSYS_SRC_BURST_PART_COUNT, 1, PSYS_SRC_BURST_RATE, 0.0, PSYS_PART_MAX_AGE, gAge, PSYS_SRC_MAX_AGE, 0.0, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP, PSYS_SRC_BURST_RADIUS, 0.5, PSYS_SRC_INNERANGLE, 0.0, PSYS_SRC_OUTERANGLE, 0.0, PSYS_SRC_OMEGA, <0, 0, 0>, PSYS_SRC_ACCEL, <0, 0, -gGravity>, PSYS_SRC_BURST_SPEED_MIN, 1000.0, PSYS_SRC_BURST_SPEED_MAX, 1000.0, PSYS_SRC_TARGET_KEY, gCurrentTarget, PSYS_PART_FLAGS, PSYS_PART_FOLLOW_VELOCITY_MASK | PSYS_PART_FOLLOW_SRC_MASK | PSYS_PART_TARGET_POS_MASK]);
     llParticleSystem( [PSYS_PART_START_SCALE, <0.1, 0.1, 0>, PSYS_PART_END_SCALE, <1, 1, 0>,
    PSYS_PART_START_COLOR, <1, 1, 1>, PSYS_PART_END_COLOR, <1, 1, 1>,
    PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 1.0, PSYS_SRC_TEXTURE, gTexture,
    PSYS_SRC_BURST_PART_COUNT, 1, PSYS_SRC_BURST_RATE, 0.0, PSYS_PART_MAX_AGE, gAge,
    PSYS_SRC_MAX_AGE, 0.0, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP, PSYS_SRC_BURST_RADIUS, 0.5,
    PSYS_SRC_INNERANGLE, 0.0, PSYS_SRC_OUTERANGLE, 0.0, PSYS_SRC_OMEGA, <0, 0, 0>,
    PSYS_SRC_ACCEL, <0, 0, -gGravity>, PSYS_SRC_BURST_SPEED_MIN, 1000.0,
    PSYS_SRC_BURST_SPEED_MAX, 1000.0, PSYS_SRC_TARGET_KEY, gCurrentTarget,
    PSYS_PART_FLAGS, PSYS_PART_FOLLOW_VELOCITY_MASK | PSYS_PART_FOLLOW_SRC_MASK | PSYS_PART_TARGET_POS_MASK]);
}
}
 
// ping the cuffs that can be used as possible targets, if any
// ping the cuffs that can be used as possible targets, if any
pingCuffs()
pingCuffs()
Line 56: Line 105:
     integer a;
     integer a;
     integer len;
     integer len;
   
     gWorkingTargets = gRequestedTargets;
     gWorkingTargets = gRequestedTargets;
     if (gWorkingTargets != "")
     if (gWorkingTargets != "")
Line 68: Line 117:
     }
     }
}
}
 
default
default
{
{
Line 77: Line 126:
         gWorkingTargets = "";
         gWorkingTargets = "";
         gCurrentTarget = NULL_KEY;
         gCurrentTarget = NULL_KEY;
        gCurrentTargetName = "";
         gAge = 2;
         gAge = 2;
         gGravity = 0.7;
         gGravity = 0.7;
Line 82: Line 132:
         llListen(-8888, "", NULL_KEY, "");         
         llListen(-8888, "", NULL_KEY, "");         
     }
     }
 
     on_rez(integer start_param)
     on_rez(integer start_param)
     {
     {
Line 88: Line 138:
         gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
         gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
     }
     }
   
     attach(key id)
     attach(key id)
     {
     {
         string message;
         string message;
 
         message = " gone";
         message = " gone";
         if (id)
         if (id)
Line 100: Line 150:
             pingCuffs();
             pingCuffs();
         }
         }
         llSay(-8888, gCuffMessage + message); // send the "here" and "gone" message
         llSay(-8888, gCuffMessage + message);   // send the "here" and "gone" message
     }
     }
 
     listen(integer channel, string name, key id, string message)
     listen(integer channel, string name, key id, string message)
     {
     {
Line 114: Line 164:
         float tempf;
         float tempf;
         string temps;
         string temps;
 
         if (channel == -8888)
         if (channel == -8888)
         {
         {
             if (message == gCuffMessage) // quick early out for the basic response
             if (message == gCuffMessage)   // quick early out for the basic response
             {
             {
                 llSay(-8888, gCuffMessage + " ok");
                 llSay(-8888, gCuffMessage + " ok");
Line 128: Line 178:
                 if (~index)
                 if (~index)
                 {
                 {
                     if (index < 3) // the 3 particle params that can change
                     if (index < 3)   // the 3 particle params that can change
                     {
                     {
                         if (index < 2)
                         if (index < 2)
Line 135: Line 185:
                             if (tempf)
                             if (tempf)
                             {
                             {
                                 if (index) // "age"
                                 if (index)   // "age"
                                 {
                                 {
                                     gAge = tempf;
                                     gAge = tempf;
                                 } else { // "gravity"
                                 } else {       // "gravity"
                                     gGravity = tempf;
                                     gGravity = tempf;
                                 }
                                 }
                             }
                             }
                         } else { // "texture"
                         } else {   // "texture"
                             temps = llList2String(data, 1);
                             temps = llList2String(data, 1);
                             if (((key) temps) != NULL_KEY)
                             if (((key) temps) != NULL_KEY)
Line 154: Line 204:
                         }
                         }
                     }
                     }
                     if (index == 3) // "target"
                     if (index == 3)   // "target"
                     {
                     {
                         temps = "";
                         temps = "";
Line 171: Line 221:
                         {
                         {
                             gCurrentTarget = NULL_KEY;
                             gCurrentTarget = NULL_KEY;
                            gCurrentTargetName = "";
                             gRequestedTargets = temps;
                             gRequestedTargets = temps;
                             pingCuffs();
                             pingCuffs();
Line 186: Line 237:
                     if (~index) // "ok", "here" and "gone" messages from others
                     if (~index) // "ok", "here" and "gone" messages from others
                     {
                     {
                        temps = llList2String(data, 0);
                         if (index < 2) // "ok" and "here" treated the same
                         if (index < 2) // "ok" and "here" treated the same
                         {
                         {
Line 191: Line 243:
                             {
                             {
                                 cuffs = llParseString2List(gWorkingTargets, ["-"], []);
                                 cuffs = llParseString2List(gWorkingTargets, ["-"], []);
                                 i = llListFindList(cuffs, llList2List(data, 0, 0));
                                 i = llListFindList(cuffs, [temps]);
                                 if (~i)
                                 if (~i)
                                 {
                                 {
                                     gWorkingTargets = llDumpList2String(llList2List(cuffs, i + 1, -1), "-");
                                     gWorkingTargets = llDumpList2String(llList2List(cuffs, i + 1, -1), "-");
                                     gCurrentTarget = id;
                                     gCurrentTarget = id;
                                    gCurrentTargetName = temps;
                                     updateParticles();
                                     updateParticles();
                                 }
                                 }
                             }
                             }
                         } else { // "gone"
                         } else { // "gone"
                             if (gCurrentTarget == id)
                             if (gCurrentTargetName == temps) // test on name as the "gone" may not come
                             {
                             {                               // from out target prim
                                 gCurrentTarget = NULL_KEY;
                                 gCurrentTarget = NULL_KEY;
                                gCurrentTargetName = "";
                                 llParticleSystem([]);
                                 llParticleSystem([]);
                                 pingCuffs();
                                 pingCuffs();
Line 213: Line 267:
     }
     }
}
}
</lsl>
</source>
 
 
[[Category:LockMeister]]

Latest revision as of 01:40, 11 August 2015

LockMeister+ Cuffs

Overview

The LockMeister system protocol for cuffs, collars and leash handles has been around from many years and in that time a number of extensions to the protocol have been added and have been documented.

LockMeister+ is an effort to bring together those extensions and define a set of expected behaviours to allow greater interoperability and functionality between devices that use the extended protocol. The aims of LockMeister+ are:

  • To gather up the existing extensions into a new extended protocol.
  • To add functionality that can be gained from combining those extensions.
  • To define the expected behaviour for a number if classes of devices.
  • To be 100% backwards compatible with what has gone before.

The LockMeister+ Cuff reference script

The LockMeister+ cuff:

  • MUST implement the basic LockMeister query/response.
  • MUST implement the Sensations extensions to allow particle chains between cuffs ("target", "texture", "age", "gravity" commands).
  • MUST implement the new "reset" command for stopping particle chains and setting their parameters back to the default for the device.
  • MUST implement the "here" and "gone" extensions.
  • MAY implement the "col" command if needed (the reference script does not).

In addition the LockMeister+ cuff's behaviour:

  • MUST monitor "here" messages in order to connect to a target should it appear or move the particle chain to a more preferable target should it become available.
  • MUST monitor the "gone" event to remove a particle chain should the target disappear.
  • MUST send out new target requests should the current target disappear.
  • SHOULD reconnect all particle chains when the AV logs on.

Implementation Notes

This script will only run correctly in the ROOT PRIM of and object. This is because it uses an "attach" event which are not correctly handled when is child prims. On detach the child prim's "attach" event is usually not called and instead is executed when the object is reattached. To this end the "gone" command is allowed to come from a prim that is not the target prim and so should be handled by LockMeister name rather than UUID. This allows the root prim of an object to handle "attach" events on behalf of child target prims.


///////////////////////////////////////////////////////////////////////
//
// Comprehensive Lockmeister Cuff Script
// (LockMeister+ reference script)
//
// Tianna Violet
// 20 Jan 2010
//
// This is a Lockmeister cuff script that combines many of the extensions
// to the lockmeister protocol at defined here:
// http://wiki.secondlife.com/wiki/LSL_Protocol/LockMeister_System
// To the basic query response is added Amethyst Rosencrans' Sensations
// extensions for allowing particle links between cuffs and
// Henri Beauchamp's "here" and "gone" extensions which notify when a cuff
// is attached and detached.  Combining these allows a few extra features:
// * Cuffs can delete or reassign particle chains if their target object
// disappears.
// * Cuffs can attach if the target point appears.
// * Cuffs can reattach when an AV logs in.
//
// In addition is added a new command, "reset" defined as stopping the
// particle chain and resetting the Age, Gravity, and Texture back to
// their defaults.
//
// Although this script is new, reference was made to Amethyst Rosencrans'
// "Free Amethyst cuff script v2" (which is a little out of date) and
// from spying on the Lockmeister messages coming from the Amethyst cuffs
// set.
//
// Note that this script will only work in the ROOT PRIM ONLY as it
// uses an "attach" event which aren't handled properly in child prims.
//
// Note that the "gone" message is allowed to come from a prim other
// than the target prim to allow an object root prim to handle "attach"
// events on behalf of child targets.
///////////////////////////////////////////////////////////////////////
// The lockmeister name of the cuff.  See the above link for details
string LOCKMEISTER_NAME = "lcuff";
///////////////////////////////////////////////////////////////////////
string gCuffMessage;
string gRequestedTargets;
string gWorkingTargets;
key gCurrentTarget;
string gCurrentTargetName;
float gAge;
float gGravity;
string gTexture;
 
// Start or update the particle stream
updateParticles()
{
    llParticleSystem( [PSYS_PART_START_SCALE, <0.1, 0.1, 0>, PSYS_PART_END_SCALE, <1, 1, 0>,
    PSYS_PART_START_COLOR, <1, 1, 1>, PSYS_PART_END_COLOR, <1, 1, 1>,
    PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 1.0, PSYS_SRC_TEXTURE, gTexture,
    PSYS_SRC_BURST_PART_COUNT, 1, PSYS_SRC_BURST_RATE, 0.0, PSYS_PART_MAX_AGE, gAge,
    PSYS_SRC_MAX_AGE, 0.0, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP, PSYS_SRC_BURST_RADIUS, 0.5,
    PSYS_SRC_INNERANGLE, 0.0, PSYS_SRC_OUTERANGLE, 0.0, PSYS_SRC_OMEGA, <0, 0, 0>,
    PSYS_SRC_ACCEL, <0, 0, -gGravity>, PSYS_SRC_BURST_SPEED_MIN, 1000.0,
    PSYS_SRC_BURST_SPEED_MAX, 1000.0, PSYS_SRC_TARGET_KEY, gCurrentTarget,
    PSYS_PART_FLAGS, PSYS_PART_FOLLOW_VELOCITY_MASK | PSYS_PART_FOLLOW_SRC_MASK | PSYS_PART_TARGET_POS_MASK]);
}
 
// ping the cuffs that can be used as possible targets, if any
pingCuffs()
{
    list cuffs;
    integer a;
    integer len;
 
    gWorkingTargets = gRequestedTargets;
    if (gWorkingTargets != "")
    {
        cuffs = llParseString2List(gWorkingTargets, ["-"], []);
        len = llGetListLength(cuffs);
        for (a = 0; a < len; a++)
        {
            llWhisper(-8888, (string) llGetOwner() + llList2String(cuffs, a));
        }
    }
}
 
default
{
    state_entry()
    {
        gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
        gRequestedTargets = "";
        gWorkingTargets = "";
        gCurrentTarget = NULL_KEY;
        gCurrentTargetName = "";
        gAge = 2;
        gGravity = 0.7;
        gTexture = "1ffb37fa-2fc1-dbec-d8ea-0607583a03c6";
        llListen(-8888, "", NULL_KEY, "");        
    }
 
    on_rez(integer start_param)
    {
        llParticleSystem([]);
        gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
    }
 
    attach(key id)
    {
        string message;
 
        message = " gone";
        if (id)
        {
            gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
            message = " here";
            pingCuffs();
        }
        llSay(-8888, gCuffMessage + message);    // send the "here" and "gone" message
    }
 
    listen(integer channel, string name, key id, string message)
    {
        list commands = ["gravity", "age", "texture", "target", "reset"];
        list responses = ["ok", "here", "gone"];
        list data;
        list cuffs;
        integer index;
        integer i;
        integer atThis;
        float tempf;
        string temps;
 
        if (channel == -8888)
        {
            if (message == gCuffMessage)    // quick early out for the basic response
            {
                llSay(-8888, gCuffMessage + " ok");
                return;
            }
            if (((key) llGetSubString(message, 0, 35)) == llGetOwner())
            {
                data = llParseStringKeepNulls(llGetSubString(message, 36, -1), ["|"], []);
                index = llListFindList(commands, llList2List(data, 0, 0));
                if (~index)
                {
                    if (index < 3)    // the 3 particle params that can change
                    {
                        if (index < 2)
                        {
                            tempf = (float) llList2String(data, 1);
                            if (tempf)
                            {
                                if (index)    // "age"
                                {
                                    gAge = tempf;
                                } else {        // "gravity"
                                    gGravity = tempf;
                                }
                            }
                        } else {    // "texture"
                            temps = llList2String(data, 1);
                            if (((key) temps) != NULL_KEY)
                            {
                                gTexture = temps;
                            }
                        }
                        if (gCurrentTarget)
                        {
                            updateParticles();
                        }
                    }
                    if (index == 3)    // "target"
                    {
                        temps = "";
                        atThis = TRUE;
                        if (llGetListLength(data) > 1)
                        {
                            i = llListFindList(llList2ListStrided(llList2List(data, 1, -1), 0, -1, 2), [LOCKMEISTER_NAME]);
                            if (~i)
                            {
                                temps = llList2String(data, (i + 1) << 1);
                            } else {
                                atThis = FALSE;
                            }
                        }
                        if (atThis)
                        {
                            gCurrentTarget = NULL_KEY;
                            gCurrentTargetName = "";
                            gRequestedTargets = temps;
                            pingCuffs();
                            llParticleSystem([]);
                        }
                    }
                    if (index == 4) // "reset"
                    {
                        llParticleSystem([]);
                        llResetScript();
                    }
                } else { 
                    data = llParseString2List(llList2String(data, 0), [" "], []);
                    index = llListFindList(responses, llList2List(data, 1, 1));
                    if (~index) // "ok", "here" and "gone" messages from others
                    {
                        temps = llList2String(data, 0);
                        if (index < 2) // "ok" and "here" treated the same
                        {
                            if (gWorkingTargets != "")
                            {
                                cuffs = llParseString2List(gWorkingTargets, ["-"], []);
                                i = llListFindList(cuffs, [temps]);
                                if (~i)
                                {
                                    gWorkingTargets = llDumpList2String(llList2List(cuffs, i + 1, -1), "-");
                                    gCurrentTarget = id;
                                    gCurrentTargetName = temps;
                                    updateParticles();
                                }
                            }
                        } else { // "gone"
                            if (gCurrentTargetName == temps) // test on name as the "gone" may not come
                            {                                // from out target prim
                                gCurrentTarget = NULL_KEY;
                                gCurrentTargetName = "";
                                llParticleSystem([]);
                                pingCuffs();
                            }
                        }
                    }
                }
            }
        }
    }
}