Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Builders' Buddy - Building Positioning/Rotation Tool

Newfie Pendragon
Crusty and proud of it
Join date: 19 Dec 2003
Posts: 1,025
03-28-2006 19:11
On occasion I get requests to do some architectural builds. Unless they're built in-place, it can be a big pain to package it up, put it out, position/rotate such a large build. Throw on top of that the problem with pieces 'drifting' while being rotated, and it can be quite a hair-pulling experience.

So...I wrote the below scripts, which I'm calling the Builders' Buddy scripts. Drop the Base script into the packaging box, and the Component script into each piece of the building. Issue the /12345 record command, and the pieces remember their position/rotation in relationship to the Base. From then on, just move/rotate the Base, and /12345 move to have the building follow along.

First, the Base Script:
CODE

// Builders' Buddy 1.0 (Base Prim)
//
// 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?


//INSTRUCTIONS
//(These instructions use channel 1234 in the examples, but can be
// changed further down in the code to whatever channel you wish.)
//
// This is the *Base Prim* half of the Builders' Buddy system. Drop
// it into the prim that will be the container/box that will be used
// to store the building once completed. Drop the *Component* scripts
// into each 'piece' of the building.
//
// QUICK USE:
// - Drop this script in the Base.
// - Drop the "Component" Script in each building part.
// - Type: /12345 RECORD
// - Take all building parts into inventory
// - Drag building parts from inventory into Base Prim
// - Type /12345 BUILD
//
// OTHER COMMANDS
// - To reposition, move/rotate Base Prim Type: /12345 MOVE
// - To lock into position (removes scripts) Type: /12345 DONE
// - To delete building pieces: /12345 CLEAN


//////////////////////////////////////////////////////////////////////////////////////////
// Configurable Settings
integer CHAN = 12345; //Channel used to talk to Base Prim; change if you want
integer PRIMCHAN = 12346; //Channel used by Base Prim to talk to Component Prims;
// This must match in both scripts


//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
default
{
//////////////////////////////////////////////////////////////////////////////////////////
state_entry()
{
llListen(CHAN, "", llGetOwner(), "");
}

//////////////////////////////////////////////////////////////////////////////////////////
listen(integer iChan, string sName, key kID, string sText)
{
//////////////////////////////////////////////////////////////////////////////////////////
sText = llToUpper(sText);
if( sText == "RECORD" )
{
llOwnerSay("Recording positions...");
llShout(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
return;
}

//////////////////////////////////////////////////////////////////////////////////////////
if( sText == "BUILD" )
{
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
integer i;
integer iCount = llGetInventoryNumber(INVENTORY_OBJECT);

//Loop through backwards (safety precaution in case of inventory change)
llOwnerSay("Rezzing build pieces...");
for( i = iCount - 1; i >= 0; i-- )
{
llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), vThisPos, ZERO_VECTOR, rThisRot, PRIMCHAN);
}

llOwnerSay("Positioning");
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}

//////////////////////////////////////////////////////////////////////////////////////////
if( sText == "MOVE" )
{
llOwnerSay("Positioning");
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}

//////////////////////////////////////////////////////////////////////////////////////////
if( sText == "CLEAN" )
{
llShout(PRIMCHAN, "CLEAN");
return;
}

//////////////////////////////////////////////////////////////////////////////////////////
if( sText == "DONE" )
{
llShout(PRIMCHAN, "DONE");
//llDie();
llOwnerSay("Removing mover scripts.");
return;
}
}
}


The Component Script:
CODE

// Builders' Buddy 1.0 (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?


//INSTRUCTIONS
//(These instructions use channel 1234 in the examples, but can be
// changed further down in the code to whatever channel you wish.)
//
// 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.
// - Type: /12345 RECORD
// - Take all building parts into inventory
// - Drag building parts from inventory into Base Prim
// - Type /12345 BUILD
//
// OTHER COMMANDS
// - To reposition, move/rotate Base Prim Type: /12345 MOVE
// - To lock into position (removes scripts) Type: /12345 DONE
// - To delete building pieces: /12345 CLEAN

