User:Yumi Murakami/Bijocam And Other Camera Scripts

From Second Life Wiki
< User:Yumi Murakami
Revision as of 11:12, 7 August 2013 by Yumi Murakami (talk | contribs) (Created page with "This is a camera control system which allows you to place camera objects in the world and view them by clicking on them. You can also cycle between cameras using a HUD and create…")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This is a camera control system which allows you to place camera objects in the world and view them by clicking on them. You can also cycle between cameras using a HUD and create a "game-style" environment where entering certain areas gives certain camera views.

This was on sale as the BijoCam Constructions System for a while, and did surprisingly well (for something I originally developed while I was just experimenting with the then-new camera commands in LSL!) but since I found the vendor network for BijoCam collapsed ages ago and I expect people are moved on to much more wonderful things anyway, here's the source code for the BijoCam. Feel free to use and have fun with it.

Interface Object

This should be placed in an object to be worn on the HUD. It should be a 3-prim object where the 3 prims are buttons, labeled in turn: "Next Camera", "Release Camera", and "Revoke Permission" and in that order in the linkset. In the original they were just rectangles with very plain textures.

A little note about the protocol used here: the first version of BijoCam went through a full permission request-response cycle every time a camera was activated, which was silly, because I was silly at the time. The current version handles all permissions on the client. This is why the redundant response to the "request" message is still in this script - for backward compatibility with older cameras.

<lsl>

key known_owner = NULL_KEY; list permslist = []; list banlist = []; integer reqinprogress = FALSE; key reqkey; key reqob; key responsible; integer listenhandle;

integer multicamactive; list camlist; integer camid; integer multicamtransit;

hide() {

   llSetAlpha(0.0,ALL_SIDES);
   llSetLinkAlpha(2,0.0,ALL_SIDES);
   llSetLinkAlpha(3,0.0,ALL_SIDES);

}

show() {

   if (multicamactive) { llSetAlpha(1.0,ALL_SIDES);} else { llSetAlpha(0.0,ALL_SIDES);}
   llSetLinkAlpha(2,1.0,ALL_SIDES);
   llSetLinkAlpha(3,1.0,ALL_SIDES);

}


