Difference between revisions of "MLPV2 LockGuard plugin"

From Second Life Wiki
Jump to navigation Jump to search
(A plugin script for MLP2 to add LockGuard Support)
 
Line 14: Line 14:
<lsl>
<lsl>
// ----------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------
// MLPV2 Plugin for LockGuard Partical Chaining v1.1
// MLPV2 Plugin for LockGuard Partical Chaining v1.2
//  
// ----------------------------------------------------------------------------------
// Notecard .chains format:
 
// menulable;animation;anchorprimname;LGattachpoint optionalparams
// ----------------------------------------------------------------------------------
// Notecard Format
// ----------------------------------------------------------------------------------
// Notecard .CHAINDATA format:
// menulable | ballnumber | anchorprimname | LGattachpoint optionalparams
//
//
// Example data:
// Example data:
// Bound;Short Posts-1;point2;rightwrist gravity 0.2 life 0.75
// Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75
// Bound;Short Posts-1;point4;leftwrist gravity 0.2 life 0.75
// Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75
// Bound;Short Posts-1;point3;collarfrontloop gravity 1.75 life 1.5
// Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5
// Displayed;dungeon-2;point1;wrists gravity 0.0 life 2.0
// Displayed | 0 | point1 | wrists gravity 0.0 life 2.0
// Hung;dungeon-3;point1;wrists gravity 0.0 life 0.75
// Hung | 0 | point1 | wrists gravity 0.0 life 0.75
// Dangled;Hung-2A;point1;ankles gravity 0.0 life 0.75
// Dangled | 0 | point1 | ankles gravity 0.0 life 0.75
// Captured;Why Me-v2;point3;wrists
// Captured | 0 | point3 | wrists
//
 
// ----------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------
// Copyright 2011, Grey Mars. All rights reserved.
// Copyright 2011, Grey Mars. All rights reserved.
Line 55: Line 59:
// or implied, of Grey Mars.
// or implied, of Grey Mars.


// ----------------------------------------------------------------------------------
// Changes:
// ----------------------------------------------------------------------------------
// Version 1.2
// - Large overhaul of how the data on the cards is stored.  Original format was made
//  to preserve the working of the MLP2 swap function.  However no one ever uses the
//  swap function in a chained context, so why complicate the notecard files?  Plus
//  new format is more memory friendly.
// - Notecard seperator changed from ; to | to match favorite RLV plugin format
// - Changed the need for the animation name to just the ball number in the notecard
// - Removed some unused variables
// - Note card renamed to .CHAINDATA to follow MLP2 naming conventions
// - Removed sim ratings data poll.
// - Added warning if the desired target point does not exist in the linkset
// - Added more comments because comments are good.
//
// Version 1.1
// - Added a data reload on inventory change
//
// Version 1.0
// - Changed script name from ~chaincontrol
// - Cleaned up old comments and made it more readable
// - First post to lsl wiki ( https://wiki.secondlife.com/wiki/MLPV2_Addons )


// ----------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------
Line 67: Line 94:
// Globals for reading card config
// Globals for reading card config
integer ConfigLineIndex;
integer ConfigLineIndex;
list    ConfigCards;       // list of names of config cards
list    ConfigCards;           // list of names of config cards
string  ConfigCardName;     // name of card being read
string  ConfigCardName;         // name of card being read
integer ConfigCardIndex;   // index of next card to read
integer ConfigCardIndex;       // index of next card to read
key    ConfigQueryId;
key    ConfigQueryId;         // The dataserver ID for the query sent
 
// Currently unused variables.  In the future may connect with some verification
key    SimRatingReq;
string  Rating;
integer Verified;


// Variables used with chain data and pose reading.
string  Pose;                  // Pose name
// It could be worse, but it could also be a lot more
//string  Avname;                 // Av Name
// efficient.
key    Avkey;                 // Avatar Key
string  Pose;
string  Avname;
key    Avkey;


string CurrentSet;
string CurrentSet = "stand";   // stand being the menu default


list chainData = [];
list chainData = [];           // The list of all data from the notecard [lable,ballnum,attachpoint,params]
integer chainCount = 0;
integer chainCount = 0;         // How many entries in the list
integer chainStride = 4;
integer chainStride = 4;       // The stride count for the list


list primKeys = [];
list primKeys = [];             // The list of prims in the object and thier keys [primname,key]
integer primKeysCount = 0;
integer primKeysCount = 0;     // How many entries in the list
integer primKeysStride = 2;
integer primKeysStride = 2;     // The stride count for the list


list avData = [];
list avData = [];               // The list of currently chained avs [avkey,currentchains]
integer avDataCount = 0;
integer avDataCount = 0;       // How many entries in the list
integer avDataStride = 2;
integer avDataStride = 2;       // The stride count for the list


// ----------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------
Line 121: Line 140:


// add chain data to the list...
// add chain data to the list...
add_cd(string lable, string anim, string target, string params) {
add_cd(string lable, string ballnum, string target, string params) {
     chainData = (chainData=[]) + chainData + (list)lable;
     chainData = (chainData=[]) + chainData + (list)lable;
     chainData = (chainData=[]) + chainData + (list)anim;
     chainData = (chainData=[]) + chainData + (list)ballnum;
     chainData = (chainData=[]) + chainData + (list)target;
     chainData = (chainData=[]) + chainData + (list)target;
     chainData = (chainData=[]) + chainData + (list)params;
     chainData = (chainData=[]) + chainData + (list)params;
Line 129: Line 148:
}
}


