// ===================================================================== // Sliding Door // skimi3d script library ยท https://grid.skimi3d.com/scripts/sliding-door // // A door that slides aside when touched and slides back on its own. // The barn door, the shop shutter, the lift door, the pocket door that // disappears into the wall. // // It is the sister of Swing Door and takes the same shape, so once you // have set up one you know the other. The difference is that this one // moves in a straight line and never turns. // // Plain LSL. Works on a lone prim and on a child prim in a linkset. // ===================================================================== // --------------------------------------------------------------------- // Settings // --------------------------------------------------------------------- // Which way it slides, in the door's own body. <1.0, 0.0, 0.0> slides // it along its own X. If the door disappears into the floor instead of // into the wall, this is the line to change. vector DIRECTION = <1.0, 0.0, 0.0>; // How far it slides. 1.0 means "its own width along that direction", // so the door ends up exactly beside the hole it was filling. Use 0.9 // if you want it to stay slightly overlapping, or a plain number of // metres by setting IN_METRES below. float DISTANCE = 1.0; // Read DISTANCE as metres instead of as a fraction of the door. // FALSE is the friendlier setting: a door set up as a fraction still // works after you resize it. integer IN_METRES = FALSE; // How long the slide takes, in seconds. 0.0 snaps it aside. float SLIDE_TIME = 1.2; // Seconds it stays open before sliding back. 0.0 means it stays open // until someone touches it again. float AUTO_CLOSE = 8.0; // Who may open it. // 0 = everyone // 1 = only the owner // 2 = only people wearing the same group tag as the door integer WHO_MAY_OPEN = 0; // Sounds. Drop a sound into the door's Contents and write its name // here. Leave empty for a silent door. string SOUND_OPEN = ""; string SOUND_SHUT = ""; float VOLUME = 0.5; // --------------------------------------------------------------------- // Everything below here is the machinery // --------------------------------------------------------------------- // One step every tenth of a second, and not one step more. // // OpenSim gathers object updates and sends them out in bundles, about // eleven times a second. Writing the door's position more often than // that does not make the slide smoother, because the extra positions // never reach anyone's viewer. It only costs the region work. If the // slide looks choppy, give it more TIME, not more steps. float STEP = 0.1; integer gOpen = FALSE; // is it standing open right now? integer gMoving = FALSE; // mid-slide, ignore further touches vector gShutPos; // where it sits when shut rotation gShutRot; // how it sits when shut vector gTravel; // the whole journey, in the parent's frame // Put the door at some fraction of its travel. 0.0 is shut, 1.0 is // fully open. place(float f) { // Always measured from the shut position, never from the last step. // A door that added a little each time would walk away from its // frame over an afternoon of use, a few millimetres at a time. llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POS_LOCAL, gShutPos + gTravel * f]); } slide(integer open) { gMoving = TRUE; integer steps = (integer)(SLIDE_TIME / STEP); if (steps > 1) { integer i; for (i = 1; i < steps; ++i) { float f = (float)i / (float)steps; if (!open) f = 1.0 - f; place(f); llSleep(STEP); } } // Always land exactly on the end state, so a door left on the // second-to-last step cannot sit a finger's width ajar. float end = 0.0; if (open) end = 1.0; place(end); gOpen = open; gMoving = FALSE; } play(string name) { if (name == "") return; if (llGetInventoryType(name) != INVENTORY_SOUND) { // The owner can fix this. A visitor cannot, and does not need // to hear about it. llOwnerSay("There is no sound called \"" + name + "\" in my Contents."); return; } llPlaySound(name, VOLUME); } integer mayOpen(key who) { if (WHO_MAY_OPEN == 1) return who == llGetOwner(); if (WHO_MAY_OPEN == 2) return llSameGroup(who); return TRUE; } default { state_entry() { // The door learns its shut position from wherever it stands at // this moment. Put it where it belongs BEFORE you drop the // script in. list p = llGetLinkPrimitiveParams(LINK_THIS, [PRIM_POS_LOCAL, PRIM_ROT_LOCAL]); gShutPos = llList2Vector(p, 0); gShutRot = llList2Rot(p, 1); // DIRECTION is given in the door's own body, so that a door // mounted at an angle still slides along itself rather than // along the region's axes. Turned into the parent's frame here, // once, because that is the frame we can actually set. vector dir = llVecNorm(DIRECTION); float far = DISTANCE; if (!IN_METRES) { // A fraction of the door, measured along the direction it // travels. This is what keeps the setting true after a // resize: a door twice as wide slides twice as far. vector s = llGetScale(); vector along = ; far = DISTANCE * llVecMag(along); } gTravel = (dir * far) * gShutRot; gOpen = FALSE; gMoving = FALSE; llSetTimerEvent(0.0); } touch_start(integer total) { if (gMoving) return; key who = llDetectedKey(0); if (!mayOpen(who)) { llRegionSayTo(who, 0, "This door does not open for you."); return; } llSetTimerEvent(0.0); if (gOpen) { play(SOUND_SHUT); slide(FALSE); return; } play(SOUND_OPEN); slide(TRUE); if (AUTO_CLOSE > 0.0) llSetTimerEvent(AUTO_CLOSE); } timer() { llSetTimerEvent(0.0); if (!gOpen) return; play(SOUND_SHUT); slide(FALSE); } on_rez(integer start) { // A rezzed copy stands somewhere new, so its idea of shut is // worthless. Learn it again. llResetScript(); } changed(integer what) { // Linking or unlinking changes what a local position means, so // the door has to measure itself again. Only when it is shut and // still, though: reset it mid-slide and it would take its // half-open position for the shut one and never close properly. if (what & CHANGED_LINK) { if (!gOpen && !gMoving) llResetScript(); } } }