//*************************************************************** // Copyright 2008 Centre For Advanced Spatial Analysis, UCL // // Author: Joel Dearden, University College London // // Contact: j.dearden@ucl.ac.uk // // Joel Dearden, // Centre for Advanced Spatial Analysis, // University College London, // 1-19 Torrington Place, // London, // WC1E 7HB // // // This file is part of SLPedEvac. // // SLPedEvac is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // SLPedEvac is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SLPedEvac. If not, see . // //*************************************************************** //CONSTANTS START========================================= integer controlSendCh = -220211; integer controlRecCh = -293321; integer talkToPed = -1; //set on rez integer talkToPMV = -1; //set on rez integer talkToStairEntry = -2929221; //integer INVALID_DESTTYPE = -1; integer EXIT_DESTTYPE = 0; integer SIGN_DESTTYPE = 1; integer STAIRENTRY_DESTTYPE = 2; //integer STAIRTOP_DESTTYPE = 3; integer START_DESTTYPE = 4; integer scanRange = 96; //metres float minDirMod; float maxDirMod; integer fsNEITHER = 0; integer fsLEFT = 1; integer fsRIGHT = 2; float rgh_thrshld_flat_xy = 1.5; //metres float rgh_thrshld_flat_z = 2.5; //metres float rgh_thrshld_staircase = 0.5; //metres float localSpcRng = 2; //metres float minLocalSpc = 4.6; //metres squared float maxLocalSpc = 6.8; //metres squared float pedArea = 0.1; //metres square float minFlatVel = 0.4; //m/s float maxFlatVel = 1.5; //m/s float minStaircaseVel = 0.325; //m/s float maxStaircaseVel = 0.8; //m/s integer vertPMVOffset = 4; //metres integer maxDestMoves = 10; //CONSTANTS END========================================= //VARIABLES START========================================= vector dest; integer destType = START_DESTTYPE; integer currentDestMoves = 0; integer flatmove = TRUE; list visitedSigns = []; integer destCheckStep = -1; list sensorList = []; list rotList = []; integer crrntSnsrListIndx = -1; integer maxSensorListIndex = -1; float currentVelocity = 0; float savedMoveBearing = 0; integer favouredSide = 0; float cmltveDirMod = 0; integer firstMoveCheck = TRUE; string lastLengthMessage = ""; float slopeAngle; float downSlopeBearing; float staircaseBottomZ; vector flatSlopeDirectionVector; integer blockedCounter = 0; integer waitingForStaircaseEntry = FALSE; //VARIABLES END========================================= //FUNCTIONS START========================================= integer signFacingMe(vector signPos, rotation signRot) { vector distVec = signPos - llGetPos(); vector signForwardVec = <1,0,0> * signRot; float relativeBearing = llFabs(angleBetweenVectors(distVec, signForwardVec)); //llOwnerSay("relativeBearing=" + (string)relativeBearing); if(relativeBearing > PI) { relativeBearing = (2*PI) - relativeBearing; } if(relativeBearing < (135 * DEG_TO_RAD)) { //llOwnerSay("signFacingMe=FALSE"); return FALSE; } else { //llOwnerSay("signFacingMe=TRUE"); return TRUE; } } // getRotToPointAxisAt() // Gets the rotation to point the specified axis at the specified position. // @param axis The axis to point. Easiest to just use an AXIS_* constant. // @param target The target, in region-local coordinates, to point the axis at. // @return The rotation necessary to point axis at target. // Created by Ope Rand, modifyed by Christopher Omega rotation getRotToPointAxisAt(vector axis, vector target) { return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos()); } CastLOSRay(vector target) { //cancel previous timeout llSetTimerEvent(0); //set new timeout llSetTimerEvent(60); //we want to fire a LOS ray at the target //llOwnerSay("firing LOS ray at" + (string)target); vector direction = target - llGetPos(); vector velocity = direction / llVecMag(direction); llRezObject( "LOSray", llGetPos(), velocity, ZERO_ROTATION, talkToPed ); } CreatePMV(float bearing) { //rotation PMVrotation = llGetRot() * llEuler2Rot(<0,0,bearing>); rotation facing = llEuler2Rot(<0,0,bearing>); //setup vector along x-axis... //length is (current velocity / 2) plus half depth of pedestrian //rotated by facing... vector createOffsetVector = <(currentVelocity/2) + 0.25,0,0> * facing; //create the pmv llRezObject("PMV", llGetPos() + createOffsetVector + <0,0,vertPMVOffset>, <0,0,0>, facing, talkToPed); //make sure pmv has time to start listening llSleep(1.0); //send length message lastLengthMessage = "ln:" + (string)currentVelocity; llSay(talkToPMV, lastLengthMessage); } CreateSPMV(float bearing) { //calculate slope vector vector slopeVector = <(currentVelocity/2) + 0.25,0,0>; //llOwnerSay("slopeVector=" + (string)slopeVector); float relativeBearing = relativeBearing(downSlopeBearing, bearing); //llOwnerSay("downSlopeBearing=" + (string)downSlopeBearing); //llOwnerSay("bearing=" + (string)bearing); //llOwnerSay("relativeBearing(downSlopeBearing, bearing)=" + (string)relativeBearing); slopeVector *= llEuler2Rot(<0,0,relativeBearing>); //llOwnerSay("slopeVector after rotation by relativeBearing=" + (string)slopeVector); slopeVector *= llEuler2Rot(<0,slopeAngle,0>); //llOwnerSay("slopeVector after rotation by slopeAngle=" + (string)slopeVector); //rotate to face bearing slopeVector *= llEuler2Rot(<0,0,-relativeBearing>); //undo relative rotation //llOwnerSay("slopeVector after rotation by -relativeBearing=" + (string)slopeVector); slopeVector *= llEuler2Rot(<0,0,bearing>); //llOwnerSay("slopeVector after rotation by bearing=" + (string)slopeVector); //calculate horizontal and vertical components vector h; h.x = slopeVector.x; h.y = slopeVector.y; h.z = 0; vector v; v.x = 0; v.y = 0; v.z = slopeVector.z; //if more than 45* off staircase bearing if(llFabs(relativeBearing) > (PI/2)) { //upslope from pedestrian //rez sPMV at pedPos + flatMovementVector – verticalMovementVector + verticalOffset llRezObject("sPMV", llGetPos() + h + v + <0,0,vertPMVOffset>, <0,0,0>, llEuler2Rot(<0,0,bearing>), talkToPed); } else { //downslope from pedestrian //rez sPMV at pedPos + flatMovementVector + verticalMovementVector + verticalOffset llRezObject("sPMV", llGetPos() + h - v + <0,0,vertPMVOffset>, <0,0,0>, llEuler2Rot(<0,0,bearing>), talkToPed); } //make sure pmv has time to start listening llSleep(1.0); //send length + slope angle + down-slope-bearing message lastLengthMessage = "lns:" + (string)currentVelocity + ";" + (string)slopeAngle + "/" + (string)downSlopeBearing; llSay(talkToPMV, lastLengthMessage); } float relativeBearing(float base, float measure) { float angle = measure - base; return angle; } ResendPMVLength() { llSay(talkToPMV, lastLengthMessage); } ConfirmNewPMV() { llSay(talkToPMV, "confirm"); } ClearOldPMV() { llSay(talkToPMV, "clear"); } integer roughlyEqualFlat(vector a, vector b) { //llOwnerSay("roughlyEqualFlat(" + (string)a + "," + (string)b + "="); if( (llFabs(a.x - b.x) < rgh_thrshld_flat_xy) && ((llFabs(a.y - b.y) < rgh_thrshld_flat_xy) && (llFabs(a.z - b.z) < rgh_thrshld_flat_z)) ) { //llOwnerSay("TRUE"); ////llOwnerSay("roughlyEqual(" + (string)a + "," + (string)b + ") returns TRUE"); return TRUE; } else { //llOwnerSay("FALSE"); //llOwnerSay("roughlyEqual(" + (string)a + "," + (string)b + ") returns FALSE"); return FALSE; } } integer roughlyEqualStaircase(vector a, vector b) { //llOwnerSay("roughlyEqualStaircase(" + (string)a + "," + (string)b + "="); if( (llFabs(a.x - b.x) < rgh_thrshld_staircase) && ((llFabs(a.y - b.y) < rgh_thrshld_staircase) && (llFabs(a.z - b.z) < rgh_thrshld_staircase)) ) { //llOwnerSay("TRUE"); ////llOwnerSay("roughlyEqual(" + (string)a + "," + (string)b + ") returns TRUE"); return TRUE; } else { //llOwnerSay("FALSE"); //llOwnerSay("roughlyEqual(" + (string)a + "," + (string)b + ") returns FALSE"); return FALSE; } } integer roughVectorInList(list checkList, vector pos) { integer i; //llOwnerSay("checkList=" + (string)checkList); for(i = 0; i < llGetListLength(checkList); i++) { if(roughlyEqualFlat(llList2Vector(checkList, i), pos)) { //llOwnerSay("llList2Vector(checkList, i)=" + (string)llList2Vector(checkList, i)); //llOwnerSay("pos=" + (string)pos); //llOwnerSay("are the same"); return TRUE; } } return FALSE; } //converts any bearing into a 0 to 2PI bearing float capBearingRange(float bearing) { if(bearing < 0) { //max should be -2PI bearing = (2 * PI) + bearing; } else { if(bearing > (2 * PI)) { //max should be 4PI bearing = bearing - (2 * PI); } } return bearing; } float distanceBetweenPos(vector posA, vector posB) { vector distVector; distVector.x = posA.x - posB.x; distVector.y = posA.y - posB.y; distVector.z = 0; float dist; dist = llSqrt(llPow((distVector.x), 2) + llPow((distVector.y), 2)); return dist; } //returns the bearing from this object to the specified target in the range 0 to 2PI float bearingToPos(vector target) { //find bearing of target from this pedestrian vector pedPos = llGetPos(); vector directionVector; directionVector.x = target.x - pedPos.x; directionVector.y = target.y - pedPos.y; directionVector.z = 0; float targetBearing; targetBearing = angleBetweenVectors(<1, 0, 0>, directionVector); if(targetBearing < 0) { targetBearing = (2 * PI) + targetBearing; } return targetBearing; } //returns the angle of measure relative to base float angleBetweenVectors(vector base, vector measure) { float angle; //angle of 2 relative to 1= atan2(v2.y,v2.x) - atan2(v1.y,v1.x) angle = llAtan2(measure.y, measure.x) - llAtan2(base.y, base.x); return angle; } //returns the acute angle of b relative to a float acuteAbsoluteAngleBetweenVectors(vector base, vector measure) { float angle; //angle of 2 relative to 1= atan2(v2.y,v2.x) - atan2(v1.y,v1.x) angle = llFabs(llAtan2(measure.y, measure.x) - llAtan2(base.y, base.x)); if(angle > PI) { angle = (2 * PI) - angle; } return angle; } init() { //set textures llSetTexture("f961d62a-300f-fe6d-29df-fd0e04fd1f35", ALL_SIDES); llSetTexture("c172efd0-d6c2-4484-b288-aebe2cab4403", 2); llSetTexture("44eb7ca6-1f6a-4b9f-fe7d-f51688b3dc17", 4); //initially we have reached our dest - forces us to choose a new one at the start; dest = llGetPos(); //maxNormalTurn = PI / 16; //radians minDirMod = PI / 16; //radians maxDirMod = PI / 8; //radians } //FUNCTIONS END========================================= //STATES START========================================= default { state_entry() { } on_rez(integer start_param) { //llOwnerSay("llGetFreeMemory( )=" + (string)llGetFreeMemory( )); init(); llListen(controlSendCh, "ABMcontroller", NULL_KEY, ""); llShout(controlRecCh, "pEdiN"); } listen(integer channel, string name, key id, string message) { //llOwnerSay("pedestrian: got message:" + message); //split into UUID and rayChannel integer dividerIndex = llSubStringIndex(message, ":"); if(dividerIndex != -1) { //is message for this pedestrian?... if((key)llGetSubString(message, 0, dividerIndex - 1) == llGetKey()) { //...yes integer base = (integer)llGetSubString(message, dividerIndex + 1, -1); talkToPed = base; talkToPMV = base + 1; //llOwnerSay("pedestrian: got channel range from " + (string)base + " to " + (string)(base+1)); state wander; } } } } state wander { state_entry() { llSetText("wander",<0,0,0>,1); llListen(controlSendCh, "ABMcontroller", NULL_KEY, ""); //start wander script llMessageLinked(LINK_THIS, talkToPed, "wander", NULL_KEY); } listen(integer channel, string name, key id, string message) { if(channel == controlSendCh) { if(message == "alarm") { //tell wander script to stop llMessageLinked(LINK_THIS, 0, "stopwander", NULL_KEY); llSleep(0.5); llMessageLinked(LINK_THIS, 0, "stopwander", NULL_KEY); llSleep(0.5); llMessageLinked(LINK_THIS, 0, "stopwander", NULL_KEY); } } } link_message(integer sender_num, integer num, string list_argument, key id) { if(list_argument == "confirmstopwander") { vector pos = llGetPos(); llHTTPRequest( "
/input.aspx?f=pi&px=" + (string)pos.x + "&py=" + (string)pos.y + "&pz=" + (string)pos.z + "&u=" + (string)llGetKey() + "&t=" + (string)llGetGMTclock(), [], "" ); state wait_clock; } } } state wait_clock { state_entry() { llSetText("wait_clock",<0,0,0>,1); llListen(controlSendCh, "ABMcontroller", NULL_KEY, "TiCk"); } listen(integer channel, string name, key id, string message) { if(message == "TiCk") { //llOwnerSay("pedstrian: got tickmsg: compare " + (string)llGetPos() + " and "+ (string)dest); if(waitingForStaircaseEntry) { state reach_flat_dest; } if(flatmove) { if(roughlyEqualFlat(dest, llGetPos())) { state reach_flat_dest; } else { if(currentDestMoves > maxDestMoves) { state choose_dest; } state flat_move; } } else { if(roughlyEqualStaircase(dest, llGetPos())) { state reach_staircase_bottom; } else { state staircase_move; } } } } } state reach_flat_dest { state_entry() { llSetText("reach_flat_dest",<0,0,0>,1); if(destType == START_DESTTYPE) { state choose_dest; } if(destType == EXIT_DESTTYPE) { state exit_model; } if(destType == SIGN_DESTTYPE) { //Add to visited list //llOwnerSay("added exit sign to visited list"); //llOwnerSay("oldList=" + (string)visitedSigns); visitedSigns += [dest]; //llOwnerSay("newList=" + (string)visitedSigns); state choose_dest; } if(destType == STAIRENTRY_DESTTYPE) { //ask staircase entry for staircase top and bottom positions (include our listen channel in msg) llListen(talkToPed, "", NULL_KEY, ""); llWhisper(talkToStairEntry, "sr:" + (string)talkToPed); //set timeout - staircase entry won't reply if already occupied llSetTimerEvent(5.0); //llOwnerSay("asking for staircase details..."); } } timer() { //staircase must be busy - wait until next update llSetTimerEvent(0.0); state register_move; } listen(integer channel, string name, key id, string message) { if(channel == talkToPed) { //staircase detail message // // FORMAT: sd:; if(llGetSubString(message, 0, 2) == "sd:") { //cancel retry timer llSetTimerEvent(0.0); //break msg into parts integer delimiterIndex = llSubStringIndex(message, ";"); vector staircaseTop = (vector) llGetSubString(message, 3, delimiterIndex - 1); vector staircaseBottom = (vector) llGetSubString(message, delimiterIndex + 1, -1); //move to staircase top immediately (or in stages if more than 10m) vector directionVector = llVecNorm(staircaseTop - llGetPos()); while(llVecDist(llGetPos(), staircaseTop) > 10.0) { //five times direction vector should ensure straight line distance doesn't exceed 10m llSetPos(llGetPos() + (5*directionVector)); } llSetPos(staircaseTop); //calculate slope direction vector flatSlopeDirectionVector = staircaseBottom - staircaseTop; flatSlopeDirectionVector.z = 0; //calculate staircase angle //from basic trigonometry: slope angle = invtan(x,y dist / z dist) slopeAngle = llAtan2( (staircaseTop.z - staircaseBottom.z), llSqrt(llPow((staircaseTop.x - staircaseBottom.x),2) + llPow((staircaseTop.y - staircaseBottom.y),2)) ); //llOwnerSay("slopeAngle = " + (string)slopeAngle); //calculate staircase down slope bearing downSlopeBearing = bearingToPos(staircaseBottom); //Set relevant staircaseBottom as dest dest = staircaseBottom; //save staircase bottom z so we can set it when we reach the bottom staircaseBottomZ = staircaseBottom.z; flatmove = FALSE; waitingForStaircaseEntry = FALSE; state staircase_move; } } } } state reach_staircase_bottom { state_entry() { llSetText("reach_staircase_bottom",<0,0,0>,1); flatmove = TRUE; //make sure we are at floor level now vector pos = llGetPos(); pos.z = staircaseBottomZ; llSetPos(pos); state choose_dest; } } state choose_dest { //this has lots of sub-states state_entry() { llSetText("choose_dest",<0,0,0>,1); //llOwnerSay("pedestrian: choosing dest"); currentDestMoves = 0; state choose_dest_SUBSTATE_check_exitdoors; } } state choose_dest_SUBSTATE_check_exitdoors { state_entry() { llSetText("choose_dest_SUBSTATE_check_exitdoors",<0,0,0>,1); //llOwnerSay("pedestrian: choosing dest: looking for exit?"); llSensor("exit", NULL_KEY, PASSIVE | SCRIPTED, scanRange, PI); } sensor(integer num_detected) { //llOwnerSay((string)num_detected + " exits found"); //put detected objects into sensor list sensorList = []; integer i; for(i = 0; i < num_detected; i++) { sensorList += [llDetectedPos(i)]; } //listen for ray info llListen(talkToPed, "", NULL_KEY, ""); crrntSnsrListIndx = 0; maxSensorListIndex = num_detected; CastLOSRay(llDetectedPos(crrntSnsrListIndx)); } no_sensor() { //llOwnerSay("no exits found...moving onto next dest type check.."); state choose_dest_SUBSTATE_check_staircase_entries; } listen(integer channel, string name, key id, string message) { //get visibility state of current item in list //we are checking LOS //ray info sent back will be: // // : //llOwnerSay("pedestrian: got message ?:" + message); if(message == "X") { //llOwnerSay("nothing hit by LOS ray cast: retrying"); CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //should be valid collision info integer dividerIndex = llSubStringIndex(message, ":"); string collisionObjectName = (string) llGetSubString(message, 0, dividerIndex - 1); vector collisionObjectPos = (vector) llGetSubString(message, dividerIndex + 1, -1); if(roughlyEqualFlat(collisionObjectPos,llList2Vector(sensorList, crrntSnsrListIndx))) { //this exit is visible! //llOwnerSay("pedestrian: exit at" + (string)llList2Vector(sensorList, crrntSnsrListIndx) + " is visible"); //set as dest dest = llList2Vector(sensorList, crrntSnsrListIndx); destType = EXIT_DESTTYPE; //llOwnerSay("pedestrian: my dest is an exit"); state flat_move; } else { //this exit is not visible //llOwnerSay("pedestrian: exit at" + (string)llList2Vector(sensorList, crrntSnsrListIndx) + " is NOT visible"); //llOwnerSay("crrntSnsrListIndx:" + (string)crrntSnsrListIndx + "-vs-maxSensorListIndex:" + (string)maxSensorListIndex ); //make progress crrntSnsrListIndx++; //any more to check? if(crrntSnsrListIndx < maxSensorListIndex) { //yes CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //no state choose_dest_SUBSTATE_check_staircase_entries; } } } } timer() { //something wrong with LOS ray - restart llSetTimerEvent(0); state choose_dest_SUBSTATE_check_exitdoors; } } state choose_dest_SUBSTATE_check_staircase_entries { state_entry() { llSetText("choose_dest_SUBSTATE_check_staircase_entries",<0,0,0>,1); //llOwnerSay("pedestrian: choosing dest: looking for staircase entry?"); llSensor("staircaseEntry", NULL_KEY, PASSIVE | SCRIPTED, scanRange, PI); } sensor(integer num_detected) { //put detected objects into sensor list sensorList = []; integer i; for(i = 0; i < num_detected; i++) { sensorList += [llDetectedPos(i)]; } //listen for ray info llListen(talkToPed, "", NULL_KEY, ""); crrntSnsrListIndx = 0; maxSensorListIndex = num_detected; CastLOSRay(llDetectedPos(crrntSnsrListIndx)); } no_sensor() { state choose_dest_SUBSTATE_check_new_exit_sign; } listen(integer channel, string name, key id, string message) { //get visibility state of current item in list //we are checking LOS //ray info sent back will be: // // : if(message == "X") { //llOwnerSay("nothing hit by LOS ray cast: retrying"); CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //should be valid collision info integer dividerIndex = llSubStringIndex(message, ":"); string collisionObjectName = (string) llGetSubString(message, 0, dividerIndex - 1); vector collisionObjectPos = (vector) llGetSubString(message, dividerIndex + 1, -1); if(roughlyEqualFlat(collisionObjectPos,llList2Vector(sensorList, crrntSnsrListIndx))) { //this staircaseEntry is visible! //set as dest dest = llList2Vector(sensorList, crrntSnsrListIndx); destType = STAIRENTRY_DESTTYPE; //llOwnerSay("pedestrian: my dest is a staircase entry"); state flat_move; } else { //this staircase entry is not visible //make progress crrntSnsrListIndx++; //any more to check? if(crrntSnsrListIndx < maxSensorListIndex) { //yes CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //no state choose_dest_SUBSTATE_check_new_exit_sign; } } } } timer() { //something wrong with LOS ray - restart llSetTimerEvent(0); state choose_dest_SUBSTATE_check_staircase_entries; } } state choose_dest_SUBSTATE_check_new_exit_sign { state_entry() { llSetText("choose_dest_SUBSTATE_check_new_exit_sign",<0,0,0>,1); //llOwnerSay("pedestrian: choosing dest: looking for new exit sign?"); llSensor("exitSign", NULL_KEY, PASSIVE | SCRIPTED, scanRange, PI); } sensor(integer num_detected) { //put detected objects into sensor list sensorList = []; //also save rotation to determine sign facing rotList = []; integer i; //llOwnerSay("found the following exit signs:"); for(i = 0; i < num_detected; i++) { //llOwnerSay(llDetectedName(i) + ":" + (string)llDetectedPos(i)); sensorList += [llDetectedPos(i)]; rotList += [llDetectedRot(i)]; } //listen for ray info llListen(talkToPed, "", NULL_KEY, ""); crrntSnsrListIndx = 0; maxSensorListIndex = num_detected; //llOwnerSay("so sensor list looks like this:" + (string)sensorList); //llOwnerSay("crrntSnsrListIndx=" + (string)crrntSnsrListIndx); //llOwnerSay("maxSensorListIndex=" + (string)maxSensorListIndex); CastLOSRay(llDetectedPos(crrntSnsrListIndx)); } no_sensor() { state choose_dest_SUBSTATE_check_old_exit_sign; } listen(integer channel, string name, key id, string message) { //get visibility state of current item in list //we are checking LOS //ray info sent back will be: // // : if(message == "X") { //llOwnerSay("nothing hit by LOS ray cast: retrying"); //recast LOS ray for current exit sign CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //should be valid collision info integer dividerIndex = llSubStringIndex(message, ":"); string collisionObjectName = (string) llGetSubString(message, 0, dividerIndex - 1); vector collisionObjectPos = (vector) llGetSubString(message, dividerIndex + 1, -1); if(roughlyEqualFlat(collisionObjectPos,llList2Vector(sensorList, crrntSnsrListIndx))) { //this exitsign is visible! //llOwnerSay("pedestrian: exit sign at" + (string)llList2Vector(sensorList, crrntSnsrListIndx) + " is visible"); //have we been there before? if(roughVectorInList(visitedSigns, collisionObjectPos) == FALSE) { //no! //BUT is it facing the right way? if(signFacingMe(llList2Vector(sensorList, crrntSnsrListIndx), llList2Rot(rotList, crrntSnsrListIndx))) { //yes //llOwnerSay("visitedSigns=" + (string)visitedSigns); //llOwnerSay("pedestrian: ...and we haven't been there before...so..."); //set as dest dest = llList2Vector(sensorList, crrntSnsrListIndx); destType = SIGN_DESTTYPE; //llOwnerSay("pedestrian: my dest is a new exit sign"); state flat_move; } else { //no //we've been there before //llOwnerSay("pedestrian: ...but we have been there before...so will keep looking"); //make progress crrntSnsrListIndx++; //any more to check? if(crrntSnsrListIndx < maxSensorListIndex) { //yes CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //no state choose_dest_SUBSTATE_check_old_exit_sign; } } } else { //yes //we've been there before //llOwnerSay("pedestrian: ...but we have been there before...so will keep looking"); //make progress crrntSnsrListIndx++; //any more to check? if(crrntSnsrListIndx < maxSensorListIndex) { //yes CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //no state choose_dest_SUBSTATE_check_old_exit_sign; } } } else { //this exit sign is not visible //llOwnerSay("pedestrian: exit sign at" + (string)llList2Vector(sensorList, crrntSnsrListIndx) + " is NOT visible"); //make progress crrntSnsrListIndx++; //any more to check? if(crrntSnsrListIndx < maxSensorListIndex) { //yes CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //no state choose_dest_SUBSTATE_check_old_exit_sign; } } } } timer() { //something wrong with LOS ray - restart llSetTimerEvent(0); state choose_dest_SUBSTATE_check_new_exit_sign; } } state choose_dest_SUBSTATE_check_old_exit_sign { state_entry() { llSetText("choose_dest_SUBSTATE_check_old_exit_sign",<0,0,0>,1); //llOwnerSay("pedestrian: choosing dest: looking for old exit sign?"); llSensor("exitSign", NULL_KEY, PASSIVE | SCRIPTED, scanRange, PI); } sensor(integer num_detected) { //put detected objects into sensor list sensorList = []; //also save rotation to determine sign facing rotList = []; integer i; for(i = 0; i < num_detected; i++) { sensorList += [llDetectedPos(i)]; rotList += [llDetectedRot(i)]; } //listen for ray info llListen(talkToPed, "", NULL_KEY, ""); crrntSnsrListIndx = 0; maxSensorListIndex = num_detected; CastLOSRay(llDetectedPos(crrntSnsrListIndx)); } no_sensor() { state choose_dest_SUBSTATE_check_any_exit_sign; } listen(integer channel, string name, key id, string message) { //get visibility state of current item in list //we are checking LOS //ray info sent back will be: // // : if(message == "X") { //llOwnerSay("nothing hit by LOS ray cast: retrying"); CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //should be valid collision info integer dividerIndex = llSubStringIndex(message, ":"); string collisionObjectName = (string) llGetSubString(message, 0, dividerIndex - 1); vector collisionObjectPos = (vector) llGetSubString(message, dividerIndex + 1, -1); if(roughlyEqualFlat(collisionObjectPos,llList2Vector(sensorList, crrntSnsrListIndx))) { //this exitsign is visible! //we don't care now if its an old one! //BUT is it facing the right way? if(signFacingMe(llList2Vector(sensorList, crrntSnsrListIndx), llList2Rot(rotList, crrntSnsrListIndx))) { //yes //set as dest dest = llList2Vector(sensorList, crrntSnsrListIndx); destType = SIGN_DESTTYPE; //llOwnerSay("pedestrian: my dest is an old exit sign"); state flat_move; } else { //no //make progress crrntSnsrListIndx++; //any more to check? if(crrntSnsrListIndx < maxSensorListIndex) { //yes CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //no state choose_dest_SUBSTATE_check_any_exit_sign; } } } else { //this exitsign is not visible //make progress crrntSnsrListIndx++; //any more to check? if(crrntSnsrListIndx < maxSensorListIndex) { //yes CastLOSRay(llList2Vector(sensorList, crrntSnsrListIndx)); } else { //no state choose_dest_SUBSTATE_check_any_exit_sign; } } } } timer() { //something wrong with LOS ray - restart llSetTimerEvent(0); state choose_dest_SUBSTATE_check_old_exit_sign; } } state choose_dest_SUBSTATE_check_any_exit_sign { state_entry() { llSetText("choose_dest_SUBSTATE_check_any_exit_sign",<0,0,0>,1); //llOwnerSay("pedestrian: choosing dest: looking for any exit sign?"); llSensor("exitSign", NULL_KEY, PASSIVE | SCRIPTED, scanRange, PI); } sensor(integer num_detected) { //there is a exit sign nearby //just pick the closest one //we don't care now if visible or not //we don't care if its an old one! //set as dest dest = llDetectedPos(0); destType = SIGN_DESTTYPE; //llOwnerSay("pedestrian: my dest is any exit sign"); state flat_move; } no_sensor() { //llOwnerSay("ERROR: I can't find any exit signs!"); } } state register_move { state_entry() { llSetText("register_move",<0,0,0>,1); //llOwnerSay("pedestrian: registering move"); currentDestMoves++; llListen(controlSendCh, "", NULL_KEY, ""); llShout(controlRecCh, "RegMoV"); //log move vector pos = llGetPos(); llHTTPRequest( "
/input.aspx?f=pu&px=" + (string)pos.x + "&py=" + (string)pos.y + "&pz=" + (string)pos.z + "&u=" + (string)llGetKey() + "&t=" + (string)llGetGMTclock() + "&s=" + (string)currentVelocity, [], "" ); llSetTimerEvent(10); } listen(integer channel, string name, key id, string message) { if(llGetSubString(message, 0, 2) == "ACK") { key test = (key) llGetSubString(message, 3, -1); //llOwnerSay("checking keys:" + (string)test + " against " + (string)llGetKey()); if(test == llGetKey()) { //llOwnerSay("match"); llSetTimerEvent(0.0); state wait_clock; } else { //llOwnerSay("no match"); } } } timer() { //llOwnerSay("resending regmove"); llShout(controlRecCh, "RegMoV"); } } state flat_move { state_entry() { llSetText("flat_move",<0,0,0>,1); //this just goes through three substates //1) look //2) think //3) act state flat_move_SUBSTATE_look; } } state flat_move_SUBSTATE_look { state_entry() { llSetText("flat_move_SUBSTATE_look",<0,0,0>,1); //DEBUG //llOwnerSay("look"); //scan for pedestrians nearby to determine local space for velocity update llSensor("pedestrian", NULL_KEY, PASSIVE | SCRIPTED, localSpcRng, PI_BY_TWO); } no_sensor() { //no pedestrians nearby so move at maximum speed currentVelocity = maxFlatVel; state flat_move_SUBSTATE_raycast_think; } sensor(integer total_number) // total_number is the number of objects detected. { //this can be optimised but only when we know it works! //calculate total free space float currentSpace = maxLocalSpc - (total_number * pedArea); //calculate velocity multiplier float velMult = (currentSpace - minLocalSpc) / (maxLocalSpc - minLocalSpc); //calculate current velocity currentVelocity = minFlatVel + (velMult * (maxFlatVel - minFlatVel)); state flat_move_SUBSTATE_raycast_think; } } state flat_move_SUBSTATE_raycast_think { state_entry() { llSetText("flat_move_SUBSTATE_raycast_think",<0,0,0>,1); //clear any existing PMV - we only ever want one ClearOldPMV(); //enter state and set move direction as ATCF savedMoveBearing = bearingToPos(dest); //favouredSide = fsNEITHER; //listen for PMV replies llListen(talkToPed, "", NULL_KEY, ""); //start looping move-test process... cmltveDirMod = 0; //llOwnerSay("cmltveDirMod=0"); firstMoveCheck = TRUE; CreatePMV(savedMoveBearing); } listen(integer channel, string name, key id, string message) { //llOwnerSay("flat_move_ped:got msg:" + message); if(message == "pmvbroken") { //rez a replacement CreatePMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } if(message == "lostmsg") { //PMV hasn't received length message //resend previous message ResendPMVLength(); } if(message == "safe") { //move OK - keep the new PMV savedMoveBearing += cmltveDirMod; if(firstMoveCheck == TRUE) { //ATCF move worked - reset favoured side favouredSide = fsNEITHER; } //fix the new PMV ConfirmNewPMV(); blockedCounter = 0; state flat_move_SUBSTATE_act; } if(message == "unsafe") { //the move is not safe //(the PMV will delete itself) // otherwise modify move and then repeat //when a pedestrian modifies its move it has obviously reached an obstacle and will slow down //e.g you don't sprint round a table you go round it at half speed or less and then speed up afterwards //so... if(firstMoveCheck == TRUE) { firstMoveCheck = FALSE; currentVelocity = currentVelocity/2; //we only cut velocity in half once } //Other moves means testing each of the following possibilities until we find a match //Change move by random angle away from ATCF – try (randomly) ALL LEFT or ALL RIGHT versions if(llFabs(cmltveDirMod) < ((2*PI)/3)) { //if we have a favoured side keep trying that if(favouredSide == fsLEFT) { //keep left cmltveDirMod += minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsLEFT; CreatePMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } else { if(favouredSide == fsRIGHT) { //keep right cmltveDirMod -= minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsRIGHT; CreatePMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } else { //random if(llFrand(1.0) > 0.5) { //try left cmltveDirMod += minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsLEFT; CreatePMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } else { //try right cmltveDirMod -= minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsRIGHT; CreatePMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } } } } else { //llOwnerSay("cmltveDirMod>PI so agent not moving this update"); //If we reach here then the agent can’t move this update //set the other side as favoured so we don't keep repeating the same mistake if(favouredSide == fsLEFT) { favouredSide = fsRIGHT; } else { if(favouredSide == fsRIGHT) { favouredSide = fsLEFT; } } //check for agents that have been jammed for a long time blockedCounter++; if(blockedCounter > 3) { state unjam_agent; } else { //favouredSide = fsNEITHER; state register_move; } } } } } state unjam_agent { state_entry() { llSetText("unjam_agent",<0,0,0>,1); //llOwnerSay("I'm blocked what do I do?"); state register_move; } } state flat_move_SUBSTATE_act { state_entry() { llSetText("flat_move_SUBSTATE_act",<0,0,0>,1); //DEBUG //llOwnerSay("act"); //Move the pedestrian a at current velocity in the chosen direction vector eulerBearing = <0, 0, savedMoveBearing>; rotation quatBearing = llEuler2Rot(eulerBearing); //need to rotate to face direction we are moving llSetRot(quatBearing); vector moveVector; //moveVector = <0, 1, 0> * (halfMoveRange + llFrand(halfMoveRange)); moveVector = ; moveVector = moveVector * quatBearing; vector position = llGetPos(); position += moveVector; llSetPos(position); //log move vector pos = llGetPos(); //llHTTPRequest( "
/input.aspx?f=pu&px=" + // (string)pos.x + // "&py=" // + (string)pos.y + // "&u=" /// + (string)llGetKey() + /// "&t=" + // (string)llGetGMTclock(), //[], //"" //); state register_move; } } state staircase_move { state_entry() { llSetText("staircase_move",<0,0,0>,1); //this just goes through three substates //1) look //2) think //3) act state staircase_move_SUBSTATE_look; } } state staircase_move_SUBSTATE_look { state_entry() { llSetText("staircase_move_SUBSTATE_look",<0,0,0>,1); //DEBUG //llOwnerSay("look"); //scan for pedestrians nearby to determine local space for velocity update llSensor("pedestrian", NULL_KEY, PASSIVE | SCRIPTED, localSpcRng, PI_BY_TWO); } no_sensor() { //no pedestrians nearby so move at maximum speed currentVelocity = maxStaircaseVel; state staircase_move_SUBSTATE_raycast_think; } sensor(integer total_number) // total_number is the number of objects detected. { //this can be optimised but only when we know it works! //calculate total free space float currentSpace = maxLocalSpc - (total_number * pedArea); //calculate velocity multiplier float velMult = (currentSpace - minLocalSpc) / (maxLocalSpc - minLocalSpc); //calculate current velocity currentVelocity = minStaircaseVel + (velMult * (maxStaircaseVel - minStaircaseVel)); state staircase_move_SUBSTATE_raycast_think; } } state staircase_move_SUBSTATE_raycast_think { state_entry() { llSetText("staircase_move_SUBSTATE_raycast_think",<0,0,0>,1); //clear any existing PMV - we only ever want one ClearOldPMV(); //face parallel to slope decline //llSetRot(llEuler2Rot(<0,0,downSlopeBearing>)); //enter state and set move direction as ATCF savedMoveBearing = bearingToPos(dest); //favouredSide = fsNEITHER; //listen for PMV replies llListen(talkToPed, "", NULL_KEY, ""); //start looping move-test process... cmltveDirMod = 0; //llOwnerSay("cmltveDirMod=0"); firstMoveCheck = TRUE; CreateSPMV(savedMoveBearing); } listen(integer channel, string name, key id, string message) { if(message == "pmvbroken") { //rez a replacement CreateSPMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } if(message == "lostmsg") { //PMV hasn't received length message //resend previous message ResendPMVLength(); } if(message == "safe") { //move OK - keep the new PMV savedMoveBearing += cmltveDirMod; if(firstMoveCheck == TRUE) { //ATCF move worked - reset favoured side favouredSide = fsNEITHER; } //fix the new PMV ConfirmNewPMV(); state staircase_move_SUBSTATE_act; } if(message == "unsafe") { //the move is not safe //(the PMV will delete itself) // otherwise modify move and then repeat //when a pedestrian modifies its move it has obviously reached an obstacle and will slow down //e.g you don't sprint round a table you go round it at half speed or less and then speed up afterwards //so... if(firstMoveCheck == TRUE) { firstMoveCheck = FALSE; currentVelocity = currentVelocity/2; //we only cut velocity in half once } //Other moves means testing each of the following possibilities until we find a match //Change move by random angle away from ATCF – try (randomly) ALL LEFT or ALL RIGHT versions if(llFabs(cmltveDirMod) < (PI/2)) { //if we have a favoured side keep trying that if(favouredSide == fsLEFT) { //keep left cmltveDirMod += minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsLEFT; CreateSPMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } else { if(favouredSide == fsRIGHT) { //keep right cmltveDirMod -= minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsRIGHT; CreateSPMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } else { //random if(llFrand(1.0) > 0.5) { //try left cmltveDirMod += minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsLEFT; CreateSPMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } else { //try right cmltveDirMod -= minDirMod + llFrand(maxDirMod - minDirMod); //llOwnerSay("cmltveDirMod=" + (string)cmltveDirMod); favouredSide = fsRIGHT; CreateSPMV(capBearingRange(savedMoveBearing + cmltveDirMod)); } } } } else { //llOwnerSay("cmltveDirMod>PI so agent not moving this update"); //If we reach here then the agent can’t move this update //set the other side as favoured so we don't keep repeating the same mistake if(favouredSide == fsLEFT) { favouredSide = fsRIGHT; } if(favouredSide == fsRIGHT) { favouredSide = fsLEFT; } //favouredSide = fsNEITHER; state register_move; } } } } state staircase_move_SUBSTATE_act { state_entry() { llSetText("staircase_move_SUBSTATE_act",<0,0,0>,1); //DEBUG //llOwnerSay("act"); //rotate to face direction we are moving llSetRot(llEuler2Rot(<0,0,savedMoveBearing>)); //Move the pedestrian at current velocity in the chosen direction on the staircase plane //calculate horizontal component of offset vector moveVector = ; //llOwnerSay("moveVector=" + (string)moveVector); float relativeBearing = relativeBearing(downSlopeBearing, savedMoveBearing); //llOwnerSay("downSlopeBearing=" + (string)downSlopeBearing); //llOwnerSay("savedMoveBearing=" + (string)savedMoveBearing); //llOwnerSay("relativeBearing(downSlopeBearing, savedMoveBearing)=" + (string)relativeBearing); moveVector *= llEuler2Rot(<0,0,relativeBearing>); //llOwnerSay("moveVector after rotation by relativeBearing=" + (string)moveVector); moveVector *= llEuler2Rot(<0,slopeAngle,0>); //llOwnerSay("moveVector after rotation by slopeAngle=" + (string)moveVector); //rotate to face bearing moveVector *= llEuler2Rot(<0,0,-relativeBearing>); //undo relative rotation //llOwnerSay("moveVector after rotation by -relativeBearing=" + (string)moveVector); moveVector *= llEuler2Rot(<0,0,savedMoveBearing>); //llOwnerSay("moveVector after rotation by savedMoveBearing=" + (string)moveVector); llSetPos(llGetPos() + moveVector); state register_move; } } state exit_model { state_entry() { llSetText("exit_model",<0,0,0>,1); llShout(controlRecCh, "pedOUt"); //delete any remaining PMVs ClearOldPMV(); //log exit with web server vector pos = llGetPos(); llHTTPRequest( "
/input.aspx?f=po&px=" + (string)pos.x + "&py=" + (string)pos.y + "&pz=" + (string)pos.z + "&u=" + (string)llGetKey() + "&t=" + (string)llGetGMTclock(), [], "" ); llDie(); } } //STATES END=========================================