// does this point actually referance a set of points?
// Does this point actually referance a set of points?
list UnAliasChainPoints(list originalData) {
list UnAliasChainPoints(list originalData) {
// wrists  ->  leftwrist rightwrist
// wrists  ->  leftwrist rightwrist
Line 171: Line 190:
     state_entry() {
     state_entry() {
          
          
         // build prim key list
         // Now we build the prim key list...
         integer n;
         integer n;                                                     // Initialize the counter
         integer linkcount = llGetNumberOfPrims();
         integer linkcount = llGetNumberOfPrims();                       // How many items in the link set?
         for (n = 2; n <= linkcount; n++)    {
         for (n = 2; n <= linkcount; n++)    {                           // Cycle through, skipping root
             string thiselement = llGetLinkName(n);
             string thiselement = llGetLinkName(n);                     // Get the prim name
             if (thiselement != "Object") {
             if (thiselement != "Object") {                             // If the name is something besides the default...
                 primKeys = primKeys + [thiselement, llGetLinkKey(n)];
                 primKeys = primKeys + [thiselement, llGetLinkKey(n)];   // add it to primKeys
                 primKeysCount++;
                 primKeysCount++;                                       // increment counter
             }
             }                                                           // end if
         }
         }                                                               // end for loop
          
          
         state load;
         state load;                                                     // Go load the data...
     }
     }
}
}
Line 193: Line 212:
         while (n-- > 0) { // get the data off cards with the right name
         while (n-- > 0) { // get the data off cards with the right name
             item = llGetInventoryName(INVENTORY_NOTECARD, n);
             item = llGetInventoryName(INVENTORY_NOTECARD, n);
             if (llSubStringIndex(item, ".chains") != -1) {
             if (llSubStringIndex(item, ".CHAINDATA") != -1) {
                 ConfigCards = (ConfigCards=[]) + ConfigCards + (list) item;
                 ConfigCards = (ConfigCards=[]) + ConfigCards + (list) item;
             }
             }
Line 201: Line 220:
         ConfigCards = llListSort(ConfigCards, 1, TRUE);
         ConfigCards = llListSort(ConfigCards, 1, TRUE);
         next_card();
         next_card();
        SimRatingReq = llRequestSimulatorData(llGetRegionName(), DATA_SIM_RATING);
     }
     }
      
      
     dataserver(key query_id, string data) {
     dataserver(key query_id, string data) {
         if (query_id == SimRatingReq) {
          
            Rating = data;
         if (query_id != ConfigQueryId) {    return;    }   // Make sure this is the data event we asked for
            return;
       
        }
         if (data == EOF) {                     // Finished with this card...
         if (query_id != ConfigQueryId) {    return;    }                            
             if ( next_card() ) { return;  }     // Are there more cards to process?
         if (data == EOF) {
             state on;                           // All cards done, go to the on state
             if ( next_card() ) { return;  }
             state on;
         }
         }
          
          
         data = llStringTrim(data, STRING_TRIM);
         data = llStringTrim(data, STRING_TRIM);                                     // Cut off white space from the front and end...
         if (llGetSubString(data,0,0) != "/" && llStringLength(data)) {         // skip comments and blank lines
         if (llGetSubString(data,0,0) != "/" && llStringLength(data)) {             // Skip comments and blank lines
             list ldata = llParseStringKeepNulls(data, [";"], []);
             list ldata = llParseStringKeepNulls(data, ["|"], []);                   // Get the whole line for processing...
             string lable = llStringTrim(llList2String(ldata, 0), STRING_TRIM);
             string lable = llStringTrim(llList2String(ldata, 0), STRING_TRIM);     //  Extract lable
             string anim = llStringTrim(llList2String(ldata, 1), STRING_TRIM);
             string ballnum = llStringTrim(llList2String(ldata, 1), STRING_TRIM);   //  Extract ball number
             string target = llStringTrim(llList2String(ldata, 2), STRING_TRIM);
             string target = llStringTrim(llList2String(ldata, 2), STRING_TRIM);     //  Extract target point
             string param = llStringTrim(llList2String(ldata, 3), STRING_TRIM);
             string param = llStringTrim(llList2String(ldata, 3), STRING_TRIM);     //  Extract params
             add_cd(lable,anim,target,param);
             add_cd(lable,ballnum,target,param);                                     // Add all that to chainData
         }
         }
          
          
         ++ConfigLineIndex;
         ++ConfigLineIndex;                                                 // Incrament counter
         ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);       //read next line of positions notecard
         ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex); // Read next line of data notecard...
     }
     }
}
}
Line 233: Line 249:
state on {
state on {
     state_entry()  {    }
     state_entry()  {    }
     on_rez(integer arg) { llResetScript(); }
     on_rez(integer arg) { llResetScript(); }       // Object was rezed in world, reset script for freshness
     changed(integer change) { if (change & CHANGED_INVENTORY) state load; }
     changed(integer change) {
        if (change & CHANGED_INVENTORY) state load; // Inventory changed, so reload the card data to be safe
    }


     link_message(integer from, integer num, string message, key id) {
     link_message(integer from, integer num, string message, key id) {
   
       
        // Do we care about this link message?  If not, just return
         if (!((num == -11000) || (num == -11001) || (num == -11002) || (message == "POSEB"))) { return; }
         if (!((num == -11000) || (num == -11001) || (num == -11002) || (message == "POSEB"))) { return; }
      
      
         if (message == "POSEB") {
         if (message == "POSEB") {       // Is it a set change?
             CurrentSet = (string)id;
             CurrentSet = (string)id;   // Save the set name...
             return;
             return;                     // Nothing more for us, so return
         }
         }
         debug("========== START");
         debug("========== START");


        // it's a generic sit/unsit/change update
         list linedata = llParseStringKeepNulls(message,["|"],[]);   // It's a generic sit/unsit/change update, so break it down
         list linedata = llParseStringKeepNulls(message,["|"],[]);
         string param1 = llList2String(linedata, 0);                 //    Extract ball number
         string param1 = llList2String(linedata, 0);
         string param2 = llList2String(linedata, 1);                 //   Extract animation
         string param2 = llList2String(linedata, 1);
 
       
         integer findSet = llListFindList(chainData, (list)CurrentSet); // Check chainData for this set name...       
        // does this menu selection have any chain data attached to it?
         integer findAv = llListFindList(avData, [id]);                 // Check avData for the person sitting...
         integer findSet = llListFindList(chainData, (list)CurrentSet);
         integer findAv = llListFindList(avData, [id]);


         if (findSet == -1) { // The avatar wasn't chained for this anim, so clear chains if any
         if (findSet == -1) {                                           // There is no chain data for this set, so...
        debug("[!] No chain data found");
            debug("[!] No chain data found");
             if (findAv != -1) {
             if (findAv != -1) {                                         // Do we still have chain data for this av, and need to clear it?
                 list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);
                 list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]); // find the stored data
                 integer ix;
                 integer ix;                                                                         // initialize counter
                 for(ix = 0; ix < llGetListLength(itemData); ix++) {
                 for(ix = 0; ix < llGetListLength(itemData); ix++) {                                 // Step through the stored data
                     llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink");
                     llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink"); // unlink from points
                     debug("[-] " + llList2String(itemData,ix));
                     debug("[-] " + llList2String(itemData,ix));
                 }
                 }                                                                                   // end for
                 avData = llDeleteSubList(avData, findAv, findAv + 1);
                 avData = llDeleteSubList(avData, findAv, findAv + 1);                               // finally delete the stored data
             }
             }                                                                                       // end findAv if
             return;  
             return;                                                                                 // No further processing for this event needed
         }
         } else debug("[!] Chain data found");
        else debug("[!] Chain data found");
 
        if (llList2String(chainData,findSet + 1) != param1) {  return;  }    // We have data for this set, but not this ball.  Returning.
 
        list chainLog = [];                                                    // set up the list of commands [attachpoint, params]
        integer ii;                                                            // initialize counter var
        for(ii = 0; ii < llGetListLength(chainData); ii = ii + chainStride) {  // Run through the chainData list... [lable,ballnum,attachpoint,params]
            if (llList2String(chainData, ii) == CurrentSet) {                  //  if the set has a match...
                chainLog = llList2List(chainData, ii + 2, ii + 3) + chainLog;  //  add attachpoint and params to chainLog
            }                                                                  // End if
        }                                                                      // End for


        list chainLog = []; // list of connands including anim name
        integer ii; // counter var
        for(ii = 0; ii < llGetListLength(chainData); ii = ii + chainStride) { // check each entry
            if (llList2String(chainData, ii + 1) == param2) { // if it's a match add it to the new list
                chainLog = llList2List(chainData, ii + 2, ii + 3) + chainLog;
            } // end if
        } // end for
           
         // TYPE: sit
         // TYPE: sit
         // FORMAT: llMessageLinked(LINK_SET, -11000, (string)BallNum + "|" + animation, avatar);
         // FORMAT: llMessageLinked(LINK_SET, -11000, (string)BallNum + "|" + animation, avatar);
         if (num == -11000) { // A sit
         if (num == -11000) {
             debug("[!] Sit triggered");
             debug("[!] Sit triggered");
             string tempPoints = "";
             string tempPoints = "";                                                                     // set up tempPoints
             integer ix;
             integer ix;                                                                                 // initalize counter
             for(ix = 0; ix < llGetListLength(chainLog); ix = ix + 2) {
             for(ix = 0; ix < llGetListLength(chainLog); ix = ix + 2) {                                 // step through chainLog
                 integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));
                 integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));         // find the target point in the key list
                 llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(chainLog,ix + 1) +  
               
                if (findKey == -1) {                                                                    // if point does not exist..
                    llWhisper(0,"[!] " + llList2String(chainLog, ix) + " does not exist!");            // warn user
                }                                                                                      // end if
               
                 llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(chainLog,ix + 1) + // sent link command to LG items
                             " link " + llList2String(primKeys, findKey + 1));
                             " link " + llList2String(primKeys, findKey + 1));
                 debug("[+] " + llList2String(chainLog,ix + 1));
                 debug("[+] " + llList2String(chainLog,ix + 1));
                 tempPoints = tempPoints +  llList2String(chainLog,ix + 1) + "|";  
                 tempPoints = tempPoints +  llList2String(chainLog,ix + 1) + "|";                       // update tempPoints var
             }
             }                                                                                           // end for
              
              
             tempPoints = llGetSubString(tempPoints, 0, -2); // trim the final char
             tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char since we're done
             avData = (avData=[]) + avData + [id]; // add new
             avData = (avData=[]) + avData + [id];                                                       // add new key to avData [avkey,currentchains]
             avData = (avData=[]) + avData + [tempPoints]; // add new
             avData = (avData=[]) + avData + [tempPoints];                                               // add new chained points to avData
         }
         }                                                                                               // End if for sit
       
 
         // TYPE: unsit
         // TYPE: unsit
         // FORMAT:  llMessageLinked(LINK_SET, -11001, (string)BallNum, llGetPermissionsKey());
         // FORMAT:  llMessageLinked(LINK_SET, -11001, (string)BallNum, llGetPermissionsKey());
         if (num == -11001) { // An unsit
         if (num == -11001) {
             debug("[!] Unsit triggered");  
             debug("[!] Unsit triggered");  
             if (findAv != -1) {
             if (findAv != -1) {                                                                         // If avatar exists in avData...
                 list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);
                 list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);     // Get the chains we have active on av
                 integer ix;
                 integer ix;                                                                             // Initialize counter var
                 for(ix = 0; ix < llGetListLength(itemData); ix++) {
                 for(ix = 0; ix < llGetListLength(itemData); ix++) {                                     // Step through list of chain data
                     llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink");
                     llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink"); // Tell LG items to let go
                     debug("[-] " + llList2String(itemData,ix));
                     debug("[-] " + llList2String(itemData,ix));
                 }
                 }                                                                                       // End for
                 avData = llDeleteSubList(avData, findAv, findAv + 1);
                 avData = llDeleteSubList(avData, findAv, findAv + 1);                                   // Remove data from avData
             }
             }                                                                                           // End if for av found in list
         }
         }                                                                                               // End if for unsit
          
          
         // TYPE: change
         // TYPE: change
         // FORMAT: llMessageLinked(LINK_SET, -11002, (string)BallNum + "|" + animation, avatar);
         // FORMAT: llMessageLinked(LINK_SET, -11002, (string)BallNum + "|" + animation, avatar);
         if (num == -11002) { // A pose change
         if (num == -11002) {
             debug("[!] Pose change triggered");
             debug("[!] Pose change triggered");
             list itemData = [];
             list itemData = [];                                                                         // Set up itemData
             if (findAv != -1) {
             if (findAv != -1) {                                                                         // Are we already doing something to av?
                 itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);
                 itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);           // if we are, get that data
                 itemData = UnAliasChainPoints(itemData);
                 itemData = UnAliasChainPoints(itemData);                                               // unalias the data for multiple points
             }
             }                                                                                           // end if for av finding
           
 
             integer ix;
             integer ix;                                                                                 // Initialize counter var
             string tempPoints = "";
             string tempPoints = "";                                                                     // Set up a holding var
             for(ix = 0; ix < llGetListLength(chainLog); ix = ix + 2) {
             for(ix = 0; ix < llGetListLength(chainLog); ix = ix + 2) {                                 // Step through chainLog
                 integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));
                 integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));         // find location of attach point in primKeys
                 llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(chainLog,ix + 1) +  
               
                if (findKey == -1) {                                                                    // if point does not exist..
                    llWhisper(0,"[!] " + llList2String(chainLog, ix) + " does not exist!");            // warn user
                }                                                                                      // end if
               
                 llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(chainLog,ix + 1) + // Tell LG attachments to draw the chains
                             " link " + llList2String(primKeys, findKey + 1));
                             " link " + llList2String(primKeys, findKey + 1));
                 debug("[+] " + llList2String(chainLog,ix + 1));
                 debug("[+] " + llList2String(chainLog,ix + 1));
