Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Land Manipulator

ed44 Gupte
Explorer (Retired)
Join date: 7 Oct 2005
Posts: 638
04-13-2006 19:34
Here is the code for my last single script version of my land mover which sits in a non-physical prim that "flies" across your land, possibly modifying it as it goes along. I am releasing for general use. Feel free to copy and modify!

It will only work on land you own, not on group land. You can modify that by altering the move routine.

Two major modes include a sweep mode, where it carries out the selected land manipulation function at each step in a zig zag pattern and a single step mode allowing for a restricted set of functions. An "average" function checks the ground level against an "avht=x.x" value specified in a note card and forces a "raise" or "lower" action as appropriate.

You can have any number of these boxes flying around on your land, but you should give each one a different number termination, like bot1, bot2 bot3 etc, so that the huds work to the correct boxes. The channel each hud communcates on is given by these last digits.

I am releasing this to engender some research into the effects of each of the land manipulation functions. They are not straight forward, and the result of each action seems to spread much wider than the width of the brush. There is a need to develop strategies in how to make circular moats for fountains, flat areas of land and so on.

I am developing another land manipulation bot which will allow all menu functions to operate from note cards. have also cut four prims and rotated them into a linked object so that each prim's centre forms a corner from which particles can shoot to show the extent of the brush on the ground.

I hope users will post results of different actions on their land so I can provide more useful functions.

Have fun with it!


CODE


// The last good single script version.
// Place this in a non physical object.
// The name of the prim this is in should finish with a number.

integer gHandle = 0;
integer gChannel = 0;

integer LAND_AVERAGE = 10000;


integer gBrush = LAND_SMALL_BRUSH;
float gBrushWidth = 2.0;

// used in the zig zag sweep action
integer gAction = LAND_LEVEL;
float gStep = 2.0;

// limits - set for area you want to cover
// or read from note card
integer gxmin = 1;
integer gxmax = 254;
integer gymin = 1;
integer gymax = 254;

integer debugFlag = FALSE;



// gavheight is tested in the averaging function
// set for your own desired height

float gavheight = 96.0;


float gAbsFlyingHeight = -1.0;
float gRelFlyingHeight = 1.0;


// keeps track of where we are
vector gPos;

key gWKey;
key gOwnerKey;
string gOwnerName;
float gD = 0.000001f; // very tiny number

integer gwdt = 0;//watch dog timer
integer gwdtmax = 1000;

debug (string s) {
if (debugFlag)
llOwnerSay (s);
}




// Globals
integer gNotecardIndex;
integer gNotecardCount;
integer gNoteCardLine;
key gCurrentDataRequest;
string gSettingsNotecard;
integer gCardLoaded = FALSE;
integer gCardLoadTimeOut = 10;


integer StringLeftICompare( string sLeftMatch, string sLongString ) {
integer iLength;

iLength = llStringLength (sLeftMatch) - 1;
if( llToLower(llGetSubString( sLongString, 0, iLength ) ) == llToLower(sLeftMatch) )
return( TRUE );
return( FALSE );
}


string getValue( string sString) {
integer iStart = 0;
string sValue = "";
string sbValue = "";

iStart = llSubStringIndex( sString, "=" ) + 1;
if( iStart ) {
sValue = llGetSubString( sString, iStart, llStringLength(sString) - 1 );
if( llStringLength (sValue) > 0 )
{
sbValue = llToLower( sValue );
if( sbValue == "true" )
sValue = "1";
if( sbValue == "false" )
sValue = "0";
return( sValue );
}
}
return( NULL_KEY );
}





// float to string

string f2s (float f, integer n) {
integer m = n;
integer div = 1;
while (m > 0) {
div *= 10;
f *= 10.0;
--m;
}
integer i = llRound (f);
string lhs = (string) (i / div);
string rhs = (string) (i % div);

string ans;

if (n > 0)
ans = lhs + "." + rhs;
else
ans = lhs;
return ans;
}



// vector to string
string v2s (vector v) {

string s1 = f2s (v.x, 0);
string s2 = f2s (v.y, 0);
string s3 = f2s (v.z, 1);

return "(" + s1 + "," + s2 + "," + s3 + ")";
}