//////////////////////////////////////////////////////////////////////////////////////////
// Configurable Settings
integer PRIMCHAN = 12346; //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;


////////////////////////////////////////////////////////////////////////////////
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 "";
}


//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
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;
rRotation = llGetRot() / rBase;
llOwnerSay("Recorded position.");
return;
}

//////////////////////////////////////////////////////////////////////////////////////////
if( sCmd == "MOVE" )
{
//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
vector vDestPos = (vOffset * rBase) + vBase;
rotation rDestRot = rRotation * rBase;

llOwnerSay("Pos: " + (string)vDestPos + ", Rot: " + (string)rDestRot);
integer i = 0;
vector vLastPos = ZERO_VECTOR;
while( (i < 25) && (llGetPos() != vDestPos) )
{
//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)
llSetPos(llGetPos() + <0, 0, 10.0>);
} else {
//Record our spot for 'stuck' detection
vLastPos = llGetPos();
}
}
i++;

//Try to move to destination
llSetPos(vDestPos);
llSleep(0.1);
}

//Set rotation
llSetRot(rDestRot);
return;
}

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

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

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


Finally, a little script that shapes your prim into an arrow (good for orienting front/back/etc) - not needed, but might be handy
CODE

default
{
state_entry()
{
llSetPrimitiveParams([
PRIM_TYPE, PRIM_TYPE_SPHERE, PRIM_HOLE_DEFAULT, <0.45, 0.55, 0.0>, 0.0, ZERO_VECTOR, <0.35, 0.65, 0.0> ]);
llSetRot(ZERO_ROTATION);
llRemoveInventory(llGetScriptName());
}
}


All feedback and suggestions will be greatly appreciated.

- Newfie Pendragon
_____________________
Nada Epoch
The Librarian
Join date: 4 Nov 2002
Posts: 1,423
Discussion Thread
03-28-2006 20:03
/54/2b/96792/1.html
_____________________
i've got nothing. ;)
Kalidor Lazarno
Just here to have fun!!
Join date: 1 Feb 2006
Posts: 7
Menu for Builders' Buddy
03-31-2006 16:26
Threw this together it is a dialog menu Builder's Buddy, thought this nice script could use a menu ...

CODE


//Based on Builder's Buddy by Newfie Pendragon, March 2006
//you must still use the component scripts from the original post.

// 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 MOVE
// - To lock into position (removes scripts) choose DONE
// - To delete building pieces: choose CLEAN

string name1 = "RECORD"; //Name each option-these names will be your button names.
string name2 = "BUILD";
string name3 = "MOVE";
string name4 = "CLEAN";
string name5 = "DONE";

integer CHAN = 12345; //Channel used to talk to Base Prim; change if you want
integer PRIMCHAN = 12346; //Channel used by Base Prim to talk to Component Prims;
// This must match in both scripts



integer group;

key id;

list optionlist = [];