Line 332: Line 360:
                 // cut down the command line to just the attach point for compairison
                 // cut down the command line to just the attach point for compairison
                 string stripPoint = llList2String(llParseStringKeepNulls(llList2String(chainLog,ix + 1),[" "],[]), 0);
                 string stripPoint = llList2String(llParseStringKeepNulls(llList2String(chainLog,ix + 1),[" "],[]), 0);
                 tempPoints = tempPoints + stripPoint + "|";
                 tempPoints = tempPoints + stripPoint + "|";                                             // update tempPoints
             }
             }


             tempPoints = llGetSubString(tempPoints, 0, -2); // trim the final char "|"
             tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char "|"
              
              
             if (findAv != -1) { avData = llDeleteSubList(avData, findAv, findAv + 1);  } // clear old
             if (findAv != -1) { avData = llDeleteSubList(avData, findAv, findAv + 1);  }               // clear old av data if it exists
             avData = (avData=[]) + avData + [id]; // add new
             avData = (avData=[]) + avData + [id];                                                       // add new av key
             avData = (avData=[]) + avData + [tempPoints]; // add new
             avData = (avData=[]) + avData + [tempPoints];                                               // add new attach point data
              
              
             list tempData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);
             list tempData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);         // Grab the list of chained points
             tempData = UnAliasChainPoints(tempData);
             tempData = UnAliasChainPoints(tempData);                                                   // Unalias the data if it has groups
             // compaire the two data lists, remove dumplicates
             // compaire the two data lists, remove dumplicates
             integer yx;
             integer yx;                                                                                 // Initialize counter var
             for(yx = 0; yx < llGetListLength(tempData); yx++) {
             for(yx = 0; yx < llGetListLength(tempData); yx++) {                                         // step through tempData
                 integer overlapWhere = llListFindList(itemData, llList2List(tempData, yx, yx));
                 integer overlapWhere = llListFindList(itemData, llList2List(tempData, yx, yx));         // look for duplicate entries
                     if (overlapWhere != -1) {
                     if (overlapWhere != -1) {                                                           // if we find a duplicate...
                         itemData = llDeleteSubList(itemData, overlapWhere, overlapWhere);
                         itemData = llDeleteSubList(itemData, overlapWhere, overlapWhere);               //  remove it from the list
                     }
                     }                                                                                   // end duplication check if
             }
             }                                                                                           // end for
              
              
             if ((findAv != -1) && ( llGetListLength(itemData) > 0)) { // We are already tracking points for this av...
             if ((findAv != -1) && ( llGetListLength(itemData) > 0)) {                                   // Do we have left over points for av?
                 for(ix = 0; ix < llGetListLength(itemData); ix++) {
                 for(ix = 0; ix < llGetListLength(itemData); ix++) {                                     // Step through itemData
                     llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink");
                     llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink"); // tell LG to unlink
                 debug("[-] " +  llList2String(itemData,ix));
                 debug("[-] " +  llList2String(itemData,ix));
                 }
                 }                                                                                       // end for
             }
             }                                                                                           // end leftover if
         }
         }                                                                                               // end change if
     }
     }                                                                                                   // end link_message
}
}                                                                                                       // end state on
</lsl>
</lsl>


