User:Yumi Murakami/Bijocam And Other Camera Scripts

From Second Life Wiki
< User:Yumi Murakami
Revision as of 10:56, 6 December 2022 by Gwyneth Llewelyn (talk | contribs) (Adding the nice LSL header (which it fully deserves!!) :))
(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.

Original Documentation Notecard

Hello, and thanks for purchasing the BijoCam Camera Control Kit! This document is intended to help you get started with the Bijocam System as quickly and easily as possible. It'll help to read through it before you start playing too much with the bits, though. :)

USING BIJOCAM FOR JUST YOURSELF

The FIRST thing to do is to take out the "BijoCam Interface" from the kit and WEAR it. You won't see anything at first - don't worry, it's there so that other parts of the kit can activate it. By default it attaches to "HUD bottom", but you can attach it somewhere else on your HUD if you prefer (it does need to be *somewhere* on your HUD though).

You can then begin setting up cameras by dropping them into the world.

VIEWING A CAMERA: To look through a camera, click on it and select "View", or, if you have given the camera a name (see below) you can view it by entering "/202 thename" or "/202 thename view" (replacing "thename" with the name you gave the camera, of course.) If it's the first time you've ever done this, you will be asked to give permission for your camera to be controlled, and then your view will switch to the camera. To release the camera, click the "release camera" button.

    • IMPORTANT NOTE: ** If you, the user, have moved the SL camera for some reason (for example, by using "Build", or by using Alt-look, or similar) then YOUR movement of the SL camera overrides BijoCam, so nothing will seem to happen when you click "View". Giving permission to control the camera makes no difference in this case - it is coded into SL that, if you have manually moved the camera to a particular place, nothing can override you. To fix this, click on the main SL Window and press ESCAPE - this cancels any user-applied camera movement and allows BijoCam to control the camera.

VIEWING SEVERAL CAMERAS: If you have given several cameras names, you can set up a cycle by entering "/202 name1,name2,name3" where name1, name2, name3 are the names of the cameras you set up. After giving permission (if necessary), you will be shown the view through the first camera, and you can then cycle between all the ones you named by clicking on the "next camera" button.

GIVING A CAMERA A NAME: To give a camera a name, you can either rename it using the SL editor, or click on the camera and choose "Name" from the menu. It will prompt you to say "/202 " followed by the name you want. If the camera already has a name, you can change it by saying "/202 oldname name newname". If you give the camera a name using BOTH of the methods described (ie, changing the name in SL and using the name option in the menu), then it will respond to both names. Camera names must not contain spaces, and are not sensitive to case.

CHOOSING WHERE A CAMERA POINTS: You can adjust the location and view of a camera in several different ways. The first is to just move and rotate it using the regular SL interface. The second way is to use the adjustment system. To do this, click on the camera and choose "Adjust" from the menu, or if you have given the camera a name, you can also type "/202 thename adjust".

Once you do this, the camera will ask for permission to track your camera. Grant the permission, and then adjust your SL camera view to match where you want the camera to be. You can use any method available in SL - moving your avatar, using mouselook, and using alt-look. Once your screen is showing the view you want the camera to have, say "/202 ok", or (if you're in mouselook and can't chat) click the left mouse button. The camera will move to the appropriate position.

HIDING AND SHOWING A CAMERA: To make a camera invisible, click on it and choose "Hide", or type "/202 thename hide". To make it visible again, click on it and choose "Show", or type "/202 thename show". You must give a camera a name before hiding it.

REMOVING A CAMERA: You can delete cameras in the normal way. In addition, if a camera has a name, "/202 thename kill" will delete the camera.

CLEANING UP CAMERAS: Saying "/202 camera cleanup" will delete all your cameras that don't have names. This is a way of cleaning up any cameras you've rezzed that have gotten lost before you could name them.

VIEWING CAMERAS: Saying "/202 camera report" will show a list of all cameras in the area, their names and locations. Note that it shows the locations of ALL cameras, not just your own.

USING BIJOCAM FOR OTHERS

As well as setting up BijoCams for yourself to use, you can provide them for others too. In order to use your cameras, visitors will need a BijoCam Interface. (However, once they have one, it will work for every BijoCam in SL.) The interface is freely copyable, so you can give it to others, and a "dispenser panel" is also included that you can place on your land to allow others to obtain interfaces.

CAMERAS: Anyone can click on a Camera object to look through it, or say "/202 thename" to look through it. If you don't want them to be able to do this, you can set the camera Private by clicking on it and selecting "Private" or entering "/202 thename private". (To undo this, click on the camera and select "Public", or say "/202 thename public".)

CAMERA REPORT: Anyone can enter "/202 camera report" to view the names and locations of all cameras in the vicinity. Hiding a camera, or making it private, will not prevent it from showing up in this report - this is deliberate to prevent eavesdropping.

USING SENSOR FIELDS

The sensor fields allow you to build an environment that behaves as a traditional 3D game - where, as the player explores, the camera moves to particular places determined by the builder. Effectively, the sensor field lets you change the user's view to look through a particular camera when they pass through it. Note that, as always with BijoCam, the user can always release your control over their camera or revoke permission for the camera to be controlled.

There are two types of field: sensor fields (red/blue), and permission fields (green). FIELDS CAN'T BE CLICKED ON (this prevents them creating confusing displays when they're invisible within your build) - all fields can ONLY be addressed by talking on channel 202, and thus they MUST be given names when they are created. Every field starts with the name "new" - so when you rez a field, before you do anything else, say "/202 new name somethingelse". Otherwise you'll wind up with a build full of fields called "new" that you can't tell apart.