default
{
state_entry()
{
llListen(12345, "", NULL_KEY, "");
optionlist = [name1, name2, name3,name4, name5];
}

touch_start(integer total_number)
{
group = llDetectedGroup(0);
if(group == TRUE)
{
id = llDetectedKey(0);
llDialog(id, "Pick your poision", optionlist, 12345);
}
}
listen(integer channel, string name, key id, string message)
{
if(message == name1)
{
llOwnerSay("Recording positions...");
llShout(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
return;
}
if(message == name2)
{
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
integer i;
integer iCount = llGetInventoryNumber(INVENTORY_OBJECT);

//Loop through backwards (safety precaution in case of inventory change)
llOwnerSay("Rezzing build pieces...");
for( i = iCount - 1; i >= 0; i-- )
{
llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), vThisPos, ZERO_VECTOR, rThisRot, PRIMCHAN);
}

llOwnerSay("Positioning");
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if(message == name3)
{
llOwnerSay("Positioning");
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if(message == name4)
{
llShout(PRIMCHAN, "CLEAN");
return;
}
if(message == name5)
{
llShout(PRIMCHAN, "DONE");
//llDie();
llOwnerSay("Removing mover scripts.");
return;
}

}
}
Khatsworth Lazarno
Registered User
Join date: 19 Mar 2006
Posts: 6
04-17-2006 19:42
Awesome, I'll forward my friends to this script when I next see them, as a bunch of them require one. Great work! I currently use a script made by another friend of mine to do this; works like a charm.


Question though, is your script set up with a system to recognize different builds, or would 3 people building something with these scripts suddenly have all their work mixed up?
Darkhorse Golem
Registered User
Join date: 25 Oct 2005
Posts: 23
Great Idea
04-26-2006 06:47
But,

I'm having problems with the workings of it.

I follow the instructions for both sets of scripts, but when i click build (after recording and putting the connecting parts in my inv and then into the main building object) everything rezzes, and goes to all the wrong places?

any idea's please.

Thanks.
MadameThespian Underhill
"I'm Aaacting!"
Join date: 18 Dec 2002
Posts: 38
04-29-2006 14:49
Now all I need is a script that inserts the component script into each of the 398 prims of my already-built house! LOL ;)
_____________________
Support the arts...go to the theatre!
ArchTx Edo
Mystic/Artist/Architect
Join date: 13 Feb 2005
Posts: 1,993
05-18-2006 15:44
Builder's Buddy is a really cool tool!! Thanks Newfie!. Here are a couple of tips on using it (learned the hard way) and a question on how to improve it.

Tips:

1. The base installation box needs to have all rotations set at zero otherwise it the objects it rezzes come out funny.

2. If you use this to package a build for resell, the new owner will not be able to rezz the objects in the base prim unless they first reset the Builder's Buddy script inside. This is because of the <<llListen(CHAN, "", llGetOwner(), "";);>> Get Owner call I believe.

Question: Is there someway I can make this script reset itself when a new owner rezzes the Base installation box?
_____________________

VRchitecture Model Homes at http://slurl.com/secondlife/Shona/60/220/30
http://www.slexchange.com/modules.php?name=Marketplace&MerchantID=2240
http://shop.onrez.com/Archtx_Edo
Androclese Antonelli
Org. B-Day: 05/11/04
Join date: 25 Apr 2006
Posts: 96
Minor Upgrades to the Builder's Buddy Script
06-13-2006 09:37
I have gotten a lot of use out of the original script and along the way, I have made a number of changes to it.

I figure it is only fair thing to do is to publish those changes here.

The changes:
  1. I added two menu systems, one for the creator, one for the reseller. The flag is set inside the script, so make sure set it correctly when using this script in a saleable item. Turning creator flag off makes the Build button a one-time rez by removing the inventory items
  2. I added a flag to enable/disable the group use flag. This is only really usefull if you are using this to asselble items you have sold.
  3. Added a Random number generator to the menu system to keep multiple versions of the scripted items from talking to eachother
  4. Added a timer to the dialog menu to close the menu handler after 10 seconds.
  5. As requested, when the spawing object is sold/transfered to a new owner, the base prim script will reset and recognized that new owner.
I found that the best way to make these scripts work is to spawn a single prim and put all the inventory items and the base script in there. As stated above, is the rotation is set to <0, 0, 0> You'll be better off.

I hope you find these changes useful.



CODE
///////////////////////////////////////////////////////////////////////////////
//
// Builder Buddy - Root Prim Script
//
// Coded by: Newfie Pendragon
// Written On: 20060328
//
// This code is open source
//
// 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
///////////////////////////////////////////////////////////////////////////////
// User Variables
///////////////////////////////////////////////////////////////////////////////
// Set to TRUE for unlimited inventory spawns and all menu options
// Set to FALSE to a one-time inventory spawn and limited menu options
integer creator = TRUE;

// 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;

//Name each option-these names will be your button names.
string name1 = "Record";
string name2 = "Build";
string name3 = "Position";
string name4 = "Clean";
string name5 = "Done";