// return true if pos within limits
integer testPos (vector pos) {
integer ok = TRUE;
if ((pos.x < gxmin) ||
(pos.x > (gxmax - gBrushWidth)) ||
(pos.y < gymin) ||
(pos.y > (gymax - gBrushWidth)))
ok = FALSE;

return ok;
}



// do the actual land manipulation
manipLand (integer func) {
vector pos = llGetPos ();
if (testPos (pos)) {
if (func == LAND_AVERAGE) {
doAverage ();
} else {
llModifyLand (func, gBrush);
}
} else {
llOwnerSay ("Blocked - out of range");
llOwnerSay ("Positioned at x=" + f2s (pos.x, 2) + ", y=" + f2s (pos.y, 2));
}
}


// general routines



// this could be important for those land manupulation functions that
// depend on flying height

adjustHeight () {
float oldHeight = gPos.z;
if (gRelFlyingHeight > -1) {
gPos.z = llGround (<0,0,0>) + gRelFlyingHeight;
} else {
if (gAbsFlyingHeight > -1) {
gPos.z = gAbsFlyingHeight;
}
}
if (llFabs (gPos.z - oldHeight) > gD)
llSetPos (gPos);
}





// This routine is very important

// Make changes carefully as your object might escape

// currently will not work on group owned land

integer move (vector vmd) {
vector oldPos = gPos;
vector newPos = gPos + vmd;
integer blocked = FALSE;
gWKey = llGetLandOwnerAt (newPos);
debug ("owner is " + (string ) gWKey);
if (gWKey != gOwnerKey) {
blocked = TRUE;
debug ("blocked - owner change");
} else {
if (testPos (newPos)) {
llSetPos (newPos);
gPos = llGetPos();
float g = llGround(<0,0,0>);
string s = " new pos " + v2s (gPos) +
" ground at " + f2s (g, 1) +
" owner:" + (string) llKey2Name (gWKey);
debug (s);
if ((llFabs (newPos.x - gPos.x) > gD) ||
(llFabs (newPos.y - gPos.y) > gD)) {
llSetPos (oldPos);
gPos = llGetPos ();
debug ("restored to " + v2s (gPos));
blocked = TRUE;
debug ("Blocked - did not reach destn");
} else {
blocked = FALSE;
adjustHeight ();
debug ("ok at " + s);
}
} else {
blocked = TRUE;
debug ("blocked - out of range");
}
}
return blocked;
}




// assume on own land
// move to SW position

moveHome () {
integer blocked = FALSE;
// integer dy;
debug ("moving to home SW");
while (!blocked) {
blocked = move (<0, -1, 0>);
}
blocked = FALSE;
while (!blocked) {
blocked = move (<-1, 0, 0>);
}
debug ("last of moveHome");
}



// I am still playing with this to get some measure
// of land variation that makes sense statistically.

// this could be worked into the stats function further down

// First, square each of the scores.
// Determine N, which is the number of scores.
// Compute the sum of X and the sum of Xsquared.
// Then, calculate the standard deviation as illustrated below.
// score(X) Xsquared
// 8 64
// 25 625
// 7 49 N = 9
// 5 25
// 8 64 sum of X = 87
// 3 9
// 10 100 sum of Xsquared = 1161
// 12 144
// 9 81
// --- ----
// 87 1161
//
// Standard Deviation = square root of[ (sum of Xsquared -((sum of X)*(sum of X)/N))/ (N-1)) ]
// = square root[(1161)-(87*87)/9)/(9-1)]
// = square root[(1161-(7569/9)/8)]
// = square root[(1161-841)/8]
// = square root[320/8]
// = square root[40]
// = 6.32
//
//




float grdhighest = 0.0;
float grdlowest = 10000.0;
float grdtotalheight = 0.0;
float grdtotalqty = 0.0;

