Difference between revisions of "Builders Buddy v1"

From Second Life Wiki
Jump to navigation Jump to search
m (Changed quick-use instructions)
(Numerous page changes, updated to reflect official version/source code.)
Line 3: Line 3:
This is a repost of the current Builder's Buddy scripts, as originally released on the prior LSL wiki.  There are two scripts; One goes in a "base" prim, which is the piece that is moved/rotated/etc.  The component script goes into each linked set that makes up the rest of the large build.  In short, only one base script, many component scripts.  Got it?
This is a repost of the current Builder's Buddy scripts, as originally released on the prior LSL wiki.  There are two scripts; One goes in a "base" prim, which is the piece that is moved/rotated/etc.  The component script goes into each linked set that makes up the rest of the large build.  In short, only one base script, many component scripts.  Got it?


== Note to Sellers! ==
'''The most current "official" version of these scripts is 1.9.'''
 
== Note to Sellers ==
Due to a number of people confused by the copyright wording, here's a recap: '''Use this script any way you want.  SELL IT EVEN!  Just make sure I am given credit as the creator.'''  That's the only restriction.
Due to a number of people confused by the copyright wording, here's a recap: '''Use this script any way you want.  SELL IT EVEN!  Just make sure I am given credit as the creator.'''  That's the only restriction.
== Note to Contributors ==
Due to even more people confused by multiple variations of this script, please note that '''this wiki version is not the official version.'''  The official version is the copy sitting in my inventory.  '''If you wish to add features, please contact me for inclusion in the official version.'''


== The Base Script ==  
== The Base Script ==  
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
  // Builders' Buddy 2.0 (Base Script)
  // Builders' Buddy 1.9 (Base Script)
  // by Newfie Pendragon, March 2006
  // by Newfie Pendragon, March 2006
  //
  //
Line 68: Line 74:
  //      - Changed rez sequence to be less affected by lag/gray goo fence
  //      - Changed rez sequence to be less affected by lag/gray goo fence
  //      - Timer always on, less code/more reliable
  //      - Timer always on, less code/more reliable
// v2.0 - 20070829 - Huney Jewell
//      - Added configurable constant to determine, which Menu Options will be displayed
//      - Menu Option 'Clean' now also deletes the prim which contains this script
///////////////////////////////////////////////////////////////////////////////
 
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
  // Configurable Settings
  // Configurable Settings
 
 
  // Channel used by Base Prim to talk to Component Prims
  // Channel used by Base Prim to talk to Component Prims
  // This channel must be the same one in the component script
  // This channel must be the same one in the component script
Line 83: Line 86:
  integer PRIMCHAN = -19730611;    // Channel used by Base Prim to talk to Component Prims;
  integer PRIMCHAN = -19730611;    // Channel used by Base Prim to talk to Component Prims;
                                   // ***THIS MUST MATCH IN BOTH SCRIPTS!***
                                   // ***THIS MUST MATCH IN BOTH SCRIPTS!***
 
  // Set to TRUE to allow group members to use the dialog menu
  // Set to TRUE to allow group members to use the dialog menu
  // Set to FALSE to disallow group members from using the dialog menu
  // Set to FALSE to disallow group members from using the dialog menu
  integer ingroup = TRUE;
  integer ingroup = TRUE;
 
  //Set to FALSE if you dont want the script to say anything while 'working'
  //Set to FALSE if you dont want the script to say anything while 'working'
  integer chatty = TRUE;
  integer chatty = TRUE;
 
  //How long to listen for a menu response before shutting down the listener
  //How long to listen for a menu response before shutting down the listener
  float fListenTime = 30.0;
  float fListenTime = 30.0;
 
  //How often (in seconds) to perform any timed checks
  //How often (in seconds) to perform any timed checks
  float fTimerRate = 0.25;
  float fTimerRate = 0.25;
 
  //How long to sit still before exiting active mode
  //How long to sit still before exiting active mode
  float fStoppedTime = 30.0;
  float fStoppedTime = 30.0;
 
  //SL sometimes blocks rezzing to prevent "gray goo" attacks
  //SL sometimes blocks rezzing to prevent "gray goo" attacks
  //How long we wait (seconds) before we assume SL blocked our rez attempt
  //How long we wait (seconds) before we assume SL blocked our rez attempt
  integer iRezWait = 3;
  integer iRezWait = 3;
 
   
//Specify which Menu Options will be displayed
   
  //FALSE will restrict full options to creator
  //TRUE will offer full options to anyone
integer fullOptions = TRUE;
 
 
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
  // DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN
  // DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
 
  //Name each option-these names will be your button names.
  //Name each option-these names will be your button names.
  string optRecord = "Record";
  string optRecord = "Record";
Line 121: Line 119:
  string optClean = "Clean";
  string optClean = "Clean";
  string optDone = "Done";
  string optDone = "Done";
 
  //Menu option descriptions
  //Menu option descriptions
  string descRecord = ": Record the position of all parts\n";
  string descRecord = ": Record the position of all parts\n";
Line 129: Line 127:
  string descClean = ": De-Rez all pieces\n";
  string descClean = ": De-Rez all pieces\n";
  string descDone = ": Remove all BB scripts and freeze parts in place.\n";
  string descDone = ": Remove all BB scripts and freeze parts in place.\n";
 
  integer MENU_CHANNEL;
  integer MENU_CHANNEL;
  integer MENU_HANDLE;
  integer MENU_HANDLE;
Line 145: Line 143:
  integer iRezIndex;
  integer iRezIndex;
  //integer bTimerOn = FALSE;
  //integer bTimerOn = FALSE;
 
  //To avoid flooding the sim with a high rate of movements
  //To avoid flooding the sim with a high rate of movements
  //(and the resulting mass updates it will bring), we used
  //(and the resulting mass updates it will bring), we used
Line 157: Line 155:
     return;
     return;
  }
  }
 
 
  rez_object()
  rez_object()
  {
  {
Line 164: Line 162:
     llRezObject(llGetInventoryName(INVENTORY_OBJECT, iRezIndex), llGetPos(), ZERO_VECTOR, llGetRot(), PRIMCHAN);
     llRezObject(llGetInventoryName(INVENTORY_OBJECT, iRezIndex), llGetPos(), ZERO_VECTOR, llGetRot(), PRIMCHAN);
     iLastRez = llGetUnixTime();
     iLastRez = llGetUnixTime();
 
     if(!bRezzing) {
     if(!bRezzing) {
         bRezzing = TRUE;
         bRezzing = TRUE;
Line 170: Line 168:
     }
     }
  }
  }
 
  post_rez_object()
  post_rez_object()
  {
  {
Line 176: Line 174:
         llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, iRezIndex));
         llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, iRezIndex));
  }
  }
 
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
Line 186: Line 184:
         llResetScript();
         llResetScript();
     }
     }
 
     ///////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////
     state_entry () {
     state_entry () {
         //Use which menu?
         //Use which menu?
         if ( llGetCreator() == llGetOwner() || fullOptions) {
         if ( llGetCreator() == llGetOwner() ) {
             //Display all options
             //Display all options
             optionlist = [optPos, optClean, optDone, optRecord, optReset, optBuild];
             optionlist = [optPos, optClean, optDone, optRecord, optReset, optBuild];
Line 199: Line 197:
             title += optClean + descClean;
             title += optClean + descClean;
             title += optDone + descDone;
             title += optDone + descDone;
 
         } else {
         } else {
             //Display limited options
             //Display limited options
Line 207: Line 205:
             title += optDone + descDone;
             title += optDone + descDone;
         }
         }
 
         //Record our position
         //Record our position
         vLastPos = llGetPos();
         vLastPos = llGetPos();
         rLastRot = llGetRot();
         rLastRot = llGetRot();
 
         llSetTimerEvent(fTimerRate);
         llSetTimerEvent(fTimerRate);
     }
     }
 
     ///////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////
     touch_start (integer total_number) {
     touch_start (integer total_number) {
Line 229: Line 227:
         }
         }
     }
     }
 
     ///////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////
     listen(integer channel, string name, key id, string message) {
     listen(integer channel, string name, key id, string message) {
Line 257: Line 255:
         if ( message == optClean ) {
         if ( message == optClean ) {
             llRegionSay(PRIMCHAN, "CLEAN");
             llRegionSay(PRIMCHAN, "CLEAN");
            llDie();
             return;
             return;
         }
         }
Line 266: Line 263:
         }
         }
     }
     }
 
     ///////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////
     moving_start()
     moving_start()
Line 277: Line 274:
         }
         }
     }
     }
 
     ///////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////
     object_rez(key id) {
     object_rez(key id) {
         //The object rezzed, perform any post-rez processing
         //The object rezzed, perform any post-rez processing
         post_rez_object();
         post_rez_object();
 
         //Move on to the next object
         //Move on to the next object
         //Loop through backwards (safety precaution in case of inventory change)
         //Loop through backwards (safety precaution in case of inventory change)
Line 289: Line 286:
             //Attempt to rez it
             //Attempt to rez it
             rez_object();
             rez_object();
 
         } else {
         } else {
             //Rezzing complete, now positioning
             //Rezzing complete, now positioning
Line 298: Line 295:
         }
         }
     }
     }
 
     ///////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////
     timer() {
     timer() {
Line 308: Line 305:
             }
             }
         }
         }
 
         //Are we rezzing?
         //Are we rezzing?
         if(bRezzing) {
         if(bRezzing) {
Line 318: Line 315:
             }
             }
         }
         }
 
         //Open listener?
         //Open listener?
         if( iListenTimeout != 0 )
         if( iListenTimeout != 0 )
Line 330: Line 327:
         }
         }
     }
     }
 
     ///////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////
     on_rez(integer iStart)
     on_rez(integer iStart)
Line 341: Line 338:
== The Component Script ==
== The Component Script ==
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
  // Builders' Buddy 1.91 (Component Pieces)
  // Builders' Buddy 1.9 (Component Pieces)
  // by Newfie Pendragon, March 2006
  // by Newfie Pendragon, March 2006
  //
  //
Line 363: Line 360:
  //
  //
  // QUICK USE:
  // QUICK USE:
  //  - Drop this script in every component except the base object.
  //  - Drop this script in the Base.
  //  - Drop the "Base" Script in the base object if not done so already.
  //  - Drop the "Component" Script in each building part.
  //  - Touch  your Base, and choose RECORD
  //  - Touch  your Base, and choose RECORD
  //  - Take all building parts into inventory
  //  - Take all building parts into inventory
Line 385: Line 382:
  // v1.7 - 20060821 - Correction for non-zero rotation (thanks Ed44 Gupta!)
  // v1.7 - 20060821 - Correction for non-zero rotation (thanks Ed44 Gupta!)
  // v1.9 - 20070630 - Added check to keep within sim edges
  // v1.9 - 20070630 - Added check to keep within sim edges
// v1.91- 20070910 - Edited 'quickuse' instructions of this script
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
 
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
  // Configurable Settings
  // Configurable Settings
Line 393: Line 389:
  integer PRIMCHAN = -19730611;    // Channel used by Base Prim to talk to Component Prims;
  integer PRIMCHAN = -19730611;    // Channel used by Base Prim to talk to Component Prims;
                                   // ***THIS MUST MATCH IN BOTH SCRIPTS!***
                                   // ***THIS MUST MATCH IN BOTH SCRIPTS!***
 
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
  // Runtime Variables (Dont need to change below here unless making a derivative)
  // Runtime Variables (Dont need to change below here unless making a derivative)
Line 402: Line 398:
  rotation rDestRot;
  rotation rDestRot;
  integer bRecorded = FALSE;
  integer bRecorded = FALSE;
 
 
  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////
  string first_word(string In_String, string Token)
  string first_word(string In_String, string Token)
Line 412: Line 408:
     if(Token == "") Token = " ";
     if(Token == "") Token = " ";
     integer pos = llSubStringIndex(In_String, Token);
     integer pos = llSubStringIndex(In_String, Token);
 
     //Found it?
     //Found it?
     if( pos >= 1 )
     if( pos >= 1 )
Line 419: Line 415:
         return In_String;
         return In_String;
  }
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////
  string other_words(string In_String, string Token)
  string other_words(string In_String, string Token)
Line 427: Line 423:
     // the an empty string.
     // the an empty string.
     if( Token == "" ) Token = " ";
     if( Token == "" ) Token = " ";
 
     integer pos = llSubStringIndex(In_String, Token);
     integer pos = llSubStringIndex(In_String, Token);
 
     //Found it?
     //Found it?
     if( pos >= 1 )
     if( pos >= 1 )
Line 436: Line 432:
         return "";
         return "";
  }
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////
  do_move()
  do_move()
Line 445: Line 441:
     {
     {
         list lParams = [];
         list lParams = [];
 
         //If we're not there....
         //If we're not there....
         if( llGetPos() != vDestPos )
         if( llGetPos() != vDestPos )
Line 461: Line 457:
             }
             }
         }
         }
 
         //Try to move to destination
         //Try to move to destination
         //Upgraded to attempt to use the llSetPrimitiveParams fast-move hack
         //Upgraded to attempt to use the llSetPrimitiveParams fast-move hack
Line 474: Line 470:
         i++;
         i++;
     }
     }
 
     //Set rotation
     //Set rotation
     llSetRot(rDestRot);
     llSetRot(rDestRot);
  }
  }
 
 
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
Line 491: Line 487:
         llListen(PRIMCHAN, "", NULL_KEY, "");
         llListen(PRIMCHAN, "", NULL_KEY, "");
     }
     }
 
     //////////////////////////////////////////////////////////////////////////////////////////
     //////////////////////////////////////////////////////////////////////////////////////////
     on_rez(integer iStart)
     on_rez(integer iStart)
Line 502: Line 498:
         }
         }
     }
     }
 
     //////////////////////////////////////////////////////////////////////////////////////////
     //////////////////////////////////////////////////////////////////////////////////////////
     listen(integer iChan, string sName, key kID, string sText)
     listen(integer iChan, string sName, key kID, string sText)
     {
     {
         string sCmd = llToUpper(first_word(sText, " "));
         string sCmd = llToUpper(first_word(sText, " "));
 
         if( sCmd == "RECORD" )
         if( sCmd == "RECORD" )
         {
         {
Line 514: Line 510:
             vector vBase = (vector)llList2String(lParams, 0);
             vector vBase = (vector)llList2String(lParams, 0);
             rotation rBase = (rotation)llList2String(lParams, 1);
             rotation rBase = (rotation)llList2String(lParams, 1);
 
             vOffset = (llGetPos() - vBase) / rBase;
             vOffset = (llGetPos() - vBase) / rBase;
             rRotation = llGetRot() / rBase;
             rRotation = llGetRot() / rBase;
Line 521: Line 517:
             return;
             return;
         }
         }
 
         //////////////////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////////////////
         if( sCmd == "MOVE" )
         if( sCmd == "MOVE" )
Line 527: Line 523:
             //Don't move if we've not yet recorded a position
             //Don't move if we've not yet recorded a position
             if( !bRecorded ) return;
             if( !bRecorded ) return;
 
             //Also ignore commands from bases with a different owner than us
             //Also ignore commands from bases with a different owner than us
             //(Anti-hacking measure)
             //(Anti-hacking measure)
             if( llGetOwner() != llGetOwnerKey(kID) ) return;
             if( llGetOwner() != llGetOwnerKey(kID) ) return;
 
 
             //Calculate our destination position
             //Calculate our destination position
             sText = other_words(sText, " ");
             sText = other_words(sText, " ");
Line 538: Line 534:
             vector vBase = (vector)llList2String(lParams, 0);
             vector vBase = (vector)llList2String(lParams, 0);
             rotation rBase = (rotation)llList2String(lParams, 1);
             rotation rBase = (rotation)llList2String(lParams, 1);
 
             //Calculate our destination position
             //Calculate our destination position
             vDestPos = (vOffset * rBase) + vBase;
             vDestPos = (vOffset * rBase) + vBase;
             rDestRot = rRotation * rBase;
             rDestRot = rRotation * rBase;
 
             //Make sure our calculated position is within the sim
             //Make sure our calculated position is within the sim
             if(vDestPos.x < 0.0) vDestPos.x = 0.0;
             if(vDestPos.x < 0.0) vDestPos.x = 0.0;
Line 549: Line 545:
             if(vDestPos.y > 255.0) vDestPos.y = 255.0;
             if(vDestPos.y > 255.0) vDestPos.y = 255.0;
             if(vDestPos.x > 768.0) vDestPos.x = 768.0;
             if(vDestPos.x > 768.0) vDestPos.x = 768.0;
 
             //Turn on our timer to perform the move?
             //Turn on our timer to perform the move?
             if( !bNeedMove )
             if( !bNeedMove )
Line 558: Line 554:
             return;
             return;
         }
         }
 
         //////////////////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////////////////
         if( sCmd == "DONE" )
         if( sCmd == "DONE" )
Line 566: Line 562:
             return;
             return;
         }
         }
 
         //////////////////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////////////////
         if( sCmd == "CLEAN" )
         if( sCmd == "CLEAN" )
Line 574: Line 570:
             return;
             return;
         }
         }
 
         //////////////////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////////////////
         if( sCmd == "RESET" )
         if( sCmd == "RESET" )
Line 581: Line 577:
         }
         }
     }
     }
 
     //////////////////////////////////////////////////////////////////////////////////////////
     //////////////////////////////////////////////////////////////////////////////////////////
     timer()
     timer()
Line 587: Line 583:
         //Turn ourselves off
         //Turn ourselves off
         llSetTimerEvent(0.0);
         llSetTimerEvent(0.0);
 
         //Do we need to move?
         //Do we need to move?
         if( bNeedMove )
         if( bNeedMove )
Line 598: Line 594:
     }
     }
  }
  }
 
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////
Line 610: Line 606:
     }
     }
  }
  }





Revision as of 18:23, 5 November 2007

This is a repost of the current Builder's Buddy scripts, as originally released on the prior LSL wiki. There are two scripts; One goes in a "base" prim, which is the piece that is moved/rotated/etc. The component script goes into each linked set that makes up the rest of the large build. In short, only one base script, many component scripts. Got it?

The most current "official" version of these scripts is 1.9.

Note to Sellers

Due to a number of people confused by the copyright wording, here's a recap: Use this script any way you want. SELL IT EVEN! Just make sure I am given credit as the creator. That's the only restriction.

Note to Contributors

Due to even more people confused by multiple variations of this script, please note that this wiki version is not the official version. The official version is the copy sitting in my inventory. If you wish to add features, please contact me for inclusion in the official version.


The Base Script

///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.9 (Base Script)
// by Newfie Pendragon, March 2006
//
// This script is distributed with permission that it may be used in
// any way, or may be further modified/included in resale items.
// HOWEVER, if this script is used as part of a for-sale product,
// it is required that appropriate credit be given to Newfie for
// the script (or portions used in derivatives).  That's a fair price
// in exchange for unlimited use of this script, dontcha think?
//
//  SL Forum thread and new versions found here:
//  http://forums.secondlife.com/showthread.php?t=96792
///////////////////////////////////////////////////////////////////////////////
//
//  Script Purpose & Use
//
//  Functions are dependent on the "component script"
//
//  QUICK USE:
//  - Drop this script in the Base.
//  - Drop the "Component" Script in each building part.
//  - Touch  your Base, and choose RECORD
//  - Take all building parts into inventory
//  - Drag building parts from inventory into Base Prim
//  - Touch your base and choose BUILD
//
//  OTHER COMMANDS from the Touch menu
//  - To reposition, move/rotate Base Prim choose POSITION
//  - To lock into position (removes scripts) choose DONE
//  - To delete building pieces: choose CLEAN
//
///////////////////////////////////////////////////////////////////////////////
//
//  History
//
// v1.0 - 20060328 - Newfie Pendragon
//      - Original Version
// v1.1 - 20060331 - Kalidor Lazarno
//      - Added a Dialog Engine to the base script
// v1.5 - 20060612 - Androclese Antonelli
//      - Added a random number generator to the dialog engine to elimintate
//        problems with multiple BB boxes cross-talking
//      - Added a timer to the listen command to put it asleep after 10sec.
//      - Added a Menu Description
//      - Added n "creator" flag so the owner could use the same object with full
//        menu options and only a single flag change
//      - Added an "ingroup" flag to enable/disable the same group use function
//      - Non-Admin usage cleans the inventory items as they spawn
// v1.6 - 20060624 - Newfie Pendragon
//      - Added active repositioning (building moves as the base piece moves)
//      - Added "Reset" Option to unlink parts from base temporarily
//      - Modified creator flag to automatically set based if owner is creator
//      - Minor changes to improve code readability (for those learning LSL)
// v1.8 - 20070429 - Newfie Pendragon
//      - Added a variable to allow a user to tweak how long a listener is open,
//        and changed the default to 30 seconds.
// v1.9 - 20070630 - Newfie Pendragon
//      - Changed to use llRegionSay - no more 96m max distance (same sim)
//      - Changed rez sequence to be less affected by lag/gray goo fence
//      - Timer always on, less code/more reliable
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// Configurable Settings


// Channel used by Base Prim to talk to Component Prims
// This channel must be the same one in the component script
// A negative channel is used because it elimited accidental activations
// by an Avatar talking on obscure channels
integer PRIMCHAN = -19730611;    // Channel used by Base Prim to talk to Component Prims;
                                 // ***THIS MUST MATCH IN BOTH SCRIPTS!***

// Set to TRUE to allow group members to use the dialog menu
// Set to FALSE to disallow group members from using the dialog menu
integer ingroup = TRUE;

//Set to FALSE if you dont want the script to say anything while 'working'
integer chatty = TRUE;

//How long to listen for a menu response before shutting down the listener
float fListenTime = 30.0;

//How often (in seconds) to perform any timed checks
float fTimerRate = 0.25;

//How long to sit still before exiting active mode
float fStoppedTime = 30.0;

//SL sometimes blocks rezzing to prevent "gray goo" attacks
//How long we wait (seconds) before we assume SL blocked our rez attempt
integer iRezWait = 3;


///////////////////////////////////////////////////////////////////////////////
// DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN
///////////////////////////////////////////////////////////////////////////////

//Name each option-these names will be your button names.
string optRecord = "Record";
string optReset = "Reset";
string optBuild = "Build";
string optPos = "Position";
string optClean = "Clean";
string optDone = "Done";

//Menu option descriptions
string descRecord = ": Record the position of all parts\n";
string descReset = ": Forgets the position of all parts\n";
string descBuild = ": Rez inv. items and position them\n";
string descPos = ": Reposition the parts to a new location\n";
string descClean = ": De-Rez all pieces\n";
string descDone = ": Remove all BB scripts and freeze parts in place.\n";

integer MENU_CHANNEL;
integer MENU_HANDLE;
key agent;
key objectowner;
integer group;
string title = "";
list optionlist = [];
integer bMoving;
vector vLastPos;
rotation rLastRot;
integer bRezzing;
integer iListenTimeout = 0;
integer iLastRez = 0;
integer iRezIndex;
//integer bTimerOn = FALSE;

//To avoid flooding the sim with a high rate of movements
//(and the resulting mass updates it will bring), we used
// a short throttle to limit ourselves
announce_moved()
{
    llRegionSay(PRIMCHAN, "MOVE " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
    llResetTime();        //Reset our throttle
    vLastPos = llGetPos();
    rLastRot = llGetRot();
    return;
}


rez_object()
{
    //Rez the object indicated by iRezIndex
    llRezObject(llGetInventoryName(INVENTORY_OBJECT, iRezIndex), llGetPos(), ZERO_VECTOR, llGetRot(), PRIMCHAN);
    iLastRez = llGetUnixTime();

    if(!bRezzing) {
        bRezzing = TRUE;
        //timer_on();
    }
}

post_rez_object()
{
    if ( llGetCreator() != llGetOwner() )
        llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, iRezIndex));
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
default {
    ///////////////////////////////////////////////////////////////////////////////
    changed(integer change) {
        if(change & CHANGED_OWNER)
        llResetScript();
    }

    ///////////////////////////////////////////////////////////////////////////////
    state_entry () {
        //Use which menu?
        if ( llGetCreator() == llGetOwner() ) {
            //Display all options
            optionlist = [optPos, optClean, optDone, optRecord, optReset, optBuild];
            title = optRecord + descRecord;
            title += optReset + descReset;
            title += optBuild + descBuild;
            title += optPos + descPos;
            title += optClean + descClean;
            title += optDone + descDone;

        } else {
            //Display limited options
            optionlist = [optBuild, optPos, optDone];
            title = optBuild + descBuild;
            title += optPos + descPos;
            title += optDone + descDone;
        }

        //Record our position
        vLastPos = llGetPos();
        rLastRot = llGetRot();

        llSetTimerEvent(fTimerRate);
    }

    ///////////////////////////////////////////////////////////////////////////////
    touch_start (integer total_number) {
        group = llDetectedGroup(0); // Is the Agent in the objowners group?
        agent = llDetectedKey(0); // Agent's key
        objectowner = llGetOwner(); // objowners key
        // is the Agent = the owner OR is the agent in the owners group
        if ( (objectowner == agent) || ( group && ingroup )  )  {
            iListenTimeout = llGetUnixTime() + llFloor(fListenTime);
            MENU_CHANNEL = llFloor(llFrand(-99999.0 - -100));
            MENU_HANDLE = llListen(MENU_CHANNEL,"","","");
            llDialog(agent, title, optionlist, MENU_CHANNEL);
            //timer_on();
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    listen(integer channel, string name, key id, string message) {
        if ( message == optRecord ) {
            llOwnerSay("Recording positions...");
            llRegionSay(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
            return;
        }
        if( message == optReset ) {
            llOwnerSay("Forgetting positions...");
            llShout(PRIMCHAN, "RESET");
            return;
        }
        if ( message == optBuild ) {
            if(chatty) llOwnerSay("Rezzing build pieces...");
            iRezIndex = llGetInventoryNumber(INVENTORY_OBJECT) - 1;
            rez_object();
            return;
        }
        if ( message == optPos ) {
            if(chatty) llOwnerSay("Positioning");
            vector vThisPos = llGetPos();
            rotation rThisRot = llGetRot();
            llRegionSay(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
            return;
        }
        if ( message == optClean ) {
            llRegionSay(PRIMCHAN, "CLEAN");
            return;
        }
        if ( message == optDone ) {
            llRegionSay(PRIMCHAN, "DONE");
            if(chatty) llOwnerSay("Removing Builder's Buddy scripts.");
            return;
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    moving_start()
    {
        if( !bMoving )
        {
            bMoving = TRUE;
            //timer_on();
            announce_moved();
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    object_rez(key id) {
        //The object rezzed, perform any post-rez processing
        post_rez_object();

        //Move on to the next object
        //Loop through backwards (safety precaution in case of inventory change)
        iRezIndex--;
        if(iRezIndex >= 0) {
            //Attempt to rez it
            rez_object();

        } else {
            //Rezzing complete, now positioning
            iLastRez = 0;
            bRezzing = FALSE;
            if(chatty) llOwnerSay("Positioning");
            llRegionSay(PRIMCHAN, "MOVE " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    timer() {
        //Did we change position/rotation?
        if( (llGetRot() != rLastRot) || (llGetPos() != vLastPos) )
        {
            if( llGetTime() > fTimerRate ) {
                announce_moved();
            }
        }

        //Are we rezzing?
        if(bRezzing) {
            //Did the last one take too long?
            if((llGetUnixTime() - iLastRez) >= iRezWait) {
                //Yes, retry it
                if(chatty) llOwnerSay("Reattempting rez of most recent piece");
                rez_object();
            }
        }

        //Open listener?
        if( iListenTimeout != 0 )
        {
            //Past our close timeout?
            if( iListenTimeout <= llGetUnixTime() )
            {
                iListenTimeout = 0;
                llListenRemove(MENU_HANDLE);
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////////
    on_rez(integer iStart)
    {
        //Reset ourselves
        llResetScript();
    }
}

The Component Script

///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.9 (Component Pieces)
// by Newfie Pendragon, March 2006
//
// This script is distributed with permission that it may be used in
// any way, or may be further modified/included in resale items.
// HOWEVER, if this script is used as part of a for-sale product,
// it is required that appropriate credit be given to Newfie for
// the script (or portions used in derivatives).  That's a fair price
// in exchange for unlimited use of this script, dontcha think?
//
//  SL Forum thread and new versions found here:
//  http://forums.secondlife.com/showthread.php?t=96792
///////////////////////////////////////////////////////////////////////////////
// INSTRUCTIONS
//  This is the *Component Piece* half of the Builders' Buddy system.
//  Drop it into each 'piece' of the building.  Drop the Base Prim Script
//  into the prim  that will be the container/box that will be used to
//  store the building once completed.  It can be in each individual
//  prim, but if you link as much as possible (and put the script in the link
//  set), it'll be much more neighbourly and less strain on the sim.
//
// QUICK USE:
//  - Drop this script in the Base.
//  - Drop the "Component" Script in each building part.
//  - Touch  your Base, and choose RECORD
//  - Take all building parts into inventory
//  - Drag building parts from inventory into Base Prim
//  - Touch your base and choose BUILD
//
///////////////////////////////////////////////////////////////////////////////
//  History
//
// v1.0 - 20060328 - Newfie Pendragon - Original Version
// v1.5 - 20060612 - Androclese Antonelli
//        - (See base script for details)
// v1.6 - 20060624 - Newfie Pendragon
//      - Added active repositioning (pieces move as the base piece moves)
//      - Pieces use WarpPos technique to instantanetly move large distances
//        - Pieces no longer move until the the "Record" option has been used
//        at least once
//      - Pieces will not move if base is not same owner as the pieces
//      - Pieces no longer 'bounce' when hitting the ground
// v1.7 - 20060821 - Correction for non-zero rotation (thanks Ed44 Gupta!)
// v1.9 - 20070630 - Added check to keep within sim edges
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// Configurable Settings
float fTimerInterval = 0.25;     // Time in seconds between movement 'ticks'
integer PRIMCHAN = -19730611;    // Channel used by Base Prim to talk to Component Prims;
                                 // ***THIS MUST MATCH IN BOTH SCRIPTS!***

//////////////////////////////////////////////////////////////////////////////////////////
// Runtime Variables (Dont need to change below here unless making a derivative)
vector vOffset;
rotation rRotation;
integer bNeedMove;
vector vDestPos;
rotation rDestRot;
integer bRecorded = FALSE;


////////////////////////////////////////////////////////////////////////////////
string first_word(string In_String, string Token)
{
    //This routine searches for the first word in a string,
    // and returns it.  If no word boundary found, returns
    // the whole string.
    if(Token == "") Token = " ";
    integer pos = llSubStringIndex(In_String, Token);

    //Found it?
    if( pos >= 1 )
        return llGetSubString(In_String, 0, pos - 1);
    else
        return In_String;
}

////////////////////////////////////////////////////////////////////////////////
string other_words(string In_String, string Token)
{
    //This routine searches for the other-than-first words in a string,
    // and returns it.  If no word boundary found, returns
    // the an empty string.
    if( Token == "" ) Token = " ";

    integer pos = llSubStringIndex(In_String, Token);

    //Found it?
    if( pos >= 1 )
        return llGetSubString(In_String, pos + 1, llStringLength(In_String));
    else
        return "";
}

////////////////////////////////////////////////////////////////////////////////
do_move()
{
    integer i = 0;
    vector vLastPos = ZERO_VECTOR;
    while( (i < 5) && (llGetPos() != vDestPos) )
    {
        list lParams = [];

        //If we're not there....
        if( llGetPos() != vDestPos )
        {
            //We may be stuck on the ground...
            //Did we move at all compared to last loop?
            if( llGetPos() == vLastPos )
            {
                //Yep, stuck...move straight up 10m (attempt to dislodge)
                lParams = [ PRIM_POSITION, llGetPos() + <0, 0, 10.0> ];
                //llSetPos(llGetPos() + <0, 0, 10.0>);
            } else {
                //Record our spot for 'stuck' detection
                vLastPos = llGetPos();
            }
        }

        //Try to move to destination
        //Upgraded to attempt to use the llSetPrimitiveParams fast-move hack
        //(Newfie, June 2006)
        integer iHops = llAbs(llCeil(llVecDist(llGetPos(), vDestPos) / 10.0));
        integer x;
        for( x = 0; x < iHops; x++ ) {
            lParams += [ PRIM_POSITION, vDestPos ];
        }
        llSetPrimitiveParams(lParams);
        //llSleep(0.1);
        i++;
    }

    //Set rotation
    llSetRot(rDestRot);
}


//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
default
{
    //////////////////////////////////////////////////////////////////////////////////////////
    state_entry()
    {
        //Open up the listener
        llListen(PRIMCHAN, "", NULL_KEY, "");
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    on_rez(integer iStart)
    {
        //Set the channel to what's specified
        if( iStart != 0 )
        {
            PRIMCHAN = iStart;
            state reset_listeners;
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    listen(integer iChan, string sName, key kID, string sText)
    {
        string sCmd = llToUpper(first_word(sText, " "));

        if( sCmd == "RECORD" )
        {
            sText = other_words(sText, " ");
            list lParams = llParseString2List(sText, [ "|" ], []);
            vector vBase = (vector)llList2String(lParams, 0);
            rotation rBase = (rotation)llList2String(lParams, 1);

            vOffset = (llGetPos() - vBase) / rBase;
            rRotation = llGetRot() / rBase;
            bRecorded = TRUE;
            llOwnerSay("Recorded position.");
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "MOVE" )
        {
            //Don't move if we've not yet recorded a position
            if( !bRecorded ) return;

            //Also ignore commands from bases with a different owner than us
            //(Anti-hacking measure)
            if( llGetOwner() != llGetOwnerKey(kID) ) return;


            //Calculate our destination position
            sText = other_words(sText, " ");
            list lParams = llParseString2List(sText, [ "|" ], []);
            vector vBase = (vector)llList2String(lParams, 0);
            rotation rBase = (rotation)llList2String(lParams, 1);

            //Calculate our destination position
            vDestPos = (vOffset * rBase) + vBase;
            rDestRot = rRotation * rBase;

            //Make sure our calculated position is within the sim
            if(vDestPos.x < 0.0) vDestPos.x = 0.0;
            if(vDestPos.x > 255.0) vDestPos.x = 255.0;
            if(vDestPos.y < 0.0) vDestPos.y = 0.0;
            if(vDestPos.y > 255.0) vDestPos.y = 255.0;
            if(vDestPos.x > 768.0) vDestPos.x = 768.0;

            //Turn on our timer to perform the move?
            if( !bNeedMove )
            {
                llSetTimerEvent(fTimerInterval);
                bNeedMove = TRUE;
            }
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "DONE" )
        {
            //We are done, remove script
            llRemoveInventory(llGetScriptName());
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "CLEAN" )
        {
            //Clean up
            llDie();
            return;
        }

        //////////////////////////////////////////////////////////////////////////////////////////
        if( sCmd == "RESET" )
        {
            llResetScript();
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    timer()
    {
        //Turn ourselves off
        llSetTimerEvent(0.0);

        //Do we need to move?
        if( bNeedMove )
        {
            //Perform the move and clean up
            do_move();
            bNeedMove = FALSE;
        }
        return;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
state reset_listeners
{
    //////////////////////////////////////////////////////////////////////////////////////////
    state_entry()
    {
        state default;
    }
}