This is my humble contribution to the topic:
Version 2.0 of Base Script
Also published in LSL Wiki: (
http://wiki.secondlife.com/wiki/Builders_BuddyRead script history for explanation of changes
///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 2.0 (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:
//
/54/2b/96792/1.html///////////////////////////////////////////////////////////////////////////////
//
// 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
// 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
// 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;
//Specify which Menu Options will be displayed
//FALSE will restrict full options to creator
//TRUE will offer full options to anyone
integer fullOptions = FALSE;
///////////////////////////////////////////////////////////////////////////////
// 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() || fullOptions) {
//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"

;
llDie();
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();
}
}
Version 1.9 of Component Pieces
///////////////////////////////////////////////////////////////////////////////
// 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:
//
/54/2b/96792/1.html///////////////////////////////////////////////////////////////////////////////
// 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;
}
}
Have fun,
greetz Huney