default {

  attach(key whoto) {
      if (whoto != NULL_KEY) {
          if (known_owner != llGetOwner()) {
              permslist = [];
              banlist = [];
              known_owner = llGetOwner();
          }
          reqinprogress = FALSE;
          multicamactive = FALSE;
          multicamtransit = FALSE;
          hide();
          llRequestPermissions(llGetOwner(),PERMISSION_CONTROL_CAMERA);
          llListen(202,"",llGetOwner(),"");
          llOwnerSay("The BijoCam Interface is a free item.  If you have been charged for it, please IM Yumi Murakami.");
          
       //   if (llGetInventoryNumber(INVENTORY_SCRIPT) > 1) {
       //       llOwnerSay("Warning: This BijoCam Interface has had extra scripts added to it.");
       //       llOwnerSay("Please check that you trust these scripts before proceeding.");
       //   }
      } else {
          llRequestPermissions(llGetOwner(),0);
      }
  }
  
  changed(integer change) {
      if ((change & CHANGED_REGION) || (change & CHANGED_TELEPORT)) {
          multicamactive = FALSE;
          llClearCameraParams();
          hide();
       }
   }
      
  run_time_permissions(integer perms) {
      if (perms & PERMISSION_CONTROL_CAMERA) {
          llListen(-13542,"",NULL_KEY,"");
      } else {
          if (llGetAttached() != 0) llOwnerSay("Unable to get permission to control camera.");
      }
  }
  
  listen(integer channel, string name, key speaker, string message) {
      if (channel == 202) {
          if (llSubStringIndex(message,",") == -1) return;
          camlist = llParseString2List(message,[","],[]);
          camid = 0;
          multicamactive = TRUE;
          multicamtransit = TRUE;
          llSay(202,llList2String(camlist,0));
          return;
      }
      
      if (channel == -9897) {
          llListenRemove(listenhandle);
          reqinprogress = FALSE;
          if (message == "Yes") {
              permslist += reqkey;
              llShout(-13542,(string)reqob+"allow");
          } else {
              if (message == "Never") {
                  banlist += reqkey;
                  llShout(-13542,(string)reqob+"deny");
              } else 
                  llShout(-13542,(string)reqob+"deny");
          }
          return;
      }
      
      key targetKey = llGetSubString(message,0,35);
      string command = llGetSubString(message,36,-1);
      
      key remowner = llGetOwnerKey(speaker);
      if (targetKey != llGetOwner()) return;
    
      
      if (llListFindList(permslist,[remowner]) == -1) {
          if (llListFindList(banlist,[remowner]) != -1) {
              llShout(-13542,(string)speaker+"deny");
              return;
          }
          if (command == "request") {
              if (reqinprogress) {
                  llShout(-13542,(string)speaker+"busy");
                  return;
              }
              reqinprogress = TRUE;
              listenhandle = llListen(-9897,"",llGetOwner(),"");
              reqkey = remowner;
              reqob = speaker;
              llRequestAgentData(reqkey,DATA_NAME);
              return;
          } else return;
      }
      
      if (command == "request") {
          llShout(-13542,(string)speaker+"allow");
          return;
      }
      
      if (command == "release") {
          multicamactive = FALSE;
          llClearCameraParams();
          hide();
          return;
      }
      
      if (llGetSubString(command,0,5) == "remote") {
          string destination = llGetSubString(command,6,-1);
          if (llSubStringIndex(destination,",") == -1) {
              llShout(202,destination);
              return;
         } else { 
              camlist = llParseString2List(destination,[","],[]);
              camid = 0;
              multicamactive = TRUE;
              multicamtransit = TRUE;
              llShout(202,llList2String(camlist,0));
              return;
           }
       }
      
      if (llGetSubString(command,0,9) == "fastremote") {
          string destination = llGetSubString(command,10,-1);
          if (llSubStringIndex(destination,",") == -1) {
              llShout(202,destination + "|fast");
              return;
         } else { 
              camlist = llParseString2List(destination,[","],[]);
              camid = 0;
              multicamactive = TRUE;
              multicamtransit = TRUE;
              llShout(202,llList2String(camlist,0)+"|fast");
              return;
           }
       }
       
      list camerastuff = llParseString2List(command,["|"],[]);
      if (multicamactive) {
          if (multicamtransit) multicamtransit = FALSE; else multicamactive = FALSE;
      }
      
      responsible = speaker;
      list castedcamerastuff = [12,1,21,1,22,1,13] + (vector)(llList2String(camerastuff,0)) + [17] + (vector)(llList2String(camerastuff,1));
      
      llSetCameraParams(castedcamerastuff);
      show();
          
  }
   
  touch_start(integer touchers) {
      integer which = llDetectedLinkNumber(0);
      if (which == 1) {
          if (!multicamactive) return;
          camid++;
          if (camid >= llGetListLength(camlist)) camid = 0;
          multicamtransit = TRUE;
          llShout(202,llList2String(camlist,camid)+"|fast");
          return;
      }
      if (which == 2) {
          llClearCameraParams();
          hide();
          return;
      }
      if (which == 3) {
          llClearCameraParams();
          hide();
          integer tloc = llListFindList(permslist,[responsible]);
          permslist = llDeleteSubList(permslist,tloc,tloc);
          return;
      }
   }
   
  dataserver(key qid, string data) {
      llDialog(llGetOwner(),"Do you want to allow objects owned by " + data + " to control your camera?",["Yes","No","Never"],-9897);
  }
                  

} </lsl>

Camera Object

These cameras can be rezzed and positioned and activated once you are wearing the interface. The original prim was just a simple pyramid.

<lsl>


setCameraDriver(key target) {

   //llShout(-13542, (string)(target) + "12|1|21|1|22|1|13|" + (string)llGetPos() + "|17|" +
   //        (string) (llGetPos() + llRot2Up(llGetRot())  ));
   llShout(-13542, (string)(target) + (string)llGetPos() + "|" + (string)(llGetPos() + llRot2Up(llGetRot())));
   

}

integer dialoglistenhandle; integer oklistenhandle; integer status = 0; vector cameraloc; rotation camerarot; string obname = "null"; string ownername; integer private = FALSE;

adjusted() {

   status = 0;
   cameraloc = llGetCameraPos();
   camerarot = llGetCameraRot();
   llReleaseControls();
   llSetRot(llEuler2Rot(<0,90*DEG_TO_RAD,0>) * camerarot);
   while (llVecDist(llGetPos(), cameraloc) > 0.001) llSetPos(cameraloc);
   if (!llOverMyLand(llGetKey())) {
       llDialog(llGetOwner(),"Warning!  The camera is over land you don't own.  If it's land you don't have permission to build on, the camera could be returned.  Please re-adjust the camera if you don't intend this.",[],1);
   }

}