Line 370: Line 398:
  //////////////////////////////////////////////////
  //////////////////////////////////////////////////
  //  Part 1: menulable
  //  Part 1: menulable
  //  Part 2: animation chain is linked to
  //  Part 2: ball chain is linked to
  //  Part 3: prim name to link to
  //  Part 3: prim name to link to
  //  Part 4: LG attach point
  //  Part 4: LG attach point
Line 381: Line 409:
  //      color # # #
  //      color # # #
  //////////////////////////////////////////////////
  //////////////////////////////////////////////////
  Bound;Short Posts-1;point2;rightwrist gravity 0.2 life 0.75
  Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75
  Bound;Short Posts-1;point4;leftwrist gravity 0.2 life 0.75
  Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75
  Bound;Short Posts-1;point3;collarfrontloop gravity 1.75 life 1.5
  Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5
  Displayed;dungeon-2;point1;wrists gravity 0.0 life 2.0
  Displayed | 0 | point1 | wrists gravity 0.0 life 2.0
  Hung;dungeon-3;point1;wrists gravity 0.0 life 0.75
  Hung | 0 | point1 | wrists gravity 0.0 life 0.75
  Dangled;Hung-2A;point1;ankles gravity 0.0 life 0.75
  Dangled | 0 | point1 | ankles gravity 0.0 life 0.75
  Captured;Why Me-v2;point3;wrists gravity 0.0 life 0.25
  Captured | 0 | point3 | wrists