//Menu option descriptions
string desc1 = " : Record the position of all parts\n";
string desc2 = " : Spawn inv. items and position them\n";
string desc3 = " : Reposition the parts to a new location\n";
string desc4 = " : De-Rez all pieces\n";
string desc5 = " : Remove all BB scripts and make the parts permanent.\n";

// 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;

///////////////////////////////////////////////////////////////////////////////
// DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN
///////////////////////////////////////////////////////////////////////////////
integer MENU_CHANNEL;
integer MENU_HANDLE;
key agent;
key objectowner;
integer group;
string title = "";
list optionlist = [];

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

state_entry () {

if ( creator ) {
optionlist = [name1, name2, name3, name4, name5, " "];
title = name1 + desc1;
title += name2 + desc2;
title += name3 + desc3;
title += name4 + desc4;
title += name5 + desc5;

} else {
optionlist = [name2, name3, name5];
title = name2 + desc2;
title += name3 + desc3;
title += name5 + desc5;
}
}

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 ) ) {
MENU_CHANNEL = llFloor(llFrand(-99999.0 - -100));
MENU_HANDLE = llListen(MENU_CHANNEL,"","","");
llDialog(agent, title, optionlist, MENU_CHANNEL);
llSetTimerEvent(10.0);
}
}
listen(integer channel, string name, key id, string message) {
if ( message == name1 ) {
llOwnerSay("Recording positions...");
llShout(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
return;
}
if ( message == name2 ) {
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
integer i;
integer iCount = llGetInventoryNumber(INVENTORY_OBJECT);

//Loop through backwards (safety precaution in case of inventory change)
llOwnerSay("Rezzing build pieces...");
for( i = iCount - 1; i >= 0; i-- )
{
llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), vThisPos, ZERO_VECTOR, rThisRot, PRIMCHAN);
if ( creator == FALSE ) {
llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, i));
}
}

llOwnerSay("Positioning");
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if ( message == name3 ) {
llOwnerSay("Positioning");
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if ( message == name4 ) {
llShout(PRIMCHAN, "CLEAN");
return;
}
if ( message == name5 ) {
llShout(PRIMCHAN, "DONE");
//llDie();
llOwnerSay("Removing mover scripts.");
return;
}
}

timer() {
//so the menu timeout and close its listener
llSetTimerEvent(0.0);
llListenRemove(MENU_HANDLE);
}
}
_____________________
The Sculpted Garden

Originally Born 5/11/2004 - New AV, Old Player.

If you leave the game, don't delete your account, just make it 'free'. You'll lose your inventory like I did. *sniff*
Newfie Pendragon
Crusty and proud of it
Join date: 19 Dec 2003
Posts: 1,025
06-24-2006 14:05
Hi all:


As promised, here is my own latest set of enhancements. First, the list of changes:

Base Script:
  1. Added active repositioning (building moves as the base piece moves)
  2. Added "Reset" Option to unlink parts from base temporarily
  3. Modified creator flag to automatically set if owner is creator or not
  4. Minor changes to improve code readability (for those learning LSL)



Component Script:
  1. Pieces use WarpPos technique to instantanetly move large distances
  2. Pieces no longer move until the the "Record" option has been used at least once
  3. Pieces will not move if base is not same owner as the pieces
  4. Pieces no longer 'bounce' when hitting the ground



And now the scripts:

Builders' Buddy Base Script v1.6:
CODE

///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.6 (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
///////////////////////////////////////////////////////////////////////////////
//
// 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)

///////////////////////////////////////////////////////////////////////////////
// User Variables
///////////////////////////////////////////////////////////////////////////////

// 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;

//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 = " : Spawn 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 make the parts permanent.\n";

//How often (in seconds) to check for change in position when moving
float fMovingRate = 0.25;

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

//Minimum amount of time (in seconds) between movement updates
float fShoutRate = 0.25;

// 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;

///////////////////////////////////////////////////////////////////////////////
// DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN
///////////////////////////////////////////////////////////////////////////////
integer MENU_CHANNEL;
integer MENU_HANDLE;
key agent;
key objectowner;
integer group;
string title = "";
list optionlist = [];
integer bMoving;
vector vLastPos;
rotation rLastRot;
integer iListenTimeout = 0;