default {

   state_entry() {
       cameraloc = llGetPos();
       camerarot = llGetRot();
       ownername = llKey2Name(llGetOwner());
       llListen(-13542,"","","");
       llListen(202,"","","");
       status = 0;
   } 
   
   on_rez(integer param) {
       llResetScript();
   }
   
   touch_start(integer total_number)
   {
       if (llDetectedKey(0) == llGetOwner()) {
           dialoglistenhandle = llListen(-13999,"",llGetOwner(),"");
           llDialog(llGetOwner(),"Please choose an option.",["Public","-","-","Hide","Show","Private","View","Adjust","Name"],-13999);
       } else {
           if ((private) && (llDetectedKey(0) != llGetOwner())) return;
           llShout(-13542, (string)(llDetectedKey(0)) + "request");
       }
   }
   
   listen(integer channel, string name, key speaker, string message) {
       if (channel == -13542) {
           key targetKey = llGetSubString(message,0,35);
           string command = llGetSubString(message,36,-1);
           if (targetKey != llGetKey()) return;
           if (command == "allow") {
               // Speaker was their attachment so..
               setCameraDriver(llGetOwnerKey(speaker));
               return;
           }
       }
       if (channel == 202) {
           if ((status == 1) && (message == "ok") && (speaker == llGetOwner())) {
               adjusted(); 
           }
           if ((status == 2) && (speaker == llGetOwner())) {
               obname = llToLower(message);
               status=0;
               llOwnerSay("Camera renamed: " + obname);
               return;
           }
           list bits = llParseString2List(message,[" "],[]);
           string tname = llToLower(llList2String(bits,0));
           integer fast = FALSE;
           if (llGetSubString(tname,-5,-1) == "|fast") {
               tname = llGetSubString(tname,0,-6);
               fast = TRUE;
           }   
           string lcslname = llToLower(llGetObjectName());
           if (  ( (obname != "null") && (obname == tname) ) ||
                 ( (lcslname != "camera") && (lcslname == tname) )) {
               if (llGetListLength(bits) == 1) {
                   if ((private) && (llGetOwnerKey(speaker) != llGetOwner())) return;
                   if (fast) setCameraDriver(llGetOwnerKey(speaker));
                   else llShout(-13542, (string)(llGetOwnerKey(speaker)) + "request");
                   return;
               }   
               string command = llToLower(llList2String(bits,1));
               if (command == "view") {
                   if ((private) && (llGetOwnerKey(speaker) != llGetOwner())) return;
                   if (fast) setCameraDriver(llGetOwnerKey(speaker)); 
                   else llShout(-13542, (string)(llGetOwnerKey(speaker)) + "request");
                   return;
               }
               if (command == "name") {
                   if (speaker != llGetOwner()) return;
                   obname = llList2String(bits,2);
                   llOwnerSay("Camera renamed: " + obname);
                   return;
               }
               if (command == "show") {
                   if (speaker != llGetOwner()) return;
                   llSetAlpha(1.0,ALL_SIDES);
                   llSetTexture("5748decc-f629-461c-9a36-a35a221fe21f",ALL_SIDES);
                   llOwnerSay("(" + obname + ") Camero shown.");
                   return;
               }   
               if (command == "hide") {
                   if (speaker != llGetOwner()) return;
                   llSetAlpha(0.0,ALL_SIDES);
                   llSetTexture("5748decc-f629-461c-9a36-a35a221fe21f",ALL_SIDES);
                   llOwnerSay("(" + obname + ") Camera hidden.");
                   return;
               }
               if (command == "private") {
                   if (speaker != llGetOwner()) return;
                   private = TRUE;
                   llOwnerSay("(" + obname + ") Camera now private.");
                   return;
               }
               if (command == "public") {
                   if (speaker != llGetOwner()) return;
                   private = FALSE;
                   llOwnerSay("(" + obname + ") Camera now public.");
                   return;
               }
               if (command == "adjust") {
                   if (speaker != llGetOwner()) return;
                   llRequestPermissions(llGetOwner(),PERMISSION_TRACK_CAMERA | PERMISSION_TAKE_CONTROLS);
                   return;
               }    
               if (command == "kill") {
                   if (speaker != llGetOwner()) return;
                   llDie();
                   return;
               }
           }
           if (tname == "camera") {
               string command = llToLower(llList2String(bits,1));
               if (command == "report") {
                   string priv = "";
                   if (private) priv = "(Private)";
                   llSay(0,"(" + obname + ") [" + ownername + "] " + priv +": " + (string)(llGetPos()) + " " + (string)(llRot2Euler(llGetRot()) * RAD_TO_DEG));
                   return;
               }
               if (command == "cleanup") {
                   if (speaker != llGetOwner()) return;
                   if (obname != "null") return;
                   if (llToLower(llGetObjectName()) != "camera") return;
                   llDie();
                   return;
               }
           }
       }
   
       if (channel == -13999) {
           llListenRemove(dialoglistenhandle);
           if (message == "View") {
               llShout(-13542, (string)(speaker) + "request");
               return;
           }
           if (message == "Name") {
               llOwnerSay("Please say /202 followed by the new name for this camera.");
               status = 2;
               return;
           }
           if (message == "Hide") {
               if ((llToLower(llGetObjectName()) == "camera") && (obname == "null")) {
                   llOwnerSay("Please give the camera a name before hiding it.");
                   return;
               }
               llSetAlpha(0.0,ALL_SIDES);
               llSetTexture("5748decc-f629-461c-9a36-a35a221fe21f",ALL_SIDES);
               return;
           }
           if (message == "Show") {   
               llSetAlpha(1.0,ALL_SIDES);
               llSetTexture("5748decc-f629-461c-9a36-a35a221fe21f",ALL_SIDES);
               return;
           }        
           if (message == "Adjust") {
               llRequestPermissions(llGetOwner(),PERMISSION_TRACK_CAMERA | PERMISSION_TAKE_CONTROLS);
               return;
           }
           if (message == "Public") {
               llOwnerSay("Camera now public.");
               private = FALSE;
               return;
           }
           if (message == "Private") {
               llOwnerSay("Camera now private.");
               private = TRUE;
               return;
           }
           
       }
       
   }
   
   control(key id, integer held, integer change) {
       if (held & CONTROL_ML_LBUTTON) {
           adjusted();
       }    
   }
   
   run_time_permissions(integer perms) {
       if (perms & (PERMISSION_TAKE_CONTROLS | PERMISSION_TRACK_CAMERA)) {
           llTakeControls(CONTROL_ML_LBUTTON, 1, 0);
           status = 1;
           llDialog(llGetOwner(),"Please adjust your SL camera until it matches the view you want this camera to provide.  You can move your avatar, use Alt+look, or use Mouselook freely.  Once adjusted, say '/202 ok' in chat (if not in mouselook) or click the left mouse button (if in mouselook) to lock in the camera position and angle.  If you have moved a long way from the camera, you may need to shout (press CTRL+RETURN instead of RETURN)",[],1);
       } else {
           llOwnerSay("You must grant the requested permissions for camera adjustment to work.");
       }
   }         

} </lsl>

