<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Grey+Mars</id>
	<title>Second Life Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Grey+Mars"/>
	<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/wiki/Special:Contributions/Grey_Mars"/>
	<updated>2026-06-22T09:24:24Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1142756</id>
		<title>MLPV2 LockGuard plugin</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1142756"/>
		<updated>2011-05-06T19:41:34Z</updated>

		<summary type="html">&lt;p&gt;Grey Mars: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Back to [[MLPV2_Addons]]&lt;br /&gt;
* [[MLPV2]]&lt;br /&gt;
&lt;br /&gt;
Basically this is an alternate way of adding particle chain support to MLP2 using the LockGuard protocol.  When the object is rezzed in world the script makes a list of the prim names and keys for later.  It then reads the note card with the linking information each time inventory changes.  When MLP2 gives pose info the script determines if it pertains to it&#039;s list and if so notifies lockguard enabled attachments of what to do and what prim keys to do it to.&lt;br /&gt;
&lt;br /&gt;
So what do you need to do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
Figure out what prims you want to use as anchor points, and give each a unique name.  Personally I recommend the boring yet easy to remember format of point1, point2, point3, etc.  Remember these names for later.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
Add the following script to your furniture.  It may complain, but you can ignore that for now.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// MLPV2 Plugin for LockGuard Particle Chaining v1.3&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Notecard Format&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Notecard .CHAINDATA format:&lt;br /&gt;
// menulable | ballnumber | anchorprimname | LGattachpoint optionalparams&lt;br /&gt;
//&lt;br /&gt;
// Example data:&lt;br /&gt;
// Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
// Displayed | 0 | point1 | wrists gravity 0.0 life 2.0&lt;br /&gt;
// Hung | 0 | point1 | wrists gravity 0.0 life 0.75&lt;br /&gt;
// Dangled | 0 | point1 | ankles gravity 0.0 life 0.75&lt;br /&gt;
// Captured | 0 | point3 | wrists&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Copyright 2011, Grey Mars. All rights reserved.&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Redistribution and use in source and binary forms, with or without modification, are&lt;br /&gt;
// permitted provided that the following conditions are met:&lt;br /&gt;
//&lt;br /&gt;
//   1. Redistributions of source code must retain the above copyright notice, this list of&lt;br /&gt;
//      conditions and the following disclaimer.&lt;br /&gt;
//&lt;br /&gt;
//   2. Redistributions in binary form must reproduce the above copyright notice, this list&lt;br /&gt;
//      of conditions and the following disclaimer in the documentation and/or other materials&lt;br /&gt;
//      provided with the distribution.&lt;br /&gt;
//&lt;br /&gt;
// THIS SOFTWARE IS PROVIDED BY GREY MARS ``AS IS&#039;&#039; AND ANY EXPRESS OR IMPLIED&lt;br /&gt;
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND&lt;br /&gt;
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; OR&lt;br /&gt;
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR&lt;br /&gt;
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&lt;br /&gt;
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON&lt;br /&gt;
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING&lt;br /&gt;
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF&lt;br /&gt;
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;br /&gt;
//&lt;br /&gt;
// The views and conclusions contained in the software and documentation are those of the&lt;br /&gt;
// authors and should not be interpreted as representing official policies, either expressed&lt;br /&gt;
// or implied, of Grey Mars.&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Changes:&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Version 1.3&lt;br /&gt;
// - Fixed an error that was causing multiple avatar chain sets from working&lt;br /&gt;
// - Fixed chainData not properly clearing on a reload&lt;br /&gt;
// - Changed the order of operations in the changed state for better tracking and simplified&lt;br /&gt;
//&lt;br /&gt;
// Version 1.2&lt;br /&gt;
// - Large overhaul of how the data on the cards is stored.  Original format was made&lt;br /&gt;
//   to preserve the working of the MLP2 swap function.  However no one ever uses the&lt;br /&gt;
//   swap function in a chained context, so why complicate the notecard files?  Plus&lt;br /&gt;
//   new format is more memory friendly.&lt;br /&gt;
// - Note card seperator changed from ; to | to match favorite RLV plugin format&lt;br /&gt;
// - Changed the need for the animation name to just the ball number in the note card&lt;br /&gt;
// - Removed some unused variables&lt;br /&gt;
// - Note card renamed to .CHAINDATA to follow MLP2 naming conventions&lt;br /&gt;
// - Removed sim ratings data poll.&lt;br /&gt;
// - Added warning if the desired target point does not exist in the linkset&lt;br /&gt;
// - Added more comments because comments are good.&lt;br /&gt;
//&lt;br /&gt;
// Version 1.1&lt;br /&gt;
// - Added a data reload on inventory change&lt;br /&gt;
//&lt;br /&gt;
// Version 1.0&lt;br /&gt;
// - Changed script name from ~chaincontrol&lt;br /&gt;
// - Cleaned up old comments and made it more readable&lt;br /&gt;
// - First post to lsl wiki ( https://wiki.secondlife.com/wiki/MLPV2_Addons )&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Globals&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
integer VERBOSE = FALSE;        // Give extra info&lt;br /&gt;
&lt;br /&gt;
integer LGChannel = -9119;      // The LG listen channel&lt;br /&gt;
integer LGHandle;               // Handle ID for LG messages&lt;br /&gt;
&lt;br /&gt;
// Globals for reading card config&lt;br /&gt;
integer ConfigLineIndex;&lt;br /&gt;
list    ConfigCards;            // list of names of config cards&lt;br /&gt;
string  ConfigCardName;         // name of card being read&lt;br /&gt;
integer ConfigCardIndex;        // index of next card to read&lt;br /&gt;
key     ConfigQueryId;          // The dataserver ID for the query sent&lt;br /&gt;
&lt;br /&gt;
string  Pose;                   // Pose name&lt;br /&gt;
key     Avkey;                  // Avatar Key&lt;br /&gt;
&lt;br /&gt;
string CurrentSet = &amp;quot;stand&amp;quot;;    // stand being the menu default&lt;br /&gt;
&lt;br /&gt;
list chainData = [];            // The list of all data from the notecard [lable,ballnum,attachpoint,params]&lt;br /&gt;
integer chainCount = 0;         // How many entries in the list&lt;br /&gt;
integer chainStride = 4;        // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
list primKeys = [];             // The list of prims in the object and thier keys [primname,key]&lt;br /&gt;
integer primKeysCount = 0;      // How many entries in the list&lt;br /&gt;
integer primKeysStride = 2;     // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
list avData = [];               // The list of currently chained avs [avkey,currentchains]&lt;br /&gt;
integer avDataCount = 0;        // How many entries in the list&lt;br /&gt;
integer avDataStride = 2;       // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Support functions&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
// Debugging outputs&lt;br /&gt;
debug(string text) {   if (VERBOSE) llOwnerSay(text);   }&lt;br /&gt;
&lt;br /&gt;
// get the next card in case there is more than one&lt;br /&gt;
integer next_card() {&lt;br /&gt;
    if (ConfigCardIndex &amp;gt;= llGetListLength(ConfigCards)) {&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        return (FALSE);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    ConfigLineIndex = 0;&lt;br /&gt;
    ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);&lt;br /&gt;
    ConfigCardIndex++;&lt;br /&gt;
    ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);&lt;br /&gt;
    debug(&amp;quot;====================\n[?] Reading file: &amp;quot; + ConfigCardName);&lt;br /&gt;
    return (TRUE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// add chain data to the list...&lt;br /&gt;
add_cd(string lable, string ballnum, string target, string params) {&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)lable;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)ballnum;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)target;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)params;&lt;br /&gt;
    ++chainCount;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Does this point actually referance a set of points?&lt;br /&gt;
