Jump to content

Wandering Soulstar

Resident
  • Posts

    421
  • Joined

  • Last visited

Posts posted by Wandering Soulstar

  1. My Elevator is finally finished and all the bugs (hopefully) out of the code. I have posted the full code in the library if anyone is interested (HERE). Thanks a million to everyone that helped in my questions while I was working on it. And if you'd like to come by and give it a try please do! You can find my home HERE

    • Like 3
  2. Components
    These are all very short and hopefully clear scripts
    Button - Button to call the elevator .. will change colors based on status and give relevant messages. One at the top and one at the bottom

    //[PHP]
    //
    //
    // *******************************************************************
    // button
    // 12/18
    // v1.0.0
    //
    // button to call the elevator .. will change colors based on status
    // and give relevant messages
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    
    //specific to the button used
    //prim that we change the color on
    string COLOR_PRIM = "colour prim";
    
    //face we change
    integer BUT_FACE = 3;
    //end specifics
    //......
    
    //modifiable based on preferences
    //colors
    vector RED = <1.000, 0.255, 0.212>;
    vector GREEN = <0.180, 0.800, 0.251>;
    vector YELLOW = <1.000, 0.863, 0.000>;
    vector GREY = <0.502, 0.502, 0.502>;
    
    //time for a flash
    float FLASH_TIME = 1.0;
    
    //channels
    integer CHAT_CH = 0;
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer MAIN_CH = -140100;
    //end modifiable
    //......
    
    //set on start
    //link to prim we set color on
    integer COLOR_LINK = 0;
    
    //will hold either up or down, set in obj desc
    string ME;
    
    //holds the help text, set in state entry
    string HELP_TEXT;
    //end start set
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    string EMPTY_STR = "";
    string FUNC_SEP = "|";
    string DATA_SEP = "^";
    
    string NEW_LINE = "\n";
    
    //functions
    //data requests to me and returns
    string F_REQ_STATUS = "req status";
    string F_STATUS = "status";
    string F_REQ_POSITION = "req posit";
    string F_POSITION = "posit";
    //my call to ~main
    string F_CALL = "call";
    
    //positions
    string POSIT_MOVING = "moving";
    
    //status
    string S_READY = "ready";
    string S_INUSE = "inuse";
    string S_INROUTE = "inroute";
    string S_DISABLED = "disab";
    
    //voice messages from users
    string HELP = "help";
    
    //End Constants
    //......
    
    //######
    //Global Variables
    //handle when listening for a help call
    integer gOpenChatHandle = 0;
    
    //current status
    string gCurState;
    
    //current position of the elevator
    string gCurPosit;
    
    //for the flash
    vector gColor;
    integer gBright;
    float gGlow;
    
    //counter used in timer for help
    float gCounter;
    
    //End Global Variables
    //......
    
    
    //######
    //Functions
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        MAIN_CH += ownerMod;
    }
    //End Functions
    //......
    
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();
           
            //get what button I am up/down
            ME = llGetObjectDesc();
    
            //gett he link to the prim we set the color on
            integer found = FALSE;
            integer numLinks = llGetNumberOfPrims();
            integer ctr  = 0;
            while (ctr < numLinks && !found)
            {
                ctr++;
                found = (llGetLinkName(ctr) == COLOR_PRIM);
                if (found){COLOR_LINK = ctr;}
            }
            //if we don't find it error will occur
    
            //set default state to disabled
            gCurState = S_DISABLED;
            gColor = GREY;
            gBright = FALSE;
            gGlow = 0.0;
            llSetLinkPrimitiveParamsFast(COLOR_LINK,[PRIM_COLOR, BUT_FACE, gColor, 1.0, PRIM_FULLBRIGHT, BUT_FACE, gBright, PRIM_GLOW, BUT_FACE, gGlow]);        
                 
            //colours will be
            //green when ready to be called, either empty at other posit, or at my posit
            //S_READY
            //red when being used either at the other location, or moving to the other location
            //S_INUSE
            //flashing yellow when moving to my location
            //S_INROUTE
            //non-bright/glow grey .. out of service
            //S_OUTSERVICE
    
            //Help Text
            HELP_TEXT = NEW_LINE + "When the elevator arrives you can enter, and should hold onto one of the side railings (select to sit)";
            HELP_TEXT += NEW_LINE + "Once seated all you need to do is say go in open chat";
            HELP_TEXT += NEW_LINE + NEW_LINE + "There is room in the elevator for 4 passengers to hold the rail.";
            HELP_TEXT += NEW_LINE + "Traveling with more could cause the elevator to fail, and in an case would be un comfortable for the extra passenger(s)";
            HELP_TEXT += NEW_LINE + NEW_LINE + "The button colors indicate the status of the elevator:";
            HELP_TEXT += NEW_LINE + "Green: Ready";
            HELP_TEXT += NEW_LINE + "Yellow(flashing): Inroute to this position";
            HELP_TEXT += NEW_LINE + "Red: Currently in use";
            HELP_TEXT += NEW_LINE + "Grey: Out-of Service. A technician will have it repaired in under an hour";
            HELP_TEXT += NEW_LINE + NEW_LINE + "If you have any additional questions please feel free to contact me Wanda(Wandering) Soulstar";
    
            //listen to the main channel
            llListen(MAIN_CH, EMPTY_STR, NULL_KEY, EMPTY_STR);
            
            //ask the elevator for status I can set my color
            llShout(MAIN_CH, F_REQ_STATUS);
            //and request the position
            llShout (MAIN_CH, F_REQ_POSITION);
            
        }
    
        listen(integer channel, string name, key id, string message)
        {
            if (channel == CHAT_CH)
            {
                //AV has requested help
                if (llToLower(llStringTrim(message, STRING_TRIM)) == HELP)
                {
                    //if we are not inroute we tuen off the timer
                    if (gCurState != S_INROUTE){llSetTimerEvent(0.0);}
                    
                    //stop listening for a help request
                    llListenRemove(gOpenChatHandle);
                    gOpenChatHandle = 0;
                    gCounter = 0;
    
                    //give help
                    llWhisper(CHAT_CH, HELP_TEXT);
                }
            }
            else if (channel == MAIN_CH)
            {
                list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]);
                string func = llList2String(params, 0);
                string newState = EMPTY_STR;
    
                if (func == F_STATUS)
                {
                    newState = llList2String(params, 1);
                }
                else if (func == F_POSITION)
                {
                    //here we only change the status if it is moving towards us
                    //params will be current position (up, dwn, moving) and if moving, the target (up, down)
                    params = llParseString2List(llList2String(params, 1), [DATA_SEP], [EMPTY_STR]);
                    gCurPosit = llList2String(params, 0);
                    
                    if (gCurPosit == POSIT_MOVING)
                    {
                        //if moving to me the it is in route
                        if (llList2String(params, 1) == ME)
                        {
                            newState = S_INROUTE;
                        }
                        else
                        {
                            newState = S_INUSE;
                        }
                    }
                }//end Position
    
                //if we have a status and it has changed .. see what we might need to do
                if (newState != EMPTY_STR && newState != gCurState)
                {
                    gCurState = newState;
                    //default values, only changed when disabled
                    gBright = TRUE;
                    gGlow = 0.1;
                    
                    //set the button color
                    if (gCurState == S_READY)
                    {
                        gColor = GREEN;
                    }
                    else if (gCurState == S_INUSE)
                    {
                        gColor = RED;
                    }
                    else if (gCurState == S_INROUTE)
                    {
                        gColor = YELLOW;                  
                    }
                    else if (gCurState == S_DISABLED)
                    {
                        gColor = GREY;
                        gBright = FALSE;
                        gGlow = 0.0;
                    }
    
                    llSetLinkPrimitiveParamsFast(COLOR_LINK,[PRIM_COLOR, BUT_FACE, gColor, 1.0, PRIM_FULLBRIGHT, BUT_FACE, gBright, PRIM_GLOW, BUT_FACE, gGlow] );
    
                    //if we are going to flash .. check to set timer
                    //if not chat handle we have no timer going
                    if (gCurState == S_INROUTE && gOpenChatHandle == 0)
                    {
                        llSetTimerEvent(FLASH_TIME);
                    }
                    //otherwise, unles we are listening for help, turn off any timer
                    else if (gOpenChatHandle == 0)
                    {
                        llSetTimerEvent(0.0);
                    }
                }   
            }//end main_ch  
        }
    
        touch_end(integer total_number)
        {
            string message = EMPTY_STR;
    
            //based on the current state give a message and do an actions
            if (gCurState == S_READY)
            {
                //just call the elevator
                //if it is here it will open the doors
                //if not will begin the journey to me
                llShout(MAIN_CH, F_CALL + FUNC_SEP + ME);
    
                //if it is not here already let them know it is coming            
                if (gCurPosit != ME)
                {
                    message = "Elevator has been called, it will be with you shortly";
                }            
            }
            else if (gCurState == S_INUSE)
            {
                message = "The elevator is currently in use. The button will turn green when it is ready, then you can call it.";
            }
            else if (gCurState == S_INROUTE)
            {
                message =  "The Elevator is on its way, please be patient.";
            }
            else if (gCurState == S_DISABLED)
            {
                message =  "The Elevator is currently out of service. A technician has been called.";
            }
    
            //send the message if we have one
            if (message != EMPTY_STR){llWhisper(CHAT_CH, message);}
    
            //if we have not offered help already, do so
            if (gOpenChatHandle == 0)
            {
                llWhisper(CHAT_CH, "If you would like help, just say help in chat. I'll be listening for 1 minute");
                gOpenChatHandle = llListen(CHAT_CH, EMPTY_STR, NULL_KEY, EMPTY_STR);
    
                //set a timer if we are not already in flash mode
                if (gCurState != S_INROUTE)
                {
                    //since flash mode means timer events need to happen every second, we will count the number of times
                    //we hit the event so that at 60 secs we can turn off the listen
                    gCounter = 0;
                    llSetTimerEvent(FLASH_TIME);
                }
            }
        }
    
        timer()
        {
            //increment to counter to stop listening
            if (gOpenChatHandle != 0){gCounter = gCounter + FLASH_TIME;}
    
            //if a minute has passed, stop listening
            if (gCounter >= 60.0)
            {
                llListenRemove(gOpenChatHandle);
                gOpenChatHandle = 0;
                gCounter = 0;
    
                //and if not in flash, stop the timer 
                if (gCurState != S_INROUTE){llSetTimerEvent(0.0);}
            }
    
            //if we are flashing, chage the state
            if (gCurState == S_INROUTE)
            {
                //for bright gets the opposite
                gBright = !gBright;
                //for glow by using Fabs we cycle between 0.1 and 0
                gGlow = llFabs(gGlow - 0.1);
                
                llSetLinkPrimitiveParamsFast(COLOR_LINK,[PRIM_FULLBRIGHT, BUT_FACE, gBright, PRIM_GLOW, BUT_FACE, gGlow]);
            }
        }
    }
    
    //[/PHP]


    Detector - Simple detection of if an av has entered or left the elevator. Set in an invisible prim the size of the elevator, one at the top and one at the bottom

    
    //[PHP]
    //
    //
    // *******************************************************************
    // detector
    // 12/18
    // v01.0.0
    //
    // simple detection of if an av has entered or left the elevator
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    
    //modifiable based on preferences
    //channels
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer MAIN_CH = -140100;
    //end modifiable
    //......
    
    //set on start
    //will hold either up or down, set in obj desc
    string ME;
    //range for scans .. based on a square elevator and so 1/2 the width
    float CHECK_RANGE;
    //end start set
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    string EMPTY_STR = "";
    string FUNC_SEP = "|";
    string DATA_SEP = "^";
    
    //functions
    //data requests to me and returns
    string F_POSITION = "posit";
    string F_REQ_POSITION = "req posit";
    //advise on AV movement
    string F_ENTER = "enter";
    string F_EXIT = "exit";
    //call to me and possible returns
    string F_PAX_UPDATE = "pax list";
    string F_NO_PAX = "empty";
    string F_CHECK_PAXS = "check paxs";
    
    //parameters
    string POSIT_MOVING = "moving";
    
    //End Constants
    //......
    
    //######
    //Global Variables
    //flag to respond to collision events
    integer gActive = FALSE;
    //End Global Variables
    //......
    
    //######
    //Functions
    
    //main function .. get the av keys and advise main
    advise_collisions(integer total_number, string action)
    {
        //get the keys of all avs that entered 
        integer x;
        list avs = [];
        for (x = 0; x < total_number; x++)
        {
            if (llDetectedType(x) & AGENT){avs = avs + [llDetectedKey(x)];}
        }
    
        //if we actual had any avs .. then we will advise main
        if (llGetListLength(avs) > 0)
        {
            string avKeys = llDumpList2String(avs, DATA_SEP);
            llWhisper(MAIN_CH, action + FUNC_SEP + avKeys);
        }
    }
    
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        MAIN_CH += ownerMod;
    }
    //End Functions
    //......
    
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();
                    
            //set volume detect so that prim is 'phantom'
            llVolumeDetect(TRUE);
    
            //will correspond to position (up/down)
            ME = llGetObjectDesc();
    
            //set the target for sit close center
            //this is so that the unsit does not send them flying
            llSitTarget(<0.0, 0.1, 0.0>, ZERO_ROTATION);
    
            //set my range for when I need to do a sensor .. 1/2 my x
            vector size = llGetScale();
            CHECK_RANGE = size.x * 0.5;
    
            //listen tto the main channel
            llListen(MAIN_CH, EMPTY_STR, NULL_KEY, EMPTY_STR);
    
            //Ask main for where the elevator .. if main does not respond, not on yet
            llShout(MAIN_CH, F_REQ_POSITION);
        }
    
        listen(integer channel, string name, key id, string message)
        {
            if (channel == MAIN_CH)
            {
    			//main advising where the elevator is
                list data = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]);
                string function = llList2String(data, 0);
                if (function == F_POSITION)
                {
                    //positoin will be up/down/moving
                    data = llParseString2List(llList2String(data, 1), [DATA_SEP], [EMPTY_STR]);
    
                    //set activate flag ... we care about collisions when the elevator is:
                    //  stoped in our position
                    //  moving to our position (this is to get a count on arrival)
                    //this has passed position and when moving .. target
                    string curPosit = llList2String(data, 0);
                    gActive = (curPosit == ME);
                    if (!gActive && curPosit == POSIT_MOVING){gActive = (llList2String(data, 1) == ME);}  
                }
                //main asking for a sensor check for pax
                //certain actions will not trigger an exit (sit outside, LM TP out, map click TP out, LogOut)
                else if (function == F_CHECK_PAXS)
                {
                    llSensor("", NULL_KEY, AGENT, CHECK_RANGE, PI);
                }
            }
        }
        
        changed(integer change)
        {
            //do not want anyone sitting on the detector by accident
            if(change & CHANGED_LINK)
            {
                integer links = 0;
                if(llGetObjectPrimCount(llGetKey()) < (links = llGetNumberOfPrims()))
                {
                    llUnSit(llGetLinkKey(links));
                    llWhisper(0, "You need to sit by clicking on the handrail. You need to cam closer to the rail");
                }
            }
        }
        
        collision_start(integer total_number)
        {
            //pass if someone has come into the elevator/detector
            if (gActive){advise_collisions(total_number, F_ENTER);}    
        }
    
        collision_end(integer total_number)
        {
            //pass if someone has left the elevator/detector
            if (gActive){advise_collisions(total_number, F_EXIT);}         
        }
        sensor(integer total_number)
        {
            //pass the avs that were detected
            if (gActive){advise_collisions(total_number, F_PAX_UPDATE);}
        }
        no_sensor()
        {
            //tell main we are empty
            llWhisper(MAIN_CH, F_NO_PAX);
        }
    }
    
    //[/PHP]


    Bridge - This moves the bridge at the top of the elevator 'shaft'. This was needed because I had already built the rest before I put in the elevator, and at the top there was a gap between the landing and the elevator.

    //[PHP]
    //
    //
    // *******************************************************************
    // bridge
    // 12/18
    // v1.0.0
    //
    // this moves the bridge at the top of the elevator 'shaft' this is
    // because there is a gap there and would not want people to fall. 
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    
    //instance specific to the instalation
    //in world positions of the bridge
    vector END_POS_OPEN = <13.35265, 209.81580, 111.35027>;
    vector END_POS_CLOSED = <13.35265, 210.15483, 111.35027>;
    //end instance
    //......
    
    //modifiable based on preferences
    //how much I move each loop .. assumes y axis, base for open
    float CHG_AMOUNT = -0.1;
    
    //channels
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer MAIN_CH = -140100;
    integer BRIDGE_CH = -140120;
    //end modifiable
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    string FUNC_SEP = "|";
    string EMPTY_STR = "";
    
    //functions
    //call to me and the return
    string F_MOVE = "move";
    string F_READY = "ready";
    
    //positions
    string POSIT_OPEN = "open";
    string POSIT_CLOSED = "closed";
    
    //End Constants
    //......
    
    //######
    //Global Variables
    //my position POSIT_OPEN or POSIT_CLOSED
    string gCurPosit;
    //End Global Variables
    //......
    
    //######
    //Functions
    
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        MAIN_CH += ownerMod;
        BRIDGE_CH += ownerMod;
    }
    //End Functions
    //......
    
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();
            
            //default expected position is closed
            gCurPosit = POSIT_CLOSED;
            llSetPos(END_POS_CLOSED);
            
            //listen on my channel
            llListen(BRIDGE_CH, EMPTY_STR, NULL_KEY, EMPTY_STR);
        }
        
        listen(integer channel, string name, key id, string message)
        {
            if (channel == BRIDGE_CH)
            {
                list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]);
                if (llList2String(params, 0) == F_MOVE)
                {
                    //move the bridge to a new position
                    //second param will be which way we are moving: POSIT_OPEN or POSIT_CLOSED
                    string target = llList2String(params,1);
                    //only proceeed if a target was passed
                    if (target != EMPTY_STR)
                    {
                        //defualts are to open
                        float chg = CHG_AMOUNT;
                        vector endPos = END_POS_OPEN;
    
                        if (target == POSIT_CLOSED)
                        {
                            // the bridge moves along y axis, so just flip the change factor
                            chg *= -1;
                            endPos = END_POS_CLOSED;
                        }
    
                        //flag to exit .. check to see if for some reason we are already at the target (should not happen)
                        //if so then we'll just finish
                        integer exit = (gCurPosit == target);
                        //get where I currently am
                        vector curPos = llGetPos();
                        //move
                        while (!exit)
                        {
                            //next position
                            curPos.y += chg;
    
                            //check if we have moved past the end
                            if ((target == POSIT_CLOSED && curPos.y > endPos.y)||(target == POSIT_OPEN && curPos.y < endPos.y))
                            {
                                curPos = endPos;
                            }
                            //set new position and check if we are done
                            llSetPos(curPos);
                            exit = (curPos == endPos);
                        }
                        gCurPosit = target;
                    }
                   
                    //advise main we are done
                    llWhisper(MAIN_CH, F_READY);
                }
            } 
        }
    }
    
    //[/PHP]


    Boundary - Will sit in prims along the parcel boundary to detect when a catastrophically failed elevator, in physical state, is about to tumble off the parcel and stop it

    //[PHP]
    //
    //
    // *******************************************************************
    // boundary
    // 12/18
    // v1.0.0
    //
    // will sit in prims along the parcel boundary to detect when a
    // catastrophically failed elevator, in physical stat, is about
    // to tumble off the parcel
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    //specific to the elevator used
    //name of the elevator .. stored in the prim
    string ELEVATOR = "elevator";
    //end specifics
    //......
    
    //modifiable based on preferences
    //channel
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer BOUNDARY_CH = -140140;
    //end modifiable
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    //function
    //obnly calll I make
    string F_HIT_BOUNDARY = "hit";
    
    //End Constants
    //......
    
    //######
    //Functions
    
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        BOUNDARY_CH += ownerMod;
    }
    //End Functions
    //......
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();        
            //set volume detect so that I am in-efect phantom
            llVolumeDetect(TRUE);
        }
        
        collision_start(integer total_number)
        {
            //only care if the elevator hits me
            //using a shout as there may be more than one prim in the set
            //and not sure how far from the center the collision might be        
            if (llDetectedName(0) == ELEVATOR){llShout(BOUNDARY_CH, F_HIT_BOUNDARY);}
        }  
    }
    
    //[/PHP]


    Repair - In the case of a catastrophic failure, where the elevator goes off the track, this simple controller will kill the existing elevator and rez a new one. Has a copy of the elevator in its inventory.

    //[PHP]
    //
    //
    // *******************************************************************
    // repair
    // 12/18
    // v1.0.0
    //
    // in the case of a catastrophic failure, where the elevator goes
    // off the track, this simple controller will kill the existing
    // elevator and rez a new one
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    //specific to the elevator used
    //name of elevator
    string ELEVATOR = "elevator";
    //end specifics
    //......
    
    //modifiable based on preferences
    //channels
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer MAIN_CH = -140100;
    integer REPAIR_CH = -140150;
    //end modifiable
    //......
    
    //set on start
    //base down position of elevator .. sent by main
    vector BASE_POS;
    //end start set
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    string EMPTY_STR = "";
    string FUNC_SEP = "|";
    
    //functions
    //data request and return
    string F_REQ_DWN_POSIT = "req_dwn";
    string F_DWN_POSIT = "dwn_pos";
    //call to me and advise to ~main
    string F_REPAIR = "repair";
    string F_DIE = "die";
    
    //flag for elevator to reset itself on rez
    integer REPAIR_REZ = -5689321;
    
    //End Constants
    //......
    
    //######
    //Functions
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        MAIN_CH += ownerMod;
        REPAIR_CH += ownerMod;
    }
    //End Functions
    //......
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();
                    
            llListen(REPAIR_CH, EMPTY_STR, NULL_KEY, EMPTY_STR);
            //ask for the base of the elevator
            llShout(MAIN_CH, F_REQ_DWN_POSIT);
        }
        
        listen(integer channel, string name, key id, string message)
        {
            if (channel == REPAIR_CH)
            {
                list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]);
                string function = llList2String(params, 0);
                
                if (function == F_REPAIR)
                {
                    //kill the exiting instance
                    llShout(MAIN_CH, F_DIE);
                    //rez a new copy
                    llRezAtRoot(ELEVATOR, BASE_POS, ZERO_VECTOR, ZERO_ROTATION, REPAIR_REZ);
                }
                else if (function == F_DWN_POSIT)
                {
                    BASE_POS = (vector)llList2String(params, 1);
                }
            }
        }
    }
    
    //[/PHP]

     

  3. Core - Positioner
    Used to set the location, pose, and camera for the AVs that 'sit' in the elevator. It keeps the Main apprised of when people sit or stand up.

    //[PHP]
    //
    //
    // *******************************************************************
    // positioner
    // 12/18
    // v1.0.0
    //
    // used to set the location and pose for the AVs taht 'sit' in the elevator...
    // there are four 'hidden' prims each with this script
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    
    //modifiable based on preferences
    //channels
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer MAIN_CH = -140100;
    integer POSIT_CH = -140110;
    
    //available poses .. either system or inventory
    list ELEV_STANDS = ["stand_2", "stand_3", "stand_4"];
    //end modifiable
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    string EMPTY_STR = "";
    string DATA_SEP = "^";
    
    //functions
    //advise to ~main
    string F_SEATED = "seated";
    string F_STOOD = "stood";
    //~main call to me
    string F_UNSEAT = "unsit";
    
    //system sit
    string DEF_SIT = "sit";
    
    //End Constants
    //......
    
    //######
    //Global Variables
    //holds the key of seated AV
    key gCurUser = NULL_KEY;
    //holds the actual pose used
    string gPose;
    //End Global Variables
    //......
    
    //######
    //Functions
    
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        MAIN_CH += ownerMod;
        POSIT_CH += ownerMod;
    }
    //End Functions
    //......
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();        
            //clear any existing sit target
            llSitTarget(ZERO_VECTOR, ZERO_ROTATION);
    
            //then get the vector and rot for the target, as well as the cameras
            //stored in the prim's desc (sit vector^sit rot^eye offset^camera offset)
            list vals = llParseString2List(llGetObjectDesc(), [DATA_SEP], [EMPTY_STR]);
            //and set
            llSitTarget((vector)llList2String(vals, 0), (rotation)llList2String(vals, 1));
    
            //finally set the camera
            llSetCameraEyeOffset((vector)llList2String(vals, 2));
            llSetCameraAtOffset((vector)llList2String(vals, 3));
        }
    
        changed(integer change)
        {
            if(change & CHANGED_LINK)
            {
                //see if I was sat on .. or someone that was sitting has gotten up
                key user = llAvatarOnSitTarget();
                if (user != NULL_KEY)
                {
                    //they sat ... request permissions to pose them
                    //when permission is granted we advise main
                    llRequestPermissions(user, PERMISSION_TRIGGER_ANIMATION);
                }
                //if we had someone .. then they have stood up and we need to stop their animation
                //and advise main
                else if (gCurUser != NULL_KEY)
                {
                    //if using just system anims do not need to turn off
                    //code here to remind when using others
                    //llStopAnimation(gPose);
                    llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_STOOD, gCurUser);
                    gCurUser = NULL_KEY;
                }
            }
        }
        
        link_message(integer sender, integer channel, string function, key data)
        {
            if (channel == POSIT_CH)
            {
                //call from main to unseat any AV
                if (function == F_UNSEAT && gCurUser != NULL_KEY)
                {
                    llUnSit(gCurUser);
                    //if using just system anims do not need to turn off
                    //code here to remind when using others
                    //llStopAnimation(gPose);
                }
            }
        }
        
         run_time_permissions(integer permissions)
        {
            if (permissions & PERMISSION_TRIGGER_ANIMATION)
            {
                //get their key
                gCurUser = llGetPermissionsKey();
                llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_SEATED, gCurUser);
    
                ///stop the default sit
                llStopAnimation(DEF_SIT);
                
                //pick one
                integer use = (integer)llFrand(3.0);
                //hold to turn off later (when not using system) & start
                gPose = llList2String(ELEV_STANDS, use);
                llStartAnimation(gPose);          
            } 
        }                 
    }
    
    //[/PHP]

     

  4. Core - Move
    Actual mover of the elevator .. separated out from main principally so that main can receive the collision calls from the detector when moving to a new position. Also contains malfunction code. Located in the root Prim. Again, the basics were easy; the fun came in coding the malfunctions, particularly the catastrophic failure. This was my first time writing code for physical objects .. and has opened up a whole sideline of ideas for my next project :-). In a catastrophic failure one thing that will happen is the windows will 'break' Have yet to create the texture for these so for the moment just going blank.
    Note: If you look closely at the code, along with Main, you'll see how you can force the elevator to malfunction. Being in the elevator during a catastrophic malfunction is a laugh. If you figure this out and want to try at my place, come on by! (HERE)

    //[PHP]
    //
    //
    // *******************************************************************
    // move
    // 12/18
    // v1.0.0
    //
    // actual mover of the elevator .. separated out from main principally
    // so that main can receive the collision calls from the detector when
    // moving to a new position.
    //
    // this also contains the code for the malfunction scenarios
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    
    //instance constants specific to the instalation
    //for directed fails where we cause the failure
    vector FAIL_POINT_VECTOR = <13.35530, 208.23230, 104.96169>;
    //end instance
    //......
    
    //specific to the elevator used
    //holds the positions/rotations for when the doors go askew
    //nw/ne door, sw/se door
    //pos^rot^pos^rot
    list ASKEW_VALUES = ["<-0.817486, 1.158951, 1.709961>^<0.004181, -0.095755, -0.043419, 0.994449>^<0.769676, 1.200195, 1.709961>^<-0.026177, 0.000000, -0.999657, 0.000000>", "<-0.707977, -1.549805, 1.709961>^<0.000000, 0.052336, 0.000000, 0.998630>^<0.911160, -1.403122, 1.709961>^<0.000000, 0.043619, -0.999048, 0.000000>"];
    //hold the values when we need to set the windows
    //door up, door dwn, wide up, wide dwn, nrw up, nrw dwn, roof
    //until I have defined the texture .. use blank :5748decc-f629-461c-9a36-a35a221fe21f
    //face^UUID for texture
    list WINDOW_VALUES = ["2^5748decc-f629-461c-9a36-a35a221fe21f", "3^5748decc-f629-461c-9a36-a35a221fe21f", "3^5748decc-f629-461c-9a36-a35a221fe21f", "4^5748decc-f629-461c-9a36-a35a221fe21f", "4^5748decc-f629-461c-9a36-a35a221fe21f", "5^5748decc-f629-461c-9a36-a35a221fe21f", "1^5748decc-f629-461c-9a36-a35a221fe21f"];
    
    //end specific
    //......
    
    //modifiable based on preferences
    //increments in which we move along the z axis .. base to move up
    float CHG_AMOUNT = 0.5;
    float FAST_CHG_AMOUNT = 2.0;
    
    //threshold for a malfunction
    //a random # between 0 and 100 is generated, malfunctioning if it is higher than the threshold
    //each pax over the max (set in main) will reduce this by 20 points
    //to avaid malfunctions set very high (200+)
    integer FAIL_THRESHOLD = 99;
    
    //force applied in catastrophy
    vector CAT_PUSH_VECTOR = <-0.5, 0.0, -5.0>;
    vector CAT_ROT_VECTOR = <1.0, 0.0, -1.0>;
    
    //single letter codes the user can add to a go to force a malfunction
    //b = simple stop break
    string FC_STOP = "b";
    string FC_FALL_STOP = "s";
    string FC_FALL_FULL = "f";
    string FC_CATASTROPHIC = "c";
    
    //channels
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer MAIN_CH = -140100;
    integer MOVE_CH = -140130;
    integer BOUNDRY_CH = -140140;
    //end modifiable
    //......
    
    //set on start
    //elevator world positions .. sent by main
    vector UP_POSIT;
    vector DWN_POSIT;
    
    //hold the possible actions when we are in a catastrophic failure
    //loaded with data when we get the links from main
    list BREAK_LIST;
    //hold the links for the lights be turned off -- sent by main
    list LIGHT_LINKS = [];
    //end start set
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    string EMPTY_STR = "";
    string FUNC_SEP = "|";
    string DATA_SEP = "^";
    
    integer NOT_FOUND = -1;
    
    //functions
    //~main call to me and possible returns
    string F_MOVE = "move";
    string F_MOVED = "moved";
    string F_MALFUNC = "malfunc";
    //~main advising 
    string F_REPAIRED = "repaired";
    //data request and return
    string F_REQ_LINKS = "req links";
    string F_LINKS = "links";
    //when hit a boundary (on catas. tumble)
    string F_HIT_BOUNDRY = "hit";
    
    //positions
    string POSIT_UP = "up";
    string POSIT_DWN = "dwn";
    string POSIT_DWN_5 = "dwn 5";
    
    //malfunction types
    string FAIL_CATASTROPHIC = "catastrophy";
    string FAIL_STOP = "stop";
    string FAIL_FALL_FULL = "fall full";
    string FAIL_FALL_STOP = "fall stop";
    
    //match fail codes to internal constant
    list FAIL_CODES = [FC_STOP, FAIL_STOP, FC_FALL_STOP, FAIL_FALL_STOP, FC_FALL_FULL, FAIL_FALL_FULL, FC_CATASTROPHIC, FAIL_CATASTROPHIC];
    
    //break actions in catastrophy
    string BA_WINDOW = "win";
    string BA_ASKEW = "ask";
    
    //flags for move speed
    string P_FAST = "fast";
    
    //End Constants
    //......
    
    //######
    //Global Variables
    //where we are moving to
    string gTargetPosit = EMPTY_STR;
    //## of extra passengers
    integer gOvrloadPax = 0;
    //flag if we have overloaded .. needed as # will be set to - during failure
    integer gOvrLoad = FALSE;
    //for directed fails
    string gFailCode = EMPTY_STR;
    
    //End Global Variables
    //......
    
    //######
    //State Variables
    //used in malfunction
    //hold the failure state to the timer
    string sFailure;
    
    //used in catastrophic
    //position to see when we stop moving
    vector sElevPos;
    
    //End State Variables
    //......
    
    //######
    //Functions
    //princiapl work function to move the elevator
    //chg: is the distance moved in each loop
    integer do_move(float chg, integer failure)
    {
        //set the target end pos value, default to up
        //same for change
        vector endPos = UP_POSIT;
        string target = POSIT_UP;
    
        if (gTargetPosit == POSIT_DWN || gTargetPosit == POSIT_DWN_5)
        {
            chg *= -1;
            target = POSIT_DWN;
            endPos = DWN_POSIT;
            //maybe only movng dwn 5m
            vector cur = llGetPos();
            if(gTargetPosit == POSIT_DWN_5){endPos.z = cur.z - 5.0;}   
        }
    
        //if there is not a user directed malfunction .. check to see if one occurs
        //if we are here because of a failure .. skip
        
        float failHeight = 0.0;
        float failUpper = 0.0;
        float failLower = 0.0;
        if (!failure)
        {
            integer willFail = FALSE;
            if (gFailCode == EMPTY_STR)
            {
                //seed the failure probability
                //for each extra person an additional 20% chance
                float failThresh = FAIL_THRESHOLD;
                failThresh = failThresh - (gOvrloadPax * 20);
    
                //see if we will fail
                willFail = ((integer)llFrand(101.0) >= failThresh);
            }
            //we have a user directed fail
            else
            {
                willFail = TRUE;
            }
    
            if (willFail)
            {
                //now if we will fail we determine at what point
                //possible is bottom z plus 2m to top z less 2m
                float range = (UP_POSIT.z - 2.0) - (DWN_POSIT.z + 2.0);
                //in case this comws to a random 0.0 add 0.1
                failHeight = llFrand(range) + 0.1;            
                //now set a range to catch when we move into it
                failUpper = DWN_POSIT.z + failHeight + (llFabs(chg) * 2);
                failLower = DWN_POSIT.z + failHeight - (llFabs(chg) * 2);
            }
        }
    
        //flag to exit
        integer exit = FALSE;
        integer fail = FALSE;
        vector curPos = llGetPos();
    
        //move
        while (!exit && !fail)
        {
            curPos.z += chg;
    
            //if we have a fail height ... check if we are in the range
            if (failHeight > 0.0){fail = (curPos.z >= failLower && curPos.z <= failUpper);}
    
            //continue if there has been no failure
            if (!fail)
            {
                //check if we went past and reset
                if ((target == POSIT_UP && curPos.z > endPos.z)||(target == POSIT_DWN && curPos.z < endPos.z)){curPos = endPos;}
    
                //set and check for done
                llSetPos(curPos);
                exit = (curPos == endPos);
            }
        }
        return !fail;
    
    }
    
    //set the break list .. i.e. what are the break parameters tied to the links
    set_break_list(string data)
    {
        //these are in the following order
        //doors(x4)^wide walls(x2)^narrowWalls(x4)^roof link^light links(light link, tube link)
        list params = llParseStringKeepNulls(data, [DATA_SEP], [EMPTY_STR]);
        
        //we need to create the break list
        //list is comprised of action^link^ ...
        //     then for break: list index for face & UUID for texture
        //     for askew: other link ^index for posit^rots for settig askew
         
        BREAK_LIST = [];
        //first the doors
        integer x;
        //hold the previous link, for setting askew
        integer lastLink;
        //used to track which of the Askew list values we need .. only two
        integer idx = 0;
        
        for (x = 0; x < 4; x++)
        {
            integer link = llList2Integer(params, x);
    
            //door windows
            BREAK_LIST += [llDumpList2String([BA_WINDOW, link, 0], DATA_SEP)];
            BREAK_LIST += [llDumpList2String([BA_WINDOW, link, 1], DATA_SEP)];
    
            //on 1 & 3 set the askew break
            if (x == 1 || x == 3)
            {
                BREAK_LIST = BREAK_LIST + [llDumpList2String([BA_ASKEW, lastLink, link, idx], DATA_SEP)];
                idx++;
            }
            lastLink = link;    
        }
        //then the wide walls
        for (x = 4; x < 6; x++)
        {
            BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 2], DATA_SEP)];
            BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 3], DATA_SEP)];
        }
        //the narrow walls
        for (x = 6; x < 10; x++)
        {
            BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 4], DATA_SEP)];
            BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 5], DATA_SEP)];
        }                
        //the roof
        BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, 10), 6], DATA_SEP)];
    
        //hold the light params
        LIGHT_LINKS = llCSV2List(llList2String(params, 11));
    
        //randomise the list once to get the mix up started
        BREAK_LIST = llListRandomize(BREAK_LIST, 1);
    
        //and finally grab the elevator positions
        UP_POSIT = (vector)llList2String(params, 12);
        DWN_POSIT = (vector)llList2String(params, 13);
    
        
    }
    
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        MAIN_CH += ownerMod;
        MOVE_CH += ownerMod;
        BOUNDRY_CH += ownerMod;
    }
    //End Functions
    //......
    
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();
    
            //call for the links
            llMessageLinked(LINK_THIS, MAIN_CH, F_REQ_LINKS, NULL_KEY);
        }
        
        link_message(integer sender, integer channel, string function, key data)
        {
            if (channel == MOVE_CH)
            {
                if (function == F_LINKS)
                {
                    set_break_list((string)data);
    
                    //ready to run
                    state running;
                }
            }//end main ch
        }
    }
    
    //we are ready to work ...
    state running
    {
        link_message(integer sender, integer channel, string function, key data)
        {
            if (channel == MOVE_CH)
            {
                list params;
                if (function == F_MOVE)
                {
                    //get the passed values
                    params = llParseStringKeepNulls((string)data, [DATA_SEP], [EMPTY_STR]);
                    gTargetPosit = llList2String(params, 0);
                    gOvrloadPax = llList2Integer(params, 1);
                    gFailCode = llList2String(params, 2);
    
                    //last param indicates what speed we move
                    //default to normal
                    //a fast indicates it is a call .. and so need to keep from failing
                    //parameter to move indicates we are in a failure .. which avoids a new one .. so we default to FALSE
                    integer fail = FALSE;
                    float chg = CHG_AMOUNT;
                    if (llList2String(params, 3) == P_FAST)
                    {
                        chg = FAST_CHG_AMOUNT;
                        fail = TRUE;
                    }
    
                    //translate to one of the actual codes
                    integer idx = llListFindList(FAIL_CODES, [gFailCode]);
                    if (idx != NOT_FOUND)
                    {
                        gFailCode = llList2String(FAIL_CODES, idx + 1);
                    }
                    else
                    {
                        gFailCode = EMPTY_STR;
                    }
                    //we pass the chg vector to use because in malfunctions there can be faster (falling) moves
                    if (do_move(chg, fail))
                    {
                        //done .. let main know
                        llMessageLinked(LINK_THIS, MAIN_CH, F_MOVED, NULL_KEY);
                    }
                    else
                    {
                        //had a malfunction ....
                        state malfunction;
                    }
                }//end move
                else if (function == F_LINKS)
                {
                    set_break_list((string)data);
                }
            }//end main ch
        }    
    }
    
    //state we enter when there has been a malfunction in the elevator
    state malfunction
    {
        state_entry()
        {
            //initialise state variables
            sFailure = EMPTY_STR;
            
            //so we failed, now need to determine the type of failure
            //if a directed fail we already have this
            // move to worse by 5% for every overloaded pax
            //if fail code already set leave at 100
            integer level = 100;
            if (gFailCode == EMPTY_STR){level = (integer)llFrand(100.0) - (gOvrloadPax * 5);}
    
            // catastrophic fall .. off track, on side, doors off hinge - 1%
            if (level <= 1 || gFailCode == FAIL_CATASTROPHIC)
            {
                sFailure = FAIL_CATASTROPHIC;
                gTargetPosit = POSIT_DWN;
            }
            // full fall then stop - 19%
            else if (level <= 20 || gFailCode == FAIL_FALL_FULL)
            {
                sFailure = FAIL_FALL_FULL;
                gTargetPosit = POSIT_DWN;
            }
            // 5m fall then stop - 30%
            else if (level <= 50 || gFailCode == FAIL_FALL_STOP )
            {
                gTargetPosit = POSIT_DWN_5;
                sFailure = FAIL_FALL_STOP;
                //need to drop 5 ms, or to bottom which-ever
                //basically if we are not going to end at 1 m above, then we continue down
                vector cur = llGetPos();
                if ((cur.z - DWN_POSIT.z) < 4.0)
                {
                    sFailure = FAIL_FALL_FULL;
                    gTargetPosit = POSIT_DWN;
                }
            }
            // just stop - 50%
            else
            {
                sFailure = FAIL_STOP;
            }
                
            //clear overload so no more failure, holding flag for later notification
            gOvrLoad = (gOvrloadPax > 0);
            //by setting to -1 we ensure no falure
            gOvrloadPax = -1;
            //and clear any manual fail codes
            gFailCode = EMPTY_STR;
    
            //if we need to move, we move, fast
            if (sFailure != FAIL_STOP)
            {
                do_move(FAST_CHG_AMOUNT, TRUE);
            }
    
            //owner advised of failure type
            llOwnerSay("Elevator malfunction " + sFailure);
    
            //if a catastrophic .. more to do
            if (sFailure == FAIL_CATASTROPHIC)
            {
                state catastrophic_failure;
            }
            else
            {
                //advise the users of the mechanical failure
                //add in if was brought on by overcrowding
                llWhisper(0, "Our appologies, there has been a mechanical malfunction with the elevator");
                if (gOvrLoad){llWhisper(0, "This was caused by the overloading of the elevator");}
    
                //if we have ended all the way down then return to main, advising of failure
                //this will keep the elevator out of service until the owner comes by, or one hour has passed
                if (sFailure == FAIL_FALL_FULL)
                {
                    llWhisper(0, "The doors will open in a moment. The elevator is now out of service, a mechanic has been called");
                    llMessageLinked(LINK_THIS, MAIN_CH, F_MALFUNC, sFailure);
                    state await_repair;
                }
                //if we are stuck then
                else //FAIL_FALL_STOP or FAIL_STOP
                {
                    //advise that mechanic is on the way
                    llWhisper(0, "Please standby, a mechainic has been called");
    
                    //timer started - 15 sec for FAIL_STOP, 30 sec for FAIL_FALL_STOP
                    float time = 15.0;
                    if (sFailure == FAIL_FALL_STOP){time = 30.0;}
                    llSetTimerEvent(time);
                }
            }
        }
    
        touch_end(integer total_number)
        {
            //owner touch will 'short-circuit' the wait period
            if (llDetectedKey(0) == llGetOwner()){llSetTimerEvent(0.01);}
        }
    
        timer()
        {
            llSetTimerEvent(0.0);
            //then FAIL_FALL_STOP return to ground, elevator out of service
            //FAIL_STOP 10% chance need to return to ground and out of service, otherwise will continue
            //ignore if fail code was manually set
            if (sFailure == FAIL_STOP && gFailCode == EMPTY_STR)
            {
                if ((integer)llFrand(10.0) <= 1){sFailure = FAIL_FALL_STOP;}
            }
    
            if (sFailure == FAIL_FALL_STOP)
            {
                llWhisper(0,"Due to the malfunction the elevator will be returning to the ground and then will be out of service");
                gTargetPosit = POSIT_DWN;
            }
            llWhisper(0, "The elevator is about to resume motion, please ensure you are holding the rail");
    
            do_move(CHG_AMOUNT, TRUE);
            
    
            if (sFailure == FAIL_STOP)
            {
                llMessageLinked(LINK_THIS, MAIN_CH, F_MOVED, NULL_KEY);
                state running;
            }
            else//FAIL_FALL_STOP
            {
                llMessageLinked(LINK_THIS, MAIN_CH, F_MALFUNC, sFailure);
                state await_repair;
            }
    
        }
    
        state_exit()
        {
            sFailure = EMPTY_STR;
        }
    }
    
    //if we have had a catastrophic failure we move to this state
    state catastrophic_failure
    {
        state_entry()
        {
            //get it's position so we can start tracking and see when it stops moving
            sElevPos = llGetPos();
    
            //listen for a call from parcel boundry to keep us from leaving
            //filter for the only thing we want to hear
            llListen(BOUNDRY_CH, EMPTY_STR, NULL_KEY, F_HIT_BOUNDRY);
    
            //turn off light  
            llSetLinkPrimitiveParamsFast(llList2Integer(LIGHT_LINKS, 0), [PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.0, 0.0, 0.0, PRIM_LINK_TARGET, llList2Integer(LIGHT_LINKS, 1), PRIM_FULLBRIGHT, ALL_SIDES, FALSE, PRIM_GLOW, ALL_SIDES, 0.0]);
            
            //first we set physical
            llSetStatus(STATUS_PHYSICS, TRUE);
            //the we give it a bit of a push ..
            //... down
            vector push = llGetMass()* CAT_PUSH_VECTOR;
            llApplyImpulse(push, FALSE);        
            //..spin
            llApplyRotationalImpulse(CAT_ROT_VECTOR, FALSE);
    
            //set the time to check for end of movement  ... and break things as we tumble
            llSetTimerEvent(0.1); 
        }
    
        listen(integer channel, string name, key id, string message)
        {
            llSetTimerEvent(0.0);
            //only listening to the boundry channel and only for the hit call
            //so we've hit the boundry .. remove physics should stop us in our tracks
            llSetStatus(STATUS_PHYSICS, FALSE);
            //go to await repair .. final actions covered in exit
            state await_repair;        
        }
    
        timer()
        {
            //check to see if we've stopped
            vector curPos = llGetPos();
            integer stopped = (curPos == sElevPos);
    
            if (stopped)
            {
                //done ..
                llSetTimerEvent(0.0);
                llSetStatus(STATUS_PHYSICS, FALSE);
                //go to await repair .. final actions covered in exit
                state await_repair;
            }
            else
            {
                //store new posit
                sElevPos = curPos;
                
                //on each bounce we check to break things a bit
                // we have a list of actions that can occur .. we randomly pick one
                // we do it, and remove from the list
                //actions are
                // for each door
                //    break the windows
                //    askew the door
                // for each side/roof
                //    break the windows
                //first randomise the list
                    //list is comprised of action^link^ ...
                    //     then for break list index for face & UUID for texture
                    //     for askew other link ^index for (west loc_pos^loc_rot east loc_pos^loc_rot)
                    //
                    //Askew values
                    // nw/ne door, sw/se door
                    // pos^rot^pos^rot
                    //window values
                    // door up, door dwn, wide up, wide dwn, nw up, nw dw, roof
                    // face^texture UUID     
                //note perhaps need to have these textures on an object at top and btem so they are already loaded?
    
                //mixup the list
                BREAK_LIST = llListRandomize(BREAK_LIST, 1);
                //take the first one
                list params = llParseString2List(llList2String(BREAK_LIST, 0), [DATA_SEP], [EMPTY_STR]);
                //remove from the list
                BREAK_LIST = llDeleteSubList(BREAK_LIST, 0, 0);
                //now do the action
                string action = llList2String(params, 0);
                integer link = llList2Integer(params, 1);
                
                if (action == BA_WINDOW)
                {
                    list winParams = llParseString2List(llList2String(WINDOW_VALUES, llList2Integer(params, 2)), [DATA_SEP], [EMPTY_STR]);
                    llSetLinkPrimitiveParamsFast(link, [PRIM_TEXTURE, llList2Integer(winParams, 0), llList2Key(winParams, 1), ZERO_VECTOR, ZERO_VECTOR, 1.0]);
                }
                else if (action == BA_ASKEW)
                {
                    integer secondLink = llList2Integer(params, 2);                
                    list askewParams = llParseString2List(llList2String(ASKEW_VALUES, llList2Integer(params, 3)), [DATA_SEP], [EMPTY_STR]);
                    llSetLinkPrimitiveParamsFast(link, [PRIM_POS_LOCAL, (vector)llList2String(askewParams, 0), PRIM_ROT_LOCAL, (rotation)llList2String(askewParams, 2), PRIM_LINK_TARGET, secondLink, PRIM_POS_LOCAL, (vector)llList2String(askewParams, 2), PRIM_ROT_LOCAL, (rotation)llList2String(askewParams, 3)]);     
                }
            }
        }
    
        state_exit()
        {
            //advise the users of the mechanical failure
            //add in if was brought on by overcrowding
            llWhisper(0, "Please exit the elevator with care. We hope no one was injured.");
            if (gOvrLoad){llWhisper(0, "This accident was caused by the overloading of the elevator");}
    
            //Advising main failure
            //this will keep the elevator out of service until the owner comes by, or one hour has passed
            llWhisper(0, "A mechanic has been called");
            llMessageLinked(LINK_THIS, MAIN_CH, F_MALFUNC, FAIL_CATASTROPHIC);        
           
        }
        
    }
    
    //basic state, wait for notification from ~main that we are repaired
    state await_repair
    {
        link_message(integer sender, integer channel, string function, key data)
        {
            if (channel == MOVE_CH)
            {
                if (function == F_REPAIRED)
                {
                    state running;
                }
            }
        }
    }
    
    //[/PHP]

     

  5. Core - Main
    The core script for controlling the elevator...coordinates the efforts of all the other scripts and components. Contains the code to for the elevator doors as well as the light. Located in the root Prim.
    For the most part fairly straight forward, other than the hoops I had to jump through when it came to keeping track of who was in the elevator. Obviously if someone sits/unsits, that we know, but for a number of things I wanted to know if someone was just standing in the elevator, and that is where things got complicated. The Collision events did not behave entirely as expected (see More Collisions ... for details).

    //[PHP]
    //
    //
    // *******************************************************************
    // main
    // 12/18
    // v1.0.0
    //
    // core script for controlling the elevator...coordinates the efforts
    // of all the other scripts and components.
    //
    // Writen by Wandering Soulstar
    // *******************************************************************
    
    //######
    //Constants
    
    //instance specific to the instalation
    //elevator world positions
    vector UP_POSIT = <13.35530,208.23233, 111.22778>;
    vector DWN_POSIT = <13.35530, 208.23233, 58.43259>;
    //end instance
    //......
    
    //specific to the elevator used
    //door positions .. relative to root(local) values
    vector W_UP_OPEN = <-1.050721, 1.200195, 1.709961>;
    vector E_UP_OPEN = <1.054483, 1.200195, 1.709961>;
    vector W_UP_CLOSE = <-0.399941, 1.200195, 1.709961>;
    vector E_UP_CLOSE = <0.399750, 1.200195, 1.709961>;
    vector W_DWN_OPEN = <-1.050721, -1.549805, 1.709961>;
    vector E_DWN_OPEN = <1.054483, -1.549805, 1.709961>;
    vector W_DWN_CLOSE = <-0.399940, -1.549805, 1.709961>;
    vector E_DWN_CLOSE = <0.399750, -1.549805, 1.709961>;
    
    //name of door prims
    string DOOR_NAME = "elevator door";
    
    //light prim names
    string LIGHT_SRC_NAME = "light source";
    string LIGHT_TUBE_NAME = "flourescent light";
    
    //light faces to set
    integer BRIGHT_FACE = 0;
    integer IN_GLOW_FACE = 0;
    integer OUT_GLOW_FACE = 3;
    
    //walls and roof prim names
    string ROOF_NAME = "roof";
    string W_WALL_NAME = "side wall";
    string N_WALL_NAME = "small wall";
    
    //maximum number passagers before overlaod
    //NOTE - this is based on the number of positioners, i.e. number of sit positions
    integer MAX_PASSENGERS = 4;
    
    //hold the links of the doors
    //NOTE - code is based on the door design .. changing this is more than
    //just changing the below constants
    list DOOR_LINKS = [0, 0, 0, 0];
    //the individual doors .. stored in their desc
    list DOOR_POSITS = ["up w", "up e", "dwn w", "dwn e"];
    //end specifics
    //......
    
    //modifiable based on preferences
    //amount the doors move on each interation
    float DEF_DOOR_CHG = 0.01;
    
    //color and glow settings
    vector LIGHT_COLOR = <1.000, 1.000, 0.906>;
    float IN_GLOW = 0.3;
    float OUT_GLOW = 0.1;
    
    //light settings
    //off
    list LGT_P_OFF = [PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.0, 0.0, 0.0];
    //full
    list LGT_P_FULL = [PRIM_POINT_LIGHT, TRUE, LIGHT_COLOR, 1.0, 3.0, 0.75];
    //dim
    list LGT_P_DIM = [PRIM_POINT_LIGHT, TRUE, LIGHT_COLOR, 0.25, 2.5, 0.75];
    //plus glows & brights
    list LGT_GB_ON = [PRIM_FULLBRIGHT, BRIGHT_FACE, TRUE, PRIM_GLOW, IN_GLOW_FACE, IN_GLOW, PRIM_GLOW, OUT_GLOW_FACE, OUT_GLOW];
    list LGT_GB_OFF = [PRIM_FULLBRIGHT, BRIGHT_FACE, FALSE, PRIM_GLOW, ALL_SIDES, 0.0];
    
    //times for different timers
    //these and timer flags explained in default:timer event
    float TIME_EMPTY = 30.0;
    float TIME_FREE = 90.0;
    float TIME_DISABLE = 60.0;
    float TIME_REPAIR = 3600.0;
    float TIME_CHECKSIT = 60.0;
    float TIME_CHECKGO = 90.0;
    float TIME_CHECKPAX = 60.0;
    float TIME_BRIDGE = 30.0;
    
    //channels
    integer CHAT_CH = 0;
    //NOTE - the following are seeds that are then modified by set_owner_channel ensure that
    //they are changed on all relevant modules
    integer DIALOG_CH = -140101;
    integer MAIN_CH = -140100;
    integer POSIT_CH = -140110;
    integer BRIDGE_CH = -140120;
    integer MOVE_CH = -140130;
    //integer BOUNDARY_CH = -140140; .. not used in this module, noted for clarity
    integer REPAIR_CH = -140150;
    //end modifiable
    //......
    
    //set on start
    //holds all the link information, built on start
    string LINK_DATA;
    //the link to the light source and tube
    integer LIGHT_SRC_LINK = 0;
    integer LIGHT_TUBE_LINK = 0;
    //end start set
    //......
    
    //general constants
    //NOTE - take care in modifying any of these. Will need to check other modules
    //for use of same constants and change accordingly
    string EMPTY_STR = "";
    string FUNC_SEP = "|";
    string DATA_SEP = "^";
    
    integer NOT_FOUND = -1;
    
    //functions
    //data requests to me and returns
    string F_REQ_POSITION = "req posit";
    string F_POSITION = "posit";
    string F_REQ_STATUS = "req status";
    string F_STATUS = "status";
    string F_REQ_LINKS = "req links";
    string F_LINKS = "links";
    string F_REQ_DWN_POSIT = "req_dwn";
    string F_DWN_POSIT = "dwn_pos";
    //for AV 'Tracking'
    //~detector
    string F_ENTER = "enter";
    string F_EXIT = "exit";
    //~positioner
    string F_SEATED = "seated";
    string F_STOOD = "stood";
    //call to ~detector and possible returns
    string F_CHECK_PAXS = "check paxs";
    string F_PAX_UPDATE = "pax list";
    string F_NO_PAX = "empty";
    //call to ~positioner
    string F_UNSEAT = "unsit";
    //frm bridge
    string F_READY = "ready";
    //called from a button
    string F_CALL = "call";
    //to ~move and returns, F_MOVE also call to bridge
    string F_MOVE = "move";
    string F_MOVED = "moved";
    string F_MALFUNC = "malfunc";
    //advise move of repair
    string F_REPAIRED = "repaired";
    //cal to ~repair script .. 
    string F_REPAIR = "repair";
    //advise to me to end
    string F_DIE = "die";
    
    //status for availability
    string S_READY = "ready";
    string S_INUSE = "inuse";
    string S_DISABLED = "disab";
    //and during movement
    string S_DEPARTING = "deprt";
    string S_ARRIVING = "arrv";
    
    //dialog options
    string OPT_OK = "Ok";
    string OPT_CANCEL = "Cancel";
    
    //positions
    string POSIT_UP = "up";
    string POSIT_DWN = "dwn";
    string POSIT_MOVING = "moving";
    
    //bridge positions
    string POSIT_OPEN = "open";
    string POSIT_CLOSED = "closed";
    
    //door states
    string DS_OPEN = "open";
    string DS_CLOSE = "close";
    
    //sun states
    string DAY = "d";
    string NIGHT = "n";
    
    //malfunction types
    string FAIL_CATASTROPHIC = "catastrophy";
    string FAIL_STOP = "stop";
    string FAIL_FALL_FULL = "fall full";
    string FAIL_FALL_STOP = "fall stop";
    
    //timer flags
    //these and times explained in default:timer event
    string TF_EMPTY = "empty";
    string TF_FREE = "free";
    string TF_DISABLE = "disable";
    string TF_REPAIR = "repair";
    string TF_CHECKSIT = "chk_sit";
    string TF_CHECKGO = "chk_go";
    string TF_CHECKPAX = "chk_pax";
    string TF_BRIDGE = "bridge";
    
    //voice messages from users
    string GO = "go";
    string YES = "yes";
    
    //flags for move speed
    string P_FAST = "fast";
    string P_SLOW = "slw";
    
    //flag for elevator to reset itself on rez
    integer REPAIR_REZ = -5689321;
    
    //End Constants
    //......
    
    //######
    //Global Variables
    
    //my current position
    string gCurPosit = POSIT_DWN;
    //current movement (arriving/departing)
    string gMvStatus = EMPTY_STR;
    //status of the elevator (Ready, InUse, Disabled)
    string gCurStatus = EMPTY_STR;
    //current door state
    string gCurDoor = POSIT_CLOSED;
    //position moving to
    string gTargetPosit = EMPTY_STR;
    //failure type
    string gFailure = EMPTY_STR;
    //for directed fail
    string gFailCode = EMPTY_STR;
    //hold identifer for type of timer set
    string gTimerFlag = EMPTY_STR;
    //how fast we move
    string gMvSpeed = P_SLOW;
    //flag to announce or not the arrival
    integer gCalled = FALSE;
    
    //lists of pax/seated
    list gPaxList = [];
    list gSeatedList = [];
    
    //chat handle
    integer gOpenChatHandle = 0;
    
    //End Global Variables
    //......
    
    
    //######
    //Functions
    
    //begin the move process ... close doors and if we are at the top
    //advise the bridge to close .. waiting for it to respond
    start_move()
    {
        //start the doors to close .. this will set the lights
        set_doors(DS_CLOSE);
        
        //set our target destination
        if (gCurPosit == POSIT_DWN)
        {
            gTargetPosit = POSIT_UP;
        }
        else
        {
            gTargetPosit = POSIT_DWN;
        }
        
        //cur status is departing
        gMvStatus = S_DEPARTING;
        
        //if at top call the bridge to close on return this will go to continue
        if (gCurPosit == POSIT_UP)
        {
            llWhisper(BRIDGE_CH, F_MOVE + FUNC_SEP + POSIT_CLOSED);
            //set a timer in case it is broken
            gTimerFlag = TF_BRIDGE;
            llSetTimerEvent(TIME_BRIDGE);
        }
        //otherwise just continue
        else
        {
            continue_move();
        }
    }
    
    //will come here after the bridge has advised ready or no need to call bridge
    //this will be either at the start of a move (departing), finished moving, which will have called bridge if needed (arriving)
    continue_move()
    {
        //if status is departing
        if (gMvStatus == S_DEPARTING)
        {
            if (gCurStatus != S_DISABLED){gCurStatus = S_INUSE;}
            
            //advise we are moving
            llShout(MAIN_CH, F_POSITION + FUNC_SEP + POSIT_MOVING + DATA_SEP + gTargetPosit);
            
            //check for overlaod
            //default is none
            integer ovrLoad = 0;
            if (llGetListLength(gPaxList) > MAX_PASSENGERS){ovrLoad = (llGetListLength(gPaxList) - MAX_PASSENGERS);}
            
            //call the move script passing: destination, any overloaded AVs (i.e. more than MX, and if there was a force fail called 
            llMessageLinked(LINK_THIS, MOVE_CH, F_MOVE, llDumpList2String([gTargetPosit, ovrLoad, gFailCode, gMvSpeed], DATA_SEP));
            //this will return arriving
    
            //finally set the pax list to be only seated, this will allow for the standing pax to be counted on arrival
            gPaxList = gSeatedList; 
        }
        else if (gMvStatus == S_ARRIVING)
        {
            //advise of position
            gCurPosit = gTargetPosit;
            llShout(MAIN_CH, F_POSITION + FUNC_SEP + gCurPosit);
            
            //open the doors ... will set the lights
            set_doors(DS_OPEN);
    
            //if we were not called from the button, announce arrival
            if (!gCalled && gCurStatus != S_DISABLED){llWhisper(CHAT_CH, "We have arrived. We hope that you enjoyed the trip");}
            gCalled = FALSE;
    
            //set our status ... if there was a failure our status is already disabled
            //else we are in use
            if (gCurStatus != S_DISABLED){gCurStatus = S_INUSE;}
            
            //advise on our status
            llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
            
            //unseat the avatars
            llMessageLinked(LINK_ALL_OTHERS, POSIT_CH, F_UNSEAT, NULL_KEY);
    
            //start a timer to autmatically make the elevator available even if they have not all left
            //or no one enters
            //will be canceled if someone new enters
            gTimerFlag = TF_FREE;
            llSetTimerEvent(TIME_FREE);       
        }   
    }
    
    //set the doors to either open or closed (action) the lights as well
    set_doors(string action)
    {
        //our position tells us which doors to use
        //their current status is stored so we'll check against the action and if already there just ignore
        if (gCurDoor != action)
        {
            //get the sun for light setting determination
            vector sunPos = llGetSunDirection();
            string sun = NIGHT;
            if (sunPos.z > 0){sun = DAY;}
    
    
            //if we are opening, and night .. set light to full
            if (action == DS_OPEN && sun == NIGHT)
            {
                llSetLinkPrimitiveParamsFast(LIGHT_SRC_LINK, LGT_P_FULL + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_ON);
            }
    
            //now which doors we use .. set the idx to pull links from DOOR_LINKS
            //order is [up w, up e, dwn w, dwn e]
            //default for up
            integer startIdx = 0;                
            if (gCurPosit == POSIT_DWN){startIdx = 2;}
            
            //now get the links
            integer lnkWest = llList2Integer(DOOR_LINKS, startIdx);
            integer lnkEast = llList2Integer(DOOR_LINKS, startIdx + 1);  
    
            //set the direction mod
            //direction mod is for west, east will then be -dir mod
            //default for Open
            float dirMod = -1.0;
            if (action == DS_CLOSE){dirMod = 1.0;}
            
            //from these we create the chg vectors
            //assumption is doors move along the x axis
            float chgWest = DEF_DOOR_CHG * dirMod;
            float chgEast = DEF_DOOR_CHG * -dirMod;      
                    
            //prep the end positions
            //default is up/opening
            vector endWest = W_UP_OPEN;
            vector endEast = E_UP_OPEN;
    
            //now adjust as needed        
            if (gCurPosit == POSIT_UP && action == DS_CLOSE)
            {
                endWest = W_UP_CLOSE;
                endEast = E_UP_CLOSE;            
            }
            else if (gCurPosit == POSIT_DWN && action == DS_OPEN)
            {
                endWest = W_DWN_OPEN;
                endEast = E_DWN_OPEN;             
            }
            else if (gCurPosit == POSIT_DWN && action == DS_CLOSE)
            {
                endWest = W_DWN_CLOSE;
                endEast = E_DWN_CLOSE;             
            }
    
            //get our cur positions
            vector curWest = (vector)llList2String(llGetLinkPrimitiveParams(lnkWest, [PRIM_POS_LOCAL]), 0);
            vector curEast = (vector)llList2String(llGetLinkPrimitiveParams(lnkEast, [PRIM_POS_LOCAL]), 0);
    
            //flag to exit loop
            integer exit = FALSE;
    
            //start moving
            while (!exit)
            {
                //calc new posits
                curWest.x += chgWest;
                curEast.x += chgEast;
    
                //see if we have gone past the end ... to set to end
                //check will depend on open/close
                if (action == DS_OPEN)
                {
                    if (curWest.x < endWest.x){curWest.x = endWest.x;}
                    if (curEast.x > endEast.x){curEast.x = endEast.x;}
                }
                else
                {
                    if (curWest.x > endWest.x){curWest.x = endWest.x;}
                    if (curEast.x < endEast.x){curEast.x = endEast.x;}
                }
    
                //move
                llSetLinkPrimitiveParamsFast(lnkWest, [PRIM_POS_LOCAL, curWest, PRIM_LINK_TARGET, lnkEast, PRIM_POS_LOCAL, curEast]);
    
                //check if done
                exit = ((curWest.x == endWest.x) && (curEast.x == endEast.x));
            }     
            gCurDoor = action;
            //
    
            //if we are closing set ligths
            if (action == DS_CLOSE)
            {
                //defult to off
                list params = LGT_P_OFF + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_OFF;
                //if night and someone is in set to dim
                if (sun == NIGHT && llGetListLength(gPaxList) != 0)
                {
                    params = LGT_P_DIM + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_ON;
                }
                llSetLinkPrimitiveParamsFast(LIGHT_SRC_LINK, params);
            }
        }
    }
    
    //maintians the pax and seated lists
    //action: what has happened (enter/seated, exit/stood)
    //paxs: list of UUIDs to AV that the action 'happened'
    integer update_pax_list(string action, list paxs)
    {
        //we have a list of all and a list of who is seated
        //for seated/stood paxs list will only have one item, enter/exit might have more
    
        //if enter ... and if not already in pax list, add
        //if a sit ... add to sit list, and if not already in pax list, add
        if (action == F_SEATED || action == F_ENTER)
        {
            //check if already in pax list an add if not
            integer num = llGetListLength(paxs);
            integer x;
            for (x = 0; x < num; x++)
            {
                integer posit = llListFindList(gPaxList, llList2List(paxs, x, x));
                if (posit == NOT_FOUND){gPaxList = gPaxList + llList2List(paxs, x, x);}
            }
            //if a seated add to said list    
            if (action == F_SEATED){gSeatedList += paxs;}
        }
        //if an exit ... was not seated, so just remove from pax list
        //if a stand ... remove from both lists, if they stood in the elevator
        //    will fire a collision enter and so they will be added back to the list    
        else if (action == F_EXIT || action == F_STOOD)
        {
            //first the pax list (common to both)
            integer num = llGetListLength(paxs);
            integer x;
            integer posit;
    
            for (x = 0; x < num; x++)
            {
                //if we find the passed key, remove from the list
                posit = llListFindList(gPaxList, llList2List(paxs, x, x));
                if (posit != NOT_FOUND){gPaxList = llDeleteSubList(gPaxList, posit, posit);}
            }
    
            //then if a stand, from the seated list
            if (action == F_STOOD)
            {
                posit = llListFindList(gSeatedList, llList2List(paxs, x, x));
                if (posit != NOT_FOUND){gSeatedList = llDeleteSubList(gSeatedList, posit, posit);}            
            }
        }
    
        //finally we return the new total number of pax
        return llGetListLength(gPaxList);
        
    }
    
    //common function to modify channels to be special for the owner
    //usually the first thing called
    set_owner_channel()
    {
        //create the mod
        integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6));
    
        //now add to each of the defined channels
        DIALOG_CH += ownerMod;
        MAIN_CH += ownerMod;
        POSIT_CH += ownerMod;
        BRIDGE_CH += ownerMod;
        MOVE_CH += ownerMod;
        REPAIR_CH += ownerMod;
    }
    //End Functions
    //......
    
    
    //Body
    default
    {
        state_entry()
        {
            //initialise channels
            set_owner_channel();
    
            //default/expected postion of the elevator on script start is dwn, with doors closed
            //although set in definition, resetting here for clarity
            gCurPosit = POSIT_DWN;
            gCurDoor = POSIT_CLOSED;
            //check if night to set the light
            vector sunPos = llGetSunDirection();
            if (sunPos.z <= 0)
            {
                llSetLinkPrimitiveParamsFast(LIGHT_SRC_LINK, LGT_P_DIM + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_ON);
            }
            //status
            gCurStatus = S_READY;
                   
            //first we need to get ahold of the links ... while this could be hardcoded, changes to the linkset could break
            //For the doors the order is important
            //DOOR_LINKS is preped as [0,0,0,0]
            //this will correspond to [up(n) w, up (n) e, dwn(s) w, dwn(s) e] DOOR_POSITS
            //Assume we will find only the light links we want ...
            //for the walls .. theses temp stored in two lists w(ide)Walls & n(arrow)Walls, order is irrelevant
    
            integer numLinks = llGetNumberOfPrims();
            integer ctr  = 0;
            string desc;
            integer posit;
            list wWallLinks = [];
            list nWallLinks = [];
            integer roofLink = 0;
    
            while (ctr < numLinks)
            {
                //move to the next (first) link
                ctr++;
                string name = llGetLinkName(ctr);
                if (name == DOOR_NAME)
                {
                    //may have found one
                    //description will tell us which
                    desc = llList2String(llGetLinkPrimitiveParams(ctr, [PRIM_DESC]), 0);
    
                    //get the array position of the door
                    posit = llListFindList(DOOR_POSITS, [desc]);
    
                    //skip in case there is garbage there
                    if (posit != NOT_FOUND)
                    {
                        //store
                        DOOR_LINKS = llListReplaceList(DOOR_LINKS, [ctr], posit, posit);
                    }
                }
                else if (name == LIGHT_SRC_NAME)
                {
                    //if more than one found will just overwrite
                    LIGHT_SRC_LINK = ctr;
                }
                else if (name == LIGHT_TUBE_NAME)
                {
                    //if more than one found will just overwrite
                    LIGHT_TUBE_LINK = ctr;
                }          
                else if (name == ROOF_NAME)
                {
                    //if more than one found will just overwrite
                    roofLink = ctr;
                }
                //for these if we find more than needed it will break the code later (manly only in a catastrophic failure
                else if (name == W_WALL_NAME)
                {
                    wWallLinks += [ctr];             
                }
                else if (name == N_WALL_NAME)
                {
                    nWallLinks += [ctr];             
                }              
            }//end while
    
            //pass links etc to move
            //first create the data string, these are in the following order
            //doors(in order up(n) w, up(n) e, dwn(s) w, dwn(s) e)^wide walls(x2)^narrowWalls(x4)^roof link^light links(light link, tube link)^upPos^downPos
            LINK_DATA = llDumpList2String(DOOR_LINKS + wWallLinks + nWallLinks + [roofLink] + [llList2CSV([LIGHT_SRC_LINK, LIGHT_TUBE_LINK])] + [UP_POSIT, DWN_POSIT], DATA_SEP);
            llMessageLinked(LINK_THIS, MOVE_CH, F_LINKS, (key)LINK_DATA);
    
            //let everyone know (mainly detectors) where we are
            llShout(MAIN_CH, F_POSITION + FUNC_SEP + gCurPosit);
                            
            //advise repair of the elevator's down posit
            llShout(REPAIR_CH, F_DWN_POSIT + FUNC_SEP + (string)DWN_POSIT);
    
            //advise the buttons
            llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
    
            //listen on my channel .. calls from detectors, bridge, buttons
            llListen(MAIN_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); 
    
            llSay(CHAT_CH, "Elevator ready");
        }
    
        listen(integer channel, string name, key id, string message)
        {
            if (channel == MAIN_CH && gCurStatus == S_DISABLED)
            {
                //recovery from a catastrophic failure
                if (message == F_DIE){llDie();}
            }
            //will only listen to other calls i we are not disabled
            else if(gCurStatus != S_DISABLED)
            {
                if (channel == MAIN_CH)
                {
                    list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]);
                    string function = llList2String(params, 0);
    
                    if (function == F_READY)
                    {
                        //this will always be the bridge, either out or retracted
                        //kill the timer and continue
                        llSetTimerEvent(0.0);
                        gTimerFlag = EMPTY_STR;
                        continue_move();
                    }
                    else if (function == F_REQ_POSITION)
                    {
                        //a detector asking for the position of the elevator
                        llRegionSayTo(id, MAIN_CH, F_POSITION + FUNC_SEP + gCurPosit);
                    }
                    else if (function == F_REQ_STATUS)
                    {
                        //a button asking for the status of the elevator
                        //if moving we reply with position
                        if (gCurPosit == POSIT_MOVING)
                        {
                            message = F_POSITION + FUNC_SEP + gCurPosit + DATA_SEP + gTargetPosit;
                        }
                        //else with status
                        else
                        {
                            message = F_STATUS + FUNC_SEP + gCurStatus;
                        }
                        llRegionSayTo(id, MAIN_CH, message);
                    }
                    else if (function == F_REQ_DWN_POSIT)
                    {
                        llRegionSayTo(id, REPAIR_CH, F_DWN_POSIT + FUNC_SEP + (string)DWN_POSIT);
                    }
                    else if (function == F_ENTER || function == F_EXIT)
                    {
                        //will have passed keys of those that entered
                        list paxs = llParseString2List(llList2String(params, 1), [DATA_SEP], [EMPTY_STR]);
                        //how many did we have
                        integer curPax = llGetListLength(gPaxList);
                        //we update our list
                        integer newPax = update_pax_list(function, paxs);
    
                        //other actions are only if at rest
                        if (gCurPosit != POSIT_MOVING)
                        {
                            //if first one
                            if (curPax == 0)
                            {
                                //advise to sit
                                llWhisper(CHAT_CH, "To start the elevator you must grab a hand rail (sit)");
                                //set a timer to check if they have sat
                                gTimerFlag = TF_CHECKSIT;
                                llSetTimerEvent(TIME_CHECKSIT);
    
                                //and set status to in use
                                gCurStatus = S_INUSE;
                                //advise the buttons of status
                                llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
                            }
    
                            //advise about over crowding
                            if (newPax > MAX_PASSENGERS)
                            {
                                llWhisper(CHAT_CH, "The elevator is only rated for " + (string)MAX_PASSENGERS + ". Overcrowding could cause failures");
                            }
                            //if we are empty start a timer to close the doors/dim the lights
                            else if (newPax == 0)
                            {
                                gTimerFlag = TF_EMPTY;
                                llSetTimerEvent(TIME_EMPTY);
                                //advise the buttons of status
                                gCurStatus = S_READY;
                                llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
                            }
                        }
                    }//end enter/exit
                    //return form an update check on anyone being in the elevator
                    else if (function == F_PAX_UPDATE)
                    {
                        //update the list
                        gPaxList = llParseString2List(llList2String(params, 1), [DATA_SEP], [EMPTY_STR]) + gSeatedList;
    
                        //set a timer to check again .. due to the edge cases where TPs, external sits, and logouts do not register
                        gTimerFlag = TF_CHECKPAX;
                        llSetTimerEvent(TIME_CHECKPAX);
                    }
                    //check on pax shows us empty
                    else if (function == F_NO_PAX)
                    {
                        gPaxList = [];
                        //start the timer to close the doors
                        gTimerFlag = TF_EMPTY;
                        llSetTimerEvent(TIME_EMPTY);
                    }
                    //we have been called by a button .. can only be called if I am ready
                    else if (function == F_CALL && gCurStatus == S_READY)
                    {
                        string caller = llList2String(params, 1);
                        //already here so just open the doors
                        if (caller == gCurPosit)
                        {
                            set_doors(DS_OPEN);
                        }
                        //we need to move to the caller
                        else
                        {
                            //make sure to clear timer flag and timers
                            llSetTimerEvent(0.0);
                            gTimerFlag = EMPTY_STR;
    
                            //stop any listen listen
                            if (gOpenChatHandle != 0)
                            {
                                llListenRemove(gOpenChatHandle);
                                gOpenChatHandle = 0;
                            }
                            gMvSpeed = P_FAST;
                            gCalled = TRUE;
                            start_move();
                        }
                    }
                }//end main ch
                else if (channel == CHAT_CH)
                {
                    gFailCode = EMPTY_STR;
                    message = llStringTrim(llToLower(message), STRING_TRIM);
                    integer continue = (message == YES);
    
                    //if not a yes .. check to see if any auto fail params were passed:
                    //b = simple stop break
                    //s = fall a bit and stop
                    //f = fall to bottom
                    //c = catastrophic
                    if (!continue)
                    {
                        list params = llParseString2List(message, [" "], [EMPTY_STR]);
                        message = llList2String(params, 0);
    
                        if (message == GO)
                        {
                            //check for a fail param
                            if (llGetListLength(params) > 1){gFailCode = llList2String(params, 1);}
                            //if the number of seated AVs is less than the number of AVs in the elevator advise and ask to continue
                            if (llGetListLength(gSeatedList) < llGetListLength(gPaxList))
                            {
                                llWhisper(CHAT_CH, "Not all passengers are in position, this could be dangerous. If you would like to continue just say yes");
                                //reset timer to new Check go timer
                                gTimerFlag = TF_CHECKGO;
                                llSetTimerEvent(TIME_CHECKGO);
                            }
                            else
                            {
                                continue = TRUE;
                            }
                        }
                    }//end !continue
    
                    if (continue)
                    {
                        //make sure to clear timer flag and timers
                        llSetTimerEvent(0.0);
                        gTimerFlag = EMPTY_STR;
    
                        //stop listen
                        if (gOpenChatHandle != 0)
                        {
                            llListenRemove(gOpenChatHandle);
                            gOpenChatHandle = 0;
                        }
                        gMvSpeed = P_SLOW;
                        start_move();
                    }
                }//end Chat CH
            }//end !disabled        
        }
        
        link_message(integer sender, integer channel, string function, key data)
        {
            if(channel == MAIN_CH)
            {
                //call from one of the positioners
                if (function == F_SEATED || function == F_STOOD)
                {
                    //how many were seated
                    integer curSeated = llGetListLength(gSeatedList);
                    //we update our list
                    //will have passed key of AV seated or stood
                    update_pax_list(function, [(string)data]);
                    //see how many seated now
                    integer newSeated = llGetListLength(gSeatedList);
    
                    //if no one is seated
                    if (newSeated == 0)
                    {
                        //if active, turn off the open chat listen
                        if (gOpenChatHandle != 0)
                        {
                            llListenRemove(gOpenChatHandle);
                            gOpenChatHandle = 0;
                        }
                        //reset the check sit flag ...
                        //in an arrival this will be overridden by a free timer
                        //if we are in disabled mode ignore this
                        if (gCurStatus != S_DISABLED)
                        {
                            gTimerFlag = TF_CHECKSIT;
                            llSetTimerEvent(TIME_CHECKSIT);
                        }
                    }
                    //if someone has sat and we are disabled .. unseat them and advise
                    else if (newSeated > 0 && gCurStatus == S_DISABLED)
                    {
                        //unseat the avatars
                        llMessageLinked(LINK_ALL_OTHERS, POSIT_CH, F_UNSEAT, NULL_KEY);
                        llWhisper(CHAT_CH, "The Elevator is out of service");                
                    }
                    // if first seated start listen and advise
                    else if (newSeated > 0 && curSeated == 0)
                    {
                        gOpenChatHandle = llListen(CHAT_CH, EMPTY_STR, NULL_KEY, EMPTY_STR);
                        llWhisper(CHAT_CH, "When everyone is seated and you are ready, just say go in chat. I will listen for one minute");
    
                        //set a flag to stop listening if they do not say go
                        gTimerFlag = TF_CHECKGO;
                        llSetTimerEvent(TIME_CHECKGO);
                        //and set our status to inuse .. in the case it had gone to ready while they stood around
                        gCurStatus = S_INUSE;
                        llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
    
                    }
                }//end seated/stood
                //calls from the mover
                else if (function == F_MOVED)
                {
                    //set our status
                    gMvStatus = S_ARRIVING;
    
                    //if at top call the bridge
                    if (gTargetPosit == POSIT_UP)
                    {
                        llWhisper(BRIDGE_CH, F_MOVE + FUNC_SEP + POSIT_OPEN);
                        gTimerFlag = TF_BRIDGE;
                        llSetTimerEvent(TIME_BRIDGE); 
                    }
                    else
                    {
                        continue_move();
                    }
                }
                else if (function == F_REQ_LINKS)
                {
                    llMessageLinked(LINK_THIS, MOVE_CH, F_LINKS, (key)LINK_DATA);
                }
                else if (function == F_MALFUNC)
                {
                    //we are currently disabled
                    gFailure = (string)data;
                    gCurStatus = S_DISABLED;
    
                    if (gFailure != FAIL_CATASTROPHIC)
                    {
                        //finish the move .. i.e. open doors etc.
                        gTargetPosit = POSIT_DWN;
                        gMvStatus = S_ARRIVING;
                        gMvSpeed = P_SLOW;
                        continue_move();
    
                        //advise they have 1 min to get out
                        llWhisper(CHAT_CH, "Please exit as quickly as possible. In one minute the elevator will shut down and close");
                        gTimerFlag = TF_DISABLE;
                        llSetTimerEvent(TIME_DISABLE);
                    }
                    llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
                }
            }//end main ch
        }
    
        on_rez(integer start_param)
        {
            //check if being rezzed by the repair script. i.e. new instance
            if (start_param == REPAIR_REZ){llResetScript();}
        }
    
        touch_end(integer total_number)
        {
            //only care if we are disabled
            if (gCurStatus == S_DISABLED)
            {
                //if the owner, re-enable .. all repaired
                if (llDetectedKey(0) == llGetOwner())
                {
                    //anything but a catastrophic we are now ready
                    if (gFailure != FAIL_CATASTROPHIC)
                    {
                        llSetTimerEvent(0.0);
                        gCurStatus = S_READY;
                        gTimerFlag = EMPTY_STR;
                        llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
                        //and advise the move that it is repaired
                        llMessageLinked(LINK_THIS, MOVE_CH, F_REPAIRED, NULL_KEY);
                    }
                    //for a catastrophic we'll need to rez a new elevator
                    else
                    {
                        llShout(REPAIR_CH, F_REPAIR);
                    }
                }          
            }
        }
    
    
        timer()
        {
            llSetTimerEvent(0.0);
            
            //Empty so close doors
            if (gTimerFlag == TF_EMPTY)
            {            
                set_doors(DS_CLOSE);
            }
            //AV has been in the elevator for time, but not sat, return status to ready
            //after arrival .. if they have not left the elevator after a time we make it callable
            //time has passed so a technician has repaired
            else if (gTimerFlag == TF_CHECKSIT || gTimerFlag == TF_FREE || gTimerFlag == TF_REPAIR)
            {
                //could be here for a free timer when disabled .. check for that
                if (gTimerFlag != TF_FREE || (gTimerFlag == TF_FREE && gCurStatus != S_DISABLED)){gCurStatus = S_READY;}
                llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);
    
                //if was a checksit or free .. need to see if we actually have anybody left
                //certain actions will not trigger an exit (sit outside, LM TP out, map click TP out, LogOut)
                //tell the active detector to run a sensor            
                if (gTimerFlag == TF_CHECKSIT || gTimerFlag == TF_FREE){llWhisper(MAIN_CH, F_CHECK_PAXS);}
            }
            else if (gTimerFlag == TF_CHECKPAX)
            {
                //call to check for Pax
                
                //tell the active detector to run a sensor
                llWhisper(MAIN_CH, F_CHECK_PAXS);
            }
            //AV has been seated and not said go ... advise and stop listen
            else if (gTimerFlag == TF_CHECKGO)
            {
                if (gOpenChatHandle != 0)
                {
                    llListenRemove(gOpenChatHandle);
                    gOpenChatHandle = 0;
                }
                llWhisper(CHAT_CH, "I did not hear anything from you. To re-enable, everyone must stand and then sit again.");
                gCurStatus = S_READY;
                llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus);     
            }
            //after a non-catastrophic failure .. we give them a bit to get out of the elevator ..
            //then close up and wait for either owner to repair, or an hour to pass
            else if (gTimerFlag == TF_DISABLE)
            {
                set_doors(DS_CLOSE);
                gTimerFlag = TF_REPAIR;
                llSetTimerEvent(TIME_REPAIR);
            }
            //bridge did not respond, broken for some reason
            //advise owner and continue
            else if (gTimerFlag == TF_BRIDGE)
            {
                llOwnerSay("There was a failure in the Bridge, it did not respond");
                continue_move();
            }
    
            gTimerFlag = EMPTY_STR;  
        }
    }
    //[/PHP]

     

  6. Hi All,

    In the following you will find code I have written for an Elevator. While it is pretty specific to the build I wrote it for, I hope there are things in here that are of use to the community. During the development of this I pestered the forum quite a bit, and without the help garnered there I doubt I would have been able to get this done. The relevant threads are linked below at the bottom of this post, and I wanted to give special thanks to @Wulfie Reanimator  @Rolig Loon @Love Zhaoying @Innula Zenovka @ellestones and @Qie Niangao for their help and constant willingness to answer all of (my) our questions.

    The elevator linkset is made up of:

    • Two east/west wide mesh piece walls
    • Four north/south narrow mesh piece walls (two each side)
    • Four north/south mesh piece doors (two each side), with north doors opening at top, and south at bottom
    • A mesh fluorescent tube light, and a single prim that actually sheds the light using values for FOV etc set into the prim
    • A mesh piece roof
    • A base mesh piece bar that is the root
    • Four invisible prims used for sit positions
    • Each of the walls and doors has upper and lower faces that are the windows
    • Total LI:10
    • Two buttons of 4 LI each are also part of the system These are not meshed as I could not get MeshStudio to mesh them with the same look.
    • Two detector prims of 1 LI each
    • One Bridge of 2 LI
    • Two Boundary Prims, 1LI

    Total LI for the solution: 23


    You can see this in action at my home HERE . If you would like a copy of the elevator please IM me inworld .. happy to share 🙂

    There are a number of scripts that went into this, and I'll post them following in the following order:

    Core - Scripts in the Elevator Linkset

    • Main - The core script for controlling the elevator...coordinates the efforts of all the other scripts and components. Contains the code for the elevator doors as well as the light. In the root Prim.
    • Move - Actual mover of the elevator .. separated out from main principally so that main can receive the collision calls from the detector when moving to a new position. Also contains malfunction code. In the root Prim.
    • Positioner - Used to set the location, pose, and camera for the AVs that 'sit' in the elevator. In each of the invisible positioner prims that are part of the elevator linkset.

    Components - Scripts in other prims

    • Button - Button to call the elevator .. will change colors based on status and give relevant messages. One at the top and one at the bottom
    • Detector - Simple detection of if an av has entered or left the elevator. Set in an invisible prim the size of the elevator, one at the top and one at the bottom
    • Bridge - This moves the bridge at the top of the elevator 'shaft'. This was needed because I had already buolt the rest before I put in the elevator, and at the top there was a gap between the landing and the elevator.
    • Boundary - Will sit in prims along the parcel boundary to detect when a catastrophically failed elevator, in physical state, is about to tumble off the parcel and stop it
    • Repair - In the case of a catastrophic failure, where the elevator goes off the track, this simple controller will kill the existing elevator and rez a new one. Has a copy of the elevator in its inventory.

    Hopefully I have commented sufficiently the code for it to be understandable, but I am sure there will be questions so do not hesitate to ask.

    Coding Guidelines
    I try to follow the same 'guidelines throughout my coding. It has been 20+ years since I coded professionally but I try to follow consistent guidelines when coding. The following are the guidelines I use that will hopefully help you read my code

    Variables

    • NAME_NAME - Constants. In some cases these are not true constants, but set on load or as near as possible. They will not be modified anywhere else in the code
    • gName - Global variable that can be modified anywhere
    • sName - State variable that is 'global' just to a particular state
    • nameName - All local variables start in lowercase. If, for readability, they are comprised of more than one word, the additional words start with a Cap
    • name_name - either a function or a state

    I tend to use constants a lot, even for simple things like "" .. this is for a few reasons:

    1. Ensures I do not have unintentional errors, like an accidental " "
    2. When there is cross module communication, ensure I am passing the correct values
    3. Let’s me use auto-complete in LSLEditor 🙂

    The constant definitions in this solution are divided into various sections:

    • Instance - If using my elevator, specific to each installation of the elevator (inworld positions)
    • Specific - These are specific to the actual elevator used. You will need to either change these for another elevator, or ensure that the pieces of you elevator match these
    • Modifiable preference - These are setting you can change without affecting the functioning of the elevator. Things like the light setting, how fast the elevator moves, channel number, etc.
    • Set on Start -Constants that are set at the start, either being passed values from other scripts, calculated, or read from the object descriptions
    • General - These really should not be changed. They are internal to the solution, and changing them has no effect on the functioning of the elevator. To the contrary changing some could cause failures if they are not changed in all modules that use them, particular things like function names (see below), status values, etc.

    Communication
    When communicating between scripts, be it with Link Messages or in chat channels I try to follow the same procedure. All messages provide a function name, and if required parameters.

    • In a link message, you'll see that I have changed the definition of the event to: link_message(integer sender, integer channel, string function, key data). The channnel param has a channel number (to simulate a listen event), function has the function name, and data has the values, separated by ^ (DATA_SEP)
    • In a listen event since we only have control over the message parameter, this is passed as function|data^data^...

    Topic Links
    Automatic Sit Animation
    More Animation Scripting Question
    Collision 'Face'
    More Collisions ...
    'Controlling' Physical Prims
     

     

    • Thanks 4
  7. Ok .. was able to get inworld to do some simple tests.

    Scenario: 4x4x4 prim with the following code in it:

    
    //######
    //Functions
    integer get_av_count(integer total_number)
    {
        //filter out any objects .. to only get count of AVs
        integer x;
        integer numAVs = 0;
        for (x = 0; x < total_number; x++)
        {
            if (llDetectedType(x) & AGENT){numAVs++;}
        }
    
        return numAVs;  
      
    }
    //End Functions
    //......
    
    
    //Body
    default
    {
        state_entry()
        {
            //set volume detect so that prim is 'phantom'
            llVolumeDetect(TRUE);
        }
    
        collision_start(integer total_number)
        {
            integer numAVs = get_av_count(total_number);
    
            llWhisper(0, "Enter");
            llWhisper(0, "Tot: " + (string)total_number);
            llWhisper(0, "Avs: " + (string)numAVs);
    
            integer x;
            list names;
            for (x = 0; x < total_number; x++)
            {
                names = names + llDetectedName(x);
            }
            llWhisper(0, llList2CSV(names));
            
        }
    
        collision_end(integer total_number)
        {
            integer numAVs = get_av_count(total_number);
    
            llWhisper(0, "Exit");
            llWhisper(0, "Tot: " + (string)total_number);
            llWhisper(0, "Avs: " + (string)numAVs);
    
            integer x;
            list names;
            for (x = 0; x < total_number; x++)
            {
                names = names + llDetectedName(x);
            }
            llWhisper(0, llList2CSV(names));     
        }
    }

    ... then stuck another prim inside the 4x4x4 prim

    • Walk in/out: _start/_end events fire
    • Cam in to sit: Nothing
    • Cam out to sit outside: Nothing
    • Inside 4x4x4 sit: Nothing
    • Inside 4x4x4 stand:: _start event fires
    • Move the interior (or an exterior) prim in/out with or without seated AV: Nothing
    • move the interior prim with AV standing on it: _start/_end fire
    • Log Out : Nothing
    • Log In: _start fires

    Then I tried a number of TP combinations:

    • double click TP in/out: _start/_end fire
    • double click TP out from sit: Nothing
    • map click TP out: Nothing
    • LM TP out: Nothing ++ no _start event fires on re-entry, until after an _end event has fired or 5-10 mins have passed this only affect the AV in question
    • LM TP in: _start event fires

    So in summary some very strange and inconsistent behavior.

     

     

  8. I too vaguely remember reading something back years ago when I first started with LSL that said something about potential problems with touch_start, and have always just used touch_end ... only times I have a touch_start is when I want something to happen differently if the user has held down the touch for a bit .. but then is always paired with a touch_end that then checks the time between the _start and _end to see if the click was held long enought.

  9. hmmm ... and so therefore what happens when they stand inside the detector prim .. will another collision_start event be raised at that point? And conversely if they sit on the prim inside the detector will a collision_end be raised?

     

  10. Hi All,

    This is a follow-on to the topic I started a few days back "Collision 'Face'" found HERE. After that discussion my design decision was to simply have a prim the size of the elevator, with collision_start giving me the count when someone entered the elevator and collision_end the number when someone exited. As I was cleaning up my comments and code a bit though, a doubt came to mind:

    Does a seated AV raise the _start/_end events? If I have an AV seated on a prim, and that prim is moved into the detector prim, will the total_number parameter passed to the event be 2, and so I'll be able to find the AV? The wiki is not clear in my view on this. Whilst is does say:

    Quote

    Will not detect collisions between an avatar sitting on the task and the task itself (avatars are linked to objects they sit on so there is no collision ..)

    In this case the AV is seated (linked) on a different prim from the one with the script. If it does count it, that would lead me to understand that a linkset that collided with the detector would be treated for the event as all the individual prims it is made up of?

    To the contrary, i.e. the total_number passed in the suggested scenario is only 1, and that one being the prim itself, it would lead me to conclude that if the AV stepped into the detector prim (raising collision_start), and then sat on a prim that was inside said detector, then a collision_end would be raised?

    Anyone?

  11. 21 minutes ago, Rolig Loon said:

    tart a timer when it falls, A reasonable time later, turn it non-physical.  All done.

    Does that mean that turning it non-physical will stop it's tumbling .. i.e. freeze it in whatever location/position it is in at that instant?

  12. One idea that just came to mind for stopping the physical elevator tumbling, but not quite sure how/if it would work:

    • Put up some transparent walls along the parcel border. These would normally be phantom, but could turn solid/physical while the elevator is tumbling, so that they would stop it? Tend to remember that unless they are physical it would go through them, and would likely have to shore them up so that the elevator did not tip them over
    • Or the same idea, but only have them there to detect the collision from the elevator and then they could send a message to the elevator. That brings me back to the question though of how to stop the tumbling elevator.
  13. Hi All,

    Next chapter in the scripting of my elevator. I have all the rest of the code written, though not tested yet (not getting a lot of in-world time). What I have left is how to script one specific scenario. While I built a good solid elevator, things fail now and then, particularly if people overload the elevator. So I have a check whilst the elevator is moving to see if there has been a malfunction. IF a malfunction occurs there are four things that could happen, listed below in order of likelihood:

    1. the elevator comes to a stop for a minute while the remote technicians get it going again.
    2. It falls a few meters, is stopped for a bit, and then goes to the bottom, being out-of-service until a technician (me) actually comes by to reset it (or an hour passes)
    3. It falls all the way to the bottom, but the emergency brake is able to stop it, again going out of service
    4. OR - Catastrophic Failure .. it falls and the brake does not stop it, it breaks off of the guide pole, and tumbles down the hill, ending up at a random angle with the doors broken and askew (note: this is an exterior elevator, not in an enclosed shaft, that travels up and down a pole using electromagnetic forces to move)

    It is the fourth scenario that I am trying to figure out how best to have occur and code it. What I was thinking was that I could turn the elevator physical, and thus let the SL physics engine take care of it falling and tumbling. This though brings me to my questions.

    • I am imagining that the only way I can tell is it has stopped tumbling is with a timer event and llGetPos .. checking to see when it stops changing. Is there any other way that anyone can think of?
    • What I do not want is for it to tumble off my land (and onto the Linden Road). Not only because I do not want to block the road, but as well I do not want it returned to my inventory. So again I could be tracking its position or as well use llOverMyLand, though this would only tell me after the fact. What I want to do is stop it before it leaves. So my question is, if I track where it is, and somehow know when it is about to leave the parcel, would setting it's Physical to FALSE stop it in it's tracks?
  14. @animats

    Thanks for the suggestion but is over the top for what I need and as well would require a lot of additional code. All I need to know is if anyone has come into the elevator or has left. Do not care about the individual, rather just the total number of passengers inside the elevator. Tracking the AV would mean keeping track of all that have entered, and considering the space inside is not extensive (it is an elevator after all) the changes in position would be minimal, with their location inside the elevator being irrelevant.

  15. The problem is that llGiveInventory takes a string as it's second parameter, not an integer. And that string is the name of the inventory item to give. What you'll need to do is using llGetInventoryNumber, and llGetInventoryName, compile a list of the inventory items and then best use llGiveInventoryList to give the items to the AV.

     

     

    • Thanks 1
  16. Actually, with the requirement that I have a 'sit' script in each sit prim, I have pulled the change event from the main. The code in the individual scripts is:

     

        changed(integer change)
        {
            if(change & CHANGED_LINK)
            {
                integer num = 0;
                //see if I was sat on .. or someone that was sitting has gotten up
                key user = llAvatarOnSitTarget();
                if (user != NULL_KEY)
                {
                    //they sat ... request permissions to pose them
                    llRequestPermissions(user, PERMISSION_TRIGGER_ANIMATION);
                    num = 1;
                }
                //if we had someone .. then they have stood up and we need to stop their animation
                else if (gCurUser != NULL_KEY)
                {
                    llStopAnimation(ELEV_STAND);
                    gCurUser = NULL_KEY;
                    num = -1;
                }
                //advise main
                if (num != 0)
                {
                    string sNum = (string)num;
                    llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_SEATED, sNum);
                }
            }
        }
        
    
        run_time_permissions(integer permissions)
        {
            if (permissions & PERMISSION_TRIGGER_ANIMATION)
            {
                //get their key
                gCurUser = llGetPermissionsKey();
                
                ///stop the default sit
                llStopAnimation(DEF_SIT);
    
                //and start mine
                llStartAnimation(ELEV_STAND);
              
            } 
        }        

     

  17. Thanks @ellestones that is what I understood:

        changed(integer change)
        {
            if(change & CHANGED_LINK)
            {
                integer num = 0;
                //see if I was sat on .. or someone that was sitting has gotten up
                key user = llAvatarOnSitTarget();
                if (user != NULL_KEY)
                {
                    //they sat ... request permissions to pose them
                    llRequestPermissions(user, PERMISSION_TRIGGER_ANIMATION);
                    num = 1;
                }
                //if we had someone .. then they have stood up and we need to stop their animation
                else if (gCurUser != NULL_KEY)
                {
                    llStopAnimation(ELEV_STAND);
                    gCurUser = NULL_KEY;
                    num = -1;
                }
                //advise main
                if (num != 0)
                {
                    string sNum = (string)num;
                    llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_SEATED, sNum);
                }
            }
        }
        
        link_message(integer sender, integer channel, string function, key data)
        {
            if (channel == MAIN_CH)
            {
                //call from main to unseat any AV
                if (function == F_UNSEAT && gCurUser != NULL_KEY)
                {
                    llUnSit(gCurUser);
                    llStopAnimation(ELEV_STAND);
                }
            }
        }
        
         run_time_permissions(integer permissions)
        {
            if (permissions & PERMISSION_TRIGGER_ANIMATION)
            {
                //get their key
                gCurUser = llGetPermissionsKey();
                
                ///stop the default sit
                llStopAnimation(DEF_SIT);
    
                //and start mine
                llStartAnimation(ELEV_STAND);
              
            } 
        }   

     

  18. hmmm .. ok . .now this leads me to other questions <ha ha> ... if I now have a script in each of the sitter prims am I correct in my understanding that the changed event will fire in each of these four scripts when an AV sits or stands, and the way for a particular script to know it it was them that was sat on is through llAvatarOnSitTarget(), coming up null if not there and with a key if so?

×
×
  • Create New...