Jump to content

Elevator


Wandering Soulstar
 Share

You are about to reply to a thread that has been inactive for 1959 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

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
Link to comment
Share on other sites

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]

 

Link to comment
Share on other sites

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]

 

Link to comment
Share on other sites

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]

 

Link to comment
Share on other sites

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]

 

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1959 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...