8882947c811b011d9d5300ff3d263925479e490f
[feisty_meow.git] / huffware / huffotronic_scripts / TL_Door_fredmod_v4.5.txt
1 
2 // fred huffhines mods:
3 //
4 // took away the universal skeleton key that was lodged in this script.
5 //
6 // stopped considering door's scale; this is not usually needed, plus we were blowing
7 //      past the SL limit on object names.
8 //
9 // moved to only storing a couple digits after the decimal point; this is another
10 //      crucial thing to limit the size of the object name.
11 //
12 // added a "toggle" command that behaves like touch, in that the door will be opened
13 //      or closed based on current state.
14 //
15 // made the sensor distance required before the door will listen to someone into a
16 //      configurable parameter, instead of the woefully tiny, hard-coded 5 meters.
17 //
18 // added debugging flag and switchable logging for debugging mode.
19 //
20 // *original license and author info below...*
21 //
22 // plus, timeless prototype said this about using the script in osgrid and elsewhere:
23 //      "hi, thanks for asking, yes you may use the door script in other grids."
24 //
25 // this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
26 // do not use it in objects without fully realizing you are implicitly accepting that license.
27 //
28 // more fred huffhines mods: (circa march 2012)
29 //   added PASS_COMMANDS flag, which can be used to pass along any commands we
30 // hear to anyone else on our same channel within the object.
31 //   added stifling of commands heard so they don't get re-sent, causing endless
32 // loops of door openings.
33
34
35 //------------------------------------------------------
36 // Timeless Linked Door Script by Timeless Prototype
37 //------------------------------------------------------
38 // The latest version of this script can always be found
39 // in the Library section of the wiki:
40 // http://www.secondlife.com/badgeo/
41 // This script is free to use, but whereever it is used
42 // the SCRIPT's permissions MUST be set to:
43 // [x] Next owner can modify
44 // [x] Next owner can copy
45 // [x] Next owner can transfer
46 // [x] Allow anyone to copy
47 // [x] Share with group
48
49 //------------------------------------------------------
50 // USAGE INSTRUCTIONS FOR EVERYDAY USE:
51 //------------------------------------------------------
52 // Say the following commands on channel 0:
53 // 'unlock'     - Unlocks all doors in range.
54 // 'lock'       - Locks all doors in range and allows
55 //                only the permitted users to open it.
56 // To open the door, either Touch it, Walk into it or
57 // say 'open' or say 'close'.
58
59 //------------------------------------------------------
60 // USAGE INSTRUCTIONS FOR BUILDERS:
61 //------------------------------------------------------
62 // 1. Copy and paste this script into the door prim and
63 //    change the settings (see further down).
64 // 2. The door prim must be linked to at least one other
65 //    prim (could be linked to the house for example).
66 // 3. The door prim MUST NOT be the root prim.
67 // 4. Use Edit Linked Parts to move, rotate and size the
68 //    door prim for the closed state.
69 // 5. When ready, stand close to the door and say
70 //    '/door closed' (this records the closed door
71 //    position, rotation and size to the object's
72 //    name and description).
73 // 6. Use the Edit Linked parts to move, rotate and size
74 //    the door prim for the opened state.
75 // 7. When ready, stand close to the door and say
76 //    '/door opened' (this records the opened door
77 //    position, rotation and size).
78 // 8. Once recorded it will not accept these commands
79 //    again. If you do need to redo the settings then
80 //    delete the Name and Description of the door prim
81 //    (these are where the position information is
82 //    stored), and then follow the steps above again.
83 //    Note: deleting the object name won't save, so set
84 //    the object name to 'Object' to reset the object
85 //    name.
86
87 //------------------------------------------------------
88 // Change these settings to suit your needs.
89 //------------------------------------------------------
90 // To mute any/all of the sounds set the sound string(s)
91 // to "" (empty string).
92 // To get the UUID of a sound, right click on the sound
93 // in your inventory and choose "Copy Asset UUID", then
94 // paste the UUID in here.
95 string      doorOpenSound       = "Door open";
96 string      doorCloseSound      = "Door close";
97 string      confirmedSound      = "69743cb2-e509-ed4d-4e52-e697dc13d7ac";
98 string      accessDeniedSound   = "58da0f9f-42e5-8a8f-ee51-4fac6c247c98";
99 string      doorBellSound       = ""; // Setting to empty stops door announcements too.
100 float       autoCloseTime       = 120.0; // 0 seconds to disable auto close.
101 integer     allowGroupToo       = TRUE; // Set to FALSE to disallow same group access to door.
102 list        allowedAgentUUIDs   = []; // Comma-separated, quoted list of avatar UUIDs who are allowed access to this door.
103 integer     listenChannel       = 100008;
104 float       RESPONSE_DISTANCE   = 120.0;  // how far to allow a command from users with permission.
105 integer     DEBUGGING           = FALSE;
106 integer     PASS_COMMANDS       = TRUE;
107     // if true, then we will order other doors to open when we do.
108     // we use the listenChannel as the id to pass commands on, so that only the doors listening to
109     // the same place will hear our commands.
110 integer     command_is_a_response  = FALSE;
111     // if this is true, then the door open and close must not re-echo their actions.
112
113 //------------------------------------------------------
114 // Leave the rest of the settings alone, these are
115 // handled by the script itself.
116 //------------------------------------------------------
117 integer     isLocked            = FALSE; // Only when the door is locked do the permissions apply.
118 integer     isOpen              = TRUE;
119 vector      openPos             = ZERO_VECTOR;
120 rotation    openRot             = ZERO_ROTATION;
121 vector      closedPos           = ZERO_VECTOR;
122 rotation    closedRot           = ZERO_ROTATION;
123 key         openerKey           = NULL_KEY;
124 key         closerKey           = NULL_KEY;
125 integer     isSetup             = FALSE;
126 integer     listenHandle        = 0;
127 string      avatarName          = "";
128
129 // zooms the sub-prim to a new rotation and position.
130 jump_to_position(vector position, rotation new_rot)
131 {
132     list config_blast = [ 
133         PRIM_SIZE, llGetScale(), ///????
134         // first jump away from where we started, trying to get past an opensim bug.
135 //        PRIM_POSITION, ZERO_VECTOR,
136 //        PRIM_POSITION, ZERO_VECTOR,
137 //        PRIM_POSITION, ZERO_VECTOR,
138 //        PRIM_POSITION, ZERO_VECTOR,
139 //        PRIM_POSITION, ZERO_VECTOR,
140         PRIM_ROTATION, ZERO_ROTATION * new_rot / llGetRootRotation(),
141 //        PRIM_POSITION, position,
142 //        PRIM_POSITION, position,
143 //        PRIM_POSITION, position,
144 //        PRIM_POSITION, position,
145 //        PRIM_POSITION, position,
146         PRIM_POSITION, position
147         ];
148     llSetLinkPrimitiveParams(llGetLinkNumber(), config_blast);
149 if (DEBUGGING) llOwnerSay("want pos=" + (string)position + ", got=" + (string)llGetLocalPos()
150 + ", and want rot=" + (string)new_rot + ", got=" + (string)llGetLocalRot());
151 }
152
153 mySayName(integer channel, string objectName, string message)
154 {
155     string name = llGetObjectName();
156     llSetObjectName(objectName);
157     llSay(0, "/me " + message);
158     llSetObjectName(name);
159 }
160
161 mySay(integer channel, string message)
162 {
163     string name = llGetObjectName();
164     llSetObjectName("Door");
165     llSay(0, message);
166     llSetObjectName(name);
167 }
168
169 myOwnerSay(string message)
170 {
171     string name = llGetObjectName();
172     llSetObjectName("Door");
173     llOwnerSay(message);
174     llSetObjectName(name);
175 }
176
177 mySoundConfirmed()
178 {
179     if (confirmedSound != "")
180     {
181         llTriggerSound(confirmedSound, 1.0);
182     }
183 }
184
185 mySoundAccessDenied()
186 {
187     if (accessDeniedSound != "")
188     {
189         llTriggerSound(accessDeniedSound, 1.0);
190     }
191 }
192
193 myGetDoorParams()
194 {
195     isSetup = FALSE;
196     if (llSubStringIndex(llGetObjectDesc(), "D;") == 0 && llSubStringIndex(llGetObjectName(), "D;") == 0)
197     {
198         list nameWords = llParseString2List(llGetObjectName(), [";"], []);
199         list descWords = llParseString2List(llGetObjectDesc(), [";"], []);
200         if (llGetListLength(nameWords) != 3 || llGetListLength(descWords) != 3)
201         {
202             myOwnerSay("The door prim's name and/or description has invalid syntax and/or number of parameters. Delete the door prim's name and description and setup the door prim again.");
203         }
204         else
205         {
206             openPos = (vector)llList2String(nameWords, 1);
207             openRot = (rotation)llList2String(nameWords, 2);
208             closedPos = (vector)llList2String(descWords, 1);
209             closedRot = (rotation)llList2String(descWords, 2);
210             isSetup = TRUE;
211         }
212 //llSay(0, "got open pos=" + (string)(openPos) + " rot=" + (string)(openRot));
213 //llSay(0, "got close pos=" + (string)(closedPos) + " rot=" + (string)(closedRot));
214         
215     }
216 }
217
218 // if open_state is true, the parms are for an open door.
219 mySetDoorParams(integer open_state, vector Pos, rotation Rot)
220 {
221     if (open_state) {
222         // parms for open state.
223         llSetObjectName("D;" + vector_chop(Pos) + ";" + rotation_chop(Rot));
224     } else {
225         // parms for closed state.
226         llSetObjectDesc("D;" + vector_chop(Pos) + ";" + rotation_chop(Rot));
227     }
228     isSetup = TRUE;
229 }
230
231 integer myPermissionCheck(key id)
232 {
233     integer hasPermission = FALSE;
234     if (isLocked == FALSE) {
235         if (DEBUGGING) llOwnerSay("perm--unlocked: okay");
236         hasPermission = TRUE;
237     } else if (llGetOwnerKey(id) == llGetOwner()) {
238         if (DEBUGGING) llOwnerSay("perm--is owner: okay");
239         hasPermission = TRUE;
240     } else if (allowGroupToo == TRUE && llSameGroup(id)) {
241         if (DEBUGGING) llOwnerSay("perm--same group: okay");
242         hasPermission = TRUE;
243     } else if (llListFindList(allowedAgentUUIDs, [(string)id]) != -1) {
244         if (DEBUGGING) llOwnerSay("perm--in list: okay");
245         hasPermission = TRUE;
246     } else {
247         if (DEBUGGING) llOwnerSay("perm--not found anywhere: bad perms");
248     }
249     return hasPermission;
250 }
251
252 myOpenDoor()
253 {
254     isOpen = FALSE;
255     myToggleDoor();
256 }
257
258 myCloseDoor()
259 {
260     isOpen = TRUE;
261     myToggleDoor();
262 }
263
264 myToggleDoor()
265 {
266     if (isSetup == FALSE)
267     {
268         myOwnerSay("The door prim has not been configured yet. Please read the usage instructions in the door script.");
269     }
270     else if (llGetLinkNumber() == 0 || llGetLinkNumber() == 1)
271     {
272         myOwnerSay("The door prim must be linked to at least one other prim and the door prim must not be the root prim");
273     }
274     else
275     {
276         isOpen = !isOpen;
277 if (DEBUGGING) llOwnerSay("door open state is now=" + (string)isOpen);
278         if (isOpen)
279         {
280 if (DEBUGGING) llOwnerSay("opening the door.");
281             if (doorBellSound != "")
282             {
283                 llTriggerSound(doorBellSound, 1.0);
284                 if (avatarName != "")
285                 {
286                     mySayName(0, avatarName, "is at the door.");
287                     avatarName = "";
288                 }
289             }
290             if (doorOpenSound != "")
291             {
292                 llTriggerSound(doorOpenSound, 1.0);
293             }
294             jump_to_position(openPos, openRot);
295 //            list config_blast = [ PRIM_POSITION, llGetLocalPos() + <4, 4, 4>,
296 //                PRIM_ROTATION, ZERO_ROTATION * openRot / llGetRootRotation(),
297 //                PRIM_POSITION, openPos
298 ////                PRIM_SIZE, llGetScale() 
299 //                ];
300 //            llSetPrimitiveParams(config_blast);
301 //if (DEBUGGING) llOwnerSay("want pos=" + (string)openPos + ", got=" + (string)llGetLocalPos()
302 //+ ", and want rot=" + (string)openRot + ", got=" + (string)llGetLocalRot());
303
304             if (PASS_COMMANDS && !command_is_a_response) {
305                 // Door API.
306                 llMessageLinked(LINK_SET, listenChannel, "cmd|door|open", llGetKey());
307             }
308             command_is_a_response = FALSE;  // took care of that one.
309         }
310         else
311         {
312 if (DEBUGGING) llOwnerSay("closing the door.");
313             if (doorCloseSound != "")
314             {
315                 llTriggerSound(doorCloseSound, 1.0);
316             }
317             jump_to_position(closedPos, closedRot);
318 //            list config_blast = [ 
319 //                PRIM_ROTATION, ZERO_ROTATION * closedRot / llGetRootRotation(),
320 //                PRIM_POSITION, closedPos
321 ////                PRIM_SIZE, llGetScale()
322 //                ];
323 //            llSetPrimitiveParams(config_blast);
324 //if (DEBUGGING) llOwnerSay("want pos=" + (string)closedPos + ", got=" + (string)llGetLocalPos()
325 //+ ", and want rot=" + (string)closedRot + ", got=" + (string)llGetLocalRot());
326             if (PASS_COMMANDS && !command_is_a_response) {
327                 // Door API.
328                 llMessageLinked(LINK_SET, listenChannel, "cmd|door|close", llGetKey());
329             }
330             command_is_a_response = FALSE;  // took care of that one.
331         }
332         
333         llSetTimerEvent(0.0);
334         if (isOpen == TRUE && autoCloseTime != 0.0)
335         {
336             llSetTimerEvent(autoCloseTime);
337         }
338     }
339 }
340
341 //////////////
342 // from hufflets...
343
344 // returns the index of the first occurrence of "pattern" inside
345 // the "full_string".  if it is not found, then a negative number is returned.
346 integer find_substring(string full_string, string pattern)
347 { return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
348
349 // returns text for a floating point number, but includes only
350 // three digits after the decimal point.
351 string float_chop(float to_show)
352 {
353     integer mant = llAbs(llRound(to_show * 1000.0) / 1000);
354     string neg_sign;
355     if (to_show < 0.0) neg_sign = "-";
356     string dec_s = (string)((llRound(to_show * 1000.0) - mant * 1000) / 1000.0);
357     dec_s = llGetSubString(llGetSubString(dec_s, find_substring(dec_s, ".") + 1, -1), 0, 2);
358     // strip off all trailing zeros.
359     while (llGetSubString(dec_s, -1, -1) == "0")
360         dec_s = llDeleteSubString(dec_s, -1, -1);
361     string to_return = neg_sign + (string)mant;
362     if (llStringLength(dec_s)) to_return += "." + dec_s;
363     return to_return;
364 }
365
366 // returns a prettier form for vector text, with chopped floats.
367 string vector_chop(vector to_show)
368 {
369     return "<" + float_chop(to_show.x) + ","
370         + float_chop(to_show.y) + ","
371         + float_chop(to_show.z) + ">";
372 }
373
374 // similarly, for a rotation.
375 string rotation_chop(rotation to_show)
376 {
377     return "<" + float_chop(to_show.x) + ","
378         + float_chop(to_show.y) + ","
379         + float_chop(to_show.z) + ","
380         + float_chop(to_show.s) + ">";
381 }
382
383 //////////////
384 // huffware script: auto-retire, by fred huffhines, version 2.5.
385 // distributed under BSD-like license.
386 //   !!  keep in mind that this code must be *copied* into another
387 //   !!  script that you wish to add auto-retirement capability to.
388 // when a script has auto_retire in it, it can be dropped into an
389 // object and the most recent version of the script will destroy
390 // all older versions.
391 //
392 // the version numbers are embedded into the script names themselves.
393 // the notation for versions uses a letter 'v', followed by two numbers
394 // in the form "major.minor".
395 // major and minor versions are implicitly considered as a floating point
396 // number that increases with each newer version of the script.  thus,
397 // "hazmap v0.1" might be the first script in the "hazmap" script continuum,
398 // and "hazmap v3.2" is a more recent version.
399 //
400 // example usage of the auto-retirement script:
401 //     default {
402 //         state_entry() {
403 //            auto_retire();  // make sure newest addition is only version of script.
404 //        }
405 //     }
406 // this script is partly based on the self-upgrading scripts from markov brodsky
407 // and jippen faddoul.
408 //////////////
409 auto_retire() {
410     string self = llGetScriptName();  // the name of this script.
411     list split = compute_basename_and_version(self);
412     if (llGetListLength(split) != 2) return;  // nothing to do for this script.
413     string basename = llList2String(split, 0);  // script name with no version attached.
414     string version_string = llList2String(split, 1);  // the version found.
415     integer posn;
416     // find any scripts that match the basename.  they are variants of this script.
417     for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
418 //log_it("invpo=" + (string)posn);
419         string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
420         if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
421             // found a basic match at least.
422             list inv_split = compute_basename_and_version(curr_script);
423             if (llGetListLength(inv_split) == 2) {
424                 // see if this script is more ancient.
425                 string inv_version_string = llList2String(inv_split, 1);  // the version found.
426                 // must make sure that the retiring script is completely the identical basename;
427                 // just matching in the front doesn't make it a relative.
428                 if ( (llList2String(inv_split, 0) == basename)
429                     && ((float)inv_version_string < (float)version_string) ) {
430                     // remove script with same name from inventory that has inferior version.
431                     llRemoveInventory(curr_script);
432                 }
433             }
434         }
435     }
436 }
437 //
438 // separates the base script name and version number.  used by auto_retire.
439 list compute_basename_and_version(string to_chop_up)
440 {
441     // minimum script name is 2 characters plus a version.
442     integer space_v_posn;
443     // find the last useful space and 'v' combo.
444     for (space_v_posn = llStringLength(to_chop_up) - 3;
445         (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
446         space_v_posn--) {
447         // look for space and v but do nothing else.
448 //log_it("pos=" + (string)space_v_posn);
449     }
450     if (space_v_posn < 2) return [];  // no space found.
451 //log_it("space v@" + (string)space_v_posn);
452     // now we zoom through the stuff after our beloved v character and find any evil
453     // space characters, which are most likely from SL having found a duplicate item
454     // name and not so helpfully renamed it for us.
455     integer indy;
456     for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
457 //log_it("indy=" + (string)space_v_posn);
458         if (llGetSubString(to_chop_up, indy, indy) == " ") {
459             // found one; zap it.  since we're going backwards we don't need to
460             // adjust the loop at all.
461             to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
462 //log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
463         }
464     }
465     string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
466     // ditch the space character for our numerical check.
467     string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
468     // strip out a 'v' if there is one.
469     if (llGetSubString(chop_suffix, 0, 0) == "v")
470         chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
471     // if valid floating point number and greater than zero, that works for our version.
472     string basename = to_chop_up;  // script name with no version attached.
473     if ((float)chop_suffix > 0.0) {
474         // this is a big success right here.
475         basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
476         return [ basename, chop_suffix ];
477     }
478     // seems like we found nothing useful.
479     return [];
480 }
481 //
482 //////////////
483
484 default
485 {
486     state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
487     on_rez(integer parm) { state rerun; }
488 }
489 state rerun { state_entry() { state default; } }
490
491 state real_default
492 {
493     state_entry()
494     {
495         auto_retire();
496         listenHandle = llListen(listenChannel, "", NULL_KEY, "");
497         myGetDoorParams();
498         myCloseDoor();
499     }
500     
501     on_rez(integer parm) { llResetScript(); }
502
503     touch_start(integer total_number)
504     {
505         command_is_a_response = FALSE;
506         if (myPermissionCheck(llDetectedKey(0)) == TRUE)
507         {
508             avatarName = llDetectedName(0);
509             myToggleDoor();
510         }
511         else
512         {
513             mySoundAccessDenied();
514         }
515     }
516     
517     timer()
518     {
519         myCloseDoor();
520     }
521     
522     link_message(integer sender_num, integer num, string str, key id)
523     {
524         // Door API. The API is here in case you want to create PIN entry keypads or whatever.
525         if (num == listenChannel) {
526             if (id == llGetKey()) return;  // don't listen to our own commands.
527             command_is_a_response = TRUE;
528             if (str == "cmd|door|open") myOpenDoor();
529             else if (str == "cmd|door|close") myCloseDoor();
530             else if (str == "cmd|door|discover")
531                 llMessageLinked(LINK_SET, listenChannel, "cmd|door|discovered|" + (string)llGetKey(),
532                     llGetKey());
533             else command_is_a_response = FALSE;
534 //hmmm: above protocol seems redundant, but sending back the original id (like this did before)
535 //      in the id field does not fit in with our usual schemes very well.
536         }
537     }
538     
539     listen(integer channel, string name, key id, string message)
540     {
541 //        if (DEBUGGING) llOwnerSay("heard: " + message);
542         command_is_a_response = FALSE;  // don't get involved with the link message checking.
543
544         // Performance note: it's quicker to compare the strings than to compare permissions each time anyone says anything on this channel.
545         if (message == "open")
546         {
547             if (myPermissionCheck(id) == TRUE)
548             {
549                 // Only open the door if the person is quite close to this door.
550                 openerKey = id;
551                 closerKey = NULL_KEY;
552                 avatarName = name;
553                 llSensor(name, id, AGENT, RESPONSE_DISTANCE, TWO_PI);
554             }
555             else
556             {
557                 mySoundAccessDenied();
558             }
559         }
560         else if (message == "close")
561         {
562             if (myPermissionCheck(id) == TRUE)
563             {
564                 openerKey = NULL_KEY;
565                 closerKey = id;
566                 avatarName = name;
567                 // Only close the door if the person is quite close to this door.
568                 llSensor(name, id, AGENT, RESPONSE_DISTANCE, TWO_PI);
569             }
570             else
571             {
572                 mySoundAccessDenied();
573             }
574         }
575         else if (message == "lock")
576         {
577             if (myPermissionCheck(id) == TRUE)
578             {
579                 isLocked = TRUE;
580                 mySoundConfirmed();
581             }
582             else
583             {
584                 mySoundAccessDenied();
585             }
586         }
587         else if (message == "unlock")
588         {
589             if (myPermissionCheck(id) == TRUE)
590             {
591                 isLocked = FALSE;
592                 mySoundConfirmed();
593             }
594             else
595             {
596                 mySoundAccessDenied();
597             }
598         }
599         else if (message == "toggle")
600         {
601             if (myPermissionCheck(id) == TRUE)
602             {
603                 avatarName = name;
604                 myToggleDoor();
605             }
606             else
607             {
608                 mySoundAccessDenied();
609             }
610         }
611         else if (message == "/door opened" && llSubStringIndex(llGetObjectName(), "D;") == -1)
612         {
613             if (llGetOwnerKey(id) == llGetOwner())
614             {
615                 mySoundConfirmed();
616                 openPos = llGetLocalPos();
617                 openRot = llGetLocalRot();
618                 isOpen = TRUE;
619                 mySetDoorParams(TRUE, openPos, openRot);
620 //llSay(0, "set open pos=" + (string)(openPos) + " rot=" + (string)(openRot));
621             }
622             else
623             {
624                 mySoundAccessDenied();
625             }
626         }
627         else if (message == "/door closed" && llSubStringIndex(llGetObjectDesc(), "D;") == -1)
628         {
629             if (llGetOwnerKey(id) == llGetOwner())
630             {
631                 mySoundConfirmed();
632                 closedPos = llGetLocalPos();
633                 closedRot = llGetLocalRot();
634                 isOpen = FALSE;
635                 mySetDoorParams(FALSE, closedPos, closedRot);
636 //llSay(0, "set close pos=" + (string)(closedPos) + " rot=" + (string)(closedRot));
637             }
638             else
639             {
640                 mySoundAccessDenied();
641             }
642         }
643     }
644     
645     sensor(integer num_detected)
646     {
647         if (openerKey != NULL_KEY)
648         {
649             integer i;
650             for (i = 0; i < num_detected; i++)
651             {
652                 if (llDetectedKey(i) == openerKey && myPermissionCheck(llDetectedKey(i)) == TRUE)
653                 {
654                     myOpenDoor();
655                 }
656             }
657             openerKey = NULL_KEY;
658         }
659         else
660         {
661             integer i;
662             for (i = 0; i < num_detected; i++)
663             {
664                 if (llDetectedKey(i) == closerKey && myPermissionCheck(llDetectedKey(i)) == TRUE)
665                 {
666                     myCloseDoor();
667                 }
668             }
669             closerKey = NULL_KEY;
670         }
671     }
672
673 //------------------------------------------------------
674 // Uncomment the following code if you particularly want
675 // collisions to affect the door state.    
676 //------------------------------------------------------
677
678 //    collision_start(integer num_detected)
679 //    {
680 //        integer i;
681 //        for (i = 0; i < num_detected; i++)
682 //        {
683 //            if (myPermissionCheck(llDetectedKey(i)) == TRUE)
684 //            {
685 //                avatarName = llDetectedName(i);
686 //                myOpenDoor();
687 //            }
688 //            else if (llDetectedType(i) & AGENT)
689 //            {
690 //                mySoundAccessDenied();
691 //            }
692 //        }
693 //    }
694
695 }
696