//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()
{
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
llResetTime(); //Reset our throttle
vLastPos = llGetPos();
rLastRot = llGetRot();
return;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
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();

}

///////////////////////////////////////////////////////////////////////////////
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() + 10;
MENU_CHANNEL = llFloor(llFrand(-99999.0 - -100));
MENU_HANDLE = llListen(MENU_CHANNEL,"","","");
llDialog(agent, title, optionlist, MENU_CHANNEL);
llSetTimerEvent(fShoutRate);
}
}

///////////////////////////////////////////////////////////////////////////////
listen(integer channel, string name, key id, string message) {
if ( message == optRecord ) {
llOwnerSay("Recording positions...");
llShout(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
return;
}
if( message == optReset ) {
llOwnerSay("Forgetting positions...");
llShout(PRIMCHAN, "RESET");
return;
}
if ( message == optBuild ) {
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
integer i;
integer iCount = llGetInventoryNumber(INVENTORY_OBJECT);

//Loop through backwards (safety precaution in case of inventory change)
llOwnerSay("Rezzing build pieces...");
for( i = iCount - 1; i >= 0; i-- )
{
llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), vThisPos, ZERO_VECTOR, rThisRot, PRIMCHAN);
if ( llGetCreator() != llGetOwner() )
llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, i));
}

llOwnerSay("Positioning");
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if ( message == optPos ) {
llOwnerSay("Positioning");
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if ( message == optClean ) {
llShout(PRIMCHAN, "CLEAN");
return;
}
if ( message == optDone ) {
llShout(PRIMCHAN, "DONE");
//llDie();
llOwnerSay("Removing mover scripts.");
return;
}
}

///////////////////////////////////////////////////////////////////////////////
moving_start()
{
if( !bMoving )
{
bMoving = TRUE;
llSetTimerEvent(0.0); //Resets the timer if already running
llSetTimerEvent(fMovingRate);
announce_moved();
}
}

///////////////////////////////////////////////////////////////////////////////
timer() {
//Were we moving?
if( bMoving )
{
//Did we change position/rotation?
if( (llGetRot() != rLastRot) || (llGetPos() != vLastPos) )
{
if( llGetTime() > fShoutRate ) {
announce_moved();
}
}
} else {
//Have we been sitting long enough to consider ourselves stopped?
if( llGetTime() > fStoppedTime )
bMoving = FALSE;
}

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

//Stop the timer?
if( (iListenTimeout == 0) && ( !bMoving ) )
{
llOwnerSay("Stopping Timer");
llSetTimerEvent(0.0);
}
}

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



Builders' Buddy Component Piece v1.6:
CODE

///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.6 (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
//
// OTHER COMMANDS
// - To reposition, move/rotate Base Prim Type: /12345 MOVE
// - To lock into position (removes scripts) Type: /12345 DONE
// - To delete building pieces: /12345 CLEAN
///////////////////////////////////////////////////////////////////////////////
// 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
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// 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;
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;

//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;
}
}



Enjoy!
- Newfie Pendragon
_____________________
MC Seattle
Registered User
Join date: 3 Apr 2006
Posts: 63
06-30-2006 21:17
I posted version 1.6 on the slprotocol wiki so people on the teen grid could access the script. Probably should be put on the LSL Wiki by someone with an account there.

http://labs.highenergychemistry.com/slprotocol/index.php?title=Builders_Buddy
_____________________
Valgaav Xingjian
Registered User
Join date: 29 Sep 2005
Posts: 5
Updates
07-04-2006 14:36
Might want to cross post to the other thread. I only found this update by searching for Newfie's posts. Thank you guys so much for working on this script! I can't wait to try it out =)
ed44 Gupte
Explorer (Retired)
Join date: 7 Oct 2005
Posts: 638
07-07-2006 07:22
Newfie,