Sensor Field

This is used if you want to create a game-style moving camera. It detects an avie passing through it and sends a command to activate a nearby camera.

The older versions of BijoCam also had "permission fields" which requested camera permission as you entered an area. The newer versions don't need them and they were only included in the kits for backwards compatibility.

The prim for this needs to be a box of no thickness with one side red and one side blue. The size doesn't matter as long as the avie will pass through it at the point where you want the camera to change - I usually used a large doorway-style size, but I'm assured that tripwire size will work OK.

<lsl> string redside = ""; string blueside = ""; string obname = "new";

default {

   state_entry() {
       llVolumeDetect(TRUE);
       llListen(202,"",llGetOwner(),"");
       llListen(-13542,"",NULL_KEY,"");
   }
   on_rez(integer param) {
       llResetScript();
   }
   
   listen(integer channel, string name, key thekey, string message) {
       string slname = llToLower(llGetObjectName());
       list bits = llParseString2List(message,[" "],[]);
       string tname = llToLower(llList2String(bits,0));
       if ( ( (slname != "bijocam sensor field") && (tname == slname) ) ||
            ( (obname != "null") && (tname == obname) )) {
           string command = llList2String(bits,1);
           if (command == "redside") {
               redside = llList2String(bits,2);
               llOwnerSay("(" + obname + ") Red side command set to: " + redside);
               if (redside == "release") redside = "";
               return;
           }
           if (command == "blueside") {
               blueside = llList2String(bits,2);
               llOwnerSay("(" + obname + ") Blue side command set to: " + blueside);
               if (blueside == "release") blueside = "";
               return;
           }
           if (command == "name") {
               obname = llToLower(llList2String(bits,2));
               llOwnerSay("Field renamed: " + obname);
               return;
           }
           if (command == "hide") {
               llSetAlpha(0.0,ALL_SIDES);
               llSetTexture("f54a0c32-3cd1-d49a-5b4f-7b792bebc204",ALL_SIDES);
               llOwnerSay("(" + obname + ") Field hidden.");
               return;
           }
           if (command == "show") {
               llSetAlpha(0.6,ALL_SIDES);
               llSetTexture("5748decc-f629-461c-9a36-a35a221fe21f",ALL_SIDES);
               llOwnerSay("(" + obname + ") Field shown.");
               return;
           }   
           if (command == "kill") {
               llDie();
           }
       }
       
   }
   
       
   collision_end(integer num) {
       vector direction = llDetectedVel(0) / llGetRot();
       float x = direction.x;
       if (x < 0) {
           if (redside == "") {
               llShout(-13542,(string)(llDetectedKey(0)) + "release");
           } else {
               llShout(-13542,(string)(llDetectedKey(0)) + "fastremote" + redside);
           }
       } else {
           if (blueside == "") {
               llShout(-13542,(string)(llDetectedKey(0)) + "release");
           } else {
               llShout(-13542,(string)(llDetectedKey(0)) + "fastremote" + blueside);
           }
       }  
   }


} </lsl>


