// ===================================================================== // Swing Door // skimi3d script library ยท https://grid.skimi3d.com/scripts/swing-door // // A door that swings open around its hinge edge when touched, and // closes itself again afterwards. It works on a single prim and on a // child prim inside a linkset, which is the usual case: the door leaf // linked to the house. // // Plain LSL. No OSSL, nothing that phones home, nothing that only // works on this grid. // // Everything worth changing is in the block below. The rest is // commented so you can read why it does what it does. // ===================================================================== // --------------------------------------------------------------------- // Settings // --------------------------------------------------------------------- // Which edge the door hangs on, measured in the door's own body, where // 0.5 is a face and 0.0 is the middle. <0.0, -0.5, 0.0> means the hinge // runs down the -Y edge. If your door swings around its middle like a // revolving door, this is the line you got wrong. vector HINGE = <0.0, -0.5, 0.0>; // How far it swings, in degrees. A negative number swings it the other // way, which is usually quicker than moving the hinge to the far edge. float ANGLE = 90.0; // The axis it turns around, in the door's own body. <0.0, 0.0, 1.0> is // upright and is what a normal door wants. Change it for a hatch or a // drawbridge. vector AXIS = <0.0, 0.0, 1.0>; // How long the swing takes, in seconds. 0.0 snaps it open with no // animation at all. float SWING_TIME = 0.7; // Seconds it stays open before it closes itself. 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 the name 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 swing smoother, because the extra positions // never reach anyone's viewer. It only costs the region work. If your // swing 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-swing, ignore further touches vector gShutPos; // where it sits when closed rotation gShutRot; // how it sits when closed vector gHinge; // the hinge point, in the door's own body // Put the door at some fraction of its swing. 0.0 is shut, 1.0 is // fully open. place(float f) { rotation turn = llAxisAngle2Rot(AXIS, ANGLE * DEG_TO_RAD * f); rotation r = turn * gShutRot; // The hinge is a fixed point of the door: it has to end up in the // same place before and after the turn. Everything else follows // from that one sentence. // // shutPos + hinge * shutRot == newPos + hinge * newRot // // Solved for the centre of the prim, which is what we can actually // set. Doing it this way means the door cannot drift, however many // times it swings, because every position is computed from the // closed one and never from the last step. vector p = gShutPos + gHinge * gShutRot - gHinge * r; llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POS_LOCAL, p, PRIM_ROT_LOCAL, r]); } swing(integer open) { gMoving = TRUE; integer steps = (integer)(SWING_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. Without this last line a // door left on the second-to-last step would sit a degree ajar, and // that gap grows every time you change SWING_TIME. 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) { // Said to the owner only. A visitor cannot fix this 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 is // standing 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); // The hinge setting is given in fractions of the door, so that // the same numbers keep working when you resize it. Turned into // real metres here, once. vector s = llGetScale(); gHinge = ; 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); swing(FALSE); return; } play(SOUND_OPEN); swing(TRUE); if (AUTO_CLOSE > 0.0) llSetTimerEvent(AUTO_CLOSE); } timer() { llSetTimerEvent(0.0); if (!gOpen) return; play(SOUND_SHUT); swing(FALSE); } on_rez(integer start) { // A rezzed copy is standing somewhere new, so its idea of shut // is worthless. Learn it again. llResetScript(); } changed(integer what) { // Linking or unlinking changes what a local position even means, // so the door has to measure itself again. Only when it is shut // and still, though: reset it mid-swing and it would take its // half-open position for the shut one and never close properly. if (what & CHANGED_LINK) { if (!gOpen && !gMoving) llResetScript(); } } }