Revision as of 12:08, 22 April 2011

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's list and if so notifies lockguard enabled attachments of what to do and what prim keys to do it to.

So what do you need to do?

Step 1: 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.

Step 2: Add the following script to your furniture. It may complain, but you can ignore that for now.

<lsl> // ---------------------------------------------------------------------------------- // MLPV2 Plugin for LockGuard Partical Chaining v1.2 // ----------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------- // Notecard Format // ---------------------------------------------------------------------------------- // Notecard .CHAINDATA format: // menulable | ballnumber | anchorprimname | LGattachpoint optionalparams // // Example data: // Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75 // Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75 // Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5 // Displayed | 0 | point1 | wrists gravity 0.0 life 2.0 // Hung | 0 | point1 | wrists gravity 0.0 life 0.75 // Dangled | 0 | point1 | ankles gravity 0.0 life 0.75 // Captured | 0 | point3 | wrists

// ---------------------------------------------------------------------------------- // Copyright 2011, Grey Mars. All rights reserved. // ---------------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY GREY MARS ``AS IS AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // The views and conclusions contained in the software and documentation are those of the // authors and should not be interpreted as representing official policies, either expressed // or implied, of Grey Mars.

// ---------------------------------------------------------------------------------- // Changes: // ---------------------------------------------------------------------------------- // Version 1.2 // - Large overhaul of how the data on the cards is stored. Original format was made // to preserve the working of the MLP2 swap function. However no one ever uses the // swap function in a chained context, so why complicate the notecard files? Plus // new format is more memory friendly. // - Notecard seperator changed from ; to | to match favorite RLV plugin format // - Changed the need for the animation name to just the ball number in the notecard // - Removed some unused variables // - Note card renamed to .CHAINDATA to follow MLP2 naming conventions // - Removed sim ratings data poll. // - Added warning if the desired target point does not exist in the linkset // - Added more comments because comments are good. // // Version 1.1 // - Added a data reload on inventory change // // Version 1.0 // - Changed script name from ~chaincontrol // - Cleaned up old comments and made it more readable // - First post to lsl wiki ( https://wiki.secondlife.com/wiki/MLPV2_Addons )