Free Camera Seat

Here's something that wasn't part of BijoCam, but might be interesting anyway. It's a script which allows you to create a seat where an avie can sit and cycle through a series of camera positions. The camera positions need to be stored in a notecard inside the object.


<lsl> integer listenHandle = -1;

list positions; list rots; key sitter; integer gotperms; integer currentCamera;

setCameraLoc() {

   if (!gotperms) return;
   vector savedPos = llList2Vector(positions,currentCamera);
   rotation savedRot = llList2Rot(rots,currentCamera);
   vector pos = llGetPos() + (savedPos * llGetRot());
   rotation rot = savedRot * llGetRot();
   llSetCameraParams([CAMERA_ACTIVE,TRUE,CAMERA_FOCUS_LOCKED,TRUE,CAMERA_POSITION_LOCKED,TRUE,CAMERA_POSITION,pos,CAMERA_FOCUS,pos + (<0.1,0,0>*rot)]);
   llRegionSay(-1928371,(string)pos + "|" + (string)rot);
   llSleep(2.0);

}

integer notecardState; integer notecardSize; integer notecardLine;

default {

   state_entry() {
       if (llGetInventoryType("Camera List") != INVENTORY_NOTECARD) {
         llOwnerSay("Notecard missing! Script can't start.  Click to try again.");
         return;
       }
       positions = [];
       rots = [];
       llSitTarget(<0,0,0.1>,ZERO_ROTATION);
       llOwnerSay("Setting up..");
       notecardState = 0;
       llGetNumberOfNotecardLines("Camera List");
   }
   
   changed(integer thechange) {
       if (llAvatarOnSitTarget() != NULL_KEY) {
           llUnSit(llAvatarOnSitTarget());
           llOwnerSay("Sorry, still setting up!..");
       }
   }
   
   
   dataserver(key request, string response) {
       if (notecardState == 0) {
           notecardSize = (integer)response;
           if (notecardSize == 0) {
               llOwnerSay("Notecard is empty!  Script can't start.  Click to try again.");
               return;
           }
           notecardState = 1;
           notecardLine = 0;
           llGetNotecardLine("Camera List",0);
           return;
       }
       if (notecardState == 1) {
           list bits = llParseString2List(response,["|"],[]);
           if (llGetListLength(bits) != 2) {
               llOwnerSay("Bad notecard line " + (string)(notecardLine+1)+"! (" + response + ")  Script can't start.  Click to try again.");
               return;
           }
           positions += [(vector)llList2String(bits,0)];
           rots += [(rotation)llList2String(bits,1)];
           notecardLine++;
           
           if (notecardLine >= (notecardSize-1)) {
               state run;
           } else {
               llGetNotecardLine("Camera List",notecardLine);
           }
       }
   }
   
   on_rez(integer junk) {
       llResetScript();
   }
   touch_start(integer count) {
       if (llDetectedKey(0) != llGetOwner()) return;
       llResetScript();
   }

}