You still need to change the "record" code in the component to divide the vOffset by rBase. That way the base can have a non zero rotation.

CODE
vOffset = (llGetPos() - vBase) / rBase; 


Otherwise a great script! Works spectacularly!

Ed
Newfie Pendragon
Crusty and proud of it
Join date: 19 Dec 2003
Posts: 1,025
08-20-2006 14:15
Hi all - I've updated the script to incorporate the suggestion ed44 made (Thanks! I hate rotations lol), and attached below. The base prim script at this time is still v1.6, and the child piece component is at 1.7.

CODE

///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.7 (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
//
// OTHER COMMANDS
// - To reposition, move/rotate Base Prim Type: /12345 MOVE
// - To lock into position (removes scripts) Type: /12345 DONE
// - To delete building pieces: /12345 CLEAN
///////////////////////////////////////////////////////////////////////////////
// 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!)
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// 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;

//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;
}
}



I will also be adding these scripts to the LSL wiki library.

- Newfie Pendragon
_____________________
ArchTx Edo
Mystic/Artist/Architect
Join date: 13 Feb 2005
Posts: 1,993
10-10-2006 19:02
CODE
// OTHER COMMANDS 
// - To reposition, move/rotate Base Prim Type: /12345 MOVE
// - To lock into position (removes scripts) Type: /12345 DONE
// - To delete building pieces: /12345 CLEAN


These commands do not work in the latest versions of the script as they no longer say on channel 12345.
_____________________

VRchitecture Model Homes at http://slurl.com/secondlife/Shona/60/220/30
http://www.slexchange.com/modules.php?name=Marketplace&MerchantID=2240
http://shop.onrez.com/Archtx_Edo
Poppet McGimsie
Proprietrix, WUNDERBAR
Join date: 28 Jul 2006
Posts: 197
11-01-2006 05:17
This is a fabulous script, and I use it on my houses now.

BUT the active positioning is kind of annoying when you don't want it to be happening. For example, if you rez a house, and you want to change the position of the prim with the base script in it, relative to the house, you can't do it without removing all of the component scripts first and then re-putting them into the component parts, and I can't seem to do that without forgetting a section or two and having to do it several times before getting it right.

Also, with active positioning, what does the "position" function do anymore?

How about killing 2 birds with 1 stone by making the "position" function turn active positioning on and off? That would be spiffy.

Anyone know how to do that? I sure don't!!! But if you agree it's a good idea, and you are a scripting guru (hi Ed44!) maybe you could do it?
Valradica Vanek
Registered User
Join date: 1 Aug 2006
Posts: 78
Thanks and a Question
11-03-2006 09:14
This is a great piece of work for those of us relegated to the sandbox for our building.

I have been through the whole script(s) as an educational exercise (since I had thought about doing something like this) and I have a question. Where are you storing the relative location and rotation of the child objects? is it just being stored in the resident variables of the active child script? if so, this means that if the script is reset for some reason, the part needs to be "re-stored", correct? What happens if the server crashes and the scripts are reset? do I lose all this alignment information?

Just curious and thanks for all your hard work.

Valradica
Fire Centaur
Creator
Join date: 2 Nov 2006
Posts: 149
sounds great! what about derezz?
02-08-2007 00:57
This script sounds really intersting! I am the owner of English Village - an environment for teachers and students in SL.

I am currently in the midst of creating new classrooms for everyone, and what Id like to do - is outfit each classroom with a holodeck like functionality -

so... for instance, id have a set of buttons on the lecturn, and when pressed, a bunch of items I had preplaced and packaged up, would appear, then when another button was pressed, they would derezz and a new set would appear....

anyway your script could derezz the rezzed items?

If anyone would like to help me with this - I would be greatful - and give ya a cash bonus for the tweaks required! thanks!

Fire
_____________________
Second Life to your Facebook Profile!
http://www.facebook.com/p.php?api_key=00b8153d7f12a4c7228b6c927fb2c537
Newfie Pendragon
Crusty and proud of it
Join date: 19 Dec 2003
Posts: 1,025
04-29-2007 15:25
Based on some recurring feedback I've been getting (both here on the forums and in IM), I've slightly tweaked the script to reduce some of the sensitivity lag in the script. There is a new variable in the top of the script named "fListenTime" that now allows a person to control how long the script listens for a menu response.

