Recipe 3.2: Open Door
Doors are very common in the Second Life world. Most doors open as soon as an avatar touches them. Some doors only open for specific users. Many doors open for any avatar.
A door in Second Life usually opens by rotating the door's object by 90 degrees. This means a simple flat cube is insufficient for a door. This is because objects in Second Life are always rotated about their center. Think about a door in real life. Does the door rotate about its center when opened? No. A door rotates about its hinges when it opens. The hinges are on the side. Because of this, a door in Second Life is normally a cube with a cut path to remove one side of the rectangle. This causes the center of the door to appear to be on its side.
To create such a door, create a rectangular cube and set a cut path begin of 0.375 and a cut path end of 0.875. Additionally, set the texture's repeats per face horizontal to 2, and the horizontal texture offset to 0.5. All other values can remain at their default state. If the sample objects are obtained from this book, a perfectly setup door object can be seen. Refer to Appendix A on how to obtain sample objects and code for this book. Figure 3.3 shows a door and the center point.
Figure 3.3: Door with Center

This Recipe shows how to create an open door that will open for any avatar. The script for such a door is shown in Listing 3.2.
Listing 3.2: Open Door (OpenDoor.lsl)
// From the book: // // Scripting Recipes for Second Life // by Jeff Heaton (Encog Dod in SL) // ISBN: 160439000X // Copyright 2007 by Heaton Research, Inc. // // This script may be freely copied and modified so long as this header // remains unmodified. // // For more information about this book visit the following web site: // // http://www.heatonresearch.com/articles/series/22/ float TIMER_CLOSE = 5.0; integer DIRECTION = -1; // direction door opens in. Either 1 (outwards) or -1 (inwards); integer DOOR_OPEN = 1; integer DOOR_CLOSE = 2; vector originalPos; door(integer what) { rotation rot; rotation delta; vector eul; llSetTimerEvent(0); if ( what == DOOR_OPEN ) { llTriggerSound("doorOpen", 1); eul = <0, 0, 90*DIRECTION>; //90 degrees around the z-axis, in Euler form } else if ( what == DOOR_CLOSE) { llTriggerSound("doorClose", 1); eul = <0, 0, 90*-DIRECTION>; //90 degrees around the z-axis, in Euler form } eul *= DEG_TO_RAD; //convert to radians rotation rot = llGetRot(); delta = llEuler2Rot(eul); rot = delta * rot; llSetRot(rot); } default { on_rez(integer start_param) { llResetScript(); } state_entry() { originalPos = llGetPos(); } touch_start(integer total_number) { door(DOOR_OPEN); state open_state; } moving_end() { originalPos = llGetPos(); } } state open_state { state_entry() { llSetTimerEvent(TIMER_CLOSE); } touch_start(integer num) { door(DOOR_CLOSE); llSetPos(originalPos); state default; } timer() { door(DOOR_CLOSE); llSetPos(originalPos); state default; } moving_start() { door(DOOR_CLOSE); state default; } }
The door script begins by defining a number of variables. The TIMER_CLOSE variable defines how long until the door swings closed. The DIRECTION variable specifies the direction the door opens, either 1 (outwards) or -1 (inwards). The DOOR_OPEN and DOOR_CLOSE variables are used as constants to tell the door function what state the door should move to. The originalPos variable holds the original position of the door. This is useful because sometimes the door will slightly change positions when opening or closing. Doors are particularly prone to changing positions when they hit something in the process of opening or closing. These variables can be seen here.
float TIMER_CLOSE = 5.0; integer DIRECTION = -1; integer DOOR_OPEN = 1; integer DOOR_CLOSE = 2; vector originalPos;
The door script defines one global function, named door. This function should be passed either DOOR_OPEN or DOOR_CLOSE for its single parameter. The door function begins by creating a few variables that will be needed as the door is rotated.
door(integer what)
{
rotation rot;
rotation delta;
vector eul;
llSetTimerEvent(0); Next, the open state is handled. The door is rotated by 90 degrees. To do this, a vector is created that specifies a rotation of zero in both the x and y coordinate system, but a value of -90 or +90 in the z-coordinate. Additionally, the door open sound is played.
if ( what == DOOR_OPEN )
{
llTriggerSound("doorOpen", 1);
eul = <0, 0, 90*DIRECTION>; Next, the close state is handled. The door is rotated by 90 degrees. To do this, a vector is created that specifies a rotation of zero in both the x and y coordinate system, but a value of -90 or +90 in the z-coordinate. The opposite direction of the open state is specified. Additionally, the door open sound is played.
} else if ( what == DOOR_CLOSE)
{
llTriggerSound("doorClose", 1);
eul = <0, 0, 90*-DIRECTION>;
}Second life expresses rotations not in degrees or radians but in a form called a quaternion. This is a four component rotation that has x, y, z and s components. Most scripts do not deal with quaternions directly, but rather convert radians into quaternions using the llEuler2Rot function.
This can seen before the eul variable is converted into radians. Next, the current rotation is obtained by calling llGetRot. The change in rotation, called delta, is obtained by converting the radians into a quaternion rotation. This is done by calling the llEuler2Rot function.
eul *= DEG_TO_RAD; //convert to radians rotation
rot = llGetRot();
delta = llEuler2Rot(eul);
rot = delta * rot;
llSetRot(rot);
}The above code is very common in Second Life scripts that need to deal with angles.
Next, the two states for the door must be handled. The default state is when the door is closed. The opened state, as its name implies, handles the state where the door is opened. The door begins in the default closed state. The door includes an on_rez event that resets the script. This ensures that the script is properly setup if it is sold or transferred. For the simple open door this is not really necessary, since the open door does not care about its owner. However, for objects that operate differently for their owner, a call to llResetScript should always be set in the on_rez event handler.
default
{
on_rez(integer start_param)
{
llResetScript();
}
state_entry()
{
originalPos = llGetPos();
}When the door is touched, and it is in the closed state, open the door and switch to the open state.
touch_start(integer total_number)
{
door(DOOR_OPEN);
state open_state;
}If the user moves the door, grab a new copy of originalPos.
moving_end()
{
originalPos = llGetPos();
}
}The open state begins by setting a timer. This door will close automatically after the time specified by TIMER_CLOSE elapses. If the user touches the closed door before the timer is up, immediately close and return to the default state.
state open_state
{
state_entry()
{
llSetTimerEvent(TIMER_CLOSE);
}
touch_start(integer num)
{
door(DOOR_CLOSE);
llSetPos(originalPos);
state default;
}Once the timer event occurs, return the door to a closed state and set the position back to the original position. Return to the default state.
timer()
{
door(DOOR_CLOSE);
llSetPos(originalPos);
state default;
}If the user begins to move the door, close the door and return to the default state.
moving_start()
{
door(DOOR_CLOSE);
state default;
}
}This script implements a basic unlocked door. The next two Recipes demonstrate two different types of locking doors.