stats () {
float height = 0.0;

integer hblocked = FALSE;
integer vblocked = FALSE;
float xd = gStep;

grdtotalheight = 0.0;
grdtotalqty + 0.0;
grdhighest = 0.0;
grdlowest = 10000.0;

debug ("sweeping . . . . . ");
while (!vblocked) {
hblocked = FALSE;
while (!hblocked) {

height = llGround(<0,0,0>);
grdtotalheight += height;
grdtotalqty += 1.0;
if (height < grdlowest)
grdlowest = height;
if (height > grdhighest)
grdhighest = height;

hblocked = move (<xd, 0, 0>);
//llSleep (1);
}
xd *= -1.0;
debug ("end one sweep xd=" + (string) xd);
vblocked = move (<0, gStep, 0>);
//llSleep (1);
}


llOwnerSay ("average height = " + f2s ((grdtotalheight / grdtotalqty), 1));
llOwnerSay ("heights taken = " + f2s (grdtotalqty, 0));
llOwnerSay ("highest height = " + f2s (grdhighest, 1));
llOwnerSay (" lowest height = " + f2s (grdlowest, 1));

moveHome ();
}





doAverage () {
float g1 = llGround (<0,0,0>);
if (g1 < gavheight - 0.15) {
llModifyLand (LAND_RAISE, gBrush);
llModifyLand (LAND_SMOOTH, gBrush);
}
else
if (g1 > gavheight + 0.15) {
llModifyLand (LAND_LOWER, gBrush);
llModifyLand (LAND_SMOOTH, gBrush);
}
}




// move in a zig zag pattern starting from SW corner

doSweep () {
float g1 = 0.0;
float g2 = 0.0;
float xd = gStep;
integer hblocked = FALSE;
integer vblocked = FALSE;


debug ("sweeping . . . . . ");
while (!vblocked) {
hblocked = FALSE;
while (!hblocked) {

g1 = llGround(<0,0,0>);

if (gAction == LAND_AVERAGE)
doAverage ();
else
llModifyLand (gAction, gBrush);

g2 = llGround(<0,0,0>);
if (llFabs (g1 - g2) > 0.01)
llOwnerSay ("changed land at " + f2s (gPos.x,2) + "," + f2s (gPos.y,2) + " from " + f2s (g1,2) + " to " + f2s (g2,2));



hblocked = move (<xd, 0, 0>);
//llSleep (1);
}
xd *= -1.0;
debug ("end one sweep xd=" + (string) gStep);
vblocked = move (<0, gStep, 0>);
}


moveHome ();

llOwnerSay ("Sweep Done!");

}





// menu constants

string miMain = "main menu";
string miHome = "home";
string miParams = "params";
string miSweep = "sweep";
string miStats = "stats";
string miDestroy = "destroy";
string miMove = "move 1 step";

string miStepSize = "step size";
string miBrushSize = "brush size";
string miActionType = "action type";

string miSmall = "small";
string miMedium = "medium";
string miLarge = "large";

string miSmooth = "smooth";
string miRaise = "raise";
string miLower = "lower";
string miLevel = "level";
string miNoise = "noise";
string miRevert = "revert";
string miAverage = "average";


string mi0p10M = "0.10 M";
string mi0p33M = "0.33 M";
string mi1p00M = "1.00 M";

string mi1p10M = "1.10 M";
string mi1p33M = "1.33 M";
string mi2p00M = "2.00 M";

string mi3p00M = "3.00 M";
string mi4p00M = "4.00 M";
string mi8p00M = "8.00 M";

string miNorth = "north";
string miEast = "east";
string miWest = "west";
string miSouth = "south";
string miNW = "nw";
string miSW = "sw";
string miNE = "ne";
string miSE = "se";
list menuListMove = [miSW,miSouth,miSW,miWest,miMain,miEast,miNW,miNort h,miNE,miLower, miRaise,miAverage];


string tool2String (integer tool) {
string s = "";
if (tool == LAND_AVERAGE)
s += "average";
else
if (tool == LAND_LEVEL)
s = "level";
else
if (tool == LAND_REVERT)
s = "revert";
else
if (tool == LAND_SMOOTH)
s = "smooth";
else
if (tool == LAND_NOISE)
s = "noise";
else
if (tool == LAND_RAISE)
s = "raise";
else
if (tool == LAND_LOWER)
s = "lower";
return s;
}

string brush2String (integer brush) {
string s = "";
if (brush == LAND_SMALL_BRUSH)
s += "small (2M)";
else
if (brush == LAND_MEDIUM_BRUSH)
s += "medium (4M)";
else
if (brush == LAND_LARGE_BRUSH)
s += "large (8M)";
return s;
}