Without further ado, here are the current script versions:

Base Script v1.8:
CODE
///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.8 (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.
///////////////////////////////////////////////////////////////////////////////
// User Variables
///////////////////////////////////////////////////////////////////////////////

// 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;

//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 = " : Spawn 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 make the parts permanent.\n";

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

//How often (in seconds) to check for change in position when moving
float fMovingRate = 0.25;

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

//Minimum amount of time (in seconds) between movement updates
float fShoutRate = 0.25;

// 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;

///////////////////////////////////////////////////////////////////////////////
// DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN
///////////////////////////////////////////////////////////////////////////////
integer MENU_CHANNEL;
integer MENU_HANDLE;
key agent;
key objectowner;
integer group;
string title = "";
list optionlist = [];
integer bMoving;
vector vLastPos;
rotation rLastRot;
integer iListenTimeout = 0;

//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()
{
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
llResetTime(); //Reset our throttle
vLastPos = llGetPos();
rLastRot = llGetRot();
return;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
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();

}

///////////////////////////////////////////////////////////////////////////////
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);
llSetTimerEvent(fShoutRate);
}
}

///////////////////////////////////////////////////////////////////////////////
listen(integer channel, string name, key id, string message) {
if ( message == optRecord ) {
llOwnerSay("Recording positions...");
llShout(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|"));
return;
}
if( message == optReset ) {
llOwnerSay("Forgetting positions...");
llShout(PRIMCHAN, "RESET");
return;
}
if ( message == optBuild ) {
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
integer i;
integer iCount = llGetInventoryNumber(INVENTORY_OBJECT);

//Loop through backwards (safety precaution in case of inventory change)
llOwnerSay("Rezzing build pieces...");
for( i = iCount - 1; i >= 0; i-- )
{
llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), vThisPos, ZERO_VECTOR, rThisRot, PRIMCHAN);
if ( llGetCreator() != llGetOwner() )
llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, i));
}

llOwnerSay("Positioning");
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if ( message == optPos ) {
llOwnerSay("Positioning");
vector vThisPos = llGetPos();
rotation rThisRot = llGetRot();
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}
if ( message == optClean ) {
llShout(PRIMCHAN, "CLEAN");
return;
}
if ( message == optDone ) {
llShout(PRIMCHAN, "DONE");
//llDie();
llOwnerSay("Removing mover scripts.");
return;
}
}

///////////////////////////////////////////////////////////////////////////////
moving_start()
{
if( !bMoving )
{
bMoving = TRUE;
llSetTimerEvent(0.0); //Resets the timer if already running
llSetTimerEvent(fMovingRate);
announce_moved();
}
}

///////////////////////////////////////////////////////////////////////////////
timer() {
//Were we moving?
if( bMoving )
{
//Did we change position/rotation?
if( (llGetRot() != rLastRot) || (llGetPos() != vLastPos) )
{
if( llGetTime() > fShoutRate ) {
announce_moved();
}
}
} else {
//Have we been sitting long enough to consider ourselves stopped?
if( llGetTime() > fStoppedTime )
bMoving = FALSE;
}

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

//Stop the timer?
if( (iListenTimeout == 0) && ( !bMoving ) )
{
llOwnerSay("Stopping Timer");
llSetTimerEvent(0.0);
}
}

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


Component Piece v1.7:
CODE
///////////////////////////////////////////////////////////////////////////////
// Builders' Buddy 1.7 (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
//
// 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.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!)
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// 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;

//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;
}
}
_____________________
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
05-11-2007 08:49
compile error on:
CODE

for( i = iCount - 1; i >= 0; i-- )
{
llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), vThisPos, ZERO_VECTOR, rThisRot, PRIMCHAN);
if ( llGetCreator() != llGetOwner() )
llRemoveInventory(llGetInventoryName(INVENTORY_OBJ ECT, i));
}