state run {

   state_entry() {
       llOwnerSay("Ready!");
       llRegionSay(-1928371,(string)llGetPos() + "|" + (string)llGetRot());  
   }
   
   on_rez(integer junk) {
       llResetScript();
   }
   
   
   changed(integer thechange) {
       if (llAvatarOnSitTarget() != NULL_KEY) {
           sitter = llAvatarOnSitTarget();
           //if (sitter != llGetOwner()) {
           //    llOwnerSay("Sorry, only the owner can view the cameras.");
           //    llUnSit(sitter);
           //    sitter = NULL_KEY;
           //    return;
          // }
           gotperms = FALSE;
           llRequestPermissions(sitter,PERMISSION_TRACK_CAMERA | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);
       } else {
           sitter = NULL_KEY;     
           llReleaseControls();
           llRegionSay(-1928371,(string)llGetPos() + "|" + (string)llGetRot());
           gotperms = FALSE;
           if (listenHandle != -1) {
               llListenRemove(listenHandle);
               listenHandle = -1;
           }
       }
   }
   
   run_time_permissions(integer newperms) {
       if ((newperms & (PERMISSION_TRACK_CAMERA | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA)) > 0) {
           gotperms = TRUE;
           llTakeControls(CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT, TRUE, FALSE);
           currentCamera = 0;
           listenHandle = llListen(-1928372,"",NULL_KEY,"");
           if (llGetListLength(positions) == 0) {
               llSay(0,"No camera positions set up!");
           } else {
               setCameraLoc();
           }
           
       } else {
           llSay(0,"Error: SL denied me camera permission for some reason. Camera movement will not work.  Sit back down to try again.");
       }
   }
   
 
   
       
   listen(integer channel, string name, key speaker, string message) {
       if (llGetOwnerKey(speaker) != llGetOwner()) return;
       string target = llGetSubString(message,0,35);
       if (target != (string)sitter) return;
       string rest = llGetSubString(message,36,-1);
       if (rest == "+") {
           currentCamera++;
           if (currentCamera >= llGetListLength(positions)) currentCamera = 0;
           setCameraLoc();
       }
       if (rest == "-") {
           currentCamera--;
           if (currentCamera < 0) currentCamera = (llGetListLength(positions)-1);
           setCameraLoc();
       }
   }
   
   control(key junk, integer down, integer edge) {
       integer pressed = down & edge;
       if (pressed & CONTROL_ROT_LEFT) {
           currentCamera--;
           if (currentCamera < 0) currentCamera = (llGetListLength(positions)-1);
           setCameraLoc();
       }
       if (pressed & CONTROL_ROT_RIGHT) {
           currentCamera++;
           if (currentCamera >= llGetListLength(positions)) currentCamera = 0;
           setCameraLoc();
       }
   }
           

} </lsl>

The above script lets the sitter cycle through cameras by pressing the left and right arrows, but you can also allow them to cycle by pressing left and right on a HUD. But they don't need to wear the HUD! This script should go in a separate object with two prims for the left and right arrows. If it's present when the above script is used, it will position itself in the avie's camera view to look like a HUD even though it's an object in the world.

<lsl>


default {

   state_entry()
   {
       llListen(-1928371,"",NULL_KEY,"");
   }
   listen(integer channel, string name, key speaker, string message) {
       
       if (llGetOwnerKey(speaker) != llGetOwner()) return;
       list bits = llParseString2List(message,["|"],[]);
       vector loc = (vector)llList2String(bits,0);
       rotation rot = (rotation)llList2String(bits,1);
       loc = loc + (<0.15,0.0,-0.063> * rot);
       while (llVecDist(llGetPos(),loc) > 0.01) llSetPos(loc);  
       llSetRot(rot);
   }
   
   touch_start(integer count) {
       integer touched = llDetectedLinkNumber(0);
       if (touched == 1) {
           llRegionSay(-1928372,(string)llDetectedKey(0) + "-");
       }
       if (touched == 2) {
           llRegionSay(-1928372,(string)llDetectedKey(0) + "+");
       }
       
   }
       

} </lsl>