Jump to content

Wandering Soulstar

Resident
  • Content Count

    421
  • Joined

  • Last visited

Everything posted by Wandering Soulstar

  1. My Elevator is finally finished and all the bugs (hopefully) out of the code. I have posted the full code in the library if anyone is interested (HERE). Thanks a million to everyone that helped in my questions while I was working on it. And if you'd like to come by and give it a try please do! You can find my home HERE
  2. Components These are all very short and hopefully clear scripts Button - Button to call the elevator .. will change colors based on status and give relevant messages. One at the top and one at the bottom //[PHP] // // // ******************************************************************* // button // 12/18 // v1.0.0 // // button to call the elevator .. will change colors based on status // and give relevant messages // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //specific to the button used //prim that we change the color on string COLOR_PRIM = "colour prim"; //face we change integer BUT_FACE = 3; //end specifics //...... //modifiable based on preferences //colors vector RED = <1.000, 0.255, 0.212>; vector GREEN = <0.180, 0.800, 0.251>; vector YELLOW = <1.000, 0.863, 0.000>; vector GREY = <0.502, 0.502, 0.502>; //time for a flash float FLASH_TIME = 1.0; //channels integer CHAT_CH = 0; //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer MAIN_CH = -140100; //end modifiable //...... //set on start //link to prim we set color on integer COLOR_LINK = 0; //will hold either up or down, set in obj desc string ME; //holds the help text, set in state entry string HELP_TEXT; //end start set //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly string EMPTY_STR = ""; string FUNC_SEP = "|"; string DATA_SEP = "^"; string NEW_LINE = "\n"; //functions //data requests to me and returns string F_REQ_STATUS = "req status"; string F_STATUS = "status"; string F_REQ_POSITION = "req posit"; string F_POSITION = "posit"; //my call to ~main string F_CALL = "call"; //positions string POSIT_MOVING = "moving"; //status string S_READY = "ready"; string S_INUSE = "inuse"; string S_INROUTE = "inroute"; string S_DISABLED = "disab"; //voice messages from users string HELP = "help"; //End Constants //...... //###### //Global Variables //handle when listening for a help call integer gOpenChatHandle = 0; //current status string gCurState; //current position of the elevator string gCurPosit; //for the flash vector gColor; integer gBright; float gGlow; //counter used in timer for help float gCounter; //End Global Variables //...... //###### //Functions //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels MAIN_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); //get what button I am up/down ME = llGetObjectDesc(); //gett he link to the prim we set the color on integer found = FALSE; integer numLinks = llGetNumberOfPrims(); integer ctr = 0; while (ctr < numLinks && !found) { ctr++; found = (llGetLinkName(ctr) == COLOR_PRIM); if (found){COLOR_LINK = ctr;} } //if we don't find it error will occur //set default state to disabled gCurState = S_DISABLED; gColor = GREY; gBright = FALSE; gGlow = 0.0; llSetLinkPrimitiveParamsFast(COLOR_LINK,[PRIM_COLOR, BUT_FACE, gColor, 1.0, PRIM_FULLBRIGHT, BUT_FACE, gBright, PRIM_GLOW, BUT_FACE, gGlow]); //colours will be //green when ready to be called, either empty at other posit, or at my posit //S_READY //red when being used either at the other location, or moving to the other location //S_INUSE //flashing yellow when moving to my location //S_INROUTE //non-bright/glow grey .. out of service //S_OUTSERVICE //Help Text HELP_TEXT = NEW_LINE + "When the elevator arrives you can enter, and should hold onto one of the side railings (select to sit)"; HELP_TEXT += NEW_LINE + "Once seated all you need to do is say go in open chat"; HELP_TEXT += NEW_LINE + NEW_LINE + "There is room in the elevator for 4 passengers to hold the rail."; HELP_TEXT += NEW_LINE + "Traveling with more could cause the elevator to fail, and in an case would be un comfortable for the extra passenger(s)"; HELP_TEXT += NEW_LINE + NEW_LINE + "The button colors indicate the status of the elevator:"; HELP_TEXT += NEW_LINE + "Green: Ready"; HELP_TEXT += NEW_LINE + "Yellow(flashing): Inroute to this position"; HELP_TEXT += NEW_LINE + "Red: Currently in use"; HELP_TEXT += NEW_LINE + "Grey: Out-of Service. A technician will have it repaired in under an hour"; HELP_TEXT += NEW_LINE + NEW_LINE + "If you have any additional questions please feel free to contact me Wanda(Wandering) Soulstar"; //listen to the main channel llListen(MAIN_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); //ask the elevator for status I can set my color llShout(MAIN_CH, F_REQ_STATUS); //and request the position llShout (MAIN_CH, F_REQ_POSITION); } listen(integer channel, string name, key id, string message) { if (channel == CHAT_CH) { //AV has requested help if (llToLower(llStringTrim(message, STRING_TRIM)) == HELP) { //if we are not inroute we tuen off the timer if (gCurState != S_INROUTE){llSetTimerEvent(0.0);} //stop listening for a help request llListenRemove(gOpenChatHandle); gOpenChatHandle = 0; gCounter = 0; //give help llWhisper(CHAT_CH, HELP_TEXT); } } else if (channel == MAIN_CH) { list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]); string func = llList2String(params, 0); string newState = EMPTY_STR; if (func == F_STATUS) { newState = llList2String(params, 1); } else if (func == F_POSITION) { //here we only change the status if it is moving towards us //params will be current position (up, dwn, moving) and if moving, the target (up, down) params = llParseString2List(llList2String(params, 1), [DATA_SEP], [EMPTY_STR]); gCurPosit = llList2String(params, 0); if (gCurPosit == POSIT_MOVING) { //if moving to me the it is in route if (llList2String(params, 1) == ME) { newState = S_INROUTE; } else { newState = S_INUSE; } } }//end Position //if we have a status and it has changed .. see what we might need to do if (newState != EMPTY_STR && newState != gCurState) { gCurState = newState; //default values, only changed when disabled gBright = TRUE; gGlow = 0.1; //set the button color if (gCurState == S_READY) { gColor = GREEN; } else if (gCurState == S_INUSE) { gColor = RED; } else if (gCurState == S_INROUTE) { gColor = YELLOW; } else if (gCurState == S_DISABLED) { gColor = GREY; gBright = FALSE; gGlow = 0.0; } llSetLinkPrimitiveParamsFast(COLOR_LINK,[PRIM_COLOR, BUT_FACE, gColor, 1.0, PRIM_FULLBRIGHT, BUT_FACE, gBright, PRIM_GLOW, BUT_FACE, gGlow] ); //if we are going to flash .. check to set timer //if not chat handle we have no timer going if (gCurState == S_INROUTE && gOpenChatHandle == 0) { llSetTimerEvent(FLASH_TIME); } //otherwise, unles we are listening for help, turn off any timer else if (gOpenChatHandle == 0) { llSetTimerEvent(0.0); } } }//end main_ch } touch_end(integer total_number) { string message = EMPTY_STR; //based on the current state give a message and do an actions if (gCurState == S_READY) { //just call the elevator //if it is here it will open the doors //if not will begin the journey to me llShout(MAIN_CH, F_CALL + FUNC_SEP + ME); //if it is not here already let them know it is coming if (gCurPosit != ME) { message = "Elevator has been called, it will be with you shortly"; } } else if (gCurState == S_INUSE) { message = "The elevator is currently in use. The button will turn green when it is ready, then you can call it."; } else if (gCurState == S_INROUTE) { message = "The Elevator is on its way, please be patient."; } else if (gCurState == S_DISABLED) { message = "The Elevator is currently out of service. A technician has been called."; } //send the message if we have one if (message != EMPTY_STR){llWhisper(CHAT_CH, message);} //if we have not offered help already, do so if (gOpenChatHandle == 0) { llWhisper(CHAT_CH, "If you would like help, just say help in chat. I'll be listening for 1 minute"); gOpenChatHandle = llListen(CHAT_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); //set a timer if we are not already in flash mode if (gCurState != S_INROUTE) { //since flash mode means timer events need to happen every second, we will count the number of times //we hit the event so that at 60 secs we can turn off the listen gCounter = 0; llSetTimerEvent(FLASH_TIME); } } } timer() { //increment to counter to stop listening if (gOpenChatHandle != 0){gCounter = gCounter + FLASH_TIME;} //if a minute has passed, stop listening if (gCounter >= 60.0) { llListenRemove(gOpenChatHandle); gOpenChatHandle = 0; gCounter = 0; //and if not in flash, stop the timer if (gCurState != S_INROUTE){llSetTimerEvent(0.0);} } //if we are flashing, chage the state if (gCurState == S_INROUTE) { //for bright gets the opposite gBright = !gBright; //for glow by using Fabs we cycle between 0.1 and 0 gGlow = llFabs(gGlow - 0.1); llSetLinkPrimitiveParamsFast(COLOR_LINK,[PRIM_FULLBRIGHT, BUT_FACE, gBright, PRIM_GLOW, BUT_FACE, gGlow]); } } } //[/PHP] Detector - Simple detection of if an av has entered or left the elevator. Set in an invisible prim the size of the elevator, one at the top and one at the bottom //[PHP] // // // ******************************************************************* // detector // 12/18 // v01.0.0 // // simple detection of if an av has entered or left the elevator // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //modifiable based on preferences //channels //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer MAIN_CH = -140100; //end modifiable //...... //set on start //will hold either up or down, set in obj desc string ME; //range for scans .. based on a square elevator and so 1/2 the width float CHECK_RANGE; //end start set //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly string EMPTY_STR = ""; string FUNC_SEP = "|"; string DATA_SEP = "^"; //functions //data requests to me and returns string F_POSITION = "posit"; string F_REQ_POSITION = "req posit"; //advise on AV movement string F_ENTER = "enter"; string F_EXIT = "exit"; //call to me and possible returns string F_PAX_UPDATE = "pax list"; string F_NO_PAX = "empty"; string F_CHECK_PAXS = "check paxs"; //parameters string POSIT_MOVING = "moving"; //End Constants //...... //###### //Global Variables //flag to respond to collision events integer gActive = FALSE; //End Global Variables //...... //###### //Functions //main function .. get the av keys and advise main advise_collisions(integer total_number, string action) { //get the keys of all avs that entered integer x; list avs = []; for (x = 0; x < total_number; x++) { if (llDetectedType(x) & AGENT){avs = avs + [llDetectedKey(x)];} } //if we actual had any avs .. then we will advise main if (llGetListLength(avs) > 0) { string avKeys = llDumpList2String(avs, DATA_SEP); llWhisper(MAIN_CH, action + FUNC_SEP + avKeys); } } //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels MAIN_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); //set volume detect so that prim is 'phantom' llVolumeDetect(TRUE); //will correspond to position (up/down) ME = llGetObjectDesc(); //set the target for sit close center //this is so that the unsit does not send them flying llSitTarget(<0.0, 0.1, 0.0>, ZERO_ROTATION); //set my range for when I need to do a sensor .. 1/2 my x vector size = llGetScale(); CHECK_RANGE = size.x * 0.5; //listen tto the main channel llListen(MAIN_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); //Ask main for where the elevator .. if main does not respond, not on yet llShout(MAIN_CH, F_REQ_POSITION); } listen(integer channel, string name, key id, string message) { if (channel == MAIN_CH) { //main advising where the elevator is list data = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]); string function = llList2String(data, 0); if (function == F_POSITION) { //positoin will be up/down/moving data = llParseString2List(llList2String(data, 1), [DATA_SEP], [EMPTY_STR]); //set activate flag ... we care about collisions when the elevator is: // stoped in our position // moving to our position (this is to get a count on arrival) //this has passed position and when moving .. target string curPosit = llList2String(data, 0); gActive = (curPosit == ME); if (!gActive && curPosit == POSIT_MOVING){gActive = (llList2String(data, 1) == ME);} } //main asking for a sensor check for pax //certain actions will not trigger an exit (sit outside, LM TP out, map click TP out, LogOut) else if (function == F_CHECK_PAXS) { llSensor("", NULL_KEY, AGENT, CHECK_RANGE, PI); } } } changed(integer change) { //do not want anyone sitting on the detector by accident if(change & CHANGED_LINK) { integer links = 0; if(llGetObjectPrimCount(llGetKey()) < (links = llGetNumberOfPrims())) { llUnSit(llGetLinkKey(links)); llWhisper(0, "You need to sit by clicking on the handrail. You need to cam closer to the rail"); } } } collision_start(integer total_number) { //pass if someone has come into the elevator/detector if (gActive){advise_collisions(total_number, F_ENTER);} } collision_end(integer total_number) { //pass if someone has left the elevator/detector if (gActive){advise_collisions(total_number, F_EXIT);} } sensor(integer total_number) { //pass the avs that were detected if (gActive){advise_collisions(total_number, F_PAX_UPDATE);} } no_sensor() { //tell main we are empty llWhisper(MAIN_CH, F_NO_PAX); } } //[/PHP] Bridge - This moves the bridge at the top of the elevator 'shaft'. This was needed because I had already built the rest before I put in the elevator, and at the top there was a gap between the landing and the elevator. //[PHP] // // // ******************************************************************* // bridge // 12/18 // v1.0.0 // // this moves the bridge at the top of the elevator 'shaft' this is // because there is a gap there and would not want people to fall. // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //instance specific to the instalation //in world positions of the bridge vector END_POS_OPEN = <13.35265, 209.81580, 111.35027>; vector END_POS_CLOSED = <13.35265, 210.15483, 111.35027>; //end instance //...... //modifiable based on preferences //how much I move each loop .. assumes y axis, base for open float CHG_AMOUNT = -0.1; //channels //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer MAIN_CH = -140100; integer BRIDGE_CH = -140120; //end modifiable //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly string FUNC_SEP = "|"; string EMPTY_STR = ""; //functions //call to me and the return string F_MOVE = "move"; string F_READY = "ready"; //positions string POSIT_OPEN = "open"; string POSIT_CLOSED = "closed"; //End Constants //...... //###### //Global Variables //my position POSIT_OPEN or POSIT_CLOSED string gCurPosit; //End Global Variables //...... //###### //Functions //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels MAIN_CH += ownerMod; BRIDGE_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); //default expected position is closed gCurPosit = POSIT_CLOSED; llSetPos(END_POS_CLOSED); //listen on my channel llListen(BRIDGE_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); } listen(integer channel, string name, key id, string message) { if (channel == BRIDGE_CH) { list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]); if (llList2String(params, 0) == F_MOVE) { //move the bridge to a new position //second param will be which way we are moving: POSIT_OPEN or POSIT_CLOSED string target = llList2String(params,1); //only proceeed if a target was passed if (target != EMPTY_STR) { //defualts are to open float chg = CHG_AMOUNT; vector endPos = END_POS_OPEN; if (target == POSIT_CLOSED) { // the bridge moves along y axis, so just flip the change factor chg *= -1; endPos = END_POS_CLOSED; } //flag to exit .. check to see if for some reason we are already at the target (should not happen) //if so then we'll just finish integer exit = (gCurPosit == target); //get where I currently am vector curPos = llGetPos(); //move while (!exit) { //next position curPos.y += chg; //check if we have moved past the end if ((target == POSIT_CLOSED && curPos.y > endPos.y)||(target == POSIT_OPEN && curPos.y < endPos.y)) { curPos = endPos; } //set new position and check if we are done llSetPos(curPos); exit = (curPos == endPos); } gCurPosit = target; } //advise main we are done llWhisper(MAIN_CH, F_READY); } } } } //[/PHP] Boundary - Will sit in prims along the parcel boundary to detect when a catastrophically failed elevator, in physical state, is about to tumble off the parcel and stop it //[PHP] // // // ******************************************************************* // boundary // 12/18 // v1.0.0 // // will sit in prims along the parcel boundary to detect when a // catastrophically failed elevator, in physical stat, is about // to tumble off the parcel // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //specific to the elevator used //name of the elevator .. stored in the prim string ELEVATOR = "elevator"; //end specifics //...... //modifiable based on preferences //channel //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer BOUNDARY_CH = -140140; //end modifiable //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly //function //obnly calll I make string F_HIT_BOUNDARY = "hit"; //End Constants //...... //###### //Functions //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels BOUNDARY_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); //set volume detect so that I am in-efect phantom llVolumeDetect(TRUE); } collision_start(integer total_number) { //only care if the elevator hits me //using a shout as there may be more than one prim in the set //and not sure how far from the center the collision might be if (llDetectedName(0) == ELEVATOR){llShout(BOUNDARY_CH, F_HIT_BOUNDARY);} } } //[/PHP] Repair - In the case of a catastrophic failure, where the elevator goes off the track, this simple controller will kill the existing elevator and rez a new one. Has a copy of the elevator in its inventory. //[PHP] // // // ******************************************************************* // repair // 12/18 // v1.0.0 // // in the case of a catastrophic failure, where the elevator goes // off the track, this simple controller will kill the existing // elevator and rez a new one // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //specific to the elevator used //name of elevator string ELEVATOR = "elevator"; //end specifics //...... //modifiable based on preferences //channels //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer MAIN_CH = -140100; integer REPAIR_CH = -140150; //end modifiable //...... //set on start //base down position of elevator .. sent by main vector BASE_POS; //end start set //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly string EMPTY_STR = ""; string FUNC_SEP = "|"; //functions //data request and return string F_REQ_DWN_POSIT = "req_dwn"; string F_DWN_POSIT = "dwn_pos"; //call to me and advise to ~main string F_REPAIR = "repair"; string F_DIE = "die"; //flag for elevator to reset itself on rez integer REPAIR_REZ = -5689321; //End Constants //...... //###### //Functions //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels MAIN_CH += ownerMod; REPAIR_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); llListen(REPAIR_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); //ask for the base of the elevator llShout(MAIN_CH, F_REQ_DWN_POSIT); } listen(integer channel, string name, key id, string message) { if (channel == REPAIR_CH) { list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]); string function = llList2String(params, 0); if (function == F_REPAIR) { //kill the exiting instance llShout(MAIN_CH, F_DIE); //rez a new copy llRezAtRoot(ELEVATOR, BASE_POS, ZERO_VECTOR, ZERO_ROTATION, REPAIR_REZ); } else if (function == F_DWN_POSIT) { BASE_POS = (vector)llList2String(params, 1); } } } } //[/PHP]
  3. Core - Positioner Used to set the location, pose, and camera for the AVs that 'sit' in the elevator. It keeps the Main apprised of when people sit or stand up. //[PHP] // // // ******************************************************************* // positioner // 12/18 // v1.0.0 // // used to set the location and pose for the AVs taht 'sit' in the elevator... // there are four 'hidden' prims each with this script // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //modifiable based on preferences //channels //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer MAIN_CH = -140100; integer POSIT_CH = -140110; //available poses .. either system or inventory list ELEV_STANDS = ["stand_2", "stand_3", "stand_4"]; //end modifiable //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly string EMPTY_STR = ""; string DATA_SEP = "^"; //functions //advise to ~main string F_SEATED = "seated"; string F_STOOD = "stood"; //~main call to me string F_UNSEAT = "unsit"; //system sit string DEF_SIT = "sit"; //End Constants //...... //###### //Global Variables //holds the key of seated AV key gCurUser = NULL_KEY; //holds the actual pose used string gPose; //End Global Variables //...... //###### //Functions //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels MAIN_CH += ownerMod; POSIT_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); //clear any existing sit target llSitTarget(ZERO_VECTOR, ZERO_ROTATION); //then get the vector and rot for the target, as well as the cameras //stored in the prim's desc (sit vector^sit rot^eye offset^camera offset) list vals = llParseString2List(llGetObjectDesc(), [DATA_SEP], [EMPTY_STR]); //and set llSitTarget((vector)llList2String(vals, 0), (rotation)llList2String(vals, 1)); //finally set the camera llSetCameraEyeOffset((vector)llList2String(vals, 2)); llSetCameraAtOffset((vector)llList2String(vals, 3)); } changed(integer change) { if(change & CHANGED_LINK) { //see if I was sat on .. or someone that was sitting has gotten up key user = llAvatarOnSitTarget(); if (user != NULL_KEY) { //they sat ... request permissions to pose them //when permission is granted we advise main llRequestPermissions(user, PERMISSION_TRIGGER_ANIMATION); } //if we had someone .. then they have stood up and we need to stop their animation //and advise main else if (gCurUser != NULL_KEY) { //if using just system anims do not need to turn off //code here to remind when using others //llStopAnimation(gPose); llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_STOOD, gCurUser); gCurUser = NULL_KEY; } } } link_message(integer sender, integer channel, string function, key data) { if (channel == POSIT_CH) { //call from main to unseat any AV if (function == F_UNSEAT && gCurUser != NULL_KEY) { llUnSit(gCurUser); //if using just system anims do not need to turn off //code here to remind when using others //llStopAnimation(gPose); } } } run_time_permissions(integer permissions) { if (permissions & PERMISSION_TRIGGER_ANIMATION) { //get their key gCurUser = llGetPermissionsKey(); llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_SEATED, gCurUser); ///stop the default sit llStopAnimation(DEF_SIT); //pick one integer use = (integer)llFrand(3.0); //hold to turn off later (when not using system) & start gPose = llList2String(ELEV_STANDS, use); llStartAnimation(gPose); } } } //[/PHP]
  4. Core - Move Actual mover of the elevator .. separated out from main principally so that main can receive the collision calls from the detector when moving to a new position. Also contains malfunction code. Located in the root Prim. Again, the basics were easy; the fun came in coding the malfunctions, particularly the catastrophic failure. This was my first time writing code for physical objects .. and has opened up a whole sideline of ideas for my next project :-). In a catastrophic failure one thing that will happen is the windows will 'break' Have yet to create the texture for these so for the moment just going blank. Note: If you look closely at the code, along with Main, you'll see how you can force the elevator to malfunction. Being in the elevator during a catastrophic malfunction is a laugh. If you figure this out and want to try at my place, come on by! (HERE) //[PHP] // // // ******************************************************************* // move // 12/18 // v1.0.0 // // actual mover of the elevator .. separated out from main principally // so that main can receive the collision calls from the detector when // moving to a new position. // // this also contains the code for the malfunction scenarios // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //instance constants specific to the instalation //for directed fails where we cause the failure vector FAIL_POINT_VECTOR = <13.35530, 208.23230, 104.96169>; //end instance //...... //specific to the elevator used //holds the positions/rotations for when the doors go askew //nw/ne door, sw/se door //pos^rot^pos^rot list ASKEW_VALUES = ["<-0.817486, 1.158951, 1.709961>^<0.004181, -0.095755, -0.043419, 0.994449>^<0.769676, 1.200195, 1.709961>^<-0.026177, 0.000000, -0.999657, 0.000000>", "<-0.707977, -1.549805, 1.709961>^<0.000000, 0.052336, 0.000000, 0.998630>^<0.911160, -1.403122, 1.709961>^<0.000000, 0.043619, -0.999048, 0.000000>"]; //hold the values when we need to set the windows //door up, door dwn, wide up, wide dwn, nrw up, nrw dwn, roof //until I have defined the texture .. use blank :5748decc-f629-461c-9a36-a35a221fe21f //face^UUID for texture list WINDOW_VALUES = ["2^5748decc-f629-461c-9a36-a35a221fe21f", "3^5748decc-f629-461c-9a36-a35a221fe21f", "3^5748decc-f629-461c-9a36-a35a221fe21f", "4^5748decc-f629-461c-9a36-a35a221fe21f", "4^5748decc-f629-461c-9a36-a35a221fe21f", "5^5748decc-f629-461c-9a36-a35a221fe21f", "1^5748decc-f629-461c-9a36-a35a221fe21f"]; //end specific //...... //modifiable based on preferences //increments in which we move along the z axis .. base to move up float CHG_AMOUNT = 0.5; float FAST_CHG_AMOUNT = 2.0; //threshold for a malfunction //a random # between 0 and 100 is generated, malfunctioning if it is higher than the threshold //each pax over the max (set in main) will reduce this by 20 points //to avaid malfunctions set very high (200+) integer FAIL_THRESHOLD = 99; //force applied in catastrophy vector CAT_PUSH_VECTOR = <-0.5, 0.0, -5.0>; vector CAT_ROT_VECTOR = <1.0, 0.0, -1.0>; //single letter codes the user can add to a go to force a malfunction //b = simple stop break string FC_STOP = "b"; string FC_FALL_STOP = "s"; string FC_FALL_FULL = "f"; string FC_CATASTROPHIC = "c"; //channels //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer MAIN_CH = -140100; integer MOVE_CH = -140130; integer BOUNDRY_CH = -140140; //end modifiable //...... //set on start //elevator world positions .. sent by main vector UP_POSIT; vector DWN_POSIT; //hold the possible actions when we are in a catastrophic failure //loaded with data when we get the links from main list BREAK_LIST; //hold the links for the lights be turned off -- sent by main list LIGHT_LINKS = []; //end start set //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly string EMPTY_STR = ""; string FUNC_SEP = "|"; string DATA_SEP = "^"; integer NOT_FOUND = -1; //functions //~main call to me and possible returns string F_MOVE = "move"; string F_MOVED = "moved"; string F_MALFUNC = "malfunc"; //~main advising string F_REPAIRED = "repaired"; //data request and return string F_REQ_LINKS = "req links"; string F_LINKS = "links"; //when hit a boundary (on catas. tumble) string F_HIT_BOUNDRY = "hit"; //positions string POSIT_UP = "up"; string POSIT_DWN = "dwn"; string POSIT_DWN_5 = "dwn 5"; //malfunction types string FAIL_CATASTROPHIC = "catastrophy"; string FAIL_STOP = "stop"; string FAIL_FALL_FULL = "fall full"; string FAIL_FALL_STOP = "fall stop"; //match fail codes to internal constant list FAIL_CODES = [FC_STOP, FAIL_STOP, FC_FALL_STOP, FAIL_FALL_STOP, FC_FALL_FULL, FAIL_FALL_FULL, FC_CATASTROPHIC, FAIL_CATASTROPHIC]; //break actions in catastrophy string BA_WINDOW = "win"; string BA_ASKEW = "ask"; //flags for move speed string P_FAST = "fast"; //End Constants //...... //###### //Global Variables //where we are moving to string gTargetPosit = EMPTY_STR; //## of extra passengers integer gOvrloadPax = 0; //flag if we have overloaded .. needed as # will be set to - during failure integer gOvrLoad = FALSE; //for directed fails string gFailCode = EMPTY_STR; //End Global Variables //...... //###### //State Variables //used in malfunction //hold the failure state to the timer string sFailure; //used in catastrophic //position to see when we stop moving vector sElevPos; //End State Variables //...... //###### //Functions //princiapl work function to move the elevator //chg: is the distance moved in each loop integer do_move(float chg, integer failure) { //set the target end pos value, default to up //same for change vector endPos = UP_POSIT; string target = POSIT_UP; if (gTargetPosit == POSIT_DWN || gTargetPosit == POSIT_DWN_5) { chg *= -1; target = POSIT_DWN; endPos = DWN_POSIT; //maybe only movng dwn 5m vector cur = llGetPos(); if(gTargetPosit == POSIT_DWN_5){endPos.z = cur.z - 5.0;} } //if there is not a user directed malfunction .. check to see if one occurs //if we are here because of a failure .. skip float failHeight = 0.0; float failUpper = 0.0; float failLower = 0.0; if (!failure) { integer willFail = FALSE; if (gFailCode == EMPTY_STR) { //seed the failure probability //for each extra person an additional 20% chance float failThresh = FAIL_THRESHOLD; failThresh = failThresh - (gOvrloadPax * 20); //see if we will fail willFail = ((integer)llFrand(101.0) >= failThresh); } //we have a user directed fail else { willFail = TRUE; } if (willFail) { //now if we will fail we determine at what point //possible is bottom z plus 2m to top z less 2m float range = (UP_POSIT.z - 2.0) - (DWN_POSIT.z + 2.0); //in case this comws to a random 0.0 add 0.1 failHeight = llFrand(range) + 0.1; //now set a range to catch when we move into it failUpper = DWN_POSIT.z + failHeight + (llFabs(chg) * 2); failLower = DWN_POSIT.z + failHeight - (llFabs(chg) * 2); } } //flag to exit integer exit = FALSE; integer fail = FALSE; vector curPos = llGetPos(); //move while (!exit && !fail) { curPos.z += chg; //if we have a fail height ... check if we are in the range if (failHeight > 0.0){fail = (curPos.z >= failLower && curPos.z <= failUpper);} //continue if there has been no failure if (!fail) { //check if we went past and reset if ((target == POSIT_UP && curPos.z > endPos.z)||(target == POSIT_DWN && curPos.z < endPos.z)){curPos = endPos;} //set and check for done llSetPos(curPos); exit = (curPos == endPos); } } return !fail; } //set the break list .. i.e. what are the break parameters tied to the links set_break_list(string data) { //these are in the following order //doors(x4)^wide walls(x2)^narrowWalls(x4)^roof link^light links(light link, tube link) list params = llParseStringKeepNulls(data, [DATA_SEP], [EMPTY_STR]); //we need to create the break list //list is comprised of action^link^ ... // then for break: list index for face & UUID for texture // for askew: other link ^index for posit^rots for settig askew BREAK_LIST = []; //first the doors integer x; //hold the previous link, for setting askew integer lastLink; //used to track which of the Askew list values we need .. only two integer idx = 0; for (x = 0; x < 4; x++) { integer link = llList2Integer(params, x); //door windows BREAK_LIST += [llDumpList2String([BA_WINDOW, link, 0], DATA_SEP)]; BREAK_LIST += [llDumpList2String([BA_WINDOW, link, 1], DATA_SEP)]; //on 1 & 3 set the askew break if (x == 1 || x == 3) { BREAK_LIST = BREAK_LIST + [llDumpList2String([BA_ASKEW, lastLink, link, idx], DATA_SEP)]; idx++; } lastLink = link; } //then the wide walls for (x = 4; x < 6; x++) { BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 2], DATA_SEP)]; BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 3], DATA_SEP)]; } //the narrow walls for (x = 6; x < 10; x++) { BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 4], DATA_SEP)]; BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, x), 5], DATA_SEP)]; } //the roof BREAK_LIST += [llDumpList2String([BA_WINDOW, llList2Integer(params, 10), 6], DATA_SEP)]; //hold the light params LIGHT_LINKS = llCSV2List(llList2String(params, 11)); //randomise the list once to get the mix up started BREAK_LIST = llListRandomize(BREAK_LIST, 1); //and finally grab the elevator positions UP_POSIT = (vector)llList2String(params, 12); DWN_POSIT = (vector)llList2String(params, 13); } //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels MAIN_CH += ownerMod; MOVE_CH += ownerMod; BOUNDRY_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); //call for the links llMessageLinked(LINK_THIS, MAIN_CH, F_REQ_LINKS, NULL_KEY); } link_message(integer sender, integer channel, string function, key data) { if (channel == MOVE_CH) { if (function == F_LINKS) { set_break_list((string)data); //ready to run state running; } }//end main ch } } //we are ready to work ... state running { link_message(integer sender, integer channel, string function, key data) { if (channel == MOVE_CH) { list params; if (function == F_MOVE) { //get the passed values params = llParseStringKeepNulls((string)data, [DATA_SEP], [EMPTY_STR]); gTargetPosit = llList2String(params, 0); gOvrloadPax = llList2Integer(params, 1); gFailCode = llList2String(params, 2); //last param indicates what speed we move //default to normal //a fast indicates it is a call .. and so need to keep from failing //parameter to move indicates we are in a failure .. which avoids a new one .. so we default to FALSE integer fail = FALSE; float chg = CHG_AMOUNT; if (llList2String(params, 3) == P_FAST) { chg = FAST_CHG_AMOUNT; fail = TRUE; } //translate to one of the actual codes integer idx = llListFindList(FAIL_CODES, [gFailCode]); if (idx != NOT_FOUND) { gFailCode = llList2String(FAIL_CODES, idx + 1); } else { gFailCode = EMPTY_STR; } //we pass the chg vector to use because in malfunctions there can be faster (falling) moves if (do_move(chg, fail)) { //done .. let main know llMessageLinked(LINK_THIS, MAIN_CH, F_MOVED, NULL_KEY); } else { //had a malfunction .... state malfunction; } }//end move else if (function == F_LINKS) { set_break_list((string)data); } }//end main ch } } //state we enter when there has been a malfunction in the elevator state malfunction { state_entry() { //initialise state variables sFailure = EMPTY_STR; //so we failed, now need to determine the type of failure //if a directed fail we already have this // move to worse by 5% for every overloaded pax //if fail code already set leave at 100 integer level = 100; if (gFailCode == EMPTY_STR){level = (integer)llFrand(100.0) - (gOvrloadPax * 5);} // catastrophic fall .. off track, on side, doors off hinge - 1% if (level <= 1 || gFailCode == FAIL_CATASTROPHIC) { sFailure = FAIL_CATASTROPHIC; gTargetPosit = POSIT_DWN; } // full fall then stop - 19% else if (level <= 20 || gFailCode == FAIL_FALL_FULL) { sFailure = FAIL_FALL_FULL; gTargetPosit = POSIT_DWN; } // 5m fall then stop - 30% else if (level <= 50 || gFailCode == FAIL_FALL_STOP ) { gTargetPosit = POSIT_DWN_5; sFailure = FAIL_FALL_STOP; //need to drop 5 ms, or to bottom which-ever //basically if we are not going to end at 1 m above, then we continue down vector cur = llGetPos(); if ((cur.z - DWN_POSIT.z) < 4.0) { sFailure = FAIL_FALL_FULL; gTargetPosit = POSIT_DWN; } } // just stop - 50% else { sFailure = FAIL_STOP; } //clear overload so no more failure, holding flag for later notification gOvrLoad = (gOvrloadPax > 0); //by setting to -1 we ensure no falure gOvrloadPax = -1; //and clear any manual fail codes gFailCode = EMPTY_STR; //if we need to move, we move, fast if (sFailure != FAIL_STOP) { do_move(FAST_CHG_AMOUNT, TRUE); } //owner advised of failure type llOwnerSay("Elevator malfunction " + sFailure); //if a catastrophic .. more to do if (sFailure == FAIL_CATASTROPHIC) { state catastrophic_failure; } else { //advise the users of the mechanical failure //add in if was brought on by overcrowding llWhisper(0, "Our appologies, there has been a mechanical malfunction with the elevator"); if (gOvrLoad){llWhisper(0, "This was caused by the overloading of the elevator");} //if we have ended all the way down then return to main, advising of failure //this will keep the elevator out of service until the owner comes by, or one hour has passed if (sFailure == FAIL_FALL_FULL) { llWhisper(0, "The doors will open in a moment. The elevator is now out of service, a mechanic has been called"); llMessageLinked(LINK_THIS, MAIN_CH, F_MALFUNC, sFailure); state await_repair; } //if we are stuck then else //FAIL_FALL_STOP or FAIL_STOP { //advise that mechanic is on the way llWhisper(0, "Please standby, a mechainic has been called"); //timer started - 15 sec for FAIL_STOP, 30 sec for FAIL_FALL_STOP float time = 15.0; if (sFailure == FAIL_FALL_STOP){time = 30.0;} llSetTimerEvent(time); } } } touch_end(integer total_number) { //owner touch will 'short-circuit' the wait period if (llDetectedKey(0) == llGetOwner()){llSetTimerEvent(0.01);} } timer() { llSetTimerEvent(0.0); //then FAIL_FALL_STOP return to ground, elevator out of service //FAIL_STOP 10% chance need to return to ground and out of service, otherwise will continue //ignore if fail code was manually set if (sFailure == FAIL_STOP && gFailCode == EMPTY_STR) { if ((integer)llFrand(10.0) <= 1){sFailure = FAIL_FALL_STOP;} } if (sFailure == FAIL_FALL_STOP) { llWhisper(0,"Due to the malfunction the elevator will be returning to the ground and then will be out of service"); gTargetPosit = POSIT_DWN; } llWhisper(0, "The elevator is about to resume motion, please ensure you are holding the rail"); do_move(CHG_AMOUNT, TRUE); if (sFailure == FAIL_STOP) { llMessageLinked(LINK_THIS, MAIN_CH, F_MOVED, NULL_KEY); state running; } else//FAIL_FALL_STOP { llMessageLinked(LINK_THIS, MAIN_CH, F_MALFUNC, sFailure); state await_repair; } } state_exit() { sFailure = EMPTY_STR; } } //if we have had a catastrophic failure we move to this state state catastrophic_failure { state_entry() { //get it's position so we can start tracking and see when it stops moving sElevPos = llGetPos(); //listen for a call from parcel boundry to keep us from leaving //filter for the only thing we want to hear llListen(BOUNDRY_CH, EMPTY_STR, NULL_KEY, F_HIT_BOUNDRY); //turn off light llSetLinkPrimitiveParamsFast(llList2Integer(LIGHT_LINKS, 0), [PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.0, 0.0, 0.0, PRIM_LINK_TARGET, llList2Integer(LIGHT_LINKS, 1), PRIM_FULLBRIGHT, ALL_SIDES, FALSE, PRIM_GLOW, ALL_SIDES, 0.0]); //first we set physical llSetStatus(STATUS_PHYSICS, TRUE); //the we give it a bit of a push .. //... down vector push = llGetMass()* CAT_PUSH_VECTOR; llApplyImpulse(push, FALSE); //..spin llApplyRotationalImpulse(CAT_ROT_VECTOR, FALSE); //set the time to check for end of movement ... and break things as we tumble llSetTimerEvent(0.1); } listen(integer channel, string name, key id, string message) { llSetTimerEvent(0.0); //only listening to the boundry channel and only for the hit call //so we've hit the boundry .. remove physics should stop us in our tracks llSetStatus(STATUS_PHYSICS, FALSE); //go to await repair .. final actions covered in exit state await_repair; } timer() { //check to see if we've stopped vector curPos = llGetPos(); integer stopped = (curPos == sElevPos); if (stopped) { //done .. llSetTimerEvent(0.0); llSetStatus(STATUS_PHYSICS, FALSE); //go to await repair .. final actions covered in exit state await_repair; } else { //store new posit sElevPos = curPos; //on each bounce we check to break things a bit // we have a list of actions that can occur .. we randomly pick one // we do it, and remove from the list //actions are // for each door // break the windows // askew the door // for each side/roof // break the windows //first randomise the list //list is comprised of action^link^ ... // then for break list index for face & UUID for texture // for askew other link ^index for (west loc_pos^loc_rot east loc_pos^loc_rot) // //Askew values // nw/ne door, sw/se door // pos^rot^pos^rot //window values // door up, door dwn, wide up, wide dwn, nw up, nw dw, roof // face^texture UUID //note perhaps need to have these textures on an object at top and btem so they are already loaded? //mixup the list BREAK_LIST = llListRandomize(BREAK_LIST, 1); //take the first one list params = llParseString2List(llList2String(BREAK_LIST, 0), [DATA_SEP], [EMPTY_STR]); //remove from the list BREAK_LIST = llDeleteSubList(BREAK_LIST, 0, 0); //now do the action string action = llList2String(params, 0); integer link = llList2Integer(params, 1); if (action == BA_WINDOW) { list winParams = llParseString2List(llList2String(WINDOW_VALUES, llList2Integer(params, 2)), [DATA_SEP], [EMPTY_STR]); llSetLinkPrimitiveParamsFast(link, [PRIM_TEXTURE, llList2Integer(winParams, 0), llList2Key(winParams, 1), ZERO_VECTOR, ZERO_VECTOR, 1.0]); } else if (action == BA_ASKEW) { integer secondLink = llList2Integer(params, 2); list askewParams = llParseString2List(llList2String(ASKEW_VALUES, llList2Integer(params, 3)), [DATA_SEP], [EMPTY_STR]); llSetLinkPrimitiveParamsFast(link, [PRIM_POS_LOCAL, (vector)llList2String(askewParams, 0), PRIM_ROT_LOCAL, (rotation)llList2String(askewParams, 2), PRIM_LINK_TARGET, secondLink, PRIM_POS_LOCAL, (vector)llList2String(askewParams, 2), PRIM_ROT_LOCAL, (rotation)llList2String(askewParams, 3)]); } } } state_exit() { //advise the users of the mechanical failure //add in if was brought on by overcrowding llWhisper(0, "Please exit the elevator with care. We hope no one was injured."); if (gOvrLoad){llWhisper(0, "This accident was caused by the overloading of the elevator");} //Advising main failure //this will keep the elevator out of service until the owner comes by, or one hour has passed llWhisper(0, "A mechanic has been called"); llMessageLinked(LINK_THIS, MAIN_CH, F_MALFUNC, FAIL_CATASTROPHIC); } } //basic state, wait for notification from ~main that we are repaired state await_repair { link_message(integer sender, integer channel, string function, key data) { if (channel == MOVE_CH) { if (function == F_REPAIRED) { state running; } } } } //[/PHP]
  5. Core - Main The core script for controlling the elevator...coordinates the efforts of all the other scripts and components. Contains the code to for the elevator doors as well as the light. Located in the root Prim. For the most part fairly straight forward, other than the hoops I had to jump through when it came to keeping track of who was in the elevator. Obviously if someone sits/unsits, that we know, but for a number of things I wanted to know if someone was just standing in the elevator, and that is where things got complicated. The Collision events did not behave entirely as expected (see More Collisions ... for details). //[PHP] // // // ******************************************************************* // main // 12/18 // v1.0.0 // // core script for controlling the elevator...coordinates the efforts // of all the other scripts and components. // // Writen by Wandering Soulstar // ******************************************************************* //###### //Constants //instance specific to the instalation //elevator world positions vector UP_POSIT = <13.35530,208.23233, 111.22778>; vector DWN_POSIT = <13.35530, 208.23233, 58.43259>; //end instance //...... //specific to the elevator used //door positions .. relative to root(local) values vector W_UP_OPEN = <-1.050721, 1.200195, 1.709961>; vector E_UP_OPEN = <1.054483, 1.200195, 1.709961>; vector W_UP_CLOSE = <-0.399941, 1.200195, 1.709961>; vector E_UP_CLOSE = <0.399750, 1.200195, 1.709961>; vector W_DWN_OPEN = <-1.050721, -1.549805, 1.709961>; vector E_DWN_OPEN = <1.054483, -1.549805, 1.709961>; vector W_DWN_CLOSE = <-0.399940, -1.549805, 1.709961>; vector E_DWN_CLOSE = <0.399750, -1.549805, 1.709961>; //name of door prims string DOOR_NAME = "elevator door"; //light prim names string LIGHT_SRC_NAME = "light source"; string LIGHT_TUBE_NAME = "flourescent light"; //light faces to set integer BRIGHT_FACE = 0; integer IN_GLOW_FACE = 0; integer OUT_GLOW_FACE = 3; //walls and roof prim names string ROOF_NAME = "roof"; string W_WALL_NAME = "side wall"; string N_WALL_NAME = "small wall"; //maximum number passagers before overlaod //NOTE - this is based on the number of positioners, i.e. number of sit positions integer MAX_PASSENGERS = 4; //hold the links of the doors //NOTE - code is based on the door design .. changing this is more than //just changing the below constants list DOOR_LINKS = [0, 0, 0, 0]; //the individual doors .. stored in their desc list DOOR_POSITS = ["up w", "up e", "dwn w", "dwn e"]; //end specifics //...... //modifiable based on preferences //amount the doors move on each interation float DEF_DOOR_CHG = 0.01; //color and glow settings vector LIGHT_COLOR = <1.000, 1.000, 0.906>; float IN_GLOW = 0.3; float OUT_GLOW = 0.1; //light settings //off list LGT_P_OFF = [PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.0, 0.0, 0.0]; //full list LGT_P_FULL = [PRIM_POINT_LIGHT, TRUE, LIGHT_COLOR, 1.0, 3.0, 0.75]; //dim list LGT_P_DIM = [PRIM_POINT_LIGHT, TRUE, LIGHT_COLOR, 0.25, 2.5, 0.75]; //plus glows & brights list LGT_GB_ON = [PRIM_FULLBRIGHT, BRIGHT_FACE, TRUE, PRIM_GLOW, IN_GLOW_FACE, IN_GLOW, PRIM_GLOW, OUT_GLOW_FACE, OUT_GLOW]; list LGT_GB_OFF = [PRIM_FULLBRIGHT, BRIGHT_FACE, FALSE, PRIM_GLOW, ALL_SIDES, 0.0]; //times for different timers //these and timer flags explained in default:timer event float TIME_EMPTY = 30.0; float TIME_FREE = 90.0; float TIME_DISABLE = 60.0; float TIME_REPAIR = 3600.0; float TIME_CHECKSIT = 60.0; float TIME_CHECKGO = 90.0; float TIME_CHECKPAX = 60.0; float TIME_BRIDGE = 30.0; //channels integer CHAT_CH = 0; //NOTE - the following are seeds that are then modified by set_owner_channel ensure that //they are changed on all relevant modules integer DIALOG_CH = -140101; integer MAIN_CH = -140100; integer POSIT_CH = -140110; integer BRIDGE_CH = -140120; integer MOVE_CH = -140130; //integer BOUNDARY_CH = -140140; .. not used in this module, noted for clarity integer REPAIR_CH = -140150; //end modifiable //...... //set on start //holds all the link information, built on start string LINK_DATA; //the link to the light source and tube integer LIGHT_SRC_LINK = 0; integer LIGHT_TUBE_LINK = 0; //end start set //...... //general constants //NOTE - take care in modifying any of these. Will need to check other modules //for use of same constants and change accordingly string EMPTY_STR = ""; string FUNC_SEP = "|"; string DATA_SEP = "^"; integer NOT_FOUND = -1; //functions //data requests to me and returns string F_REQ_POSITION = "req posit"; string F_POSITION = "posit"; string F_REQ_STATUS = "req status"; string F_STATUS = "status"; string F_REQ_LINKS = "req links"; string F_LINKS = "links"; string F_REQ_DWN_POSIT = "req_dwn"; string F_DWN_POSIT = "dwn_pos"; //for AV 'Tracking' //~detector string F_ENTER = "enter"; string F_EXIT = "exit"; //~positioner string F_SEATED = "seated"; string F_STOOD = "stood"; //call to ~detector and possible returns string F_CHECK_PAXS = "check paxs"; string F_PAX_UPDATE = "pax list"; string F_NO_PAX = "empty"; //call to ~positioner string F_UNSEAT = "unsit"; //frm bridge string F_READY = "ready"; //called from a button string F_CALL = "call"; //to ~move and returns, F_MOVE also call to bridge string F_MOVE = "move"; string F_MOVED = "moved"; string F_MALFUNC = "malfunc"; //advise move of repair string F_REPAIRED = "repaired"; //cal to ~repair script .. string F_REPAIR = "repair"; //advise to me to end string F_DIE = "die"; //status for availability string S_READY = "ready"; string S_INUSE = "inuse"; string S_DISABLED = "disab"; //and during movement string S_DEPARTING = "deprt"; string S_ARRIVING = "arrv"; //dialog options string OPT_OK = "Ok"; string OPT_CANCEL = "Cancel"; //positions string POSIT_UP = "up"; string POSIT_DWN = "dwn"; string POSIT_MOVING = "moving"; //bridge positions string POSIT_OPEN = "open"; string POSIT_CLOSED = "closed"; //door states string DS_OPEN = "open"; string DS_CLOSE = "close"; //sun states string DAY = "d"; string NIGHT = "n"; //malfunction types string FAIL_CATASTROPHIC = "catastrophy"; string FAIL_STOP = "stop"; string FAIL_FALL_FULL = "fall full"; string FAIL_FALL_STOP = "fall stop"; //timer flags //these and times explained in default:timer event string TF_EMPTY = "empty"; string TF_FREE = "free"; string TF_DISABLE = "disable"; string TF_REPAIR = "repair"; string TF_CHECKSIT = "chk_sit"; string TF_CHECKGO = "chk_go"; string TF_CHECKPAX = "chk_pax"; string TF_BRIDGE = "bridge"; //voice messages from users string GO = "go"; string YES = "yes"; //flags for move speed string P_FAST = "fast"; string P_SLOW = "slw"; //flag for elevator to reset itself on rez integer REPAIR_REZ = -5689321; //End Constants //...... //###### //Global Variables //my current position string gCurPosit = POSIT_DWN; //current movement (arriving/departing) string gMvStatus = EMPTY_STR; //status of the elevator (Ready, InUse, Disabled) string gCurStatus = EMPTY_STR; //current door state string gCurDoor = POSIT_CLOSED; //position moving to string gTargetPosit = EMPTY_STR; //failure type string gFailure = EMPTY_STR; //for directed fail string gFailCode = EMPTY_STR; //hold identifer for type of timer set string gTimerFlag = EMPTY_STR; //how fast we move string gMvSpeed = P_SLOW; //flag to announce or not the arrival integer gCalled = FALSE; //lists of pax/seated list gPaxList = []; list gSeatedList = []; //chat handle integer gOpenChatHandle = 0; //End Global Variables //...... //###### //Functions //begin the move process ... close doors and if we are at the top //advise the bridge to close .. waiting for it to respond start_move() { //start the doors to close .. this will set the lights set_doors(DS_CLOSE); //set our target destination if (gCurPosit == POSIT_DWN) { gTargetPosit = POSIT_UP; } else { gTargetPosit = POSIT_DWN; } //cur status is departing gMvStatus = S_DEPARTING; //if at top call the bridge to close on return this will go to continue if (gCurPosit == POSIT_UP) { llWhisper(BRIDGE_CH, F_MOVE + FUNC_SEP + POSIT_CLOSED); //set a timer in case it is broken gTimerFlag = TF_BRIDGE; llSetTimerEvent(TIME_BRIDGE); } //otherwise just continue else { continue_move(); } } //will come here after the bridge has advised ready or no need to call bridge //this will be either at the start of a move (departing), finished moving, which will have called bridge if needed (arriving) continue_move() { //if status is departing if (gMvStatus == S_DEPARTING) { if (gCurStatus != S_DISABLED){gCurStatus = S_INUSE;} //advise we are moving llShout(MAIN_CH, F_POSITION + FUNC_SEP + POSIT_MOVING + DATA_SEP + gTargetPosit); //check for overlaod //default is none integer ovrLoad = 0; if (llGetListLength(gPaxList) > MAX_PASSENGERS){ovrLoad = (llGetListLength(gPaxList) - MAX_PASSENGERS);} //call the move script passing: destination, any overloaded AVs (i.e. more than MX, and if there was a force fail called llMessageLinked(LINK_THIS, MOVE_CH, F_MOVE, llDumpList2String([gTargetPosit, ovrLoad, gFailCode, gMvSpeed], DATA_SEP)); //this will return arriving //finally set the pax list to be only seated, this will allow for the standing pax to be counted on arrival gPaxList = gSeatedList; } else if (gMvStatus == S_ARRIVING) { //advise of position gCurPosit = gTargetPosit; llShout(MAIN_CH, F_POSITION + FUNC_SEP + gCurPosit); //open the doors ... will set the lights set_doors(DS_OPEN); //if we were not called from the button, announce arrival if (!gCalled && gCurStatus != S_DISABLED){llWhisper(CHAT_CH, "We have arrived. We hope that you enjoyed the trip");} gCalled = FALSE; //set our status ... if there was a failure our status is already disabled //else we are in use if (gCurStatus != S_DISABLED){gCurStatus = S_INUSE;} //advise on our status llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); //unseat the avatars llMessageLinked(LINK_ALL_OTHERS, POSIT_CH, F_UNSEAT, NULL_KEY); //start a timer to autmatically make the elevator available even if they have not all left //or no one enters //will be canceled if someone new enters gTimerFlag = TF_FREE; llSetTimerEvent(TIME_FREE); } } //set the doors to either open or closed (action) the lights as well set_doors(string action) { //our position tells us which doors to use //their current status is stored so we'll check against the action and if already there just ignore if (gCurDoor != action) { //get the sun for light setting determination vector sunPos = llGetSunDirection(); string sun = NIGHT; if (sunPos.z > 0){sun = DAY;} //if we are opening, and night .. set light to full if (action == DS_OPEN && sun == NIGHT) { llSetLinkPrimitiveParamsFast(LIGHT_SRC_LINK, LGT_P_FULL + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_ON); } //now which doors we use .. set the idx to pull links from DOOR_LINKS //order is [up w, up e, dwn w, dwn e] //default for up integer startIdx = 0; if (gCurPosit == POSIT_DWN){startIdx = 2;} //now get the links integer lnkWest = llList2Integer(DOOR_LINKS, startIdx); integer lnkEast = llList2Integer(DOOR_LINKS, startIdx + 1); //set the direction mod //direction mod is for west, east will then be -dir mod //default for Open float dirMod = -1.0; if (action == DS_CLOSE){dirMod = 1.0;} //from these we create the chg vectors //assumption is doors move along the x axis float chgWest = DEF_DOOR_CHG * dirMod; float chgEast = DEF_DOOR_CHG * -dirMod; //prep the end positions //default is up/opening vector endWest = W_UP_OPEN; vector endEast = E_UP_OPEN; //now adjust as needed if (gCurPosit == POSIT_UP && action == DS_CLOSE) { endWest = W_UP_CLOSE; endEast = E_UP_CLOSE; } else if (gCurPosit == POSIT_DWN && action == DS_OPEN) { endWest = W_DWN_OPEN; endEast = E_DWN_OPEN; } else if (gCurPosit == POSIT_DWN && action == DS_CLOSE) { endWest = W_DWN_CLOSE; endEast = E_DWN_CLOSE; } //get our cur positions vector curWest = (vector)llList2String(llGetLinkPrimitiveParams(lnkWest, [PRIM_POS_LOCAL]), 0); vector curEast = (vector)llList2String(llGetLinkPrimitiveParams(lnkEast, [PRIM_POS_LOCAL]), 0); //flag to exit loop integer exit = FALSE; //start moving while (!exit) { //calc new posits curWest.x += chgWest; curEast.x += chgEast; //see if we have gone past the end ... to set to end //check will depend on open/close if (action == DS_OPEN) { if (curWest.x < endWest.x){curWest.x = endWest.x;} if (curEast.x > endEast.x){curEast.x = endEast.x;} } else { if (curWest.x > endWest.x){curWest.x = endWest.x;} if (curEast.x < endEast.x){curEast.x = endEast.x;} } //move llSetLinkPrimitiveParamsFast(lnkWest, [PRIM_POS_LOCAL, curWest, PRIM_LINK_TARGET, lnkEast, PRIM_POS_LOCAL, curEast]); //check if done exit = ((curWest.x == endWest.x) && (curEast.x == endEast.x)); } gCurDoor = action; // //if we are closing set ligths if (action == DS_CLOSE) { //defult to off list params = LGT_P_OFF + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_OFF; //if night and someone is in set to dim if (sun == NIGHT && llGetListLength(gPaxList) != 0) { params = LGT_P_DIM + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_ON; } llSetLinkPrimitiveParamsFast(LIGHT_SRC_LINK, params); } } } //maintians the pax and seated lists //action: what has happened (enter/seated, exit/stood) //paxs: list of UUIDs to AV that the action 'happened' integer update_pax_list(string action, list paxs) { //we have a list of all and a list of who is seated //for seated/stood paxs list will only have one item, enter/exit might have more //if enter ... and if not already in pax list, add //if a sit ... add to sit list, and if not already in pax list, add if (action == F_SEATED || action == F_ENTER) { //check if already in pax list an add if not integer num = llGetListLength(paxs); integer x; for (x = 0; x < num; x++) { integer posit = llListFindList(gPaxList, llList2List(paxs, x, x)); if (posit == NOT_FOUND){gPaxList = gPaxList + llList2List(paxs, x, x);} } //if a seated add to said list if (action == F_SEATED){gSeatedList += paxs;} } //if an exit ... was not seated, so just remove from pax list //if a stand ... remove from both lists, if they stood in the elevator // will fire a collision enter and so they will be added back to the list else if (action == F_EXIT || action == F_STOOD) { //first the pax list (common to both) integer num = llGetListLength(paxs); integer x; integer posit; for (x = 0; x < num; x++) { //if we find the passed key, remove from the list posit = llListFindList(gPaxList, llList2List(paxs, x, x)); if (posit != NOT_FOUND){gPaxList = llDeleteSubList(gPaxList, posit, posit);} } //then if a stand, from the seated list if (action == F_STOOD) { posit = llListFindList(gSeatedList, llList2List(paxs, x, x)); if (posit != NOT_FOUND){gSeatedList = llDeleteSubList(gSeatedList, posit, posit);} } } //finally we return the new total number of pax return llGetListLength(gPaxList); } //common function to modify channels to be special for the owner //usually the first thing called set_owner_channel() { //create the mod integer ownerMod = (integer)("0xF" + llGetSubString(llGetOwner(),0,6)); //now add to each of the defined channels DIALOG_CH += ownerMod; MAIN_CH += ownerMod; POSIT_CH += ownerMod; BRIDGE_CH += ownerMod; MOVE_CH += ownerMod; REPAIR_CH += ownerMod; } //End Functions //...... //Body default { state_entry() { //initialise channels set_owner_channel(); //default/expected postion of the elevator on script start is dwn, with doors closed //although set in definition, resetting here for clarity gCurPosit = POSIT_DWN; gCurDoor = POSIT_CLOSED; //check if night to set the light vector sunPos = llGetSunDirection(); if (sunPos.z <= 0) { llSetLinkPrimitiveParamsFast(LIGHT_SRC_LINK, LGT_P_DIM + [PRIM_LINK_TARGET, LIGHT_TUBE_LINK] + LGT_GB_ON); } //status gCurStatus = S_READY; //first we need to get ahold of the links ... while this could be hardcoded, changes to the linkset could break //For the doors the order is important //DOOR_LINKS is preped as [0,0,0,0] //this will correspond to [up(n) w, up (n) e, dwn(s) w, dwn(s) e] DOOR_POSITS //Assume we will find only the light links we want ... //for the walls .. theses temp stored in two lists w(ide)Walls & n(arrow)Walls, order is irrelevant integer numLinks = llGetNumberOfPrims(); integer ctr = 0; string desc; integer posit; list wWallLinks = []; list nWallLinks = []; integer roofLink = 0; while (ctr < numLinks) { //move to the next (first) link ctr++; string name = llGetLinkName(ctr); if (name == DOOR_NAME) { //may have found one //description will tell us which desc = llList2String(llGetLinkPrimitiveParams(ctr, [PRIM_DESC]), 0); //get the array position of the door posit = llListFindList(DOOR_POSITS, [desc]); //skip in case there is garbage there if (posit != NOT_FOUND) { //store DOOR_LINKS = llListReplaceList(DOOR_LINKS, [ctr], posit, posit); } } else if (name == LIGHT_SRC_NAME) { //if more than one found will just overwrite LIGHT_SRC_LINK = ctr; } else if (name == LIGHT_TUBE_NAME) { //if more than one found will just overwrite LIGHT_TUBE_LINK = ctr; } else if (name == ROOF_NAME) { //if more than one found will just overwrite roofLink = ctr; } //for these if we find more than needed it will break the code later (manly only in a catastrophic failure else if (name == W_WALL_NAME) { wWallLinks += [ctr]; } else if (name == N_WALL_NAME) { nWallLinks += [ctr]; } }//end while //pass links etc to move //first create the data string, these are in the following order //doors(in order up(n) w, up(n) e, dwn(s) w, dwn(s) e)^wide walls(x2)^narrowWalls(x4)^roof link^light links(light link, tube link)^upPos^downPos LINK_DATA = llDumpList2String(DOOR_LINKS + wWallLinks + nWallLinks + [roofLink] + [llList2CSV([LIGHT_SRC_LINK, LIGHT_TUBE_LINK])] + [UP_POSIT, DWN_POSIT], DATA_SEP); llMessageLinked(LINK_THIS, MOVE_CH, F_LINKS, (key)LINK_DATA); //let everyone know (mainly detectors) where we are llShout(MAIN_CH, F_POSITION + FUNC_SEP + gCurPosit); //advise repair of the elevator's down posit llShout(REPAIR_CH, F_DWN_POSIT + FUNC_SEP + (string)DWN_POSIT); //advise the buttons llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); //listen on my channel .. calls from detectors, bridge, buttons llListen(MAIN_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); llSay(CHAT_CH, "Elevator ready"); } listen(integer channel, string name, key id, string message) { if (channel == MAIN_CH && gCurStatus == S_DISABLED) { //recovery from a catastrophic failure if (message == F_DIE){llDie();} } //will only listen to other calls i we are not disabled else if(gCurStatus != S_DISABLED) { if (channel == MAIN_CH) { list params = llParseString2List(message, [FUNC_SEP], [EMPTY_STR]); string function = llList2String(params, 0); if (function == F_READY) { //this will always be the bridge, either out or retracted //kill the timer and continue llSetTimerEvent(0.0); gTimerFlag = EMPTY_STR; continue_move(); } else if (function == F_REQ_POSITION) { //a detector asking for the position of the elevator llRegionSayTo(id, MAIN_CH, F_POSITION + FUNC_SEP + gCurPosit); } else if (function == F_REQ_STATUS) { //a button asking for the status of the elevator //if moving we reply with position if (gCurPosit == POSIT_MOVING) { message = F_POSITION + FUNC_SEP + gCurPosit + DATA_SEP + gTargetPosit; } //else with status else { message = F_STATUS + FUNC_SEP + gCurStatus; } llRegionSayTo(id, MAIN_CH, message); } else if (function == F_REQ_DWN_POSIT) { llRegionSayTo(id, REPAIR_CH, F_DWN_POSIT + FUNC_SEP + (string)DWN_POSIT); } else if (function == F_ENTER || function == F_EXIT) { //will have passed keys of those that entered list paxs = llParseString2List(llList2String(params, 1), [DATA_SEP], [EMPTY_STR]); //how many did we have integer curPax = llGetListLength(gPaxList); //we update our list integer newPax = update_pax_list(function, paxs); //other actions are only if at rest if (gCurPosit != POSIT_MOVING) { //if first one if (curPax == 0) { //advise to sit llWhisper(CHAT_CH, "To start the elevator you must grab a hand rail (sit)"); //set a timer to check if they have sat gTimerFlag = TF_CHECKSIT; llSetTimerEvent(TIME_CHECKSIT); //and set status to in use gCurStatus = S_INUSE; //advise the buttons of status llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); } //advise about over crowding if (newPax > MAX_PASSENGERS) { llWhisper(CHAT_CH, "The elevator is only rated for " + (string)MAX_PASSENGERS + ". Overcrowding could cause failures"); } //if we are empty start a timer to close the doors/dim the lights else if (newPax == 0) { gTimerFlag = TF_EMPTY; llSetTimerEvent(TIME_EMPTY); //advise the buttons of status gCurStatus = S_READY; llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); } } }//end enter/exit //return form an update check on anyone being in the elevator else if (function == F_PAX_UPDATE) { //update the list gPaxList = llParseString2List(llList2String(params, 1), [DATA_SEP], [EMPTY_STR]) + gSeatedList; //set a timer to check again .. due to the edge cases where TPs, external sits, and logouts do not register gTimerFlag = TF_CHECKPAX; llSetTimerEvent(TIME_CHECKPAX); } //check on pax shows us empty else if (function == F_NO_PAX) { gPaxList = []; //start the timer to close the doors gTimerFlag = TF_EMPTY; llSetTimerEvent(TIME_EMPTY); } //we have been called by a button .. can only be called if I am ready else if (function == F_CALL && gCurStatus == S_READY) { string caller = llList2String(params, 1); //already here so just open the doors if (caller == gCurPosit) { set_doors(DS_OPEN); } //we need to move to the caller else { //make sure to clear timer flag and timers llSetTimerEvent(0.0); gTimerFlag = EMPTY_STR; //stop any listen listen if (gOpenChatHandle != 0) { llListenRemove(gOpenChatHandle); gOpenChatHandle = 0; } gMvSpeed = P_FAST; gCalled = TRUE; start_move(); } } }//end main ch else if (channel == CHAT_CH) { gFailCode = EMPTY_STR; message = llStringTrim(llToLower(message), STRING_TRIM); integer continue = (message == YES); //if not a yes .. check to see if any auto fail params were passed: //b = simple stop break //s = fall a bit and stop //f = fall to bottom //c = catastrophic if (!continue) { list params = llParseString2List(message, [" "], [EMPTY_STR]); message = llList2String(params, 0); if (message == GO) { //check for a fail param if (llGetListLength(params) > 1){gFailCode = llList2String(params, 1);} //if the number of seated AVs is less than the number of AVs in the elevator advise and ask to continue if (llGetListLength(gSeatedList) < llGetListLength(gPaxList)) { llWhisper(CHAT_CH, "Not all passengers are in position, this could be dangerous. If you would like to continue just say yes"); //reset timer to new Check go timer gTimerFlag = TF_CHECKGO; llSetTimerEvent(TIME_CHECKGO); } else { continue = TRUE; } } }//end !continue if (continue) { //make sure to clear timer flag and timers llSetTimerEvent(0.0); gTimerFlag = EMPTY_STR; //stop listen if (gOpenChatHandle != 0) { llListenRemove(gOpenChatHandle); gOpenChatHandle = 0; } gMvSpeed = P_SLOW; start_move(); } }//end Chat CH }//end !disabled } link_message(integer sender, integer channel, string function, key data) { if(channel == MAIN_CH) { //call from one of the positioners if (function == F_SEATED || function == F_STOOD) { //how many were seated integer curSeated = llGetListLength(gSeatedList); //we update our list //will have passed key of AV seated or stood update_pax_list(function, [(string)data]); //see how many seated now integer newSeated = llGetListLength(gSeatedList); //if no one is seated if (newSeated == 0) { //if active, turn off the open chat listen if (gOpenChatHandle != 0) { llListenRemove(gOpenChatHandle); gOpenChatHandle = 0; } //reset the check sit flag ... //in an arrival this will be overridden by a free timer //if we are in disabled mode ignore this if (gCurStatus != S_DISABLED) { gTimerFlag = TF_CHECKSIT; llSetTimerEvent(TIME_CHECKSIT); } } //if someone has sat and we are disabled .. unseat them and advise else if (newSeated > 0 && gCurStatus == S_DISABLED) { //unseat the avatars llMessageLinked(LINK_ALL_OTHERS, POSIT_CH, F_UNSEAT, NULL_KEY); llWhisper(CHAT_CH, "The Elevator is out of service"); } // if first seated start listen and advise else if (newSeated > 0 && curSeated == 0) { gOpenChatHandle = llListen(CHAT_CH, EMPTY_STR, NULL_KEY, EMPTY_STR); llWhisper(CHAT_CH, "When everyone is seated and you are ready, just say go in chat. I will listen for one minute"); //set a flag to stop listening if they do not say go gTimerFlag = TF_CHECKGO; llSetTimerEvent(TIME_CHECKGO); //and set our status to inuse .. in the case it had gone to ready while they stood around gCurStatus = S_INUSE; llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); } }//end seated/stood //calls from the mover else if (function == F_MOVED) { //set our status gMvStatus = S_ARRIVING; //if at top call the bridge if (gTargetPosit == POSIT_UP) { llWhisper(BRIDGE_CH, F_MOVE + FUNC_SEP + POSIT_OPEN); gTimerFlag = TF_BRIDGE; llSetTimerEvent(TIME_BRIDGE); } else { continue_move(); } } else if (function == F_REQ_LINKS) { llMessageLinked(LINK_THIS, MOVE_CH, F_LINKS, (key)LINK_DATA); } else if (function == F_MALFUNC) { //we are currently disabled gFailure = (string)data; gCurStatus = S_DISABLED; if (gFailure != FAIL_CATASTROPHIC) { //finish the move .. i.e. open doors etc. gTargetPosit = POSIT_DWN; gMvStatus = S_ARRIVING; gMvSpeed = P_SLOW; continue_move(); //advise they have 1 min to get out llWhisper(CHAT_CH, "Please exit as quickly as possible. In one minute the elevator will shut down and close"); gTimerFlag = TF_DISABLE; llSetTimerEvent(TIME_DISABLE); } llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); } }//end main ch } on_rez(integer start_param) { //check if being rezzed by the repair script. i.e. new instance if (start_param == REPAIR_REZ){llResetScript();} } touch_end(integer total_number) { //only care if we are disabled if (gCurStatus == S_DISABLED) { //if the owner, re-enable .. all repaired if (llDetectedKey(0) == llGetOwner()) { //anything but a catastrophic we are now ready if (gFailure != FAIL_CATASTROPHIC) { llSetTimerEvent(0.0); gCurStatus = S_READY; gTimerFlag = EMPTY_STR; llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); //and advise the move that it is repaired llMessageLinked(LINK_THIS, MOVE_CH, F_REPAIRED, NULL_KEY); } //for a catastrophic we'll need to rez a new elevator else { llShout(REPAIR_CH, F_REPAIR); } } } } timer() { llSetTimerEvent(0.0); //Empty so close doors if (gTimerFlag == TF_EMPTY) { set_doors(DS_CLOSE); } //AV has been in the elevator for time, but not sat, return status to ready //after arrival .. if they have not left the elevator after a time we make it callable //time has passed so a technician has repaired else if (gTimerFlag == TF_CHECKSIT || gTimerFlag == TF_FREE || gTimerFlag == TF_REPAIR) { //could be here for a free timer when disabled .. check for that if (gTimerFlag != TF_FREE || (gTimerFlag == TF_FREE && gCurStatus != S_DISABLED)){gCurStatus = S_READY;} llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); //if was a checksit or free .. need to see if we actually have anybody left //certain actions will not trigger an exit (sit outside, LM TP out, map click TP out, LogOut) //tell the active detector to run a sensor if (gTimerFlag == TF_CHECKSIT || gTimerFlag == TF_FREE){llWhisper(MAIN_CH, F_CHECK_PAXS);} } else if (gTimerFlag == TF_CHECKPAX) { //call to check for Pax //tell the active detector to run a sensor llWhisper(MAIN_CH, F_CHECK_PAXS); } //AV has been seated and not said go ... advise and stop listen else if (gTimerFlag == TF_CHECKGO) { if (gOpenChatHandle != 0) { llListenRemove(gOpenChatHandle); gOpenChatHandle = 0; } llWhisper(CHAT_CH, "I did not hear anything from you. To re-enable, everyone must stand and then sit again."); gCurStatus = S_READY; llShout(MAIN_CH, F_STATUS + FUNC_SEP + gCurStatus); } //after a non-catastrophic failure .. we give them a bit to get out of the elevator .. //then close up and wait for either owner to repair, or an hour to pass else if (gTimerFlag == TF_DISABLE) { set_doors(DS_CLOSE); gTimerFlag = TF_REPAIR; llSetTimerEvent(TIME_REPAIR); } //bridge did not respond, broken for some reason //advise owner and continue else if (gTimerFlag == TF_BRIDGE) { llOwnerSay("There was a failure in the Bridge, it did not respond"); continue_move(); } gTimerFlag = EMPTY_STR; } } //[/PHP]
  6. Hi All, In the following you will find code I have written for an Elevator. While it is pretty specific to the build I wrote it for, I hope there are things in here that are of use to the community. During the development of this I pestered the forum quite a bit, and without the help garnered there I doubt I would have been able to get this done. The relevant threads are linked below at the bottom of this post, and I wanted to give special thanks to @Wulfie Reanimator @Rolig Loon @Love Zhaoying @Innula Zenovka @ellestones and @Qie Niangao for their help and constant willingness to answer all of (my) our questions. The elevator linkset is made up of: Two east/west wide mesh piece walls Four north/south narrow mesh piece walls (two each side) Four north/south mesh piece doors (two each side), with north doors opening at top, and south at bottom A mesh fluorescent tube light, and a single prim that actually sheds the light using values for FOV etc set into the prim A mesh piece roof A base mesh piece bar that is the root Four invisible prims used for sit positions Each of the walls and doors has upper and lower faces that are the windows Total LI:10 Two buttons of 4 LI each are also part of the system These are not meshed as I could not get MeshStudio to mesh them with the same look. Two detector prims of 1 LI each One Bridge of 2 LI Two Boundary Prims, 1LI Total LI for the solution: 23 You can see this in action at my home HERE . If you would like a copy of the elevator please IM me inworld .. happy to share 🙂 There are a number of scripts that went into this, and I'll post them following in the following order: Core - Scripts in the Elevator Linkset Main - The core script for controlling the elevator...coordinates the efforts of all the other scripts and components. Contains the code for the elevator doors as well as the light. In the root Prim. Move - Actual mover of the elevator .. separated out from main principally so that main can receive the collision calls from the detector when moving to a new position. Also contains malfunction code. In the root Prim. Positioner - Used to set the location, pose, and camera for the AVs that 'sit' in the elevator. In each of the invisible positioner prims that are part of the elevator linkset. Components - Scripts in other prims Button - Button to call the elevator .. will change colors based on status and give relevant messages. One at the top and one at the bottom Detector - Simple detection of if an av has entered or left the elevator. Set in an invisible prim the size of the elevator, one at the top and one at the bottom Bridge - This moves the bridge at the top of the elevator 'shaft'. This was needed because I had already buolt the rest before I put in the elevator, and at the top there was a gap between the landing and the elevator. Boundary - Will sit in prims along the parcel boundary to detect when a catastrophically failed elevator, in physical state, is about to tumble off the parcel and stop it Repair - In the case of a catastrophic failure, where the elevator goes off the track, this simple controller will kill the existing elevator and rez a new one. Has a copy of the elevator in its inventory. Hopefully I have commented sufficiently the code for it to be understandable, but I am sure there will be questions so do not hesitate to ask. Coding Guidelines I try to follow the same 'guidelines throughout my coding. It has been 20+ years since I coded professionally but I try to follow consistent guidelines when coding. The following are the guidelines I use that will hopefully help you read my code Variables NAME_NAME - Constants. In some cases these are not true constants, but set on load or as near as possible. They will not be modified anywhere else in the code gName - Global variable that can be modified anywhere sName - State variable that is 'global' just to a particular state nameName - All local variables start in lowercase. If, for readability, they are comprised of more than one word, the additional words start with a Cap name_name - either a function or a state I tend to use constants a lot, even for simple things like "" .. this is for a few reasons: Ensures I do not have unintentional errors, like an accidental " " When there is cross module communication, ensure I am passing the correct values 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
  7. One last .. on the scenario: LM TP out: Nothing ++ no _start event fires on re-entry, .. seems to reset automatically, i.e. _start event fires after somewhere between 5 and 10 minutes
  8. Add more test scenarios to the above .. just gets weirder ...
  9. Ok .. was able to get inworld to do some simple tests. Scenario: 4x4x4 prim with the following code in it: //###### //Functions integer get_av_count(integer total_number) { //filter out any objects .. to only get count of AVs integer x; integer numAVs = 0; for (x = 0; x < total_number; x++) { if (llDetectedType(x) & AGENT){numAVs++;} } return numAVs; } //End Functions //...... //Body default { state_entry() { //set volume detect so that prim is 'phantom' llVolumeDetect(TRUE); } collision_start(integer total_number) { integer numAVs = get_av_count(total_number); llWhisper(0, "Enter"); llWhisper(0, "Tot: " + (string)total_number); llWhisper(0, "Avs: " + (string)numAVs); integer x; list names; for (x = 0; x < total_number; x++) { names = names + llDetectedName(x); } llWhisper(0, llList2CSV(names)); } collision_end(integer total_number) { integer numAVs = get_av_count(total_number); llWhisper(0, "Exit"); llWhisper(0, "Tot: " + (string)total_number); llWhisper(0, "Avs: " + (string)numAVs); integer x; list names; for (x = 0; x < total_number; x++) { names = names + llDetectedName(x); } llWhisper(0, llList2CSV(names)); } } ... then stuck another prim inside the 4x4x4 prim Walk in/out: _start/_end events fire Cam in to sit: Nothing Cam out to sit outside: Nothing Inside 4x4x4 sit: Nothing Inside 4x4x4 stand:: _start event fires Move the interior (or an exterior) prim in/out with or without seated AV: Nothing move the interior prim with AV standing on it: _start/_end fire Log Out : Nothing Log In: _start fires Then I tried a number of TP combinations: double click TP in/out: _start/_end fire double click TP out from sit: Nothing map click TP out: Nothing LM TP out: Nothing ++ no _start event fires on re-entry, until after an _end event has fired or 5-10 mins have passed this only affect the AV in question LM TP in: _start event fires So in summary some very strange and inconsistent behavior.
  10. I too vaguely remember reading something back years ago when I first started with LSL that said something about potential problems with touch_start, and have always just used touch_end ... only times I have a touch_start is when I want something to happen differently if the user has held down the touch for a bit .. but then is always paired with a touch_end that then checks the time between the _start and _end to see if the click was held long enought.
  11. hmmm ... and so therefore what happens when they stand inside the detector prim .. will another collision_start event be raised at that point? And conversely if they sit on the prim inside the detector will a collision_end be raised?
  12. Hi All, This is a follow-on to the topic I started a few days back "Collision 'Face'" found HERE. After that discussion my design decision was to simply have a prim the size of the elevator, with collision_start giving me the count when someone entered the elevator and collision_end the number when someone exited. As I was cleaning up my comments and code a bit though, a doubt came to mind: Does a seated AV raise the _start/_end events? If I have an AV seated on a prim, and that prim is moved into the detector prim, will the total_number parameter passed to the event be 2, and so I'll be able to find the AV? The wiki is not clear in my view on this. Whilst is does say: In this case the AV is seated (linked) on a different prim from the one with the script. If it does count it, that would lead me to understand that a linkset that collided with the detector would be treated for the event as all the individual prims it is made up of? To the contrary, i.e. the total_number passed in the suggested scenario is only 1, and that one being the prim itself, it would lead me to conclude that if the AV stepped into the detector prim (raising collision_start), and then sat on a prim that was inside said detector, then a collision_end would be raised? Anyone?
  13. Does that mean that turning it non-physical will stop it's tumbling .. i.e. freeze it in whatever location/position it is in at that instant?
  14. One idea that just came to mind for stopping the physical elevator tumbling, but not quite sure how/if it would work: Put up some transparent walls along the parcel border. These would normally be phantom, but could turn solid/physical while the elevator is tumbling, so that they would stop it? Tend to remember that unless they are physical it would go through them, and would likely have to shore them up so that the elevator did not tip them over Or the same idea, but only have them there to detect the collision from the elevator and then they could send a message to the elevator. That brings me back to the question though of how to stop the tumbling elevator.
  15. Hi All, Next chapter in the scripting of my elevator. I have all the rest of the code written, though not tested yet (not getting a lot of in-world time). What I have left is how to script one specific scenario. While I built a good solid elevator, things fail now and then, particularly if people overload the elevator. So I have a check whilst the elevator is moving to see if there has been a malfunction. IF a malfunction occurs there are four things that could happen, listed below in order of likelihood: the elevator comes to a stop for a minute while the remote technicians get it going again. It falls a few meters, is stopped for a bit, and then goes to the bottom, being out-of-service until a technician (me) actually comes by to reset it (or an hour passes) It falls all the way to the bottom, but the emergency brake is able to stop it, again going out of service OR - Catastrophic Failure .. it falls and the brake does not stop it, it breaks off of the guide pole, and tumbles down the hill, ending up at a random angle with the doors broken and askew (note: this is an exterior elevator, not in an enclosed shaft, that travels up and down a pole using electromagnetic forces to move) It is the fourth scenario that I am trying to figure out how best to have occur and code it. What I was thinking was that I could turn the elevator physical, and thus let the SL physics engine take care of it falling and tumbling. This though brings me to my questions. I am imagining that the only way I can tell is it has stopped tumbling is with a timer event and llGetPos .. checking to see when it stops changing. Is there any other way that anyone can think of? What I do not want is for it to tumble off my land (and onto the Linden Road). Not only because I do not want to block the road, but as well I do not want it returned to my inventory. So again I could be tracking its position or as well use llOverMyLand, though this would only tell me after the fact. What I want to do is stop it before it leaves. So my question is, if I track where it is, and somehow know when it is about to leave the parcel, would setting it's Physical to FALSE stop it in it's tracks?
  16. @animats Thanks for the suggestion but is over the top for what I need and as well would require a lot of additional code. All I need to know is if anyone has come into the elevator or has left. Do not care about the individual, rather just the total number of passengers inside the elevator. Tracking the AV would mean keeping track of all that have entered, and considering the space inside is not extensive (it is an elevator after all) the changes in position would be minimal, with their location inside the elevator being irrelevant.
  17. The problem is that llGiveInventory takes a string as it's second parameter, not an integer. And that string is the name of the inventory item to give. What you'll need to do is using llGetInventoryNumber, and llGetInventoryName, compile a list of the inventory items and then best use llGiveInventoryList to give the items to the AV.
  18. Thanks @Rolig Loon & @ellestones believe I have it clear now ... hopefully will translate into a working elevator 🙂
  19. One last question .. can more than one AV sit on the same prim? i.e. do I need to do something after the first AV has seated to keep that from happening?
  20. Actually, with the requirement that I have a 'sit' script in each sit prim, I have pulled the change event from the main. The code in the individual scripts is: changed(integer change) { if(change & CHANGED_LINK) { integer num = 0; //see if I was sat on .. or someone that was sitting has gotten up key user = llAvatarOnSitTarget(); if (user != NULL_KEY) { //they sat ... request permissions to pose them llRequestPermissions(user, PERMISSION_TRIGGER_ANIMATION); num = 1; } //if we had someone .. then they have stood up and we need to stop their animation else if (gCurUser != NULL_KEY) { llStopAnimation(ELEV_STAND); gCurUser = NULL_KEY; num = -1; } //advise main if (num != 0) { string sNum = (string)num; llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_SEATED, sNum); } } } run_time_permissions(integer permissions) { if (permissions & PERMISSION_TRIGGER_ANIMATION) { //get their key gCurUser = llGetPermissionsKey(); ///stop the default sit llStopAnimation(DEF_SIT); //and start mine llStartAnimation(ELEV_STAND); } }
  21. Thanks @ellestones that is what I understood: changed(integer change) { if(change & CHANGED_LINK) { integer num = 0; //see if I was sat on .. or someone that was sitting has gotten up key user = llAvatarOnSitTarget(); if (user != NULL_KEY) { //they sat ... request permissions to pose them llRequestPermissions(user, PERMISSION_TRIGGER_ANIMATION); num = 1; } //if we had someone .. then they have stood up and we need to stop their animation else if (gCurUser != NULL_KEY) { llStopAnimation(ELEV_STAND); gCurUser = NULL_KEY; num = -1; } //advise main if (num != 0) { string sNum = (string)num; llMessageLinked(LINK_ALL_OTHERS, MAIN_CH, F_SEATED, sNum); } } } link_message(integer sender, integer channel, string function, key data) { if (channel == MAIN_CH) { //call from main to unseat any AV if (function == F_UNSEAT && gCurUser != NULL_KEY) { llUnSit(gCurUser); llStopAnimation(ELEV_STAND); } } } run_time_permissions(integer permissions) { if (permissions & PERMISSION_TRIGGER_ANIMATION) { //get their key gCurUser = llGetPermissionsKey(); ///stop the default sit llStopAnimation(DEF_SIT); //and start mine llStartAnimation(ELEV_STAND); } }
  22. hmmm .. ok . .now this leads me to other questions <ha ha> ... if I now have a script in each of the sitter prims am I correct in my understanding that the changed event will fire in each of these four scripts when an AV sits or stands, and the way for a particular script to know it it was them that was sat on is through llAvatarOnSitTarget(), coming up null if not there and with a key if so?
×
×
  • Create New...