llOwnerSay("Positioning");
llShout(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|"));
return;
}

(INVENTORY_OBJ ECT, i), it puts the error infront of ECT
Sterling Whitcroft
Registered User
Join date: 2 Jul 2006
Posts: 678
05-18-2007 14:00
Alicia,
Remove the space in OBJ ECT and make it OBJECT.

The forum software seems to insert spaces at random moments.
Whenever you cut and paste code from the forums that wont' compile, read the code to see if spaces were inadvertantly added.
Shyan Graves
Registered User
Join date: 10 Feb 2007
Posts: 52
05-21-2007 07:44
Hi!

Alicia delete the spaces between INVENTORY_OBJ and ECT, so that the line looks like below.

llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, i));

Regards,
Shyan
Poppet McGimsie
Proprietrix, WUNDERBAR
Join date: 28 Jul 2006
Posts: 197
add customer support
05-30-2007 19:34
I use the builder's buddy for my prefab houses. Lots of people have trouble rezzing the houses, however. So I just slightly modified the touch_start part of the script to allow the object creator to rez any house even if it doesn't belong to them:

///////////////////////////////////////////////////////////////////////////////
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
objectcreator = llGetCreator(); //obj creator's key, allows creator to build the house for customers
// is the Agent = the owner OR is the agent in the owners group
if ( (objectowner == agent) || ( group && ingroup ) || (agent == objectcreator) ) {
iListenTimeout = llGetUnixTime() + 10;
MENU_CHANNEL = llFloor(llFrand(-99999.0 - -100));
MENU_HANDLE = llListen(MENU_CHANNEL,"","","";);
llDialog(agent, title, optionlist, MENU_CHANNEL);
llSetTimerEvent(fShoutRate);
}
}
////////////////////////////////////////////////////////////////////////////////

also need to add key objectcreator; to the parameters.
Nada Epoch
The Librarian
Join date: 4 Nov 2002
Posts: 1,423
Discussion Thread
05-31-2007 09:48
/54/2b/96792/1.html
_____________________
i've got nothing. ;)
Newfie Pendragon
Crusty and proud of it
Join date: 19 Dec 2003
Posts: 1,025
06-30-2007 17:52
Hi all,

I've released another version of BB scripts. There's a whole bunch of little changes, too numerous to count (that, and I've forgotten some of them), but here's the core changes:

* Updated to use LLRegionSay(). YES, that means NO MORE 96m LIMIT!
* The way the script rezzed the pieces is changed, and added a timer to catch any lost due to lag/gray goo fence. My own testing seems to hint it's a touch faster too.
* The timer has been reworked to be always on, rather than switching on/off as needed. Was getting too complex, and wasn't always working as intended. Works fine now.
* Added a check to ensure component pieces always stay within sim edges.


Below are the scripts. As usual, just sent me an IM if you'd like a copy in-world rather than cutting pasting.

- Newfie

First, the base script....
CODE

///////////////////////////////////////////////////////////////////////////////
// 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;
}

//timer_on()
//{
// //Tun on only if not already on
// if(!bTimerOn) {
//llSetTimerEvent(fTimerRate);
//bTimerOn = TRUE;
//}
//}

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() {
//Are we moving?
//if(bMoving)
//{
//Did we change position/rotation?
if( (llGetRot() != rLastRot) || (llGetPos() != vLastPos) )
{
if( llGetTime() > fTimerRate ) {
announce_moved();
}
}
//} else {
//Have we been sitting long enough to consider ourselves stopped?
//if( llGetTime() > fStoppedTime )
//bMoving = FALSE;
//}

//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);
}
}

//Stop the timer?
//if( (iListenTimeout == 0) && ( !bMoving ) && (!bRezzing) )
//{
//llSetTimerEvent(0.0);
//bTimerOn = FALSE;
//}
}

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



Now, the script for the pieces (components)...
CODE

///////////////////////////////////////////////////////////////////////////////
// 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;
}
}
_____________________