string getStatusDisplay () {
string s = "";
s += "\nBrush size=" + brush2String (gBrush);

s += "\n step=" + f2s (gStep, 2);

s += "\tsweep action=" + tool2String (gAction);

s += "\nxmin=" + f2s (gxmin, 1) + " xmax=" + f2s (gxmax, 1) + " ymin=" + f2s (gymin, 1) + " ymax=" + f2s (gymax, 1);
s += "\nWanted average height=" + f2s (gavheight, 2);

return s;

}

string getMoveMessage () {
string s = "";
float g = llGround (<0,0,0>);
string s1 = (string) llKey2Name (gWKey);
if (llStringLength (s1) < 1)
s1 = (string) gWKey;

s = "pos x=" + f2s (gPos.x, 2) +
" y=" + f2s (gPos.y, 2) +
" ground=" + f2s (g, 1) +
" owner=" + s1;

return s;

}
list menuListMain = [miHome, miParams, miSweep, miStats, miMove, miDestroy]; // Dialog box's list
list menuListParams = [miStepSize, miBrushSize, miActionType, miMain]; // Dialog box's list
list menuListParamsStepSize = [mi3p00M, mi4p00M, mi8p00M,
mi1p10M, mi1p33M, mi2p00M,
mi0p10M, mi0p33M, mi1p00M,
miParams, miMain];
list menuListParamsBrushSize = [miSmall,miMedium,miLarge, miParams, miMain];
list menuListParamsActionType = [miSmooth, miLower, miNoise, miLevel, miRaise, miRevert, miAverage, miParams, miMain];











integer getDigitValue (string s) {
integer val = -1;

if (llStringLength (s) == 1)
val = llSubStringIndex("0123456789", s);

return val;
}


default {
on_rez (integer i) {
llOwnerSay ("rezzed");
llResetScript ();
}


state_entry () {
vector x1;

llSetText( "Initializing...", <0,0.5,0>, 1.0 );

gOwnerKey = llGetOwner();
gOwnerName = (string) llKey2Name (gOwnerKey);
if (llStringLength (gOwnerName) < 1)
gOwnerName = (string) gOwnerKey;

integer gettingNumber = TRUE;
integer multiplier = 1;
string primName = llGetObjectName();
integer ptr = llStringLength (primName);
string char;
integer val = 0;
integer val1 = 0;

while (gettingNumber && (--ptr > 1)) {
char = llGetSubString(primName, ptr, ptr);
val1 = getDigitValue (char);
if (val1 > -1) {
val += (val1 * multiplier);
multiplier *= 10;
} else
gettingNumber = FALSE;
}

llOwnerSay ("gChannel=" + (string) val);
gChannel = val;

gNotecardCount = llGetInventoryNumber( INVENTORY_NOTECARD );
llOwnerSay ("gNotecardCount:" + (string) gNotecardCount);
gNotecardIndex = 0;
if (gNotecardCount > gNotecardIndex )
{
gSettingsNotecard = llGetInventoryName( INVENTORY_NOTECARD, gNotecardIndex );
llOwnerSay ("gSettingsNotecard:" + (string) gSettingsNotecard);
gNoteCardLine = 0;
gCurrentDataRequest = llGetNotecardLine( gSettingsNotecard, gNoteCardLine );
gNotecardIndex++;
} else
gCardLoaded = TRUE;



gPos = llGetPos ();

x1.x = llRound (gPos.x);
x1.y = llRound (gPos.y);
x1.z = ((float) llRound (gPos.z)) + 2.0;

string outs = "Earth Mover v 0.1 at " + f2s (x1.x, 2) + ", " + f2s (x1.y, 2) +
" owner is " + gOwnerName;
llOwnerSay(outs);
llSetPos (x1);
gPos = x1;

adjustHeight ();

llSetTouchText ("Die");
if (gCardLoaded)
state mainState;

}


dataserver( key kQuery, string sData )
{

//llOwnerSay ("dataserver kQuery:" + (string) kQuery);
//llOwnerSay ("dataserver sData:" + sData);

gCurrentDataRequest = "";
if( sData != EOF )
{
if( StringLeftICompare( "xmin=", sData ) )
gxmin = (integer) getValue( sData);

else if( StringLeftICompare( "xmax=", sData ) )
gxmax = (integer) getValue( sData);

else if( StringLeftICompare( "ymin=", sData ) )
gymin = (integer) getValue( sData);

else if( StringLeftICompare( "ymax=", sData ) )
gymax = (integer) getValue( sData);

else if( StringLeftICompare( "avht=", sData ) )
gavheight = (float) getValue( sData);

else if( StringLeftICompare( "afht=", sData ) )
gAbsFlyingHeight = (float) getValue( sData);

else if( StringLeftICompare( "rfht=", sData ) )
gRelFlyingHeight = (float) getValue( sData);

else if( StringLeftICompare( "debug=", sData ) )
debugFlag = (integer) getValue( sData);


gCurrentDataRequest = llGetNotecardLine( gSettingsNotecard, ++gNoteCardLine );
}
else
{
gNotecardIndex++;
if( gNotecardIndex < llGetInventoryNumber( INVENTORY_NOTECARD ) )
{
gSettingsNotecard = llGetInventoryName( INVENTORY_NOTECARD, gNotecardIndex );

gNoteCardLine = 0;
llGetNotecardLine( gSettingsNotecard, gNoteCardLine );
} else {
gCardLoaded = TRUE;
llOwnerSay ("wanted height on action average = " + (string) gavheight);
state mainState;
}
}
}
}