list UnAliasChainPoints(list originalData) {&lt;br /&gt;
// wrists   -&amp;gt;  leftwrist rightwrist&lt;br /&gt;
// ankles   -&amp;gt;  leftankle rightankle&lt;br /&gt;
// allfour  -&amp;gt;  leftwrist rightwrist leftankle rightankle&lt;br /&gt;
// nipples  -&amp;gt;  leftnipplering rightnipplering &lt;br /&gt;
// arms     -&amp;gt;  leftupperarm rightupperarm&lt;br /&gt;
// thighs   -&amp;gt;  leftupperthigh rightupperthigh&lt;br /&gt;
// knees    -&amp;gt;  leftknee rightknee&lt;br /&gt;
&lt;br /&gt;
    list modData = [];&lt;br /&gt;
    integer xx;&lt;br /&gt;
    &lt;br /&gt;
    // Hacky.  Feel free to optimize.&lt;br /&gt;
    for(xx = 0; xx &amp;lt; llGetListLength(originalData); xx++) {&lt;br /&gt;
        if (llToLower(llList2String(originalData,xx)) == &amp;quot;wrists&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;ankles&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;allfour&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;, &amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;nipples&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftnipplering&amp;quot;, &amp;quot;rightnipplering&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;arms&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperarm&amp;quot;, &amp;quot;rightupperarm&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;thighs&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperthigh&amp;quot;, &amp;quot;rightupperthigh&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;knees&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftknee&amp;quot;, &amp;quot;rightknee&amp;quot;];&lt;br /&gt;
        } else {    modData = modData + llList2List(originalData,xx,xx);     }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return modData;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Main Script&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
default {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        &lt;br /&gt;
        // Now we build the prim key list...&lt;br /&gt;
        integer n;                                                      // Initialize the counter&lt;br /&gt;
        integer linkcount = llGetNumberOfPrims();                       // How many items in the link set?&lt;br /&gt;
        for (n = 2; n &amp;lt;= linkcount; n++)    {                           // Cycle through, skipping root&lt;br /&gt;
            string thiselement = llGetLinkName(n);                      // Get the prim name&lt;br /&gt;
            if (thiselement != &amp;quot;Object&amp;quot;) {                              // If the name is something besides the default...&lt;br /&gt;
                primKeys = primKeys + [thiselement, llGetLinkKey(n)];   // add it to primKeys&lt;br /&gt;
                primKeysCount++;                                        // increment counter&lt;br /&gt;
            }                                                           // end if&lt;br /&gt;
        }                                                               // end for loop&lt;br /&gt;
        &lt;br /&gt;
        state load;                                                     // Go load the data...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
state load  {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        chainData = []; // reset chainData in case it&#039;s a soft reload of info&lt;br /&gt;
        avData = []; // reset avData for soft reload.  PLEASE do not be sitting while editing chain data.       &lt;br /&gt;
        string item;&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        integer n = llGetInventoryNumber(INVENTORY_NOTECARD);&lt;br /&gt;
        while (n-- &amp;gt; 0) { // get the data off cards with the right name&lt;br /&gt;
            item = llGetInventoryName(INVENTORY_NOTECARD, n);&lt;br /&gt;
            if (llSubStringIndex(item, &amp;quot;.CHAINDATA&amp;quot;) != -1) {&lt;br /&gt;
                ConfigCards = (ConfigCards=[]) + ConfigCards + (list) item;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        ConfigCardIndex = 0;&lt;br /&gt;
        ConfigCards = llListSort(ConfigCards, 1, TRUE);&lt;br /&gt;
        next_card();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dataserver(key query_id, string data) {&lt;br /&gt;
        &lt;br /&gt;
        if (query_id != ConfigQueryId) {    return;     }   // Make sure this is the data event we asked for&lt;br /&gt;
        &lt;br /&gt;
        if (data == EOF) {                      // Finished with this card...&lt;br /&gt;
            if ( next_card() ) { return;  }     // Are there more cards to process?&lt;br /&gt;
            state on;                           // All cards done, go to the on state&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        data = llStringTrim(data, STRING_TRIM);                                     // Cut off white space from the front and end...&lt;br /&gt;
        if (llGetSubString(data,0,0) != &amp;quot;/&amp;quot; &amp;amp;&amp;amp; llStringLength(data)) {              // Skip comments and blank lines&lt;br /&gt;
            list ldata = llParseStringKeepNulls(data, [&amp;quot;|&amp;quot;], []);                   // Get the whole line for processing...&lt;br /&gt;
            string lable = llStringTrim(llList2String(ldata, 0), STRING_TRIM);      //   Extract lable&lt;br /&gt;
            string ballnum = llStringTrim(llList2String(ldata, 1), STRING_TRIM);    //   Extract ball number&lt;br /&gt;
            string target = llStringTrim(llList2String(ldata, 2), STRING_TRIM);     //   Extract target point&lt;br /&gt;
            string param = llStringTrim(llList2String(ldata, 3), STRING_TRIM);      //   Extract params&lt;br /&gt;
            add_cd(lable,ballnum,target,param);                                     // Add all that to chainData&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        ++ConfigLineIndex;                                                  // Incrament counter&lt;br /&gt;
        ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex); // Read next line of data notecard...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
state on {&lt;br /&gt;
    state_entry()   {&lt;br /&gt;
            debug(&amp;quot;[!] Ready!\n====================&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    on_rez(integer arg) { llResetScript(); }        // Object was rezed in world, reset script for freshness&lt;br /&gt;
    changed(integer change) {&lt;br /&gt;
        if (change &amp;amp; CHANGED_INVENTORY) state load; // Inventory changed, so reload the card data to be safe&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    link_message(integer from, integer num, string message, key id) {&lt;br /&gt;
        &lt;br /&gt;
        // Do we care about this link message?  If not, just return&lt;br /&gt;
        if (!((num == -11000) || (num == -11001) || (num == -11002) || (message == &amp;quot;POSEB&amp;quot;))) { return; }&lt;br /&gt;
    &lt;br /&gt;
        if (message == &amp;quot;POSEB&amp;quot;) {       // Is it a set change?&lt;br /&gt;
            CurrentSet = (string)id;    // Save the set name...&lt;br /&gt;
            return;                     // Nothing more for us, so return&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        list linedata = llParseStringKeepNulls(message,[&amp;quot;|&amp;quot;],[]);   // It&#039;s a generic sit/unsit/change update, so break it down&lt;br /&gt;
        string param1 = llList2String(linedata, 0);                 //    Extract ball number&lt;br /&gt;
        string param2 = llList2String(linedata, 1);                 //    Extract animation&lt;br /&gt;
&lt;br /&gt;
        integer findSet = llListFindList(chainData, (list)CurrentSet);  // Check chainData for this set name...        &lt;br /&gt;
        integer findAv = llListFindList(avData, [id]);                  // Check avData for the person sitting...&lt;br /&gt;
        &lt;br /&gt;
        debug(&amp;quot;==========\n= SET: &amp;quot; + CurrentSet + &amp;quot;   BALL: &amp;quot; + param1 + &amp;quot;   ANIMATION: &amp;quot; + param2);&lt;br /&gt;
&lt;br /&gt;
        if (findSet == -1) {                                            // There is no chain data for this set, so...&lt;br /&gt;
            debug(&amp;quot;[!] No chain data found&amp;quot;);&lt;br /&gt;
            if (findAv != -1) {                                         // Do we still have chain data for this av, and need to clear it?&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);  // find the stored data&lt;br /&gt;
                integer ix;                                                                         // initialize counter&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                 // Step through the stored data&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // unlink from points&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                   // end for&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);                               // finally delete the stored data&lt;br /&gt;
            }                                                                                       // end findAv if&lt;br /&gt;
            return;                                                                                 // No further processing for this event needed&lt;br /&gt;
        } else debug(&amp;quot;[!] Chain data found&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        list chainLog = [];                                                     // set up the list of commands [attachpoint, params]&lt;br /&gt;
        integer ii;                                                             // initialize counter var&lt;br /&gt;
        for(ii = 0; ii &amp;lt; llGetListLength(chainData); ii = ii + chainStride) {   // Run through the chainData list... [lable,ballnum,attachpoint,params]&lt;br /&gt;
            if (llList2String(chainData, ii) == CurrentSet) {                   //   if the set has a match...&lt;br /&gt;
                if (llList2String(chainData, ii + 1) == param1) {               //   if the matching set also matches ball...&lt;br /&gt;
                    chainLog = llList2List(chainData, ii + 2, ii + 3) + chainLog;   //   add attachpoint and params to chainLog&lt;br /&gt;
                }                                                               // End ball if&lt;br /&gt;
            }                                                                   // End set if&lt;br /&gt;
        }                                                                       // End for&lt;br /&gt;
        &lt;br /&gt;
        if (chainLog == []) {   return;   }     // We have data for this set, but not this ball.  Returning.&lt;br /&gt;
&lt;br /&gt;
        // TYPE: sit&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11000, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11000) {&lt;br /&gt;
            debug(&amp;quot;[!] Sit triggered&amp;quot;);&lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;                                                                     // set up tempPoints&lt;br /&gt;
            integer ix;                                                                                 // initalize counter&lt;br /&gt;
            for(ix = 0; ix &amp;lt; llGetListLength(chainLog); ix = ix + 2) {                                  // step through chainLog&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));          // find the target point in the key list&lt;br /&gt;
                &lt;br /&gt;
                if (findKey == -1) {                                                                    // if point does not exist..&lt;br /&gt;
                    llWhisper(0,&amp;quot;[!] &amp;quot; + llList2String(chainLog, ix) + &amp;quot; does not exist!&amp;quot;);             // warn user&lt;br /&gt;
                }                                                                                       // end if&lt;br /&gt;
                &lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,ix + 1) +  // sent link command to LG items&lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,ix + 1));&lt;br /&gt;
                tempPoints = tempPoints +  llList2String(chainLog,ix + 1) + &amp;quot;|&amp;quot;;                        // update tempPoints var&lt;br /&gt;
            }                                                                                           // end for&lt;br /&gt;
            &lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char since we&#039;re done&lt;br /&gt;
            avData = (avData=[]) + avData + [id];                                                       // add new key to avData [avkey,currentchains]&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints];                                               // add new chained points to avData&lt;br /&gt;
        }                                                                                               // End if for sit&lt;br /&gt;
&lt;br /&gt;
        // TYPE: unsit&lt;br /&gt;
        // FORMAT:  llMessageLinked(LINK_SET, -11001, (string)BallNum, llGetPermissionsKey());&lt;br /&gt;
        if (num == -11001) {&lt;br /&gt;
            debug(&amp;quot;[!] Unsit triggered&amp;quot;); &lt;br /&gt;
            if (findAv != -1) {                                                                         // If avatar exists in avData...&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);      // Get the chains we have active on av&lt;br /&gt;
                integer ix;                                                                             // Initialize counter var&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                     // Step through list of chain data&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // Tell LG items to let go&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                       // End for&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);                                   // Remove data from avData&lt;br /&gt;
            }                                                                                           // End if for av found in list&lt;br /&gt;
        }                                                                                               // End if for unsit&lt;br /&gt;
        &lt;br /&gt;
        // TYPE: change&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11002, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11002) {&lt;br /&gt;
            debug(&amp;quot;[!] Pose change triggered&amp;quot;);&lt;br /&gt;
                        &lt;br /&gt;
            if (findAv != -1) {                                                                         // If avatar exists in avData...&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);      // Get the chains we have active on av&lt;br /&gt;
                integer yx;                                                                             // Initialize counter var&lt;br /&gt;
                for(yx = 0; yx &amp;lt; llGetListLength(itemData); yx++) {                                     // Step through list of chain data&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,yx) + &amp;quot; unlink&amp;quot;);  // Tell LG items to let go&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,yx));&lt;br /&gt;
                }                                                                                       // End for&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);                                   // Remove data from avData&lt;br /&gt;
            }                                                                                           // End if for av found in list        &lt;br /&gt;
            &lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;                                                                     // set up tempPoints&lt;br /&gt;
            integer zx;                                                                                 // initalize counter&lt;br /&gt;
            for(zx = 0; zx &amp;lt; llGetListLength(chainLog); zx = zx + 2) {                                  // step through chainLog&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, zx));          // find the target point in the key list&lt;br /&gt;
                &lt;br /&gt;
                if (findKey == -1) {                                                                    // if point does not exist..&lt;br /&gt;
                    llWhisper(0,&amp;quot;[!] &amp;quot; + llList2String(chainLog, zx) + &amp;quot; does not exist!&amp;quot;);             // warn user&lt;br /&gt;
                }                                                                                       // end if&lt;br /&gt;
                &lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,zx + 1) +  // sent link command to LG items&lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,zx + 1));&lt;br /&gt;
                tempPoints = tempPoints +  llList2String(chainLog,zx + 1) + &amp;quot;|&amp;quot;;                        // update tempPoints var&lt;br /&gt;
            }                                                                                           // end for&lt;br /&gt;
            &lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char since we&#039;re done&lt;br /&gt;
            avData = (avData=[]) + avData + [id];                                                       // add new key to avData [avkey,currentchains]&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints];                                               // add new chained points to avData&lt;br /&gt;
        }                                                                                               // end change if&lt;br /&gt;
    }                                                                                                   // end link_message&lt;br /&gt;
}                                                                                                       // end state on                                                                                // end state on&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 3:&#039;&#039;&#039;&lt;br /&gt;
Create the data note card.  This card needs to be named .CHAINDATA but can have a further name past that if you need to use more than one card.  The card will look something like this:&lt;br /&gt;
&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 // Chain control&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 //  Part 1: menulable&lt;br /&gt;
 //  Part 2: ball chain is linked to&lt;br /&gt;
 //  Part 3: prim name to link to&lt;br /&gt;
 //  Part 4: LG attach point&lt;br /&gt;
 //  Part 4 optional commands:&lt;br /&gt;
 //      texture key&lt;br /&gt;
 //      size # #&lt;br /&gt;
 //      life #&lt;br /&gt;
 //      gravity #&lt;br /&gt;
 //      speed #&lt;br /&gt;
 //      color # # #&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
 Displayed | 0 | point1 | wrists gravity 0.0 life 2.0&lt;br /&gt;
 Hung | 0 | point1 | wrists gravity 0.0 life 0.75&lt;br /&gt;
 Dangled | 0 | point1 | ankles gravity 0.0 life 0.75&lt;br /&gt;
 Captured | 0 | point3 | wrists&lt;/div&gt;</summary>
		<author><name>Grey Mars</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1142415</id>
		<title>MLPV2 LockGuard plugin</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1142415"/>
		<updated>2011-05-04T23:30:37Z</updated>

		<summary type="html">&lt;p&gt;Grey Mars: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Back to [[MLPV2_Addons]]&lt;br /&gt;
* [[MLPV2]]&lt;br /&gt;
&lt;br /&gt;
Basically this is an alternate way of adding particle chain support to MLP2 using the LockGuard protocol.  When the object is rezzed in world the script makes a list of the prim names and keys for later.  It then reads the note card with the linking information each time inventory changes.  When MLP2 gives pose info the script determines if it pertains to it&#039;s list and if so notifies lockguard enabled attachments of what to do and what prim keys to do it to.&lt;br /&gt;
&lt;br /&gt;
So what do you need to do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
Figure out what prims you want to use as anchor points, and give each a unique name.  Personally I recommend the boring yet easy to remember format of point1, point2, point3, etc.  Remember these names for later.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
Add the following script to your furniture.  It may complain, but you can ignore that for now.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// MLPV2 Plugin for LockGuard Partical Chaining v1.2&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Notecard Format&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Notecard .CHAINDATA format:&lt;br /&gt;
// menulable | ballnumber | anchorprimname | LGattachpoint optionalparams&lt;br /&gt;
//&lt;br /&gt;
// Example data:&lt;br /&gt;
// Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
// Displayed | 0 | point1 | wrists gravity 0.0 life 2.0&lt;br /&gt;
// Hung | 0 | point1 | wrists gravity 0.0 life 0.75&lt;br /&gt;
// Dangled | 0 | point1 | ankles gravity 0.0 life 0.75&lt;br /&gt;
// Captured | 0 | point3 | wrists&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Copyright 2011, Grey Mars. All rights reserved.&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Redistribution and use in source and binary forms, with or without modification, are&lt;br /&gt;
// permitted provided that the following conditions are met:&lt;br /&gt;
//&lt;br /&gt;
//   1. Redistributions of source code must retain the above copyright notice, this list of&lt;br /&gt;
//      conditions and the following disclaimer.&lt;br /&gt;
//&lt;br /&gt;
//   2. Redistributions in binary form must reproduce the above copyright notice, this list&lt;br /&gt;
//      of conditions and the following disclaimer in the documentation and/or other materials&lt;br /&gt;
//      provided with the distribution.&lt;br /&gt;
//&lt;br /&gt;
// THIS SOFTWARE IS PROVIDED BY GREY MARS ``AS IS&#039;&#039; AND ANY EXPRESS OR IMPLIED&lt;br /&gt;
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND&lt;br /&gt;
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; OR&lt;br /&gt;
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR&lt;br /&gt;
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&lt;br /&gt;
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON&lt;br /&gt;
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING&lt;br /&gt;
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF&lt;br /&gt;
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;br /&gt;
//&lt;br /&gt;
// The views and conclusions contained in the software and documentation are those of the&lt;br /&gt;
// authors and should not be interpreted as representing official policies, either expressed&lt;br /&gt;
// or implied, of Grey Mars.&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Changes:&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Version 1.2&lt;br /&gt;
// - Large overhaul of how the data on the cards is stored.  Original format was made&lt;br /&gt;
//   to preserve the working of the MLP2 swap function.  However no one ever uses the&lt;br /&gt;
//   swap function in a chained context, so why complicate the notecard files?  Plus&lt;br /&gt;
//   new format is more memory friendly.&lt;br /&gt;
// - Notecard seperator changed from ; to | to match favorite RLV plugin format&lt;br /&gt;
// - Changed the need for the animation name to just the ball number in the notecard&lt;br /&gt;
// - Removed some unused variables&lt;br /&gt;
// - Note card renamed to .CHAINDATA to follow MLP2 naming conventions&lt;br /&gt;
// - Removed sim ratings data poll.&lt;br /&gt;
// - Added warning if the desired target point does not exist in the linkset&lt;br /&gt;
// - Added more comments because comments are good.&lt;br /&gt;
//&lt;br /&gt;
// Version 1.1&lt;br /&gt;
// - Added a data reload on inventory change&lt;br /&gt;
//&lt;br /&gt;
// Version 1.0&lt;br /&gt;
// - Changed script name from ~chaincontrol&lt;br /&gt;
// - Cleaned up old comments and made it more readable&lt;br /&gt;
// - First post to lsl wiki ( https://wiki.secondlife.com/wiki/MLPV2_Addons )&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Globals&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
integer VERBOSE = FALSE;        // Give extra info&lt;br /&gt;
&lt;br /&gt;
integer LGChannel = -9119;      // The LG listen channel&lt;br /&gt;
integer LGHandle;               // Handle ID for LG messages&lt;br /&gt;
&lt;br /&gt;
// Globals for reading card config&lt;br /&gt;
integer ConfigLineIndex;&lt;br /&gt;
list    ConfigCards;            // list of names of config cards&lt;br /&gt;
string  ConfigCardName;         // name of card being read&lt;br /&gt;
integer ConfigCardIndex;        // index of next card to read&lt;br /&gt;
key     ConfigQueryId;          // The dataserver ID for the query sent&lt;br /&gt;
&lt;br /&gt;
string  Pose;                   // Pose name&lt;br /&gt;
//string  Avname;                 // Av Name&lt;br /&gt;
key     Avkey;                  // Avatar Key&lt;br /&gt;
&lt;br /&gt;
string CurrentSet = &amp;quot;stand&amp;quot;;    // stand being the menu default&lt;br /&gt;
&lt;br /&gt;
list chainData = [];            // The list of all data from the notecard [lable,ballnum,attachpoint,params]&lt;br /&gt;
integer chainCount = 0;         // How many entries in the list&lt;br /&gt;
integer chainStride = 4;        // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
list primKeys = [];             // The list of prims in the object and thier keys [primname,key]&lt;br /&gt;
integer primKeysCount = 0;      // How many entries in the list&lt;br /&gt;
integer primKeysStride = 2;     // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
list avData = [];               // The list of currently chained avs [avkey,currentchains]&lt;br /&gt;
integer avDataCount = 0;        // How many entries in the list&lt;br /&gt;
integer avDataStride = 2;       // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Support functions&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
// Debugging outputs&lt;br /&gt;
debug(string text) {   if (VERBOSE) llOwnerSay(text);   }&lt;br /&gt;
&lt;br /&gt;
// get the next card in case there is more than one&lt;br /&gt;
integer next_card() {&lt;br /&gt;
    if (ConfigCardIndex &amp;gt;= llGetListLength(ConfigCards)) {&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        return (FALSE);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    ConfigLineIndex = 0;&lt;br /&gt;
    ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);&lt;br /&gt;
    ConfigCardIndex++;&lt;br /&gt;
    ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);&lt;br /&gt;
    debug(&amp;quot;[?] Reading file: &amp;quot; + ConfigCardName);&lt;br /&gt;
    return (TRUE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// add chain data to the list...&lt;br /&gt;
add_cd(string lable, string ballnum, string target, string params) {&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)lable;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)ballnum;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)target;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)params;&lt;br /&gt;
    ++chainCount;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Does this point actually referance a set of points?&lt;br /&gt;
list UnAliasChainPoints(list originalData) {&lt;br /&gt;
// wrists   -&amp;gt;  leftwrist rightwrist&lt;br /&gt;
// ankles   -&amp;gt;  leftankle rightankle&lt;br /&gt;
// allfour  -&amp;gt;  leftwrist rightwrist leftankle rightankle&lt;br /&gt;
// nipples  -&amp;gt;  leftnipplering rightnipplering &lt;br /&gt;
// arms     -&amp;gt;  leftupperarm rightupperarm&lt;br /&gt;
// thighs   -&amp;gt;  leftupperthigh rightupperthigh&lt;br /&gt;
// knees    -&amp;gt;  leftknee rightknee&lt;br /&gt;
&lt;br /&gt;
    list modData = [];&lt;br /&gt;
    integer xx;&lt;br /&gt;
    &lt;br /&gt;
    // Hacky.  Feel free to optimize.&lt;br /&gt;
    for(xx = 0; xx &amp;lt; llGetListLength(originalData); xx++) {&lt;br /&gt;
        if (llToLower(llList2String(originalData,xx)) == &amp;quot;wrists&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;ankles&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;allfour&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;, &amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;nipples&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftnipplering&amp;quot;, &amp;quot;rightnipplering&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;arms&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperarm&amp;quot;, &amp;quot;rightupperarm&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;thighs&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperthigh&amp;quot;, &amp;quot;rightupperthigh&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;knees&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftknee&amp;quot;, &amp;quot;rightknee&amp;quot;];&lt;br /&gt;
        } else {    modData = modData + llList2List(originalData,xx,xx);     }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return modData;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Main Script&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
default {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        &lt;br /&gt;
        // Now we build the prim key list...&lt;br /&gt;
        integer n;                                                      // Initialize the counter&lt;br /&gt;
        integer linkcount = llGetNumberOfPrims();                       // How many items in the link set?&lt;br /&gt;
        for (n = 2; n &amp;lt;= linkcount; n++)    {                           // Cycle through, skipping root&lt;br /&gt;
            string thiselement = llGetLinkName(n);                      // Get the prim name&lt;br /&gt;
            if (thiselement != &amp;quot;Object&amp;quot;) {                              // If the name is something besides the default...&lt;br /&gt;
                primKeys = primKeys + [thiselement, llGetLinkKey(n)];   // add it to primKeys&lt;br /&gt;
                primKeysCount++;                                        // increment counter&lt;br /&gt;
            }                                                           // end if&lt;br /&gt;
        }                                                               // end for loop&lt;br /&gt;
        &lt;br /&gt;
        state load;                                                     // Go load the data...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
state load  {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        string item;&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        integer n = llGetInventoryNumber(INVENTORY_NOTECARD);&lt;br /&gt;
        while (n-- &amp;gt; 0) { // get the data off cards with the right name&lt;br /&gt;
            item = llGetInventoryName(INVENTORY_NOTECARD, n);&lt;br /&gt;
            if (llSubStringIndex(item, &amp;quot;.CHAINDATA&amp;quot;) != -1) {&lt;br /&gt;
                ConfigCards = (ConfigCards=[]) + ConfigCards + (list) item;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        ConfigCardIndex = 0;&lt;br /&gt;
        ConfigCards = llListSort(ConfigCards, 1, TRUE);&lt;br /&gt;
        next_card();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dataserver(key query_id, string data) {&lt;br /&gt;
        &lt;br /&gt;
        if (query_id != ConfigQueryId) {    return;     }   // Make sure this is the data event we asked for&lt;br /&gt;
        &lt;br /&gt;
        if (data == EOF) {                      // Finished with this card...&lt;br /&gt;
            if ( next_card() ) { return;  }     // Are there more cards to process?&lt;br /&gt;
            state on;                           // All cards done, go to the on state&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        data = llStringTrim(data, STRING_TRIM);                                     // Cut off white space from the front and end...&lt;br /&gt;
        if (llGetSubString(data,0,0) != &amp;quot;/&amp;quot; &amp;amp;&amp;amp; llStringLength(data)) {              // Skip comments and blank lines&lt;br /&gt;
            list ldata = llParseStringKeepNulls(data, [&amp;quot;|&amp;quot;], []);                   // Get the whole line for processing...&lt;br /&gt;
            string lable = llStringTrim(llList2String(ldata, 0), STRING_TRIM);      //   Extract lable&lt;br /&gt;
            string ballnum = llStringTrim(llList2String(ldata, 1), STRING_TRIM);    //   Extract ball number&lt;br /&gt;
            string target = llStringTrim(llList2String(ldata, 2), STRING_TRIM);     //   Extract target point&lt;br /&gt;
            string param = llStringTrim(llList2String(ldata, 3), STRING_TRIM);      //   Extract params&lt;br /&gt;
            add_cd(lable,ballnum,target,param);                                     // Add all that to chainData&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        ++ConfigLineIndex;                                                  // Incrament counter&lt;br /&gt;
        ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex); // Read next line of data notecard...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
state on {&lt;br /&gt;
    state_entry()   {    }&lt;br /&gt;
    on_rez(integer arg) { llResetScript(); }        // Object was rezed in world, reset script for freshness&lt;br /&gt;
    changed(integer change) {&lt;br /&gt;
        if (change &amp;amp; CHANGED_INVENTORY) state load; // Inventory changed, so reload the card data to be safe&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    link_message(integer from, integer num, string message, key id) {&lt;br /&gt;
        &lt;br /&gt;
        // Do we care about this link message?  If not, just return&lt;br /&gt;
        if (!((num == -11000) || (num == -11001) || (num == -11002) || (message == &amp;quot;POSEB&amp;quot;))) { return; }&lt;br /&gt;
    &lt;br /&gt;
        if (message == &amp;quot;POSEB&amp;quot;) {       // Is it a set change?&lt;br /&gt;
            CurrentSet = (string)id;    // Save the set name...&lt;br /&gt;
            return;                     // Nothing more for us, so return&lt;br /&gt;
        }&lt;br /&gt;
        debug(&amp;quot;========== START&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        list linedata = llParseStringKeepNulls(message,[&amp;quot;|&amp;quot;],[]);   // It&#039;s a generic sit/unsit/change update, so break it down&lt;br /&gt;
        string param1 = llList2String(linedata, 0);                 //    Extract ball number&lt;br /&gt;
        string param2 = llList2String(linedata, 1);                 //    Extract animation&lt;br /&gt;
&lt;br /&gt;
        integer findSet = llListFindList(chainData, (list)CurrentSet);  // Check chainData for this set name...        &lt;br /&gt;
        integer findAv = llListFindList(avData, [id]);                  // Check avData for the person sitting...&lt;br /&gt;
&lt;br /&gt;
        if (findSet == -1) {                                            // There is no chain data for this set, so...&lt;br /&gt;
            debug(&amp;quot;[!] No chain data found&amp;quot;);&lt;br /&gt;
            if (findAv != -1) {                                         // Do we still have chain data for this av, and need to clear it?&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);  // find the stored data&lt;br /&gt;
                integer ix;                                                                         // initialize counter&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                 // Step through the stored data&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // unlink from points&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                   // end for&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);                               // finally delete the stored data&lt;br /&gt;
            }                                                                                       // end findAv if&lt;br /&gt;
            return;                                                                                 // No further processing for this event needed&lt;br /&gt;
        } else debug(&amp;quot;[!] Chain data found&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        if (llList2String(chainData,findSet + 1) != param1) {   return;   }     // We have data for this set, but not this ball.  Returning.&lt;br /&gt;
&lt;br /&gt;
        list chainLog = [];                                                     // set up the list of commands [attachpoint, params]&lt;br /&gt;
        integer ii;                                                             // initialize counter var&lt;br /&gt;
        for(ii = 0; ii &amp;lt; llGetListLength(chainData); ii = ii + chainStride) {   // Run through the chainData list... [lable,ballnum,attachpoint,params]&lt;br /&gt;
            if (llList2String(chainData, ii) == CurrentSet) {                   //   if the set has a match...&lt;br /&gt;
                chainLog = llList2List(chainData, ii + 2, ii + 3) + chainLog;   //   add attachpoint and params to chainLog&lt;br /&gt;
            }                                                                   // End if&lt;br /&gt;
        }                                                                       // End for&lt;br /&gt;
&lt;br /&gt;
        // TYPE: sit&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11000, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11000) {&lt;br /&gt;
            debug(&amp;quot;[!] Sit triggered&amp;quot;);&lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;                                                                     // set up tempPoints&lt;br /&gt;
            integer ix;                                                                                 // initalize counter&lt;br /&gt;
            for(ix = 0; ix &amp;lt; llGetListLength(chainLog); ix = ix + 2) {                                  // step through chainLog&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));          // find the target point in the key list&lt;br /&gt;
                &lt;br /&gt;
                if (findKey == -1) {                                                                    // if point does not exist..&lt;br /&gt;
                    llWhisper(0,&amp;quot;[!] &amp;quot; + llList2String(chainLog, ix) + &amp;quot; does not exist!&amp;quot;);             // warn user&lt;br /&gt;
                }                                                                                       // end if&lt;br /&gt;
                &lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,ix + 1) +  // sent link command to LG items&lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,ix + 1));&lt;br /&gt;
                tempPoints = tempPoints +  llList2String(chainLog,ix + 1) + &amp;quot;|&amp;quot;;                        // update tempPoints var&lt;br /&gt;
            }                                                                                           // end for&lt;br /&gt;
            &lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char since we&#039;re done&lt;br /&gt;
            avData = (avData=[]) + avData + [id];                                                       // add new key to avData [avkey,currentchains]&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints];                                               // add new chained points to avData&lt;br /&gt;
        }                                                                                               // End if for sit&lt;br /&gt;
&lt;br /&gt;
        // TYPE: unsit&lt;br /&gt;
        // FORMAT:  llMessageLinked(LINK_SET, -11001, (string)BallNum, llGetPermissionsKey());&lt;br /&gt;
        if (num == -11001) {&lt;br /&gt;
            debug(&amp;quot;[!] Unsit triggered&amp;quot;); &lt;br /&gt;
            if (findAv != -1) {                                                                         // If avatar exists in avData...&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);      // Get the chains we have active on av&lt;br /&gt;
                integer ix;                                                                             // Initialize counter var&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                     // Step through list of chain data&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // Tell LG items to let go&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                       // End for&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);                                   // Remove data from avData&lt;br /&gt;
            }                                                                                           // End if for av found in list&lt;br /&gt;
        }                                                                                               // End if for unsit&lt;br /&gt;
        &lt;br /&gt;
        // TYPE: change&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11002, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11002) {&lt;br /&gt;
            debug(&amp;quot;[!] Pose change triggered&amp;quot;);&lt;br /&gt;
            list itemData = [];                                                                         // Set up itemData&lt;br /&gt;
            if (findAv != -1) {                                                                         // Are we already doing something to av?&lt;br /&gt;
                itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);           // if we are, get that data&lt;br /&gt;
                itemData = UnAliasChainPoints(itemData);                                                // unalias the data for multiple points&lt;br /&gt;
            }                                                                                           // end if for av finding&lt;br /&gt;
&lt;br /&gt;
            integer ix;                                                                                 // Initialize counter var&lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;                                                                     // Set up a holding var&lt;br /&gt;
            for(ix = 0; ix &amp;lt; llGetListLength(chainLog); ix = ix + 2) {                                  // Step through chainLog&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));          // find location of attach point in primKeys&lt;br /&gt;
                &lt;br /&gt;
                if (findKey == -1) {                                                                    // if point does not exist..&lt;br /&gt;
                    llWhisper(0,&amp;quot;[!] &amp;quot; + llList2String(chainLog, ix) + &amp;quot; does not exist!&amp;quot;);             // warn user&lt;br /&gt;
                }                                                                                       // end if&lt;br /&gt;
                &lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,ix + 1) +  // Tell LG attachments to draw the chains&lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,ix + 1));&lt;br /&gt;
&lt;br /&gt;
                // cut down the command line to just the attach point for compairison&lt;br /&gt;
                string stripPoint = llList2String(llParseStringKeepNulls(llList2String(chainLog,ix + 1),[&amp;quot; &amp;quot;],[]), 0);&lt;br /&gt;
                tempPoints = tempPoints + stripPoint + &amp;quot;|&amp;quot;;                                             // update tempPoints&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char &amp;quot;|&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            if (findAv != -1) { avData = llDeleteSubList(avData, findAv, findAv + 1);   }               // clear old av data if it exists&lt;br /&gt;
            avData = (avData=[]) + avData + [id];                                                       // add new av key&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints];                                               // add new attach point data&lt;br /&gt;
            &lt;br /&gt;
            list tempData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);          // Grab the list of chained points&lt;br /&gt;
            tempData = UnAliasChainPoints(tempData);                                                    // Unalias the data if it has groups&lt;br /&gt;
            // compaire the two data lists, remove dumplicates&lt;br /&gt;
            integer yx;                                                                                 // Initialize counter var&lt;br /&gt;
            for(yx = 0; yx &amp;lt; llGetListLength(tempData); yx++) {                                         // step through tempData&lt;br /&gt;
                integer overlapWhere = llListFindList(itemData, llList2List(tempData, yx, yx));         // look for duplicate entries&lt;br /&gt;
                    if (overlapWhere != -1) {                                                           // if we find a duplicate...&lt;br /&gt;
                        itemData = llDeleteSubList(itemData, overlapWhere, overlapWhere);               //   remove it from the list&lt;br /&gt;
                    }                                                                                   // end duplication check if&lt;br /&gt;
            }                                                                                           // end for&lt;br /&gt;
            &lt;br /&gt;
            if ((findAv != -1) &amp;amp;&amp;amp; ( llGetListLength(itemData) &amp;gt; 0)) {                                   // Do we have left over points for av?&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                     // Step through itemData&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // tell LG to unlink&lt;br /&gt;
                debug(&amp;quot;[-] &amp;quot; +  llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                       // end for&lt;br /&gt;
            }                                                                                           // end leftover if&lt;br /&gt;
        }                                                                                               // end change if&lt;br /&gt;
    }                                                                                                   // end link_message&lt;br /&gt;
}                                                                                                       // end state on&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 3:&#039;&#039;&#039;&lt;br /&gt;
Create the data note card.  This card needs to be named .CHAINDATA but can have a further name past that if you need to use more than one card.  The card will look something like this:&lt;br /&gt;
&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 // Chain control&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 //  Part 1: menulable&lt;br /&gt;
 //  Part 2: ball chain is linked to&lt;br /&gt;
 //  Part 3: prim name to link to&lt;br /&gt;
 //  Part 4: LG attach point&lt;br /&gt;
 //  Part 4 optional commands:&lt;br /&gt;
 //      texture key&lt;br /&gt;
 //      size # #&lt;br /&gt;
 //      life #&lt;br /&gt;
 //      gravity #&lt;br /&gt;
 //      speed #&lt;br /&gt;
 //      color # # #&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
 Displayed | 0 | point1 | wrists gravity 0.0 life 2.0&lt;br /&gt;
 Hung | 0 | point1 | wrists gravity 0.0 life 0.75&lt;br /&gt;
 Dangled | 0 | point1 | ankles gravity 0.0 life 0.75&lt;br /&gt;
 Captured | 0 | point3 | wrists&lt;/div&gt;</summary>
		<author><name>Grey Mars</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1141455</id>
		<title>MLPV2 LockGuard plugin</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1141455"/>
		<updated>2011-04-22T19:08:57Z</updated>

		<summary type="html">&lt;p&gt;Grey Mars: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Back to [[MLPV2_Addons]]&lt;br /&gt;
* [[MLPV2]]&lt;br /&gt;
&lt;br /&gt;
Basically this is an alternate way of adding particle chain support to MLP2 using the LockGuard protocol.  When the object is rezzed in world the script makes a list of the prim names and keys for later.  It then reads the note card with the linking information each time inventory changes.  When MLP2 gives pose info the script determines if it pertains to it&#039;s list and if so notifies lockguard enabled attachments of what to do and what prim keys to do it to.&lt;br /&gt;
&lt;br /&gt;
So what do you need to do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
Figure out what prims you want to use as anchor points, and give each a unique name.  Personally I recommend the boring yet easy to remember format of point1, point2, point3, etc.  Remember these names for later.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
Add the following script to your furniture.  It may complain, but you can ignore that for now.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// MLPV2 Plugin for LockGuard Partical Chaining v1.2&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Notecard Format&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Notecard .CHAINDATA format:&lt;br /&gt;
// menulable | ballnumber | anchorprimname | LGattachpoint optionalparams&lt;br /&gt;
//&lt;br /&gt;
// Example data:&lt;br /&gt;
// Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
// Displayed | 0 | point1 | wrists gravity 0.0 life 2.0&lt;br /&gt;
// Hung | 0 | point1 | wrists gravity 0.0 life 0.75&lt;br /&gt;
// Dangled | 0 | point1 | ankles gravity 0.0 life 0.75&lt;br /&gt;
// Captured | 0 | point3 | wrists&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Copyright 2011, Grey Mars. All rights reserved.&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Redistribution and use in source and binary forms, with or without modification, are&lt;br /&gt;
// permitted provided that the following conditions are met:&lt;br /&gt;
//&lt;br /&gt;
//   1. Redistributions of source code must retain the above copyright notice, this list of&lt;br /&gt;
//      conditions and the following disclaimer.&lt;br /&gt;
//&lt;br /&gt;
//   2. Redistributions in binary form must reproduce the above copyright notice, this list&lt;br /&gt;
//      of conditions and the following disclaimer in the documentation and/or other materials&lt;br /&gt;
//      provided with the distribution.&lt;br /&gt;
//&lt;br /&gt;
// THIS SOFTWARE IS PROVIDED BY GREY MARS ``AS IS&#039;&#039; AND ANY EXPRESS OR IMPLIED&lt;br /&gt;
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND&lt;br /&gt;
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; OR&lt;br /&gt;
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR&lt;br /&gt;
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&lt;br /&gt;
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON&lt;br /&gt;
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING&lt;br /&gt;
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF&lt;br /&gt;
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;br /&gt;
//&lt;br /&gt;
// The views and conclusions contained in the software and documentation are those of the&lt;br /&gt;
// authors and should not be interpreted as representing official policies, either expressed&lt;br /&gt;
// or implied, of Grey Mars.&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Changes:&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Version 1.2&lt;br /&gt;
// - Large overhaul of how the data on the cards is stored.  Original format was made&lt;br /&gt;
//   to preserve the working of the MLP2 swap function.  However no one ever uses the&lt;br /&gt;
//   swap function in a chained context, so why complicate the notecard files?  Plus&lt;br /&gt;
//   new format is more memory friendly.&lt;br /&gt;
// - Notecard seperator changed from ; to | to match favorite RLV plugin format&lt;br /&gt;
// - Changed the need for the animation name to just the ball number in the notecard&lt;br /&gt;
// - Removed some unused variables&lt;br /&gt;
// - Note card renamed to .CHAINDATA to follow MLP2 naming conventions&lt;br /&gt;
// - Removed sim ratings data poll.&lt;br /&gt;
// - Added warning if the desired target point does not exist in the linkset&lt;br /&gt;
// - Added more comments because comments are good.&lt;br /&gt;
//&lt;br /&gt;
// Version 1.1&lt;br /&gt;
// - Added a data reload on inventory change&lt;br /&gt;
//&lt;br /&gt;
// Version 1.0&lt;br /&gt;
// - Changed script name from ~chaincontrol&lt;br /&gt;
// - Cleaned up old comments and made it more readable&lt;br /&gt;
// - First post to lsl wiki ( https://wiki.secondlife.com/wiki/MLPV2_Addons )&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Globals&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
integer VERBOSE = FALSE;        // Give extra info&lt;br /&gt;
&lt;br /&gt;
integer LGChannel = -9119;      // The LG listen channel&lt;br /&gt;
integer LGHandle;               // Handle ID for LG messages&lt;br /&gt;
&lt;br /&gt;
// Globals for reading card config&lt;br /&gt;
integer ConfigLineIndex;&lt;br /&gt;
list    ConfigCards;            // list of names of config cards&lt;br /&gt;
string  ConfigCardName;         // name of card being read&lt;br /&gt;
integer ConfigCardIndex;        // index of next card to read&lt;br /&gt;
key     ConfigQueryId;          // The dataserver ID for the query sent&lt;br /&gt;
&lt;br /&gt;
string  Pose;                   // Pose name&lt;br /&gt;
//string  Avname;                 // Av Name&lt;br /&gt;
key     Avkey;                  // Avatar Key&lt;br /&gt;
&lt;br /&gt;
string CurrentSet = &amp;quot;stand&amp;quot;;    // stand being the menu default&lt;br /&gt;
&lt;br /&gt;
list chainData = [];            // The list of all data from the notecard [lable,ballnum,attachpoint,params]&lt;br /&gt;
integer chainCount = 0;         // How many entries in the list&lt;br /&gt;
integer chainStride = 4;        // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
list primKeys = [];             // The list of prims in the object and thier keys [primname,key]&lt;br /&gt;
integer primKeysCount = 0;      // How many entries in the list&lt;br /&gt;
integer primKeysStride = 2;     // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
list avData = [];               // The list of currently chained avs [avkey,currentchains]&lt;br /&gt;
integer avDataCount = 0;        // How many entries in the list&lt;br /&gt;
integer avDataStride = 2;       // The stride count for the list&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Support functions&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
// Debugging outputs&lt;br /&gt;
debug(string text) {   if (VERBOSE) llOwnerSay(text);   }&lt;br /&gt;
&lt;br /&gt;
// get the next card in case there is more than one&lt;br /&gt;
integer next_card() {&lt;br /&gt;
    if (ConfigCardIndex &amp;gt;= llGetListLength(ConfigCards)) {&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        return (FALSE);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    ConfigLineIndex = 0;&lt;br /&gt;
    ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);&lt;br /&gt;
    ConfigCardIndex++;&lt;br /&gt;
    ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);&lt;br /&gt;
    debug(&amp;quot;[?] Reading file: &amp;quot; + ConfigCardName);&lt;br /&gt;
    return (TRUE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// add chain data to the list...&lt;br /&gt;
add_cd(string lable, string ballnum, string target, string params) {&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)lable;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)ballnum;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)target;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)params;&lt;br /&gt;
    ++chainCount;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Does this point actually referance a set of points?&lt;br /&gt;
list UnAliasChainPoints(list originalData) {&lt;br /&gt;
// wrists   -&amp;gt;  leftwrist rightwrist&lt;br /&gt;
// ankles   -&amp;gt;  leftankle rightankle&lt;br /&gt;
// allfour  -&amp;gt;  leftwrist rightwrist leftankle rightankle&lt;br /&gt;
// nipples  -&amp;gt;  leftnipplering rightnipplering &lt;br /&gt;
// arms     -&amp;gt;  leftupperarm rightupperarm&lt;br /&gt;
// thighs   -&amp;gt;  leftupperthigh rightupperthigh&lt;br /&gt;
// knees    -&amp;gt;  leftknee rightknee&lt;br /&gt;
&lt;br /&gt;
    list modData = [];&lt;br /&gt;
    integer xx;&lt;br /&gt;
    &lt;br /&gt;
    // Hacky.  Feel free to optimize.&lt;br /&gt;
    for(xx = 0; xx &amp;lt; llGetListLength(originalData); xx++) {&lt;br /&gt;
        if (llToLower(llList2String(originalData,xx)) == &amp;quot;wrists&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;ankles&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;allfour&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;, &amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;nipples&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftnipplering&amp;quot;, &amp;quot;rightnipplering&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;arms&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperarm&amp;quot;, &amp;quot;rightupperarm&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;thighs&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperthigh&amp;quot;, &amp;quot;rightupperthigh&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;knees&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftknee&amp;quot;, &amp;quot;rightknee&amp;quot;];&lt;br /&gt;
        } else {    modData = modData + llList2List(originalData,xx,xx);     }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return modData;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Main Script&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
default {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        &lt;br /&gt;
        // Now we build the prim key list...&lt;br /&gt;
        integer n;                                                      // Initialize the counter&lt;br /&gt;
        integer linkcount = llGetNumberOfPrims();                       // How many items in the link set?&lt;br /&gt;
        for (n = 2; n &amp;lt;= linkcount; n++)    {                           // Cycle through, skipping root&lt;br /&gt;
            string thiselement = llGetLinkName(n);                      // Get the prim name&lt;br /&gt;
            if (thiselement != &amp;quot;Object&amp;quot;) {                              // If the name is something besides the default...&lt;br /&gt;
                primKeys = primKeys + [thiselement, llGetLinkKey(n)];   // add it to primKeys&lt;br /&gt;
                primKeysCount++;                                        // increment counter&lt;br /&gt;
            }                                                           // end if&lt;br /&gt;
        }                                                               // end for loop&lt;br /&gt;
        &lt;br /&gt;
        state load;                                                     // Go load the data...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
state load  {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        string item;&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        integer n = llGetInventoryNumber(INVENTORY_NOTECARD);&lt;br /&gt;
        while (n-- &amp;gt; 0) { // get the data off cards with the right name&lt;br /&gt;
            item = llGetInventoryName(INVENTORY_NOTECARD, n);&lt;br /&gt;
            if (llSubStringIndex(item, &amp;quot;.CHAINDATA&amp;quot;) != -1) {&lt;br /&gt;
                ConfigCards = (ConfigCards=[]) + ConfigCards + (list) item;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        ConfigCardIndex = 0;&lt;br /&gt;
        ConfigCards = llListSort(ConfigCards, 1, TRUE);&lt;br /&gt;
        next_card();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dataserver(key query_id, string data) {&lt;br /&gt;
        &lt;br /&gt;
        if (query_id != ConfigQueryId) {    return;     }   // Make sure this is the data event we asked for&lt;br /&gt;
        &lt;br /&gt;
        if (data == EOF) {                      // Finished with this card...&lt;br /&gt;
            if ( next_card() ) { return;  }     // Are there more cards to process?&lt;br /&gt;
            state on;                           // All cards done, go to the on state&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        data = llStringTrim(data, STRING_TRIM);                                     // Cut off white space from the front and end...&lt;br /&gt;
        if (llGetSubString(data,0,0) != &amp;quot;/&amp;quot; &amp;amp;&amp;amp; llStringLength(data)) {              // Skip comments and blank lines&lt;br /&gt;
            list ldata = llParseStringKeepNulls(data, [&amp;quot;|&amp;quot;], []);                   // Get the whole line for processing...&lt;br /&gt;
            string lable = llStringTrim(llList2String(ldata, 0), STRING_TRIM);      //   Extract lable&lt;br /&gt;
            string ballnum = llStringTrim(llList2String(ldata, 1), STRING_TRIM);    //   Extract ball number&lt;br /&gt;
            string target = llStringTrim(llList2String(ldata, 2), STRING_TRIM);     //   Extract target point&lt;br /&gt;
            string param = llStringTrim(llList2String(ldata, 3), STRING_TRIM);      //   Extract params&lt;br /&gt;
            add_cd(lable,ballnum,target,param);                                     // Add all that to chainData&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        ++ConfigLineIndex;                                                  // Incrament counter&lt;br /&gt;
        ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex); // Read next line of data notecard...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
state on {&lt;br /&gt;
    state_entry()   {    }&lt;br /&gt;
    on_rez(integer arg) { llResetScript(); }        // Object was rezed in world, reset script for freshness&lt;br /&gt;
    changed(integer change) {&lt;br /&gt;
        if (change &amp;amp; CHANGED_INVENTORY) state load; // Inventory changed, so reload the card data to be safe&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    link_message(integer from, integer num, string message, key id) {&lt;br /&gt;
        &lt;br /&gt;
        // Do we care about this link message?  If not, just return&lt;br /&gt;
        if (!((num == -11000) || (num == -11001) || (num == -11002) || (message == &amp;quot;POSEB&amp;quot;))) { return; }&lt;br /&gt;
    &lt;br /&gt;
        if (message == &amp;quot;POSEB&amp;quot;) {       // Is it a set change?&lt;br /&gt;
            CurrentSet = (string)id;    // Save the set name...&lt;br /&gt;
            return;                     // Nothing more for us, so return&lt;br /&gt;
        }&lt;br /&gt;
        debug(&amp;quot;========== START&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        list linedata = llParseStringKeepNulls(message,[&amp;quot;|&amp;quot;],[]);   // It&#039;s a generic sit/unsit/change update, so break it down&lt;br /&gt;
        string param1 = llList2String(linedata, 0);                 //    Extract ball number&lt;br /&gt;
        string param2 = llList2String(linedata, 1);                 //    Extract animation&lt;br /&gt;
&lt;br /&gt;
        integer findSet = llListFindList(chainData, (list)CurrentSet);  // Check chainData for this set name...        &lt;br /&gt;
        integer findAv = llListFindList(avData, [id]);                  // Check avData for the person sitting...&lt;br /&gt;
&lt;br /&gt;
        if (findSet == -1) {                                            // There is no chain data for this set, so...&lt;br /&gt;
            debug(&amp;quot;[!] No chain data found&amp;quot;);&lt;br /&gt;
            if (findAv != -1) {                                         // Do we still have chain data for this av, and need to clear it?&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);  // find the stored data&lt;br /&gt;
                integer ix;                                                                         // initialize counter&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                 // Step through the stored data&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // unlink from points&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                   // end for&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);                               // finally delete the stored data&lt;br /&gt;
            }                                                                                       // end findAv if&lt;br /&gt;
            return;                                                                                 // No further processing for this event needed&lt;br /&gt;
        } else debug(&amp;quot;[!] Chain data found&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        if (llList2String(chainData,findSet + 1) != param1) {   return;   }     // We have data for this set, but not this ball.  Returning.&lt;br /&gt;
&lt;br /&gt;
        list chainLog = [];                                                     // set up the list of commands [attachpoint, params]&lt;br /&gt;
        integer ii;                                                             // initialize counter var&lt;br /&gt;
        for(ii = 0; ii &amp;lt; llGetListLength(chainData); ii = ii + chainStride) {   // Run through the chainData list... [lable,ballnum,attachpoint,params]&lt;br /&gt;
            if (llList2String(chainData, ii) == CurrentSet) {                   //   if the set has a match...&lt;br /&gt;
                chainLog = llList2List(chainData, ii + 2, ii + 3) + chainLog;   //   add attachpoint and params to chainLog&lt;br /&gt;
            }                                                                   // End if&lt;br /&gt;
        }                                                                       // End for&lt;br /&gt;
&lt;br /&gt;
        // TYPE: sit&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11000, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11000) {&lt;br /&gt;
            debug(&amp;quot;[!] Sit triggered&amp;quot;);&lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;                                                                     // set up tempPoints&lt;br /&gt;
            integer ix;                                                                                 // initalize counter&lt;br /&gt;
            for(ix = 0; ix &amp;lt; llGetListLength(chainLog); ix = ix + 2) {                                  // step through chainLog&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));          // find the target point in the key list&lt;br /&gt;
                &lt;br /&gt;
                if (findKey == -1) {                                                                    // if point does not exist..&lt;br /&gt;
                    llWhisper(0,&amp;quot;[!] &amp;quot; + llList2String(chainLog, ix) + &amp;quot; does not exist!&amp;quot;);             // warn user&lt;br /&gt;
                }                                                                                       // end if&lt;br /&gt;
                &lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,ix + 1) +  // sent link command to LG items&lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,ix + 1));&lt;br /&gt;
                tempPoints = tempPoints +  llList2String(chainLog,ix + 1) + &amp;quot;|&amp;quot;;                        // update tempPoints var&lt;br /&gt;
            }                                                                                           // end for&lt;br /&gt;
            &lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char since we&#039;re done&lt;br /&gt;
            avData = (avData=[]) + avData + [id];                                                       // add new key to avData [avkey,currentchains]&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints];                                               // add new chained points to avData&lt;br /&gt;
        }                                                                                               // End if for sit&lt;br /&gt;
&lt;br /&gt;
        // TYPE: unsit&lt;br /&gt;
        // FORMAT:  llMessageLinked(LINK_SET, -11001, (string)BallNum, llGetPermissionsKey());&lt;br /&gt;
        if (num == -11001) {&lt;br /&gt;
            debug(&amp;quot;[!] Unsit triggered&amp;quot;); &lt;br /&gt;
            if (findAv != -1) {                                                                         // If avatar exists in avData...&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);      // Get the chains we have active on av&lt;br /&gt;
                integer ix;                                                                             // Initialize counter var&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                     // Step through list of chain data&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // Tell LG items to let go&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                       // End for&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);                                   // Remove data from avData&lt;br /&gt;
            }                                                                                           // End if for av found in list&lt;br /&gt;
        }                                                                                               // End if for unsit&lt;br /&gt;
        &lt;br /&gt;
        // TYPE: change&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11002, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11002) {&lt;br /&gt;
            debug(&amp;quot;[!] Pose change triggered&amp;quot;);&lt;br /&gt;
            list itemData = [];                                                                         // Set up itemData&lt;br /&gt;
            if (findAv != -1) {                                                                         // Are we already doing something to av?&lt;br /&gt;
                itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);           // if we are, get that data&lt;br /&gt;
                itemData = UnAliasChainPoints(itemData);                                                // unalias the data for multiple points&lt;br /&gt;
            }                                                                                           // end if for av finding&lt;br /&gt;
&lt;br /&gt;
            integer ix;                                                                                 // Initialize counter var&lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;                                                                     // Set up a holding var&lt;br /&gt;
            for(ix = 0; ix &amp;lt; llGetListLength(chainLog); ix = ix + 2) {                                  // Step through chainLog&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));          // find location of attach point in primKeys&lt;br /&gt;
                &lt;br /&gt;
                if (findKey == -1) {                                                                    // if point does not exist..&lt;br /&gt;
                    llWhisper(0,&amp;quot;[!] &amp;quot; + llList2String(chainLog, ix) + &amp;quot; does not exist!&amp;quot;);             // warn user&lt;br /&gt;
                }                                                                                       // end if&lt;br /&gt;
                &lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,ix + 1) +  // Tell LG attachments to draw the chains&lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,ix + 1));&lt;br /&gt;
&lt;br /&gt;
                // cut down the command line to just the attach point for compairison&lt;br /&gt;
                string stripPoint = llList2String(llParseStringKeepNulls(llList2String(chainLog,ix + 1),[&amp;quot; &amp;quot;],[]), 0);&lt;br /&gt;
                tempPoints = tempPoints + stripPoint + &amp;quot;|&amp;quot;;                                             // update tempPoints&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char &amp;quot;|&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            if (findAv != -1) { avData = llDeleteSubList(avData, findAv, findAv + 1);   }               // clear old av data if it exists&lt;br /&gt;
            avData = (avData=[]) + avData + [id];                                                       // add new av key&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints];                                               // add new attach point data&lt;br /&gt;
            &lt;br /&gt;
            list tempData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);          // Grab the list of chained points&lt;br /&gt;
            tempData = UnAliasChainPoints(tempData);                                                    // Unalias the data if it has groups&lt;br /&gt;
            // compaire the two data lists, remove dumplicates&lt;br /&gt;
            integer yx;                                                                                 // Initialize counter var&lt;br /&gt;
            for(yx = 0; yx &amp;lt; llGetListLength(tempData); yx++) {                                         // step through tempData&lt;br /&gt;
                integer overlapWhere = llListFindList(itemData, llList2List(tempData, yx, yx));         // look for duplicate entries&lt;br /&gt;
                    if (overlapWhere != -1) {                                                           // if we find a duplicate...&lt;br /&gt;
                        itemData = llDeleteSubList(itemData, overlapWhere, overlapWhere);               //   remove it from the list&lt;br /&gt;
                    }                                                                                   // end duplication check if&lt;br /&gt;
            }                                                                                           // end for&lt;br /&gt;
            &lt;br /&gt;
            if ((findAv != -1) &amp;amp;&amp;amp; ( llGetListLength(itemData) &amp;gt; 0)) {                                   // Do we have left over points for av?&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {                                     // Step through itemData&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);  // tell LG to unlink&lt;br /&gt;
                debug(&amp;quot;[-] &amp;quot; +  llList2String(itemData,ix));&lt;br /&gt;
                }                                                                                       // end for&lt;br /&gt;
            }                                                                                           // end leftover if&lt;br /&gt;
        }                                                                                               // end change if&lt;br /&gt;
    }                                                                                                   // end link_message&lt;br /&gt;
}                                                                                                       // end state on&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 3:&#039;&#039;&#039;&lt;br /&gt;
Create the data note card.  This card needs to be named .chains, but can have a further name past that if you need to use more than one card.  The card will look something like this:&lt;br /&gt;
&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 // Chain control&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 //  Part 1: menulable&lt;br /&gt;
 //  Part 2: ball chain is linked to&lt;br /&gt;
 //  Part 3: prim name to link to&lt;br /&gt;
 //  Part 4: LG attach point&lt;br /&gt;
 //  Part 4 optional commands:&lt;br /&gt;
 //      texture key&lt;br /&gt;
 //      size # #&lt;br /&gt;
 //      life #&lt;br /&gt;
 //      gravity #&lt;br /&gt;
 //      speed #&lt;br /&gt;
 //      color # # #&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
 Displayed | 0 | point1 | wrists gravity 0.0 life 2.0&lt;br /&gt;
 Hung | 0 | point1 | wrists gravity 0.0 life 0.75&lt;br /&gt;
 Dangled | 0 | point1 | ankles gravity 0.0 life 0.75&lt;br /&gt;
 Captured | 0 | point3 | wrists&lt;/div&gt;</summary>
		<author><name>Grey Mars</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MLPV2_Addons&amp;diff=1140419</id>
		<title>MLPV2 Addons</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MLPV2_Addons&amp;diff=1140419"/>
		<updated>2011-04-14T20:14:51Z</updated>

		<summary type="html">&lt;p&gt;Grey Mars: /* Patches */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Navbox/MLPV2}}&lt;br /&gt;
&lt;br /&gt;
*[[MLPV2 Give Item Add-on]]&lt;br /&gt;
*[[MLPV2 Give Folder Add-on]]&lt;br /&gt;
*[[Check Animations Permissions Tool]]&lt;br /&gt;
*[[Cleaner MLP Say|&amp;quot;Cleaner&amp;quot; MLP Say]]&lt;br /&gt;
*[[MLPV2_Texture_Changer_Add-on]]&lt;br /&gt;
*[[MLPV2 Color Changer Add-on]]&lt;br /&gt;
*[[MLPV2_Props_Texture_Changer_Add-on]]&lt;br /&gt;
*[[MLPV2_Ambiant_Sound_Add-on]]&lt;br /&gt;
*[[MLPV2_RLV_Plugin]]&lt;br /&gt;
*[[MLPV2_RLV_Capture_Plugin]]&lt;br /&gt;
*[[Reset_from_Child_Plugin]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Patches===&lt;br /&gt;
* [[MLPV2_Balls_Patch]]: Use different balls for ~ball0...5&lt;br /&gt;
* [[MLPV2_LockGuard_plugin]]: Add LockGuard support to MLP2&lt;/div&gt;</summary>
		<author><name>Grey Mars</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1140418</id>
		<title>MLPV2 LockGuard plugin</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MLPV2_LockGuard_plugin&amp;diff=1140418"/>
		<updated>2011-04-14T20:13:19Z</updated>

		<summary type="html">&lt;p&gt;Grey Mars: A plugin script for MLP2 to add LockGuard Support&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Back to [[MLPV2_Addons]]&lt;br /&gt;
* [[MLPV2]]&lt;br /&gt;
&lt;br /&gt;
Basically this is an alternate way of adding particle chain support to MLP2 using the LockGuard protocol.  When the object is rezzed in world the script makes a list of the prim names and keys for later.  It then reads the note card with the linking information each time inventory changes.  When MLP2 gives pose info the script determines if it pertains to it&#039;s list and if so notifies lockguard enabled attachments of what to do and what prim keys to do it to.&lt;br /&gt;
&lt;br /&gt;
So what do you need to do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
Figure out what prims you want to use as anchor points, and give each a unique name.  Personally I recommend the boring yet easy to remember format of point1, point2, point3, etc.  Remember these names for later.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
Add the following script to your furniture.  It may complain, but you can ignore that for now.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// MLPV2 Plugin for LockGuard Partical Chaining v1.1&lt;br /&gt;
// &lt;br /&gt;
// Notecard .chains format:&lt;br /&gt;
// menulable;animation;anchorprimname;LGattachpoint optionalparams&lt;br /&gt;
//&lt;br /&gt;
// Example data:&lt;br /&gt;
// Bound;Short Posts-1;point2;rightwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound;Short Posts-1;point4;leftwrist gravity 0.2 life 0.75&lt;br /&gt;
// Bound;Short Posts-1;point3;collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
// Displayed;dungeon-2;point1;wrists gravity 0.0 life 2.0&lt;br /&gt;
// Hung;dungeon-3;point1;wrists gravity 0.0 life 0.75&lt;br /&gt;
// Dangled;Hung-2A;point1;ankles gravity 0.0 life 0.75&lt;br /&gt;
// Captured;Why Me-v2;point3;wrists&lt;br /&gt;
//&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Copyright 2011, Grey Mars. All rights reserved.&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Redistribution and use in source and binary forms, with or without modification, are&lt;br /&gt;
// permitted provided that the following conditions are met:&lt;br /&gt;
//&lt;br /&gt;
//   1. Redistributions of source code must retain the above copyright notice, this list of&lt;br /&gt;
//      conditions and the following disclaimer.&lt;br /&gt;
//&lt;br /&gt;
//   2. Redistributions in binary form must reproduce the above copyright notice, this list&lt;br /&gt;
//      of conditions and the following disclaimer in the documentation and/or other materials&lt;br /&gt;
//      provided with the distribution.&lt;br /&gt;
//&lt;br /&gt;
// THIS SOFTWARE IS PROVIDED BY GREY MARS ``AS IS&#039;&#039; AND ANY EXPRESS OR IMPLIED&lt;br /&gt;
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND&lt;br /&gt;
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; OR&lt;br /&gt;
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR&lt;br /&gt;
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&lt;br /&gt;
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON&lt;br /&gt;
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING&lt;br /&gt;
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF&lt;br /&gt;
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;br /&gt;
//&lt;br /&gt;
// The views and conclusions contained in the software and documentation are those of the&lt;br /&gt;
// authors and should not be interpreted as representing official policies, either expressed&lt;br /&gt;
// or implied, of Grey Mars.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Globals&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
integer VERBOSE = FALSE;        // Give extra info&lt;br /&gt;
&lt;br /&gt;
integer LGChannel = -9119;      // The LG listen channel&lt;br /&gt;
integer LGHandle;               // Handle ID for LG messages&lt;br /&gt;
&lt;br /&gt;
// Globals for reading card config&lt;br /&gt;
integer ConfigLineIndex;&lt;br /&gt;
list    ConfigCards;        // list of names of config cards&lt;br /&gt;
string  ConfigCardName;     // name of card being read&lt;br /&gt;
integer ConfigCardIndex;    // index of next card to read&lt;br /&gt;
key     ConfigQueryId;&lt;br /&gt;
&lt;br /&gt;
// Currently unused variables.  In the future may connect with some verification&lt;br /&gt;
key     SimRatingReq;&lt;br /&gt;
string  Rating;&lt;br /&gt;
integer Verified;&lt;br /&gt;
&lt;br /&gt;
// Variables used with chain data and pose reading.&lt;br /&gt;
// It could be worse, but it could also be a lot more&lt;br /&gt;
// efficient.&lt;br /&gt;
string  Pose;&lt;br /&gt;
string  Avname;&lt;br /&gt;
key     Avkey;&lt;br /&gt;
&lt;br /&gt;
string CurrentSet;&lt;br /&gt;
&lt;br /&gt;
list chainData = [];&lt;br /&gt;
integer chainCount = 0;&lt;br /&gt;
integer chainStride = 4;&lt;br /&gt;
&lt;br /&gt;
list primKeys = [];&lt;br /&gt;
integer primKeysCount = 0;&lt;br /&gt;
integer primKeysStride = 2;&lt;br /&gt;
&lt;br /&gt;
list avData = [];&lt;br /&gt;
integer avDataCount = 0;&lt;br /&gt;
integer avDataStride = 2;&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Support functions&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
// Debugging outputs&lt;br /&gt;
debug(string text) {   if (VERBOSE) llOwnerSay(text);   }&lt;br /&gt;
&lt;br /&gt;
// get the next card in case there is more than one&lt;br /&gt;
integer next_card() {&lt;br /&gt;
    if (ConfigCardIndex &amp;gt;= llGetListLength(ConfigCards)) {&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        return (FALSE);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    ConfigLineIndex = 0;&lt;br /&gt;
    ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);&lt;br /&gt;
    ConfigCardIndex++;&lt;br /&gt;
    ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);&lt;br /&gt;
    debug(&amp;quot;[?] Reading file: &amp;quot; + ConfigCardName);&lt;br /&gt;
    return (TRUE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// add chain data to the list...&lt;br /&gt;
add_cd(string lable, string anim, string target, string params) {&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)lable;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)anim;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)target;&lt;br /&gt;
    chainData = (chainData=[]) + chainData + (list)params;&lt;br /&gt;
    ++chainCount;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// does this point actually referance a set of points?&lt;br /&gt;
list UnAliasChainPoints(list originalData) {&lt;br /&gt;
// wrists   -&amp;gt;  leftwrist rightwrist&lt;br /&gt;
// ankles   -&amp;gt;  leftankle rightankle&lt;br /&gt;
// allfour  -&amp;gt;  leftwrist rightwrist leftankle rightankle&lt;br /&gt;
// nipples  -&amp;gt;  leftnipplering rightnipplering &lt;br /&gt;
// arms     -&amp;gt;  leftupperarm rightupperarm&lt;br /&gt;
// thighs   -&amp;gt;  leftupperthigh rightupperthigh&lt;br /&gt;
// knees    -&amp;gt;  leftknee rightknee&lt;br /&gt;
&lt;br /&gt;
    list modData = [];&lt;br /&gt;
    integer xx;&lt;br /&gt;
    &lt;br /&gt;
    // Hacky.  Feel free to optimize.&lt;br /&gt;
    for(xx = 0; xx &amp;lt; llGetListLength(originalData); xx++) {&lt;br /&gt;
        if (llToLower(llList2String(originalData,xx)) == &amp;quot;wrists&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;ankles&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;allfour&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftankle&amp;quot;, &amp;quot;rightankle&amp;quot;, &amp;quot;leftwrist&amp;quot;, &amp;quot;rightwrist&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;nipples&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftnipplering&amp;quot;, &amp;quot;rightnipplering&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;arms&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperarm&amp;quot;, &amp;quot;rightupperarm&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;thighs&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftupperthigh&amp;quot;, &amp;quot;rightupperthigh&amp;quot;];&lt;br /&gt;
        } else if (llToLower(llList2String(originalData,xx)) == &amp;quot;knees&amp;quot;) {&lt;br /&gt;
            modData = modData + [&amp;quot;leftknee&amp;quot;, &amp;quot;rightknee&amp;quot;];&lt;br /&gt;
        } else {    modData = modData + llList2List(originalData,xx,xx);     }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return modData;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
// Main Script&lt;br /&gt;
// ----------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
default {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        &lt;br /&gt;
        // build prim key list&lt;br /&gt;
        integer n;&lt;br /&gt;
        integer linkcount = llGetNumberOfPrims();&lt;br /&gt;
        for (n = 2; n &amp;lt;= linkcount; n++)    {&lt;br /&gt;
            string thiselement = llGetLinkName(n);&lt;br /&gt;
            if (thiselement != &amp;quot;Object&amp;quot;) {&lt;br /&gt;
                primKeys = primKeys + [thiselement, llGetLinkKey(n)];&lt;br /&gt;
                primKeysCount++;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        state load;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
state load  {&lt;br /&gt;
    state_entry() {&lt;br /&gt;
        string item;&lt;br /&gt;
        ConfigCards = [];&lt;br /&gt;
        integer n = llGetInventoryNumber(INVENTORY_NOTECARD);&lt;br /&gt;
        while (n-- &amp;gt; 0) { // get the data off cards with the right name&lt;br /&gt;
            item = llGetInventoryName(INVENTORY_NOTECARD, n);&lt;br /&gt;
            if (llSubStringIndex(item, &amp;quot;.chains&amp;quot;) != -1) {&lt;br /&gt;
                ConfigCards = (ConfigCards=[]) + ConfigCards + (list) item;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        ConfigCardIndex = 0;&lt;br /&gt;
        ConfigCards = llListSort(ConfigCards, 1, TRUE);&lt;br /&gt;
        next_card();&lt;br /&gt;
        SimRatingReq = llRequestSimulatorData(llGetRegionName(), DATA_SIM_RATING);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dataserver(key query_id, string data) {&lt;br /&gt;
        if (query_id == SimRatingReq) {&lt;br /&gt;
            Rating = data;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        if (query_id != ConfigQueryId) {    return;     }                             &lt;br /&gt;
        if (data == EOF) {&lt;br /&gt;
            if ( next_card() ) { return;  }&lt;br /&gt;
            state on;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        data = llStringTrim(data, STRING_TRIM);&lt;br /&gt;
        if (llGetSubString(data,0,0) != &amp;quot;/&amp;quot; &amp;amp;&amp;amp; llStringLength(data)) {          // skip comments and blank lines&lt;br /&gt;
            list ldata = llParseStringKeepNulls(data, [&amp;quot;;&amp;quot;], []);&lt;br /&gt;
            string lable = llStringTrim(llList2String(ldata, 0), STRING_TRIM);&lt;br /&gt;
            string anim = llStringTrim(llList2String(ldata, 1), STRING_TRIM);&lt;br /&gt;
            string target = llStringTrim(llList2String(ldata, 2), STRING_TRIM);&lt;br /&gt;
            string param = llStringTrim(llList2String(ldata, 3), STRING_TRIM);&lt;br /&gt;
            add_cd(lable,anim,target,param);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        ++ConfigLineIndex;&lt;br /&gt;
        ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);       //read next line of positions notecard&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
state on {&lt;br /&gt;
    state_entry()   {    }&lt;br /&gt;
    on_rez(integer arg) { llResetScript(); }&lt;br /&gt;
    changed(integer change) { if (change &amp;amp; CHANGED_INVENTORY) state load; }&lt;br /&gt;
&lt;br /&gt;
    link_message(integer from, integer num, string message, key id) {&lt;br /&gt;
    &lt;br /&gt;
        if (!((num == -11000) || (num == -11001) || (num == -11002) || (message == &amp;quot;POSEB&amp;quot;))) { return; }&lt;br /&gt;
    &lt;br /&gt;
        if (message == &amp;quot;POSEB&amp;quot;) {&lt;br /&gt;
            CurrentSet = (string)id;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        debug(&amp;quot;========== START&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // it&#039;s a generic sit/unsit/change update&lt;br /&gt;
        list linedata = llParseStringKeepNulls(message,[&amp;quot;|&amp;quot;],[]);&lt;br /&gt;
        string param1 = llList2String(linedata, 0);&lt;br /&gt;
        string param2 = llList2String(linedata, 1);&lt;br /&gt;
        &lt;br /&gt;
        // does this menu selection have any chain data attached to it?&lt;br /&gt;
        integer findSet = llListFindList(chainData, (list)CurrentSet);&lt;br /&gt;
        integer findAv = llListFindList(avData, [id]);&lt;br /&gt;
&lt;br /&gt;
        if (findSet == -1) { // The avatar wasn&#039;t chained for this anim, so clear chains if any&lt;br /&gt;
        debug(&amp;quot;[!] No chain data found&amp;quot;);&lt;br /&gt;
            if (findAv != -1) {&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);&lt;br /&gt;
                integer ix;&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);&lt;br /&gt;
            }&lt;br /&gt;
            return;    &lt;br /&gt;
        }&lt;br /&gt;
        else debug(&amp;quot;[!] Chain data found&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        list chainLog = []; // list of connands including anim name&lt;br /&gt;
        integer ii; // counter var&lt;br /&gt;
        for(ii = 0; ii &amp;lt; llGetListLength(chainData); ii = ii + chainStride) { // check each entry&lt;br /&gt;
            if (llList2String(chainData, ii + 1) == param2) { // if it&#039;s a match add it to the new list&lt;br /&gt;
                chainLog = llList2List(chainData, ii + 2, ii + 3) + chainLog;&lt;br /&gt;
            } // end if&lt;br /&gt;
        } // end for&lt;br /&gt;
            &lt;br /&gt;
        // TYPE: sit&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11000, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11000) { // A sit&lt;br /&gt;
            debug(&amp;quot;[!] Sit triggered&amp;quot;);&lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;&lt;br /&gt;
            integer ix;&lt;br /&gt;
            for(ix = 0; ix &amp;lt; llGetListLength(chainLog); ix = ix + 2) {&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));&lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,ix + 1) + &lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,ix + 1));&lt;br /&gt;
                tempPoints = tempPoints +  llList2String(chainLog,ix + 1) + &amp;quot;|&amp;quot;; &lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2); // trim the final char&lt;br /&gt;
            avData = (avData=[]) + avData + [id]; // add new&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints]; // add new&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // TYPE: unsit&lt;br /&gt;
        // FORMAT:  llMessageLinked(LINK_SET, -11001, (string)BallNum, llGetPermissionsKey());&lt;br /&gt;
        if (num == -11001) { // An unsit&lt;br /&gt;
            debug(&amp;quot;[!] Unsit triggered&amp;quot;); &lt;br /&gt;
            if (findAv != -1) {&lt;br /&gt;
                list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);&lt;br /&gt;
                integer ix;&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);&lt;br /&gt;
                    debug(&amp;quot;[-] &amp;quot; + llList2String(itemData,ix));&lt;br /&gt;
                }&lt;br /&gt;
                avData = llDeleteSubList(avData, findAv, findAv + 1);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // TYPE: change&lt;br /&gt;
        // FORMAT: llMessageLinked(LINK_SET, -11002, (string)BallNum + &amp;quot;|&amp;quot; + animation, avatar);&lt;br /&gt;
        if (num == -11002) { // A pose change&lt;br /&gt;
            debug(&amp;quot;[!] Pose change triggered&amp;quot;);&lt;br /&gt;
            list itemData = [];&lt;br /&gt;
            if (findAv != -1) {&lt;br /&gt;
                itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);&lt;br /&gt;
                itemData = UnAliasChainPoints(itemData);&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            integer ix;&lt;br /&gt;
            string tempPoints = &amp;quot;&amp;quot;;&lt;br /&gt;
            for(ix = 0; ix &amp;lt; llGetListLength(chainLog); ix = ix + 2) {&lt;br /&gt;
                integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));&lt;br /&gt;
                llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(chainLog,ix + 1) + &lt;br /&gt;
                            &amp;quot; link &amp;quot; + llList2String(primKeys, findKey + 1));&lt;br /&gt;
                debug(&amp;quot;[+] &amp;quot; + llList2String(chainLog,ix + 1));&lt;br /&gt;
&lt;br /&gt;
                // cut down the command line to just the attach point for compairison&lt;br /&gt;
                string stripPoint = llList2String(llParseStringKeepNulls(llList2String(chainLog,ix + 1),[&amp;quot; &amp;quot;],[]), 0);&lt;br /&gt;
                tempPoints = tempPoints + stripPoint + &amp;quot;|&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            tempPoints = llGetSubString(tempPoints, 0, -2); // trim the final char &amp;quot;|&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            if (findAv != -1) { avData = llDeleteSubList(avData, findAv, findAv + 1);   } // clear old&lt;br /&gt;
            avData = (avData=[]) + avData + [id]; // add new&lt;br /&gt;
            avData = (avData=[]) + avData + [tempPoints]; // add new&lt;br /&gt;
            &lt;br /&gt;
            list tempData = llParseStringKeepNulls(llList2String(avData,findAv + 1),[&amp;quot;|&amp;quot;],[]);&lt;br /&gt;
            tempData = UnAliasChainPoints(tempData);&lt;br /&gt;
            // compaire the two data lists, remove dumplicates&lt;br /&gt;
            integer yx;&lt;br /&gt;
            for(yx = 0; yx &amp;lt; llGetListLength(tempData); yx++) {&lt;br /&gt;
                integer overlapWhere = llListFindList(itemData, llList2List(tempData, yx, yx));&lt;br /&gt;
                    if (overlapWhere != -1) {&lt;br /&gt;
                        itemData = llDeleteSubList(itemData, overlapWhere, overlapWhere);&lt;br /&gt;
                    }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            if ((findAv != -1) &amp;amp;&amp;amp; ( llGetListLength(itemData) &amp;gt; 0)) { // We are already tracking points for this av...&lt;br /&gt;
                for(ix = 0; ix &amp;lt; llGetListLength(itemData); ix++) {&lt;br /&gt;
                    llWhisper(LGChannel,&amp;quot;lockguard &amp;quot; + (string)id + &amp;quot; &amp;quot; + llList2String(itemData,ix) + &amp;quot; unlink&amp;quot;);&lt;br /&gt;
                debug(&amp;quot;[-] &amp;quot; +  llList2String(itemData,ix));&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 3:&#039;&#039;&#039;&lt;br /&gt;
Create the data note card.  This card needs to be named .chains, but can have a further name past that if you need to use more than one card.  The card will look something like this:&lt;br /&gt;
&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 // Chain control&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 //  Part 1: menulable&lt;br /&gt;
 //  Part 2: animation chain is linked to&lt;br /&gt;
 //  Part 3: prim name to link to&lt;br /&gt;
 //  Part 4: LG attach point&lt;br /&gt;
 //  Part 4 optional commands:&lt;br /&gt;
 //      texture key&lt;br /&gt;
 //      size # #&lt;br /&gt;
 //      life #&lt;br /&gt;
 //      gravity #&lt;br /&gt;
 //      speed #&lt;br /&gt;
 //      color # # #&lt;br /&gt;
 //////////////////////////////////////////////////&lt;br /&gt;
 Bound;Short Posts-1;point2;rightwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound;Short Posts-1;point4;leftwrist gravity 0.2 life 0.75&lt;br /&gt;
 Bound;Short Posts-1;point3;collarfrontloop gravity 1.75 life 1.5&lt;br /&gt;
 Displayed;dungeon-2;point1;wrists gravity 0.0 life 2.0&lt;br /&gt;
 Hung;dungeon-3;point1;wrists gravity 0.0 life 0.75&lt;br /&gt;
 Dangled;Hung-2A;point1;ankles gravity 0.0 life 0.75&lt;br /&gt;
 Captured;Why Me-v2;point3;wrists gravity 0.0 life 0.25&lt;/div&gt;</summary>
		<author><name>Grey Mars</name></author>
	</entry>
</feed>