// ---------------------------------------------------------------------------------- // Globals // ----------------------------------------------------------------------------------

integer VERBOSE = FALSE; // Give extra info

integer LGChannel = -9119; // The LG listen channel integer LGHandle; // Handle ID for LG messages

// Globals for reading card config integer ConfigLineIndex; list ConfigCards; // list of names of config cards string ConfigCardName; // name of card being read integer ConfigCardIndex; // index of next card to read key ConfigQueryId; // The dataserver ID for the query sent

string Pose; // Pose name //string Avname; // Av Name key Avkey; // Avatar Key

string CurrentSet = "stand"; // stand being the menu default

list chainData = []; // The list of all data from the notecard [lable,ballnum,attachpoint,params] integer chainCount = 0; // How many entries in the list integer chainStride = 4; // The stride count for the list

list primKeys = []; // The list of prims in the object and thier keys [primname,key] integer primKeysCount = 0; // How many entries in the list integer primKeysStride = 2; // The stride count for the list

list avData = []; // The list of currently chained avs [avkey,currentchains] integer avDataCount = 0; // How many entries in the list integer avDataStride = 2; // The stride count for the list

// ---------------------------------------------------------------------------------- // Support functions // ----------------------------------------------------------------------------------

// Debugging outputs debug(string text) { if (VERBOSE) llOwnerSay(text); }

// get the next card in case there is more than one integer next_card() {

   if (ConfigCardIndex >= llGetListLength(ConfigCards)) {
       ConfigCards = [];
       return (FALSE);
   }
   
   ConfigLineIndex = 0;
   ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);
   ConfigCardIndex++;
   ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);
   debug("[?] Reading file: " + ConfigCardName);
   return (TRUE);

}

// add chain data to the list... add_cd(string lable, string ballnum, string target, string params) {

   chainData = (chainData=[]) + chainData + (list)lable;
   chainData = (chainData=[]) + chainData + (list)ballnum;
   chainData = (chainData=[]) + chainData + (list)target;
   chainData = (chainData=[]) + chainData + (list)params;
   ++chainCount;

}

// Does this point actually referance a set of points? list UnAliasChainPoints(list originalData) { // wrists -> leftwrist rightwrist // ankles -> leftankle rightankle // allfour -> leftwrist rightwrist leftankle rightankle // nipples -> leftnipplering rightnipplering // arms -> leftupperarm rightupperarm // thighs -> leftupperthigh rightupperthigh // knees -> leftknee rightknee

   list modData = [];
   integer xx;
   
   // Hacky.  Feel free to optimize.
   for(xx = 0; xx < llGetListLength(originalData); xx++) {
       if (llToLower(llList2String(originalData,xx)) == "wrists") {
           modData = modData + ["leftwrist", "rightwrist"];
       } else if (llToLower(llList2String(originalData,xx)) == "ankles") {
           modData = modData + ["leftankle", "rightankle"];
       } else if (llToLower(llList2String(originalData,xx)) == "allfour") {
           modData = modData + ["leftankle", "rightankle", "leftwrist", "rightwrist"];
       } else if (llToLower(llList2String(originalData,xx)) == "nipples") {
           modData = modData + ["leftnipplering", "rightnipplering"];
       } else if (llToLower(llList2String(originalData,xx)) == "arms") {
           modData = modData + ["leftupperarm", "rightupperarm"];
       } else if (llToLower(llList2String(originalData,xx)) == "thighs") {
           modData = modData + ["leftupperthigh", "rightupperthigh"];
       } else if (llToLower(llList2String(originalData,xx)) == "knees") {
           modData = modData + ["leftknee", "rightknee"];
       } else {    modData = modData + llList2List(originalData,xx,xx);     }
   }
   
   return modData;   

}

// ---------------------------------------------------------------------------------- // Main Script // ----------------------------------------------------------------------------------

default {

   state_entry() {
       
       // Now we build the prim key list...
       integer n;                                                      // Initialize the counter
       integer linkcount = llGetNumberOfPrims();                       // How many items in the link set?
       for (n = 2; n <= linkcount; n++)    {                           // Cycle through, skipping root
           string thiselement = llGetLinkName(n);                      // Get the prim name
           if (thiselement != "Object") {                              // If the name is something besides the default...
               primKeys = primKeys + [thiselement, llGetLinkKey(n)];   // add it to primKeys
               primKeysCount++;                                        // increment counter
           }                                                           // end if
       }                                                               // end for loop
       
       state load;                                                     // Go load the data...
   }

}