state mainState {

on_rez (integer x) {
llResetScript ();
}

state_entry() {
gHandle = llListen( gChannel, "", "", "" );
llDialog(gOwnerKey, "Main Menu (Channel=" + (string) gChannel + ")" +
"free mem=" + (string) llGetFreeMemory () + getStatusDisplay (),
menuListMain, gChannel);

}


listen( integer rchannel, string name, key id, string message ) {
gwdt = 0;
if (message == miParams) state paramsState;
else if (message == miHome) state homeState;
else if (message == miSweep) state sweepState;
else if (message == miStats) state statsState;
else if (message == miMove) state moveState;
else if (message == miDestroy) llDie ();
}



touch_start(integer total_number) {
llDie ();
}
}





state paramsState {

state_entry() {
gHandle = llListen( gChannel, "", "", "" );
llDialog (gOwnerKey, "Params Menu " + getStatusDisplay (), menuListParams, gChannel);

}



listen( integer rchannel, string name, key id, string message )
{

gwdt = 0;
if (message == miStepSize) state paramsStepSizeState;
else if (message == miBrushSize) state paramsBrushSizeState;
else if (message == miActionType) state paramsActionTypeState;
else if (message == miMain) state mainState;
}

touch_start(integer total_number) {
llDie ();
}
}



state paramsStepSizeState {
state_entry()
{
gHandle = llListen( gChannel, "", "", "" );
llDialog (gOwnerKey, "Step Size Menu" + getStatusDisplay (), menuListParamsStepSize, gChannel);
}



listen( integer rchannel, string name, key id, string message )
{
gwdt = 0;
if (message == mi0p10M) {gStep = 0.1; state paramsState;}
else if (message == mi0p33M) {gStep = 0.333333; state paramsState;}
else if (message == mi1p00M) {gStep = 1.0; state paramsState;}
else if (message == mi1p10M) {gStep = 1.1; state paramsState;}
else if (message == mi1p33M) {gStep = 1.333333; state paramsState;}
else if (message == mi2p00M) {gStep = 2.0; state paramsState;}
else if (message == mi3p00M) {gStep = 3.0; state paramsState;}
else if (message == mi4p00M) {gStep = 4.0; state paramsState;}
else if (message == mi8p00M) {gStep = 8.0; state paramsState;}
else if (message == miParams) state paramsState;
else if (message == miMain) state mainState;
}


touch_start(integer total_number) {
llDie ();
}
}


