Combined LockMeister Handle Post and Collar

== LockMeister+ Handles, Posts and Collar ==
=== 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+ Handle reference script ===
The LockMeister+ Handle:
*MUST respond to basic "handle" messages with an "ok".
*MUST implement the "here" and "gone" messages.
<lsl>///////////////////////////////////////////////////////////////////////
//
// Lockmeister+ Handle Reference Script
//
// Tianna Violet
// 20 Jan 2010
//
// http://wiki.secondlife.com/wiki/LSL_Protocol/LockMeister_System
//
// 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.
///////////////////////////////////////////////////////////////////////
// The lockmeister name of the handle.
string LOCKMEISTER_NAME = "handle";
///////////////////////////////////////////////////////////////////////
string gCuffMessage;
default
{
 state_entry()
 {
 gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
 llListen(-8888, "", NULL_KEY, "");
 }
 attach(key id)
 {
 string message;
 message = " gone";
 if (id)
 {
 gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
 message = " here";
 }
 llSay(-8888, gCuffMessage + message);	// send the "here" and "gone" message
 }
 listen(integer channel, string name, key id, string message)
 {
 if (message == gCuffMessage)
 {
 llSay(-8888, gCuffMessage + " ok");	// the basic response
 return;
 }
 }
}
</lsl>
=== The LockMeister+ Post reference script ===
The LockMeister+ Post:
*MUST send a "posk ok" message when the post is touched the key being the toucher's UUID
*MUST respond to basic "handle" messages where the message key is its own UUID with an "ok".
*SHOULD contain 'post' in its name and script SHOULD be in the root prim so that a post that is found through a scan of nearby objects can be confirmed as a LockMeister+ post.
<lsl>///////////////////////////////////////////////////////////////////////
//
// Lockmeister+ Post Reference Script
//
// Tianna Violet
// 21 Jan 2010
//
// http://wiki.secondlife.com/wiki/LSL_Protocol/LockMeister_System
///////////////////////////////////////////////////////////////////////
// The lockmeister name of the leash.
string LOCKMEISTER_NAME = "handle";
///////////////////////////////////////////////////////////////////////
default
{
 state_entry()
 {
 llListen(-8888, "", NULL_KEY, "");
 }
 touch_start(integer total_number)
 {
 llSay(-8888, (string) llDetectedKey(0) + "post ok");
 }
 listen(integer channel, string name, key id, string message)
 {
 string postMessage;
 postMessage = (string) llGetKey() + LOCKMEISTER_NAME;
 if (message == postMessage)
 {
 llSay(-8888, postMessage + " ok");
 return;
 }
 }
}
</lsl>
=== The LockMeister+ Collar reference script ===
The LockMeister+ Collar:
*MUST look for a handle using the basic LockMeister query/response.
*MUST monitor "here" messages in order to connect to a handle should it appear.
*MUST monitor the "gone" event to remove the leash should the handle disappear.
*MUST send out new target requests should the current handle disappear.
*MUST support the "post ok" message and basic query/response to the post.
*SHOULD support basic LockMeister query/responses for the "collar" attach point.
*SHOULD reconnect the leash when the AV logs on if the attach point is still available.
*SHOULD support one touch transfer of the leash from the leash holder to a post.
*SHOULD support one touch transfer of the leash from a post to an AV with leash access.
<lsl>///////////////////////////////////////////////////////////////////////
//
// Lockmeister+ Collar Reference Script
//
// Tianna Violet
// 31 Jan 2010
//
// http://wiki.secondlife.com/wiki/LSL_Protocol/LockMeister_System
//
// the collar reponds on channel 53
//
// /53xxleash		- grab the leash
// /53xxunleash		- unleash
// /53xxleashpost	- leash to a previously touch post
//
// xx = the initials of the wearer or * to effect all collars
//
// 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.
///////////////////////////////////////////////////////////////////////
// The lockmeister name of the collar.
string LOCKMEISTER_NAME = "collar";
string gCuffMessage;
key gLeashHolder;
key gCurrentTarget;
key gPostToucher;
key gPost;
key gPending;
integer isLeashed;
string gPrefix;
// Who has access to the collar
// This is where the owner list etc is checked
integer hasAccess(key id)
{
 return TRUE; // everyone has access
}
// 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, "1ffb37fa-2fc1-dbec-d8ea-0607583a03c6",
 PSYS_SRC_BURST_PART_COUNT, 1, PSYS_SRC_BURST_RATE, 0.0, PSYS_PART_MAX_AGE, 2,
 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, -0.7>, 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]);
}
setPrefix()
{
 list l;
 l = llParseString2List(llToLower(llKey2Name(llGetOwner())), [" "], []);
 gPrefix = llGetSubString(llList2String(l, 0), 0, 0) + llGetSubString(llList2String(l, 1), 0, 0);
}
releash()
{
 if (isLeashed)
 {
 if (gLeashHolder)
 {
 llSensor("", gLeashHolder, AGENT, 30.0, PI);
 } else {
 gPending = gCurrentTarget;
 llSay(-8888, ((string) gPending + "handle"));
 }
 llParticleSystem([]);
 isLeashed = 0;
 gLeashHolder = NULL_KEY;
 gCurrentTarget = NULL_KEY;
 }
}
default
{
 state_entry()
 {
 gLeashHolder = NULL_KEY;
 gCurrentTarget = NULL_KEY;
 gPostToucher = NULL_KEY;
 gPost = NULL_KEY;
 gPending = NULL_KEY;
 isLeashed = 0;
 setPrefix();
 gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
 llListen(-8888, "", NULL_KEY, "");
 llListen(53, "", NULL_KEY, "");
 }
 listen(integer channel, string name, key id, string message)
 {
 list commands = ["leash", "unleash", "leashpost"];
 list responses = ["handle ok", "handle here", "handle gone", "post ok"];
 key target;
 integer index;
 integer len;
 if (channel == 53 && hasAccess(id))
 {
 message = llStringTrim(llToLower(message), STRING_TRIM);
 index = -1;
 if (llGetSubString(message, 0, 0) == "*")
 {
 index = 1;
 } else {
 len = llStringLength(gPrefix);
 if (llGetSubString(message, 0, len - 1) == gPrefix)
 {
 index = len;
 }
 }
 if (~index)
 {
 index = llListFindList(commands, [llGetSubString(message, index, -1)]);
 }
 if (~index)
 {
 gPending = NULL_KEY;
 if (!index)
 {
 if (gLeashHolder == NULL_KEY)
 {
 gLeashHolder = id;
 gCurrentTarget = id;
 isLeashed = 1;
 updateParticles();
 llSay(-8888, ((string) id) + "handle");
 gPending = id;
 }
 }
 if (gLeashHolder == NULL_KEY || gLeashHolder == id)
 {
 if (index == 1)
 {
 gLeashHolder = NULL_KEY;
 isLeashed = 0;
 llParticleSystem([]);
 }
 if (index == 2)
 {
 if (gPost != NULL_KEY && gPost != gCurrentTarget && gPostToucher == id)
 {
 llSay(-8888, ((string) gPost) + "handle");
 gPending = gPost;
 }
 }
 }
 }
 }
 if (channel == -8888)
 {
 if (message == gCuffMessage)
 {
 llSay(-8888, gCuffMessage + " ok"); // the basic response
 return;
 }
 target = (key) llGetSubString(message, 0, 35);
 if (target)
 {
 index = llListFindList(responses, [llGetSubString(message, 36, -1)]);
 if (~index)
 {
 if (!index && target == gPending)
 {
 gCurrentTarget = id;
 isLeashed = 1;
 if (id == target)
 {
 gLeashHolder = NULL_KEY;
 }
 gPending = NULL_KEY;
 updateParticles();
 }
 if (target == gLeashHolder)
 {
 if (index == 1 && gCurrentTarget != id)
 {
 gCurrentTarget = id;
 isLeashed = 1;
 gPending = NULL_KEY;
 updateParticles();
 }
 if (index == 2 && gCurrentTarget == id)
 {
 releash();
 }
 }
 if (index == 3)
 {
 if (target == gLeashHolder || (gLeashHolder == NULL_KEY && hasAccess(target)))
 {
 gPostToucher = target;
 gPost = id;
 if (gPost != gCurrentTarget)
 {
 if (gLeashHolder != NULL_KEY)
 {
 gLeashHolder = NULL_KEY;
 gCurrentTarget = gPost;
 isLeashed = 1;
 gPending = NULL_KEY;
 updateParticles();
 }
 } else {
 gLeashHolder = target;
 gCurrentTarget = target;
 isLeashed = 1;
 updateParticles();
 gPending = target;
 llSay(-8888, ((string) target) + "handle");
 }
 }
 }
 }
 }
 }
 }
 attach(key id)
 {
 string message;
 message = " gone";
 if (id)
 {
 gCuffMessage = (string) llGetOwner() + LOCKMEISTER_NAME;
 message = " here";
 if (isLeashed)
 {
 releash();
 }
 }
 llSay(-8888, gCuffMessage + message); // send the "here" and "gone" message
 }
 sensor(integer total_number)
 {
 gLeashHolder = llDetectedKey(0);
 gCurrentTarget = gLeashHolder;
 isLeashed = 1;
 updateParticles();
 llSay(-8888, ((string) gLeashHolder) + "handle");
 gPending = gLeashHolder;
 }
 on_rez(integer start_param)
 {
 setPrefix();
 }
}
</lsl>
Category:LockMeister