state load {

   state_entry() {
       string item;
       ConfigCards = [];
       integer n = llGetInventoryNumber(INVENTORY_NOTECARD);
       while (n-- > 0) { // get the data off cards with the right name
           item = llGetInventoryName(INVENTORY_NOTECARD, n);
           if (llSubStringIndex(item, ".CHAINDATA") != -1) {
               ConfigCards = (ConfigCards=[]) + ConfigCards + (list) item;
           }
       }
       ConfigCardIndex = 0;
       ConfigCards = llListSort(ConfigCards, 1, TRUE);
       next_card();
   }
   
   dataserver(key query_id, string data) {
       
       if (query_id != ConfigQueryId) {    return;     }   // Make sure this is the data event we asked for
       
       if (data == EOF) {                      // Finished with this card...
           if ( next_card() ) { return;  }     // Are there more cards to process?
           state on;                           // All cards done, go to the on state
       }
       
       data = llStringTrim(data, STRING_TRIM);                                     // Cut off white space from the front and end...
       if (llGetSubString(data,0,0) != "/" && llStringLength(data)) {              // Skip comments and blank lines
           list ldata = llParseStringKeepNulls(data, ["|"], []);                   // Get the whole line for processing...
           string lable = llStringTrim(llList2String(ldata, 0), STRING_TRIM);      //   Extract lable
           string ballnum = llStringTrim(llList2String(ldata, 1), STRING_TRIM);    //   Extract ball number
           string target = llStringTrim(llList2String(ldata, 2), STRING_TRIM);     //   Extract target point
           string param = llStringTrim(llList2String(ldata, 3), STRING_TRIM);      //   Extract params
           add_cd(lable,ballnum,target,param);                                     // Add all that to chainData
       }
       
       ++ConfigLineIndex;                                                  // Incrament counter
       ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex); // Read next line of data notecard...
   }

}


