Difference between revisions of "Pathfinder"
(→The Base Script: fixing a border condition and did some loop optimizations, there is definitely more that can be done) |
Kireji Haiku (talk | contribs) m (-1 >> ERR_GENERIC and 0 >> PUBLIC_CHANNEL) |
||
Line 1: | Line 1: | ||
{{LSL Header}} | {{LSL Header}} | ||
__TOC__ | __TOC__ | ||
== The Base Script == | == The Base Script == | ||
<lsl> | <lsl> | ||
// Pathfinder PotentialField by Babbage Linden | // Pathfinder PotentialField by Babbage Linden | ||
Line 65: | Line 65: | ||
return llInsertString( | return llInsertString( | ||
llDeleteSubString( | llDeleteSubString( | ||
s, | s, | ||
index, | index, | ||
index + newStringLength - 1), | index + newStringLength - 1), | ||
index, | index, | ||
newString | newString | ||
); | ); | ||
Line 75: | Line 75: | ||
string hexc="0123456789ABCDEF"; | string hexc="0123456789ABCDEF"; | ||
string int2hex(integer x) | string int2hex(integer x) | ||
{ | { | ||
Line 102: | Line 102: | ||
stringValue = "0" + stringValue; | stringValue = "0" + stringValue; | ||
} | } | ||
//llSay( | //llSay(PUBLIC_CHANNEL, "setElement " + elements + " " + (string)index + " " + stringValue); | ||
elements = replace(elements, index * gDigitsPerElement, stringValue); | elements = replace(elements, index * gDigitsPerElement, stringValue); | ||
return elements; | return elements; | ||
Line 127: | Line 127: | ||
while(0 <= --y) | while(0 <= --y) | ||
{ | { | ||
integer x = | integer x = ERR_GENERIC; | ||
while( gWidth > ++x ) | while( gWidth > ++x ) | ||
{ | { | ||
Line 134: | Line 134: | ||
s += "\n"; | s += "\n"; | ||
} | } | ||
llSay( | llSay(PUBLIC_CHANNEL,s); | ||
} | } | ||
Line 141: | Line 141: | ||
if(x < 0 || x >= gWidth || y < 0 || y >= gHeight) | if(x < 0 || x >= gWidth || y < 0 || y >= gHeight) | ||
{ | { | ||
return | return ERR_GENERIC; | ||
} | } | ||
return x + y * gWidth; | return x + y * gWidth; | ||
} | } | ||
Line 175: | Line 175: | ||
string updatePotentialField(string elements) | string updatePotentialField(string elements) | ||
{ | { | ||
// If current update region finished, switch to next region. | // If current update region finished, switch to next region. | ||
if(gCurrentUpdateMinY > gCurrentUpdateMaxY) | if(gCurrentUpdateMinY > gCurrentUpdateMaxY) | ||
Line 184: | Line 184: | ||
gCurrentUpdateMaxY = gNextUpdateMaxY; | gCurrentUpdateMaxY = gNextUpdateMaxY; | ||
resetNextUpdateRegion(); | resetNextUpdateRegion(); | ||
// If next region empty, stop updating. | // If next region empty, stop updating. | ||
if(gCurrentUpdateMinY > gCurrentUpdateMaxY) | if(gCurrentUpdateMinY > gCurrentUpdateMaxY) | ||
{ | { | ||
llSay( | llSay(PUBLIC_CHANNEL, "updatePotentialField:DONE"); | ||
llSetTimerEvent(0.0); | llSetTimerEvent(0.0); | ||
return elements; | return elements; | ||
} | } | ||
llSay( | llSay(PUBLIC_CHANNEL, "updatePotentialField:(" + (string)gCurrentUpdateMinX + "," + (string)gCurrentUpdateMinY + ") | ||
(" + (string)gCurrentUpdateMaxX + "," + (string)gCurrentUpdateMaxY + ")"); | (" + (string)gCurrentUpdateMaxX + "," + (string)gCurrentUpdateMaxY + ")"); | ||
} | } | ||
// Recalculate potentials. | // Recalculate potentials. | ||
llResetTime(); | llResetTime(); | ||
Line 204: | Line 204: | ||
while((updates < gMaxUpdates) && (gCurrentUpdateMinY <= gCurrentUpdateMaxY)) | while((updates < gMaxUpdates) && (gCurrentUpdateMinY <= gCurrentUpdateMaxY)) | ||
{ | { | ||
//llSay( | //llSay(PUBLIC_CHANNEL, "updatePotentialField:" + (string)gCurrentUpdateMinY); | ||
integer x = gCurrentUpdateMinX; | integer x = gCurrentUpdateMinX; | ||
do{ | do{ | ||
Line 214: | Line 214: | ||
} | } | ||
} | } | ||
llSetTimerEvent((llGetTime() * 2.0) + 0.1); | llSetTimerEvent((llGetTime() * 2.0) + 0.1); | ||
return elements; | return elements; | ||
Line 220: | Line 220: | ||
string calculatePotentialField(string elements, integer x, integer y) | string calculatePotentialField(string elements, integer x, integer y) | ||
{ | { | ||
integer currentValue = getElement(elements, squareToIndex(x, y)); | integer currentValue = getElement(elements, squareToIndex(x, y)); | ||
// Don't update obstacles or exits. | // Don't update obstacles or exits. | ||
if(currentValue == gMaxValue || currentValue == gMinValue) | if(currentValue == gMaxValue || currentValue == gMinValue) | ||
Line 237: | Line 237: | ||
if(minValue != currentValue) | if(minValue != currentValue) | ||
{ | { | ||
//llSay( | //llSay(PUBLIC_CHANNEL, "calculatePotentialField:" + (string)x + "," + (string)y + " currentValue " + (string)currentValue + " newValue " + (string)minValue); | ||
elements = setElement(elements, squareToIndex(x, y), minValue); | elements = setElement(elements, squareToIndex(x, y), minValue); | ||
expandUpdateRegion(x, y); | expandUpdateRegion(x, y); | ||
Line 294: | Line 294: | ||
gWidth = width; | gWidth = width; | ||
gHeight = height; | gHeight = height; | ||
gPotentialField = ""; | gPotentialField = ""; | ||
integer y = | integer y = ERR_GENERIC; | ||
while(height > ++y) | while(height > ++y) | ||
{ | { | ||
integer x = | integer x = ERR_GENERIC; | ||
while(width > ++x) | while(width > ++x) | ||
{ | { | ||
Line 312: | Line 312: | ||
init() | init() | ||
{ | { | ||
llListen( | llListen(PUBLIC_CHANNEL, "", NULL_KEY, ""); | ||
llListen(gPotentialFieldChannel, "", NULL_KEY, ""); | llListen(gPotentialFieldChannel, "", NULL_KEY, ""); | ||
} | } | ||
Line 322: | Line 322: | ||
init(); | init(); | ||
} | } | ||
on_rez(integer param) | on_rez(integer param) | ||
{ | { | ||
init(); | init(); | ||
} | } | ||
timer() | timer() | ||
{ | { | ||
gPotentialField = updatePotentialField(gPotentialField); | gPotentialField = updatePotentialField(gPotentialField); | ||
} | } | ||
listen(integer channel, string name, key id, string s) | listen(integer channel, string name, key id, string s) | ||
{ | { | ||
Line 378: | Line 378: | ||
</lsl> | </lsl> | ||
== The Rezzer Script == | == The Rezzer Script == | ||
<lsl> | <lsl> | ||
// Pathfinder Rezzer by Babbage Linden | // Pathfinder Rezzer by Babbage Linden | ||
Line 406: | Line 406: | ||
init() | init() | ||
{ | { | ||
llListen( | llListen(PUBLIC_CHANNEL, "", NULL_KEY, ""); | ||
} | } | ||
Line 415: | Line 415: | ||
init(); | init(); | ||
} | } | ||
on_rez(integer param) | on_rez(integer param) | ||
{ | { | ||
init(); | init(); | ||
} | } | ||
listen(integer channel, string name, key id, string s) | listen(integer channel, string name, key id, string s) | ||
{ | { | ||
Line 477: | Line 477: | ||
if(gPopulation < 0) {gPopulation = 0;} | if(gPopulation < 0) {gPopulation = 0;} | ||
llSetTimerEvent(gPeriod); | llSetTimerEvent(gPeriod); | ||
//llSay( | //llSay(PUBLIC_CHANNEL,"Population:" + (string)gPopulation); | ||
} | } | ||
} | } | ||
timer() | timer() | ||
{ | { | ||
Line 492: | Line 492: | ||
llSay(channel, "POS," + (string)gEntranceX + "," + (string)gEntranceY); | llSay(channel, "POS," + (string)gEntranceX + "," + (string)gEntranceY); | ||
++gPopulation; | ++gPopulation; | ||
//llSay( | //llSay(PUBLIC_CHANNEL,"Population:" + (string)gPopulation); | ||
} | } | ||
} | } | ||
</lsl> | </lsl> | ||
== The Agent Script == | == The Agent Script == | ||
<lsl> | <lsl> | ||
// Pathfinder Agent by Babbage Linden | // Pathfinder Agent by Babbage Linden | ||
Line 526: | Line 526: | ||
llListen(gChannel, "", NULL_KEY, ""); | llListen(gChannel, "", NULL_KEY, ""); | ||
} | } | ||
listen(integer channel, string name, key id, string s) | listen(integer channel, string name, key id, string s) | ||
{ | { | ||
Line 554: | Line 554: | ||
else if(command == "RESET") | else if(command == "RESET") | ||
{ | { | ||
llSay( | llSay(PUBLIC_CHANNEL,"FINISHED"); | ||
llDie(); | llDie(); | ||
} | } | ||
} | } | ||
timer() | timer() | ||
{ | { | ||
Line 567: | Line 567: | ||
} | } | ||
</lsl> | </lsl> | ||
== The Square Script == | == The Square Script == | ||
<lsl> | <lsl> | ||
// Pathfinder Square by Babbage Linden | // Pathfinder Square by Babbage Linden | ||
Line 574: | Line 574: | ||
// | // | ||
// The square listens for various commands: RESET causes it to kill itself; | // The square listens for various commands: RESET causes it to kill itself; | ||
// EXIT causes it to turn black and set itself as an exit. Touching the | // EXIT causes it to turn black and set itself as an exit. Touching the | ||
// causes it to grow and to send the ADDOBSTACLEINDEX command to the | // causes it to grow and to send the ADDOBSTACLEINDEX command to the | ||
// PotentialField script. | // PotentialField script. | ||
Line 596: | Line 596: | ||
llListen(gSquareChannel, "", NULL_KEY, ""); | llListen(gSquareChannel, "", NULL_KEY, ""); | ||
} | } | ||
touch_start(integer num) | touch_start(integer num) | ||
{ | { | ||
Line 607: | Line 607: | ||
} | } | ||
} | } | ||
listen(integer channel, string name, key id, string s) | listen(integer channel, string name, key id, string s) | ||
{ | { |
Revision as of 02:41, 8 October 2013
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
The Base Script
<lsl> // Pathfinder PotentialField by Babbage Linden // // Part of the Pathfinder Open Source Pathfinding Demo // // The potential field maintains a representation of the navigation space // as a string containing the number of steps to the exit for each square. // The potential field lists for various commands: SETSIZE sets the size // of the navigation space, the exit and intializes the potential field; // ADDOBSTACLE and ADDOBSTACLEINDEX sets one point in the navigation space // to an obstacle that must be navigated around and updates the potential // field; NEXTPOS queries the potential field to find the next point to // for an agent at a given point to move to. When the potential field is // updated, care is taken to only update parts of the field affected by // the change and to spread the update across a number of timer events to // avoid blocking and so becoming unresponsive to NEXTPOS queries. // // Available under the Creative Commons Attribution-ShareAlike 2.5 license // http://creativecommons.org/licenses/by-sa/2.5/
integer gPotentialFieldChannel = 1; integer gDigitsPerElement = 2; integer gMinValue = 0; integer gMaxValue = 255; integer gWidth = 0; integer gHeight = 0; integer gMaxUpdates = 10;
integer gCurrentUpdateMinX; integer gCurrentUpdateMinY; integer gCurrentUpdateMaxX; integer gCurrentUpdateMaxY; integer gNextUpdateMinX; integer gNextUpdateMinY; integer gNextUpdateMaxX; integer gNextUpdateMaxY;
string gPotentialField;
integer min(integer a, integer b) {
if(a < b) { return a; } return b;
}
integer max(integer a, integer b) {
if(a > b) { return a; } return b;
}
string replace(string s, integer index, string newString) {
integer newStringLength = llStringLength(newString); if(newStringLength) return llInsertString( llDeleteSubString( s, index, index + newStringLength - 1), index, newString ); return s;
}
string hexc="0123456789ABCDEF";
string int2hex(integer x) {
integer x0 = x & 0xF; string res = llGetSubString(hexc, x0, x0); if(( x = ((x >> 4) & 0x0FFFFFFF) )) { do{ res = llGetSubString(hexc, x0 = (x & 0xF), x0) + res; }while(x = x >> 4); } return res;
}
integer hex2int(string hex) {
return (integer)("0x" + hex);
}
string setElement(string elements, integer index, integer value) {
string stringValue = int2hex(value); integer len = llStringLength(stringValue); while(gDigitsPerElement >= ++len) { stringValue = "0" + stringValue; } //llSay(PUBLIC_CHANNEL, "setElement " + elements + " " + (string)index + " " + stringValue); elements = replace(elements, index * gDigitsPerElement, stringValue); return elements;
}
string getElementString(string elements, integer index) {
if(index < 0) { return int2hex(gMaxValue); } return llGetSubString(elements, index * gDigitsPerElement, index * gDigitsPerElement + gDigitsPerElement - 1);
}
integer getElement(string elements, integer index) {
return hex2int(getElementString(elements, index));
}
sayPotentialField(string elements) {
string s = "\n"; integer y = gHeight; while(0 <= --y) { integer x = ERR_GENERIC; while( gWidth > ++x ) { s += getElementString(elements, squareToIndex(x, y)) + " "; } s += "\n"; } llSay(PUBLIC_CHANNEL,s);
}
integer squareToIndex(integer x, integer y) {
if(x < 0 || x >= gWidth || y < 0 || y >= gHeight) { return ERR_GENERIC; }
return x + y * gWidth;
}
string addObstacle(string elements, integer x, integer y) {
elements = setElement(elements, squareToIndex(x, y), gMaxValue); expandUpdateRegion(x, y); return updatePotentialField(elements);
}
expandUpdateRegion(integer x, integer y) {
gNextUpdateMinX = min(gNextUpdateMinX, x - 1); gNextUpdateMinX = max(gNextUpdateMinX, 0); gNextUpdateMinY = min(gNextUpdateMinY, y - 1); gNextUpdateMinY = max(gNextUpdateMinY, 0); gNextUpdateMaxX = max(gNextUpdateMaxX, x + 1); gNextUpdateMaxX = min(gNextUpdateMaxX, gWidth - 1); gNextUpdateMaxY = max(gNextUpdateMaxY, y + 1); gNextUpdateMaxY = min(gNextUpdateMaxY, gHeight - 1);
}
resetNextUpdateRegion() {
gNextUpdateMinX = gWidth - 1; gNextUpdateMinY = gHeight - 1; gNextUpdateMaxX = 0; gNextUpdateMaxY = 0;
}
string updatePotentialField(string elements) {
// If current update region finished, switch to next region. if(gCurrentUpdateMinY > gCurrentUpdateMaxY) { gCurrentUpdateMinX = gNextUpdateMinX; gCurrentUpdateMinY = gNextUpdateMinY; gCurrentUpdateMaxX = gNextUpdateMaxX; gCurrentUpdateMaxY = gNextUpdateMaxY; resetNextUpdateRegion();
// If next region empty, stop updating. if(gCurrentUpdateMinY > gCurrentUpdateMaxY) { llSay(PUBLIC_CHANNEL, "updatePotentialField:DONE"); llSetTimerEvent(0.0); return elements; }
llSay(PUBLIC_CHANNEL, "updatePotentialField:(" + (string)gCurrentUpdateMinX + "," + (string)gCurrentUpdateMinY + ") (" + (string)gCurrentUpdateMaxX + "," + (string)gCurrentUpdateMaxY + ")"); }
// Recalculate potentials. llResetTime(); integer updates = 0; if(gCurrentUpdateMinX <= gCurrentUpdateMaxX) { while((updates < gMaxUpdates) && (gCurrentUpdateMinY <= gCurrentUpdateMaxY)) { //llSay(PUBLIC_CHANNEL, "updatePotentialField:" + (string)gCurrentUpdateMinY); integer x = gCurrentUpdateMinX; do{ elements = calculatePotentialField(elements, x, gCurrentUpdateMinY); }while(gCurrentUpdateMaxX >= ++x); //x = gCurrentUpdateMaxX + 1; this saves us from doing "++updates;" in the loop updates += x - gCurrentUpdateMinX; ++gCurrentUpdateMinY; } }
llSetTimerEvent((llGetTime() * 2.0) + 0.1); return elements;
}
string calculatePotentialField(string elements, integer x, integer y) {
integer currentValue = getElement(elements, squareToIndex(x, y));
// Don't update obstacles or exits. if(currentValue == gMaxValue || currentValue == gMinValue) { return elements; }
integer minValue = gMaxValue; minValue = min(minValue, getElement(elements, squareToIndex(x - 1, y))); minValue = min(minValue, getElement(elements, squareToIndex(x + 1, y))); minValue = min(minValue, getElement(elements, squareToIndex(x, y - 1))); minValue = min(minValue, getElement(elements, squareToIndex(x, y + 1))); minValue = min(minValue + 1, gMaxValue - 1); if(minValue != currentValue) { //llSay(PUBLIC_CHANNEL, "calculatePotentialField:" + (string)x + "," + (string)y + " currentValue " + (string)currentValue + " newValue " + (string)minValue); elements = setElement(elements, squareToIndex(x, y), minValue); expandUpdateRegion(x, y); } return elements;
}
string nextStep(string elements, integer x, integer y) {
integer nextX; integer nextY; integer minValue = gMaxValue; integer currentX = x - 1; integer currentY = y; integer currentValue = getElement(elements, squareToIndex(currentX, currentY)); if(currentValue < minValue) { minValue = currentValue; nextX = currentX; nextY = currentY; } currentX = x + 1; currentValue = getElement(elements, squareToIndex(currentX, currentY)); if(currentValue < minValue) { minValue = currentValue; nextX = currentX; nextY = currentY; } currentX = x; currentY = y - 1; currentValue = getElement(elements, squareToIndex(currentX, currentY)); if(currentValue < minValue) { minValue = currentValue; nextX = currentX; nextY = currentY; } currentY = y + 1; currentValue = getElement(elements, squareToIndex(currentX, currentY)); if(currentValue < minValue) { minValue = currentValue; nextX = currentX; nextY = currentY; } if(minValue == gMaxValue) { return ""; } return (string)nextX + "," + (string)nextY;
}
setSize(integer width, integer height, integer exitX, integer exitY) {
gWidth = width; gHeight = height;
gPotentialField = "";
integer y = ERR_GENERIC; while(height > ++y) { integer x = ERR_GENERIC; while(width > ++x) { gPotentialField = setElement(gPotentialField, squareToIndex(x, y), llAbs(exitX - x) + llAbs(exitY - y)); } } resetNextUpdateRegion(); sayPotentialField(gPotentialField);
}
init() {
llListen(PUBLIC_CHANNEL, "", NULL_KEY, ""); llListen(gPotentialFieldChannel, "", NULL_KEY, "");
}
default {
state_entry() { init(); }
on_rez(integer param) { init(); }
timer() { gPotentialField = updatePotentialField(gPotentialField); }
listen(integer channel, string name, key id, string s) { list message = llCSV2List(s); string command = llList2String(message, 0); integer x = llList2Integer(message, 1); integer y = llList2Integer(message, 2); if(command == "ADDOBSTACLE") { gPotentialField = addObstacle(gPotentialField, x, y); } else if(command == "ADDOBSTACLEINDEX") { y = x / gWidth; x %= gWidth; gPotentialField = addObstacle(gPotentialField, x, y); } else if(command == "SETSIZE") { integer width = x; integer height = y; x = llList2Integer(message, 3); y = llList2Integer(message, 4); setSize(width, height, x, y); } else if(command == "NEXTPOS") { integer channel = llList2Integer(message, 3); if(getElement(gPotentialField, squareToIndex(x, y)) == 0) { llSay(channel, "RESET"); } string nextStepString = nextStep(gPotentialField, x, y); if(llStringLength(nextStepString) > 0) { list nextStepList = llCSV2List(nextStepString); x = llList2Integer(nextStepList, 0); y = llList2Integer(nextStepList, 1); llSay(channel, "NEXTPOS," + (string)x + "," + (string)y); } } //sayPotentialField(gPotentialField); }
} </lsl>
The Rezzer Script
<lsl> // Pathfinder Rezzer by Babbage Linden // // Part of the Pathfinder Open Source Pathfinding Demo // // The rezzer listens for SETSIZE commands, which causes it to rez // a grid of Square objects of the appropriate size and set the // entry and exit points as exits. Touching the rezzer // causes it to rez and agent above the entry point at the origin. // // Available under the Creative Commons Attribution-ShareAlike 2.5 license // http://creativecommons.org/licenses/by-sa/2.5/
float gSquareWidth = 1.0; float gPeriod = 5.0; vector gOrigin; vector gEntrance; integer gEntranceX; integer gEntranceY; integer gPopulation = 0; integer gMaxPopulation = 10; integer gPotentialFieldChannel = 1; integer gSquareChannel = 2; integer gAgentChannel = 3;
init() {
llListen(PUBLIC_CHANNEL, "", NULL_KEY, "");
}
default {
state_entry() { init(); }
on_rez(integer param) { init(); }
listen(integer channel, string name, key id, string s) { list message = llCSV2List(s); string command = llList2String(message, 0); integer x = llList2Integer(message, 1); integer y = llList2Integer(message, 2); if(command == "SETSIZE") { integer width = x; integer height = y; integer exitX = llList2Integer(message, 3); integer exitY = llList2Integer(message, 4); gEntranceX = llList2Integer(message, 5); gEntranceY = llList2Integer(message, 6); gOrigin = llGetPos(); gOrigin.x -= ((width - 1) * gSquareWidth) / 2.0; gOrigin.y -= ((height - 1) * gSquareWidth) / 2.0; gEntrance = gOrigin; gEntrance.x += gEntranceX * gSquareWidth; gEntrance.y += gEntranceY * gSquareWidth; gEntrance.z += 1.0; for(y = 0; y < height; ++y) { for(x = 0; x < width; ++x) { vector pos = gOrigin; pos.x += x * gSquareWidth; pos.y += y * gSquareWidth; pos.z += 0.5; integer channel = (x + y * width); llRezObject("Square", pos, <0,0,0>, <0,0,0,0>, channel); } } llSay(gSquareChannel, "EXIT," + (string)(gEntranceX + gEntranceY * width)); llSay(gSquareChannel, "EXIT," + (string)(exitX + exitY * width)); gAgentChannel = 3; gMaxPopulation = 10; llSetTimerEvent(gPeriod); } else if(command == "RESET") { gPopulation = 0; llSay(gSquareChannel, "RESET"); integer channel; for(channel = 2; channel <= gAgentChannel; ++channel) { llSay(channel, "RESET"); } llSetTimerEvent(0); gMaxPopulation = 0; } else if(command == "FINISHED") { --gPopulation; if(gPopulation < 0) {gPopulation = 0;} llSetTimerEvent(gPeriod); //llSay(PUBLIC_CHANNEL,"Population:" + (string)gPopulation); } }
timer() { if(gPopulation >= gMaxPopulation) { llSetTimerEvent(0); return; } integer channel = gAgentChannel++; llRezObject("Agent", gEntrance, <0,0,0>, <0,0,0,0>, channel); llSay(channel, "POS," + (string)gEntranceX + "," + (string)gEntranceY); ++gPopulation; //llSay(PUBLIC_CHANNEL,"Population:" + (string)gPopulation); }
} </lsl>
The Agent Script
<lsl> // Pathfinder Agent by Babbage Linden // // Part of the Pathfinder Open Source Pathfinding Demo // // The agent periodically makes NEXTPOS queries to the PotentialField // script to find the next point to move to and can have its current // position set with the POS command. // // Available under the Creative Commons Attribution-ShareAlike 2.5 license // http://creativecommons.org/licenses/by-sa/2.5/
float gSquareWidth = 1.0;
float gStartPeriod = 2.0;
float gPeriod = 2.0;
integer gChannel;
integer gX;
integer gY;
integer gSquareChannel = 2;
integer gPotentialFieldChannel = 1;
default {
on_rez(integer param) { gChannel = param; llListen(gChannel, "", NULL_KEY, ""); }
listen(integer channel, string name, key id, string s) { list message = llCSV2List(s); string command = llList2String(message, 0); integer x = llList2Integer(message, 1); integer y = llList2Integer(message, 2); if(command == "POS") { gX = x; gY = y; llSetTimerEvent(gPeriod); } else if(command == "NEXTPOS") { integer dX = x - gX; integer dY = y - gY; vector pos = llGetPos(); pos.x += dX * gSquareWidth; pos.y += dY * gSquareWidth; llSetPos(pos); gX = x; gY = y; gPeriod = gStartPeriod; llSetTimerEvent(gPeriod); } else if(command == "RESET") { llSay(PUBLIC_CHANNEL,"FINISHED"); llDie(); } }
timer() { llSay(gPotentialFieldChannel, "NEXTPOS," + (string)gX + "," + (string)gY + "," + (string)gChannel); gPeriod *= 2.0; llSetTimerEvent(gPeriod); }
} </lsl>
The Square Script
<lsl> // Pathfinder Square by Babbage Linden // // Part of the Pathfinder Open Source Pathfinding Demo for Second Life // // The square listens for various commands: RESET causes it to kill itself; // EXIT causes it to turn black and set itself as an exit. Touching the // causes it to grow and to send the ADDOBSTACLEINDEX command to the // PotentialField script. // // Available under the Creative Commons Attribution-ShareAlike 2.5 license // http://creativecommons.org/licenses/by-sa/2.5/
integer gSquareChannel = 2; integer gPotentialFieldChannel = 1; integer gIndex; integer STATE_DEFAULT = 0; integer STATE_OBSTACLE = 1; integer STATE_EXIT = 2; integer gState = STATE_DEFAULT;
default {
on_rez(integer param) { gIndex = param; llListen(gSquareChannel, "", NULL_KEY, ""); }
touch_start(integer num) { if(gState == STATE_DEFAULT) { llSay(gPotentialFieldChannel, "ADDOBSTACLEINDEX," + (string)gIndex); llSetScale(<1,1,1>); llSetPos(llGetPos() + <0,0,0.5>); gState = STATE_OBSTACLE; } }
listen(integer channel, string name, key id, string s) { list message = llCSV2List(s); string command = llList2String(message, 0); if(command == "RESET") { llDie(); } else if(command == "EXIT") { integer index = llList2Integer(message, 1); if(index == gIndex) { llSetColor(<0,0,0>, ALL_SIDES); gState = STATE_EXIT; } } else if(command == "ENTRANCE") { integer index = llList2Integer(message, 1); if(index == gIndex) { llSetTexture("5748decc-f629-461c-9a36-a35a221fe21f", ALL_SIDES); gState = STATE_EXIT; } } }
} </lsl>