Permission fields cause a camera control permission request to be sent to anyone walking through them. By themselves, they don't do anything to the camera. However, they're important because sensor fields DON'T ask permission to control the camera - they assume you already have permission, and if you don't, they won't work. So you MUST ensure that a user walks through a permission field before they walk through any sensor fields.

All fields respond to "/202 thename hide", "/202 thename show", "/202 thename name newname", and "/202 thename kill" in the same way that cameras do.

Permission fields don't require any extra setting up: just rez them, name them, put them in the appropriate place and hide them, and you're done. Sensor fields, however, have to be set up to specify which cameras the user's view should be transferred to when the user walks through them. You do this with the commands "/202 thename redside cameraname", and "/202 thename blueside cameraname". "/202 thename redside cameraname" will cause the user's view to be switched to the camera named "cameraname" when they walk through the field onto the red-coloured side. Specifying "/202 thename redside release" will cause the camera to be released when the user walks through the field in that direction. "/202 thename blueside cameraname" and "/202 thename blueside release" do the same for when the user walks through in the other direction, towards the blue-coloured side.

If you want to offer the user the choice of several cameras, you can do this in the same way you did when you were viewing them: "/202 thename redside name1,name2,name3,name4..". In this case, the user's view will start at name1, but they will have the "next camera" button available to switch between views.

TECHY STUFF

The script in the cameras can be removed and placed into other objects to cause them to act like cameras. The camera position is set to the centre of the camera object and the camera is pointed in the positive direction of the object's Z axis.

Fields use Volume Detect sensing to avoid lagging the server with a repeated llSensor call. They detect the Av as it *leaves* the field - so be careful if you make the field any deeper than it is to start with. Also notice that the red and blue sides are detected based on the object's local coordinate system, not based on the colours of the textures, so changing the colour of parts of the object won't change its sensor properties. Although the fields are quite large by default, in fact as long as some part of the avatar touches some part of the field the avatar will be detected - so if you want to shrink them down into "tripwires", you can do so.


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.

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);
   }
                   
}

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.

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.");
        }
    }         
}

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.

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);
            }
        }  
    }

                 
}


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.


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();
        }
    }
            
}

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.

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) + "+");
        }
        
    }
        
}