state on {

   state_entry()   {    }
   on_rez(integer arg) { llResetScript(); }        // Object was rezed in world, reset script for freshness
   changed(integer change) {
       if (change & CHANGED_INVENTORY) state load; // Inventory changed, so reload the card data to be safe
   }
   link_message(integer from, integer num, string message, key id) {
       
       // Do we care about this link message?  If not, just return
       if (!((num == -11000) || (num == -11001) || (num == -11002) || (message == "POSEB"))) { return; }
   
       if (message == "POSEB") {       // Is it a set change?
           CurrentSet = (string)id;    // Save the set name...
           return;                     // Nothing more for us, so return
       }
       debug("========== START");
       list linedata = llParseStringKeepNulls(message,["|"],[]);   // It's a generic sit/unsit/change update, so break it down
       string param1 = llList2String(linedata, 0);                 //    Extract ball number
       string param2 = llList2String(linedata, 1);                 //    Extract animation
       integer findSet = llListFindList(chainData, (list)CurrentSet);  // Check chainData for this set name...        
       integer findAv = llListFindList(avData, [id]);                  // Check avData for the person sitting...
       if (findSet == -1) {                                            // There is no chain data for this set, so...
           debug("[!] No chain data found");
           if (findAv != -1) {                                         // Do we still have chain data for this av, and need to clear it?
               list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);  // find the stored data
               integer ix;                                                                         // initialize counter
               for(ix = 0; ix < llGetListLength(itemData); ix++) {                                 // Step through the stored data
                   llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink");  // unlink from points
                   debug("[-] " + llList2String(itemData,ix));
               }                                                                                   // end for
               avData = llDeleteSubList(avData, findAv, findAv + 1);                               // finally delete the stored data
           }                                                                                       // end findAv if
           return;                                                                                 // No further processing for this event needed
       } else debug("[!] Chain data found");
       if (llList2String(chainData,findSet + 1) != param1) {   return;   }     // We have data for this set, but not this ball.  Returning.
       list chainLog = [];                                                     // set up the list of commands [attachpoint, params]
       integer ii;                                                             // initialize counter var
       for(ii = 0; ii < llGetListLength(chainData); ii = ii + chainStride) {   // Run through the chainData list... [lable,ballnum,attachpoint,params]
           if (llList2String(chainData, ii) == CurrentSet) {                   //   if the set has a match...
               chainLog = llList2List(chainData, ii + 2, ii + 3) + chainLog;   //   add attachpoint and params to chainLog
           }                                                                   // End if
       }                                                                       // End for
       // TYPE: sit
       // FORMAT: llMessageLinked(LINK_SET, -11000, (string)BallNum + "|" + animation, avatar);
       if (num == -11000) {
           debug("[!] Sit triggered");
           string tempPoints = "";                                                                     // set up tempPoints
           integer ix;                                                                                 // initalize counter
           for(ix = 0; ix < llGetListLength(chainLog); ix = ix + 2) {                                  // step through chainLog
               integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));          // find the target point in the key list
               
               if (findKey == -1) {                                                                    // if point does not exist..
                   llWhisper(0,"[!] " + llList2String(chainLog, ix) + " does not exist!");             // warn user
               }                                                                                       // end if
               
               llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(chainLog,ix + 1) +  // sent link command to LG items
                           " link " + llList2String(primKeys, findKey + 1));
               debug("[+] " + llList2String(chainLog,ix + 1));
               tempPoints = tempPoints +  llList2String(chainLog,ix + 1) + "|";                        // update tempPoints var
           }                                                                                           // end for
           
           tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char since we're done
           avData = (avData=[]) + avData + [id];                                                       // add new key to avData [avkey,currentchains]
           avData = (avData=[]) + avData + [tempPoints];                                               // add new chained points to avData
       }                                                                                               // End if for sit
       // TYPE: unsit
       // FORMAT:  llMessageLinked(LINK_SET, -11001, (string)BallNum, llGetPermissionsKey());
       if (num == -11001) {
           debug("[!] Unsit triggered"); 
           if (findAv != -1) {                                                                         // If avatar exists in avData...
               list itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);      // Get the chains we have active on av
               integer ix;                                                                             // Initialize counter var
               for(ix = 0; ix < llGetListLength(itemData); ix++) {                                     // Step through list of chain data
                   llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink");  // Tell LG items to let go
                   debug("[-] " + llList2String(itemData,ix));
               }                                                                                       // End for
               avData = llDeleteSubList(avData, findAv, findAv + 1);                                   // Remove data from avData
           }                                                                                           // End if for av found in list
       }                                                                                               // End if for unsit
       
       // TYPE: change
       // FORMAT: llMessageLinked(LINK_SET, -11002, (string)BallNum + "|" + animation, avatar);
       if (num == -11002) {
           debug("[!] Pose change triggered");
           list itemData = [];                                                                         // Set up itemData
           if (findAv != -1) {                                                                         // Are we already doing something to av?
               itemData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);           // if we are, get that data
               itemData = UnAliasChainPoints(itemData);                                                // unalias the data for multiple points
           }                                                                                           // end if for av finding
           integer ix;                                                                                 // Initialize counter var
           string tempPoints = "";                                                                     // Set up a holding var
           for(ix = 0; ix < llGetListLength(chainLog); ix = ix + 2) {                                  // Step through chainLog
               integer findKey = llListFindList(primKeys, (list)llList2String(chainLog, ix));          // find location of attach point in primKeys
               
               if (findKey == -1) {                                                                    // if point does not exist..
                   llWhisper(0,"[!] " + llList2String(chainLog, ix) + " does not exist!");             // warn user
               }                                                                                       // end if
               
               llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(chainLog,ix + 1) +  // Tell LG attachments to draw the chains
                           " link " + llList2String(primKeys, findKey + 1));
               debug("[+] " + llList2String(chainLog,ix + 1));
               // cut down the command line to just the attach point for compairison
               string stripPoint = llList2String(llParseStringKeepNulls(llList2String(chainLog,ix + 1),[" "],[]), 0);
               tempPoints = tempPoints + stripPoint + "|";                                             // update tempPoints
           }
           tempPoints = llGetSubString(tempPoints, 0, -2);                                             // trim the final char "|"
           
           if (findAv != -1) { avData = llDeleteSubList(avData, findAv, findAv + 1);   }               // clear old av data if it exists
           avData = (avData=[]) + avData + [id];                                                       // add new av key
           avData = (avData=[]) + avData + [tempPoints];                                               // add new attach point data
           
           list tempData = llParseStringKeepNulls(llList2String(avData,findAv + 1),["|"],[]);          // Grab the list of chained points
           tempData = UnAliasChainPoints(tempData);                                                    // Unalias the data if it has groups
           // compaire the two data lists, remove dumplicates
           integer yx;                                                                                 // Initialize counter var
           for(yx = 0; yx < llGetListLength(tempData); yx++) {                                         // step through tempData
               integer overlapWhere = llListFindList(itemData, llList2List(tempData, yx, yx));         // look for duplicate entries
                   if (overlapWhere != -1) {                                                           // if we find a duplicate...
                       itemData = llDeleteSubList(itemData, overlapWhere, overlapWhere);               //   remove it from the list
                   }                                                                                   // end duplication check if
           }                                                                                           // end for
           
           if ((findAv != -1) && ( llGetListLength(itemData) > 0)) {                                   // Do we have left over points for av?
               for(ix = 0; ix < llGetListLength(itemData); ix++) {                                     // Step through itemData
                   llWhisper(LGChannel,"lockguard " + (string)id + " " + llList2String(itemData,ix) + " unlink");  // tell LG to unlink
               debug("[-] " +  llList2String(itemData,ix));
               }                                                                                       // end for
           }                                                                                           // end leftover if
       }                                                                                               // end change if
   }                                                                                                   // end link_message

} // end state on </lsl>

Step 3: 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:

//////////////////////////////////////////////////
// Chain control
//////////////////////////////////////////////////
//  Part 1: menulable
//  Part 2: ball chain is linked to
//  Part 3: prim name to link to
//  Part 4: LG attach point
//  Part 4 optional commands:
//      texture key
//      size # #
//      life #
//      gravity #
//      speed #
//      color # # #
//////////////////////////////////////////////////
Bound | 0 | point2 | rightwrist gravity 0.2 life 0.75
Bound | 0 | point4 | leftwrist gravity 0.2 life 0.75
Bound | 0 | point3 | collarfrontloop gravity 1.75 life 1.5
Displayed | 0 | point1 | wrists gravity 0.0 life 2.0
Hung | 0 | point1 | wrists gravity 0.0 life 0.75
Dangled | 0 | point1 | ankles gravity 0.0 life 0.75
Captured | 0 | point3 | wrists