I modified a physical elevator script for a client in Second Life, and am now exporting the build containing that elevator to an opensim grid for the client. My version of the script has working doors on the elevator shaft and the elevator car, and can be used to access multiple floors at random heights. It was modified pretty severely from a simple open-source script for a physical elevator, to add the messaging for the linked sliding doors in the elevator car, and the linked sliding doors in the shaft. The car and shaft communicate with each other, and with the call buttons, by means of a command string on a non-zero channel (17).
This script works flawlessly in the SL grid. There, the car moves straight up and down on the Z axis, and never tries to tilt or rotate the car's prims.
But on opensim, the elevator car occasionally 'tumbles'. It goes physical, but instead of only changing the Z position of the car, the root prim gets all sorts of random rotation vectors thrown at it, the car pitches at a weird angle in the shaft, and the car apparently becomes phantom and the passengers get dumped off the car and into the bottom of the shaft. I also tested it without the shaft, and when it malfunctions, the car goes tumbling all over the place, and doesn't stay anywhere near it's assigned X and Y location.
Here's the code that moves the elevator car. There's a list of floor heights at the start, with 4 values for the possible Z-height of the root prim of the elevator car, which is the floor of the car. (Lists have a "zero" position value, so the ground floor is repeated in the first and second positions in the list, allowing the floor number to directly point to the value in the list for that floor's height.)
Code:
vector alignment;
vector targetVector;
integer travelDistance;
integer numListen;
integer targetFloor;
list floorHeights = [28.726,28.726,34.17,37.995];
float fixedFloorHeight = -1; //Set to floor heights, or set to -1 to use floorHeights list
float speed = 0.25; //Valid values are 0.01 to 1.0, a Percentage of maxSpeed;
float maxSpeed = 16;
float precision = 0.25;
integer autoSpeed = TRUE;
integer initialDistance;
integer doorsopen = 1; // 1 = Car doors are open now, 2 = closed
elevate (vector end)
{
vector current = llGetPos();
travelDistance = llRound(current.z-end.z);
travelDistance = llAbs(travelDistance);
if (autoSpeed)
{
if (travelDistance < (initialDistance / 2))
{
speed -= (precision / 50);
if (speed < 0.25)
speed = 0.25;
}
else
{
speed += (precision / 25);
if (speed > 1)
speed = 1;
}
}
if (travelDistance > 30)
{
travelDistance = 30;
if (end.z > current.z)
{
end.z = current.z + 30;
}
else
{
end.z = current.z - 30;
}
}
float i = travelDistance/(maxSpeed*speed);
llMoveToTarget(end,i);
}
GotoFloor (integer floor, key id)
{
llWhisper(0, "Moving to floor #" + (string)floor);
llSay(17, "Moving to floor #" + (string)floor);
if (doorsopen == 1)
{
llMessageLinked(LINK_SET, 7, "", NULL_KEY); // send signal to close the doors
doorsopen = 2;
}
llSetStatus(STATUS_PHYSICS, TRUE);
// llLoopSound("ElevatorNoises", 1);
targetFloor = floor;
if (fixedFloorHeight > 0)
{
targetVector = alignment;
targetVector.z = alignment.z + (fixedFloorHeight * floor);
}
else
{
targetVector = alignment;
targetVector.z = llList2Float(floorHeights, floor);
}
// llWhisper(0, "At " + (string)targetVector.z + " meters...");
vector current = llGetPos();
initialDistance = llRound(current.z-targetVector.z);
initialDistance = llAbs(initialDistance);
if (autoSpeed)
{
speed = 0.01;
}
elevate(targetVector);
llSetTimerEvent(precision);
}
reset()
{
llSay(0, "Resetting Elevator...");
llSetStatus(STATUS_ROTATE_X| STATUS_ROTATE_Y| STATUS_ROTATE_Z, FALSE);
alignment = llGetPos();
llSetStatus(STATUS_PHYSICS, FALSE);
// llStopSound();
llListenRemove(numListen);
numListen = llListen( 17, "", "", "" );
}
default
{
state_entry()
{
reset();
}
object_rez(key id)
{
llResetScript();
}
listen(integer a, string n, key id, string m)
{
vector pos;
integer Floor;
float tempFloat;
if (llSubStringIndex(m, "goto floor") == 0)
{
Floor = (integer)llGetSubString(m, 10, llStringLength(m));
GotoFloor(Floor, NULL_KEY);
}
if (llSubStringIndex(m, "speed") == 0)
{
tempFloat = (float)llGetSubString(m, 5, llStringLength(m));
if ((tempFloat > 0.001) && (tempFloat <= 1.0))
{
speed = tempFloat;
}
}
if ((m=="elevator reset") && (id==llGetOwner()))
{
reset();
}
}
timer()
{
vector CurrentPos;
float tempfloat;
CurrentPos = llGetPos();
tempfloat = (CurrentPos.z - targetVector.z);
if (llFabs(tempfloat) < 2)
{
if (llFabs(tempfloat) < 0.05)
{
//Arrived at Floor
llWhisper(0, "Arrived at floor #" + (string)targetFloor);
llSay(17, "Arrived at floor #" + (string)targetFloor);
if (doorsopen == 2)
{
llMessageLinked(LINK_SET, 8, "", NULL_KEY); // send signal to open the doors
doorsopen = 1;
}
llSetTimerEvent(0);
llSetStatus(STATUS_PHYSICS, FALSE);
// llStopSound();
}
else
{
llMoveToTarget(targetVector,1.0);
}
}
else
{
if (fixedFloorHeight > 0)
{
targetVector = alignment;
targetVector.z = alignment.z + (fixedFloorHeight * targetFloor);
}
else
{
targetVector = alignment;
targetVector.z = llList2Float(floorHeights, targetFloor);
}
elevate(targetVector);
}
}
}
And here is an example of one of the call buttons that sends the elevator to a particular floor (1 in this case):
Code:
default
{
state_entry()
{
//llSay(0, "Control ready");
}
touch_start(integer total_number)
{
llSay(17, "goto floor 1");
}
}
The code in the shaft merely listens for the "arrived at" and 'moving to' messages from the car, and opens and closes the shaft doors via link messages.
Code:
door3(integer open3) {
if (open3 == 1 && door3open == 2) { // closed now, signal sent to open them
llMessageLinked(LINK_SET, 5, "", NULL_KEY); // send signal to open the doors
door3open = 1;
} else if (open3 == 2 && door3open == 1) { // open now, signal sent to close them
llMessageLinked(LINK_SET, 6, "", NULL_KEY); // send signal to close the doors
door3open = 2;
}
}
default
{
state_entry()
{
// Start listening.
stoplisten();
startlisten();
state waiting;
}
}
state waiting
{
state_entry() {
stoplisten();
startlisten();
}
listen(integer number, string name, key id, string message)
{
if(message=="Arrived at floor #1")
{
door1(1); //action here to open, for floor 1;
} else if (message=="Arrived at floor #2") {
door2(1); //action here to open, for floor 2;
} else if (message=="Arrived at floor #3") {
door3(1); //action here to open, for floor 3;
} else if (message=="Moving to floor #1" || message=="Moving to floor #2" || message=="Moving to floor #3") {
door1(2); //action here to close, for floor 1;
door2(2); //action here to close, for floor 2;
door3(2); //action here to close, for floor 3;
}
}
// timer() { // auto-close
// door1(2);
// door2(2);
// door3(2);
// }
//
// state_exit() {
// llSetTimerEvent(0);
// }
}
For the curious, here's one of the linkset move listeners for the east doors on floor 1 of the shaft. The West doors have the same script, but opposite sign for the move vectors.
Code:
// Sliding Door Script by Ceera Murakami
// Linkset Listener script. Works with Linkset Control Script in root prim.
// Place this in the prims that must move.
vector gPrimPos; //Recorded position of prim relative to the root prim
float gMoveX = 0.55;//Local offset in x-direction for this prim.
float gMoveY = 0.0; //Local offset in Y direction for this prim
default
{
link_message(integer sender_number,integer number,string message,key id)
{
if(number == 1) {
gPrimPos = llGetLocalPos();
llSetPos(<gPrimPos.x+gMoveX,gPrimPos.y+gMoveY,gPrimPos.z>);
}
if(number == 2) {
gPrimPos = llGetLocalPos();
llSetPos(<gPrimPos.x-gMoveX,gPrimPos.y-gMoveY,gPrimPos.z>);
}
}
}