Difference between revisions of "Combined LockMeister Cuff script"
(Comprehensive Lockmeister Cuff Script) |
(Updated the script slightly and added the main text) |
||
Line 1: | Line 1: | ||
{{LSL Header}} | {{LSL Header}} | ||
== | == LockMeister+ Cuffs == | ||
=== Overview === | |||
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. | |||
<lsl>/////////////////////////////////////////////////////////////////////// | <lsl>/////////////////////////////////////////////////////////////////////// | ||
// | // | ||
// Comprehensive Lockmeister Cuff Script | // Comprehensive Lockmeister Cuff Script | ||
// (LockMeister+ reference script) | |||
// | // | ||
// Tianna Violet | // Tianna Violet | ||
Line 32: | Line 64: | ||
// 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 79: | ||
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 104: | ||
integer a; | integer a; | ||
integer len; | integer len; | ||
gWorkingTargets = gRequestedTargets; | gWorkingTargets = gRequestedTargets; | ||
if (gWorkingTargets != "") | if (gWorkingTargets != "") | ||
Line 68: | Line 116: | ||
} | } | ||
} | } | ||
default | default | ||
{ | { | ||
Line 77: | Line 125: | ||
gWorkingTargets = ""; | gWorkingTargets = ""; | ||
gCurrentTarget = NULL_KEY; | gCurrentTarget = NULL_KEY; | ||
gCurrentTargetName = ""; | |||
gAge = 2; | gAge = 2; | ||
gGravity = 0.7; | gGravity = 0.7; | ||
Line 82: | Line 131: | ||
llListen(-8888, "", NULL_KEY, ""); | llListen(-8888, "", NULL_KEY, ""); | ||
} | } | ||
on_rez(integer start_param) | on_rez(integer start_param) | ||
{ | { | ||
Line 88: | Line 137: | ||
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 149: | ||
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 163: | ||
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 177: | ||
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 184: | ||
if (tempf) | if (tempf) | ||
{ | { | ||
if (index) // "age" | if (index) // "age" | ||
{ | { | ||
gAge = tempf; | gAge = tempf; | ||
} else { | } 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 203: | ||
} | } | ||
} | } | ||
if (index == 3) // "target" | if (index == 3) // "target" | ||
{ | { | ||
temps = ""; | temps = ""; | ||
Line 171: | Line 220: | ||
{ | { | ||
gCurrentTarget = NULL_KEY; | gCurrentTarget = NULL_KEY; | ||
gCurrentTargetName = ""; | |||
gRequestedTargets = temps; | gRequestedTargets = temps; | ||
pingCuffs(); | pingCuffs(); | ||
Line 186: | Line 236: | ||
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 242: | ||
{ | { | ||
cuffs = llParseString2List(gWorkingTargets, ["-"], []); | cuffs = llParseString2List(gWorkingTargets, ["-"], []); | ||
i = llListFindList(cuffs, | 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 ( | 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(); |
Revision as of 07:27, 21 January 2010
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
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.
<lsl>///////////////////////////////////////////////////////////////////////
//
// 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(); } } } } } } }
} </lsl>