state paramsBrushSizeState {

state_entry() {
gHandle = llListen( gChannel, "", "", "" );
llDialog (gOwnerKey, "Brush Size Menu small=2mx2m, medium= 4mx4m,large=8mx8m)", menuListParamsBrushSize, gChannel);
}



listen( integer rchannel, string name, key id, string message )
{
gwdt = 0;
if (message == miSmall) {gBrush = LAND_SMALL_BRUSH; gBrushWidth=2.0; state paramsState;}
else if (message == miMedium) {gBrush = LAND_MEDIUM_BRUSH; gBrushWidth=4.0; state paramsState;}
else if (message == miLarge) {gBrush = LAND_LARGE_BRUSH; gBrushWidth=8.0; state paramsState;}
else if (message == miParams) state paramsState;
else if (message == miMain) state mainState;
}



touch_start(integer total_number) {
llDie ();
}
}



state paramsActionTypeState {

state_entry()
{
gHandle = llListen( gChannel, "", "", "" );
llDialog (gOwnerKey, "Action Menu" + getStatusDisplay (), menuListParamsActionType, gChannel);

}




listen( integer rchannel, string name, key id, string message )
{
gwdt = 0;
if (message == miLevel) {gAction = LAND_LEVEL; state paramsState;}
else if (message == miSmooth) {gAction = LAND_SMOOTH; state paramsState;}
else if (message == miNoise) {gAction = LAND_NOISE; state paramsState;}
else if (message == miRaise) {gAction = LAND_RAISE; state paramsState;}
else if (message == miLower) {gAction = LAND_LOWER; state paramsState;}
else if (message == miRevert) {gAction = LAND_REVERT; state paramsState;}
else if (message == miAverage) {gAction = LAND_AVERAGE;state paramsState;}
else if (message == miParams) state paramsState;
else if (message == miMain) state mainState;
}



touch_start(integer total_number) {
llDie ();
}
}




state sweepState {


state_entry()
{
llSetTimerEvent (15);
doSweep ();
state mainState;

}


timer () {
if (++gwdt > gwdtmax)
llDie ();
}


touch_start(integer total_number) {
llDie ();
}

}

state statsState {


state_entry()
{
llSetTimerEvent (15);
stats ();
state mainState;

}


timer () {
if (++gwdt > gwdtmax)
llDie ();
}


touch_start(integer total_number) {
llDie ();
}

}








state homeState {


state_entry()
{
llSetTimerEvent (5);
moveHome ();
//llOwnerSay ("back from home");
state mainState;
}


timer () {
if (++gwdt > gwdtmax)
llDie ();
}


touch_start(integer total_number) {
llDie ();
}
}







state moveState {

state_entry()
{
gHandle = llListen( gChannel, "", "", "" );
llDialog (gOwnerKey, getMoveMessage () + getStatusDisplay (), menuListMove, gChannel);
}


listen( integer rchannel, string name, key id, string message )
{

gwdt = 0;
if (message == miNorth) move (<0, gStep, 0>);
else if (message == miSouth) move (<0, -gStep, 0>);
else if (message == miEast) move (< gStep, 0, 0>);
else if (message == miWest) move (<-gStep, 0, 0>);
else if (message == miNW) move (<-gStep, gStep, 0>);
else if (message == miNE) move (< gStep, gStep, 0>);
else if (message == miSE) move (< gStep,-gStep, 0>);
else if (message == miSW) move (<-gStep,-gStep, 0>);
else if (message == miLower) manipLand (LAND_LOWER);
else if (message == miLevel) manipLand (LAND_LEVEL);
else if (message == miRaise) manipLand (LAND_RAISE);
else if (message == miAverage) manipLand (LAND_AVERAGE);
else if (message == miMain) state mainState;

llDialog (gOwnerKey, getMoveMessage (), menuListMove, gChannel);
}


}

This a note card that you could use to pre set some of the settings. You should adapt the min/max values to suit your land's position in the sim. You can get these settings from the top of your screen by walking to each of the corners of your land. The first value is x, the second y and you ignore the z. avht can be set to whatever height you want the land to move to on executing "average" during a sweep, but remember most sims will only let you move +- 4 M.


CODE


xmin=0
xmax=255
ymin=0
ymax=255
avht=96.0
debug=false
Nada Epoch
The Librarian
Join date: 4 Nov 2002
Posts: 1,423
Discussion Thread
04-14-2006 08:49
/54/98/100256/1.html
_____________________
i've got nothing. ;)