Builders Buddy
From libsecondlife
The Base script:
/////////////////////////////////////////////////////////////////////////////// // Builders' Buddy 1.9 (Base Script) // by Newfie Pendragon, March 2006 // // This script is distributed with permission that it may be used in // any way, or may be further modified/included in resale items. // HOWEVER, if this script is used as part of a for-sale product, // it is required that appropriate credit be given to Newfie for // the script (or portions used in derivatives). That's a fair price // in exchange for unlimited use of this script, dontcha think? // // SL Forum thread and new versions found here: // http://forums.secondlife.com/showthread.php?t=96792 /////////////////////////////////////////////////////////////////////////////// // // Script Purpose & Use // // Functions are dependent on the "component script" // // QUICK USE: // - Drop this script in the Base. // - Drop the "Component" Script in each building part. // - Touch your Base, and choose RECORD // - Take all building parts into inventory // - Drag building parts from inventory into Base Prim // - Touch your base and choose BUILD // // OTHER COMMANDS from the Touch menu // - To reposition, move/rotate Base Prim choose POSITION // - To lock into position (removes scripts) choose DONE // - To delete building pieces: choose CLEAN // /////////////////////////////////////////////////////////////////////////////// // // History // // v1.0 - 20060328 - Newfie Pendragon // - Original Version // v1.1 - 20060331 - Kalidor Lazarno // - Added a Dialog Engine to the base script // v1.5 - 20060612 - Androclese Antonelli // - Added a random number generator to the dialog engine to elimintate // problems with multiple BB boxes cross-talking // - Added a timer to the listen command to put it asleep after 10sec. // - Added a Menu Description // - Added n "creator" flag so the owner could use the same object with full // menu options and only a single flag change // - Added an "ingroup" flag to enable/disable the same group use function // - Non-Admin usage cleans the inventory items as they spawn // v1.6 - 20060624 - Newfie Pendragon // - Added active repositioning (building moves as the base piece moves) // - Added "Reset" Option to unlink parts from base temporarily // - Modified creator flag to automatically set based if owner is creator // - Minor changes to improve code readability (for those learning LSL) // v1.8 - 20070429 - Newfie Pendragon // - Added a variable to allow a user to tweak how long a listener is open, // and changed the default to 30 seconds. // v1.9 - 20070630 - Newfie Pendragon // - Changed to use llRegionSay - no more 96m max distance (same sim) // - Changed rez sequence to be less affected by lag/gray goo fence // - Timer always on, less code/more reliable /////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Configurable Settings // Channel used by Base Prim to talk to Component Prims // This channel must be the same one in the component script // A negative channel is used because it elimited accidental activations // by an Avatar talking on obscure channels integer PRIMCHAN = -19730611; // Channel used by Base Prim to talk to Component Prims; // ***THIS MUST MATCH IN BOTH SCRIPTS!*** // Set to TRUE to allow group members to use the dialog menu // Set to FALSE to disallow group members from using the dialog menu integer ingroup = TRUE; //Set to FALSE if you dont want the script to say anything while 'working' integer chatty = TRUE; //How long to listen for a menu response before shutting down the listener float fListenTime = 30.0; //How often (in seconds) to perform any timed checks float fTimerRate = 0.25; //How long to sit still before exiting active mode float fStoppedTime = 30.0; //SL sometimes blocks rezzing to prevent "gray goo" attacks //How long we wait (seconds) before we assume SL blocked our rez attempt integer iRezWait = 3; /////////////////////////////////////////////////////////////////////////////// // DO NOT EDIT BELOW THIS LINE.... NO.. NOT EVEN THEN /////////////////////////////////////////////////////////////////////////////// //Name each option-these names will be your button names. string optRecord = "Record"; string optReset = "Reset"; string optBuild = "Build"; string optPos = "Position"; string optClean = "Clean"; string optDone = "Done"; //Menu option descriptions string descRecord = ": Record the position of all parts\n"; string descReset = ": Forgets the position of all parts\n"; string descBuild = ": Rez inv. items and position them\n"; string descPos = ": Reposition the parts to a new location\n"; string descClean = ": De-Rez all pieces\n"; string descDone = ": Remove all BB scripts and freeze parts in place.\n"; integer MENU_CHANNEL; integer MENU_HANDLE; key agent; key objectowner; integer group; string title = ""; list optionlist = []; integer bMoving; vector vLastPos; rotation rLastRot; integer bRezzing; integer iListenTimeout = 0; integer iLastRez = 0; integer iRezIndex; //integer bTimerOn = FALSE; //To avoid flooding the sim with a high rate of movements //(and the resulting mass updates it will bring), we used // a short throttle to limit ourselves announce_moved() { llRegionSay(PRIMCHAN, "MOVE " + llDumpList2String([ llGetPos(), llGetRot() ], "|")); llResetTime(); //Reset our throttle vLastPos = llGetPos(); rLastRot = llGetRot(); return; } rez_object() { //Rez the object indicated by iRezIndex llRezObject(llGetInventoryName(INVENTORY_OBJECT, iRezIndex), llGetPos(), ZERO_VECTOR, llGetRot(), PRIMCHAN); iLastRez = llGetUnixTime(); if(!bRezzing) { bRezzing = TRUE; //timer_on(); } } post_rez_object() { if ( llGetCreator() != llGetOwner() ) llRemoveInventory(llGetInventoryName(INVENTORY_OBJECT, iRezIndex)); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// default { /////////////////////////////////////////////////////////////////////////////// changed(integer change) { if(change & CHANGED_OWNER) llResetScript(); } /////////////////////////////////////////////////////////////////////////////// state_entry () { //Use which menu? if ( llGetCreator() == llGetOwner() ) { //Display all options optionlist = [optPos, optClean, optDone, optRecord, optReset, optBuild]; title = optRecord + descRecord; title += optReset + descReset; title += optBuild + descBuild; title += optPos + descPos; title += optClean + descClean; title += optDone + descDone; } else { //Display limited options optionlist = [optBuild, optPos, optDone]; title = optBuild + descBuild; title += optPos + descPos; title += optDone + descDone; } //Record our position vLastPos = llGetPos(); rLastRot = llGetRot(); llSetTimerEvent(fTimerRate); } /////////////////////////////////////////////////////////////////////////////// touch_start (integer total_number) { group = llDetectedGroup(0); // Is the Agent in the objowners group? agent = llDetectedKey(0); // Agent's key objectowner = llGetOwner(); // objowners key // is the Agent = the owner OR is the agent in the owners group if ( (objectowner == agent) || ( group && ingroup ) ) { iListenTimeout = llGetUnixTime() + llFloor(fListenTime); MENU_CHANNEL = llFloor(llFrand(-99999.0 - -100)); MENU_HANDLE = llListen(MENU_CHANNEL,"","",""); llDialog(agent, title, optionlist, MENU_CHANNEL); //timer_on(); } } /////////////////////////////////////////////////////////////////////////////// listen(integer channel, string name, key id, string message) { if ( message == optRecord ) { llOwnerSay("Recording positions..."); llRegionSay(PRIMCHAN, "RECORD " + llDumpList2String([ llGetPos(), llGetRot() ], "|")); return; } if( message == optReset ) { llOwnerSay("Forgetting positions..."); llShout(PRIMCHAN, "RESET"); return; } if ( message == optBuild ) { if(chatty) llOwnerSay("Rezzing build pieces..."); iRezIndex = llGetInventoryNumber(INVENTORY_OBJECT) - 1; rez_object(); return; } if ( message == optPos ) { if(chatty) llOwnerSay("Positioning"); vector vThisPos = llGetPos(); rotation rThisRot = llGetRot(); llRegionSay(PRIMCHAN, "MOVE " + llDumpList2String([ vThisPos, rThisRot ], "|")); return; } if ( message == optClean ) { llRegionSay(PRIMCHAN, "CLEAN"); return; } if ( message == optDone ) { llRegionSay(PRIMCHAN, "DONE"); if(chatty) llOwnerSay("Removing Builder's Buddy scripts."); return; } } /////////////////////////////////////////////////////////////////////////////// moving_start() { if( !bMoving ) { bMoving = TRUE; //timer_on(); announce_moved(); } } /////////////////////////////////////////////////////////////////////////////// object_rez(key id) { //The object rezzed, perform any post-rez processing post_rez_object(); //Move on to the next object //Loop through backwards (safety precaution in case of inventory change) iRezIndex--; if(iRezIndex >= 0) { //Attempt to rez it rez_object(); } else { //Rezzing complete, now positioning iLastRez = 0; bRezzing = FALSE; if(chatty) llOwnerSay("Positioning"); llRegionSay(PRIMCHAN, "MOVE " + llDumpList2String([ llGetPos(), llGetRot() ], "|")); } } /////////////////////////////////////////////////////////////////////////////// timer() { //Did we change position/rotation? if( (llGetRot() != rLastRot) || (llGetPos() != vLastPos) ) { if( llGetTime() > fTimerRate ) { announce_moved(); } } //Are we rezzing? if(bRezzing) { //Did the last one take too long? if((llGetUnixTime() - iLastRez) >= iRezWait) { //Yes, retry it if(chatty) llOwnerSay("Reattempting rez of most recent piece"); rez_object(); } } //Open listener? if( iListenTimeout != 0 ) { //Past our close timeout? if( iListenTimeout <= llGetUnixTime() ) { iListenTimeout = 0; llListenRemove(MENU_HANDLE); } } } /////////////////////////////////////////////////////////////////////////////// on_rez(integer iStart) { //Reset ourselves llResetScript(); } }
The Component script:
/////////////////////////////////////////////////////////////////////////////// // Builders' Buddy 1.9 (Component Pieces) // by Newfie Pendragon, March 2006 // // This script is distributed with permission that it may be used in // any way, or may be further modified/included in resale items. // HOWEVER, if this script is used as part of a for-sale product, // it is required that appropriate credit be given to Newfie for // the script (or portions used in derivatives). That's a fair price // in exchange for unlimited use of this script, dontcha think? // // SL Forum thread and new versions found here: // http://forums.secondlife.com/showthread.php?t=96792 /////////////////////////////////////////////////////////////////////////////// // INSTRUCTIONS // This is the *Component Piece* half of the Builders' Buddy system. // Drop it into each 'piece' of the building. Drop the Base Prim Script // into the prim that will be the container/box that will be used to // store the building once completed. It can be in each individual // prim, but if you link as much as possible (and put the script in the link // set), it'll be much more neighbourly and less strain on the sim. // // QUICK USE: // - Drop this script in the Base. // - Drop the "Component" Script in each building part. // - Touch your Base, and choose RECORD // - Take all building parts into inventory // - Drag building parts from inventory into Base Prim // - Touch your base and choose BUILD // /////////////////////////////////////////////////////////////////////////////// // History // // v1.0 - 20060328 - Newfie Pendragon - Original Version // v1.5 - 20060612 - Androclese Antonelli // - (See base script for details) // v1.6 - 20060624 - Newfie Pendragon // - Added active repositioning (pieces move as the base piece moves) // - Pieces use WarpPos technique to instantanetly move large distances // - Pieces no longer move until the the "Record" option has been used // at least once // - Pieces will not move if base is not same owner as the pieces // - Pieces no longer 'bounce' when hitting the ground // v1.7 - 20060821 - Correction for non-zero rotation (thanks Ed44 Gupta!) // v1.9 - 20070630 - Added check to keep within sim edges /////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Configurable Settings float fTimerInterval = 0.25; // Time in seconds between movement 'ticks' integer PRIMCHAN = -19730611; // Channel used by Base Prim to talk to Component Prims; // ***THIS MUST MATCH IN BOTH SCRIPTS!*** ////////////////////////////////////////////////////////////////////////////////////////// // Runtime Variables (Dont need to change below here unless making a derivative) vector vOffset; rotation rRotation; integer bNeedMove; vector vDestPos; rotation rDestRot; integer bRecorded = FALSE; //////////////////////////////////////////////////////////////////////////////// string first_word(string In_String, string Token) { //This routine searches for the first word in a string, // and returns it. If no word boundary found, returns // the whole string. if(Token == "") Token = " "; integer pos = llSubStringIndex(In_String, Token); //Found it? if( pos >= 1 ) return llGetSubString(In_String, 0, pos - 1); else return In_String; } //////////////////////////////////////////////////////////////////////////////// string other_words(string In_String, string Token) { //This routine searches for the other-than-first words in a string, // and returns it. If no word boundary found, returns // the an empty string. if( Token == "" ) Token = " "; integer pos = llSubStringIndex(In_String, Token); //Found it? if( pos >= 1 ) return llGetSubString(In_String, pos + 1, llStringLength(In_String)); else return ""; } //////////////////////////////////////////////////////////////////////////////// do_move() { integer i = 0; vector vLastPos = ZERO_VECTOR; while( (i < 5) && (llGetPos() != vDestPos) ) { list lParams = []; //If we're not there.... if( llGetPos() != vDestPos ) { //We may be stuck on the ground... //Did we move at all compared to last loop? if( llGetPos() == vLastPos ) { //Yep, stuck...move straight up 10m (attempt to dislodge) lParams = [ PRIM_POSITION, llGetPos() + <0, 0, 10.0> ]; //llSetPos(llGetPos() + <0, 0, 10.0>); } else { //Record our spot for 'stuck' detection vLastPos = llGetPos(); } } //Try to move to destination //Upgraded to attempt to use the llSetPrimitiveParams fast-move hack //(Newfie, June 2006) integer iHops = llAbs(llCeil(llVecDist(llGetPos(), vDestPos) / 10.0)); integer x; for( x = 0; x < iHops; x++ ) { lParams += [ PRIM_POSITION, vDestPos ]; } llSetPrimitiveParams(lParams); //llSleep(0.1); i++; } //Set rotation llSetRot(rDestRot); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// default { ////////////////////////////////////////////////////////////////////////////////////////// state_entry() { //Open up the listener llListen(PRIMCHAN, "", NULL_KEY, ""); } ////////////////////////////////////////////////////////////////////////////////////////// on_rez(integer iStart) { //Set the channel to what's specified if( iStart != 0 ) { PRIMCHAN = iStart; state reset_listeners; } } ////////////////////////////////////////////////////////////////////////////////////////// listen(integer iChan, string sName, key kID, string sText) { string sCmd = llToUpper(first_word(sText, " ")); if( sCmd == "RECORD" ) { sText = other_words(sText, " "); list lParams = llParseString2List(sText, [ "|" ], []); vector vBase = (vector)llList2String(lParams, 0); rotation rBase = (rotation)llList2String(lParams, 1); vOffset = (llGetPos() - vBase) / rBase; rRotation = llGetRot() / rBase; bRecorded = TRUE; llOwnerSay("Recorded position."); return; } ////////////////////////////////////////////////////////////////////////////////////////// if( sCmd == "MOVE" ) { //Don't move if we've not yet recorded a position if( !bRecorded ) return; //Also ignore commands from bases with a different owner than us //(Anti-hacking measure) if( llGetOwner() != llGetOwnerKey(kID) ) return; //Calculate our destination position sText = other_words(sText, " "); list lParams = llParseString2List(sText, [ "|" ], []); vector vBase = (vector)llList2String(lParams, 0); rotation rBase = (rotation)llList2String(lParams, 1); //Calculate our destination position vDestPos = (vOffset * rBase) + vBase; rDestRot = rRotation * rBase; //Make sure our calculated position is within the sim if(vDestPos.x < 0.0) vDestPos.x = 0.0; if(vDestPos.x > 255.0) vDestPos.x = 255.0; if(vDestPos.y < 0.0) vDestPos.y = 0.0; if(vDestPos.y > 255.0) vDestPos.y = 255.0; if(vDestPos.x > 768.0) vDestPos.x = 768.0; //Turn on our timer to perform the move? if( !bNeedMove ) { llSetTimerEvent(fTimerInterval); bNeedMove = TRUE; } return; } ////////////////////////////////////////////////////////////////////////////////////////// if( sCmd == "DONE" ) { //We are done, remove script llRemoveInventory(llGetScriptName()); return; } ////////////////////////////////////////////////////////////////////////////////////////// if( sCmd == "CLEAN" ) { //Clean up llDie(); return; } ////////////////////////////////////////////////////////////////////////////////////////// if( sCmd == "RESET" ) { llResetScript(); } } ////////////////////////////////////////////////////////////////////////////////////////// timer() { //Turn ourselves off llSetTimerEvent(0.0); //Do we need to move? if( bNeedMove ) { //Perform the move and clean up do_move(); bNeedMove = FALSE; } return; } } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// state reset_listeners { ////////////////////////////////////////////////////////////////////////////////////////// state_entry() { state default; } }