diff --git a/Move-From-Point-To-Point/Move-From-Point-To-Point/Mover Prim/Mover Script.lsl b/Move-From-Point-To-Point/Move-From-Point-To-Point/Mover Prim/Mover Script.lsl index e91cc4da..8331efc5 100644 --- a/Move-From-Point-To-Point/Move-From-Point-To-Point/Mover Prim/Mover Script.lsl +++ b/Move-From-Point-To-Point/Move-From-Point-To-Point/Mover Prim/Mover Script.lsl @@ -7,57 +7,56 @@ // :AUTHOR:Unknown // :KEYWORDS: // :CREATED:2019-08-30 11:39:48 -// :EDITED:2020-04-10 08:29:30 // :ID:1125 // :NUM:2012 -// :REV:1 +// :REV:2 // :WORLD:Second Life // :DESCRIPTION: // Move From point to pint tour guide // :CODE: -float delay = 0.1; + integer m_STEPS = 64; motionTo(vector dest,rotation rot) { - integer i; - vector currentpos = llGetPos(); - vector step = (dest - currentpos) / m_STEPS; + integer i; + vector currentpos = llGetPos(); + vector step = (dest - currentpos) / m_STEPS; - for (i = 1;i <= m_STEPS; i++) - { - if (i == (m_STEPS / 2)) - { - llSetRot(rot); - } - llSetPos(currentpos + (step * i)); + for (i = 1;i <= m_STEPS; i++) + { + if (i == (m_STEPS / 2)) + { + llSetRot(rot); + } + llSetPos(currentpos + (step * i)); - } - llSensor("Track",NULL_KEY,ACTIVE | PASSIVE,10,PI/4); + } + llSensor("Track",NULL_KEY,ACTIVE | PASSIVE,10,PI/4); } default { - state_entry() { - llSensor("Track",NULL_KEY,ACTIVE | PASSIVE,10,PI/4); - } - changed(integer what) { - if (what & CHANGED_REGION_START){ - llResetScript(); - } - } - sensor(integer n) { - motionTo(llDetectedPos(0) +(llRot2Fwd(m_NEXTROT) / 10),llDetectedRot(0)); - } - no_sensor() - { - llSetTimerEvent(10); - } - timer() { - llSensor("Track",NULL_KEY,ACTIVE | PASSIVE,10,PI/4); - llSetTimerEvent(0); - } - - + state_entry() { + llSensor("Track",NULL_KEY,ACTIVE | PASSIVE,10,PI/4); + } + changed(integer what) { + if (what & CHANGED_REGION_START){ + llResetScript(); + } + } + sensor(integer n) { + motionTo(llDetectedPos(0),llDetectedRot(0)); + } + no_sensor() + { + llSetTimerEvent(10); + } + timer() { + llSensor("Track",NULL_KEY,ACTIVE | PASSIVE,10,PI/4); + llSetTimerEvent(0); + } + + } diff --git a/Opensim Train/Train/Opensim Train/Object/2021-03-14_101046.jpg b/Opensim Train/Train/Opensim Train/Object/2021-03-14_101046.jpg new file mode 100644 index 00000000..229fe2b6 Binary files /dev/null and b/Opensim Train/Train/Opensim Train/Object/2021-03-14_101046.jpg differ diff --git a/OpensimTrain/Opensim Train/Opensim Train.prj b/OpensimTrain/Opensim Train/Opensim Train.prj new file mode 100644 index 00000000..83230a1c --- /dev/null +++ b/OpensimTrain/Opensim Train/Opensim Train.prj @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/OpensimTrain/Opensim Train/Train/2021-03-14_101046.jpg b/OpensimTrain/Opensim Train/Train/2021-03-14_101046.jpg new file mode 100644 index 00000000..e69de29b diff --git a/OpensimTrain/Opensim Train/Train/LocomotiveScript.lsl b/OpensimTrain/Opensim Train/Train/LocomotiveScript.lsl new file mode 100644 index 00000000..cd4aa6b5 --- /dev/null +++ b/OpensimTrain/Opensim Train/Train/LocomotiveScript.lsl @@ -0,0 +1,1515 @@ +// :CATEGORY:Train +// :NAME:Opensim Train +// :AUTHOR:Moundsa Mayo +// :CREATED: +// :EDITED: +// :REV:2.6.0// :WORLD:OpensimSim +// :DESCRIPTION:this is setup for non-physical, phantom movement but can be modified for physical, etc. +// :CODE: + +// [VRCLocomotiveOpensourceScript] + +string gsScriptVersion = "2.6.0"; + +// Created for VRC distribution by Moundsa Mayo +// +// Based on the original Opensource Hobo Train Script +// written by Twisted Laws sometime in 2007 or 2008 +// +// Contact the VRC for the original script unaltered along with some +// revision history removed from this version. +// + + +// train driver script example for SLRR. +// +// this is setup for non-physical, phantom movement but can be +// modified for physical, etc. + + +//===================================================================// +//==== Global Constants ============================ ====// + +integer INDEX_INVALID = -1; +integer LINK_INVALID = -32768; +string NAME_WILDCARD = "*"; + +vector COLOR_GREEN = <0.00, 1.00, 0.00>; +vector COLOR_BLACK = <0.00, 0.00, 0.00>; + +//==== Global Constants ============================== ====// +//===================================================================// + + +//===================================================================// +//==== Utility Routines ============================ ====// + +string gsHexPrefix = "0x"; +string gsHexChars = "0123456789ABCDEF"; + +string Int2Hex(integer iInt, integer iDigits) +{ + integer iWork = iInt & 0xF; + string sResult = llGetSubString(gsHexChars, iWork, iWork); + iInt = (iInt >> 4) & 0x0FFFFFFF; + + while (iInt != 0) + { + iWork = iInt & 0xF; + sResult = llGetSubString(gsHexChars, iWork, iWork) + sResult; + iInt = iInt >> 4; + } + + if (llStringLength(sResult) < iDigits) + { + sResult = "00000000" + sResult; + sResult = llGetSubString(sResult, -iDigits, - 1); + } + + return(gsHexPrefix + sResult); +} // string Int2Hex + + +// Force unsit all sitting agents +integer UnsitAllAgents() +{ + integer iIndex; + list lLinkDetails = []; + integer iAgentCount = 0; + integer iLinkCount = llGetNumberOfPrims(); + + for (iIndex = 1; iIndex <= iLinkCount; ++iIndex) + { + lLinkDetails = llGetObjectDetails(llGetLinkKey(iIndex), [OBJECT_CREATOR]); + + // Probably an Agent, so unsit and add to count + if (llList2Key(lLinkDetails, 0) == NULL_KEY) + { + ++iAgentCount; + llUnSit(llGetLinkKey(iIndex)); + } + + } // for all Links in Object + + return(iAgentCount); +} // UnsitAllAgents + + +// Sum up prims in linkset, omitting any sitting agents +// (llGetNumberOfPrims includes sitting Agents in the returned count) +integer PrimCount() +{ + integer iIndex; + list lLinkDetails = []; + integer iPrimCount = 0; + integer iLinkCount = llGetNumberOfPrims(); + + for (iIndex = 1; iIndex <= iLinkCount; ++iIndex) + { + lLinkDetails = llGetObjectDetails(llGetLinkKey(iIndex), + [OBJECT_CREATOR]); + + // Probably not an Agent, so add to count + if (llList2Key(lLinkDetails, 0) != NULL_KEY) + { + ++iPrimCount; + } + + } // for all Links in Object + + // llOwnerSay("Links=" + (string)iPrimCount); + return(iPrimCount); +} // PrimCount + + +string clip(float value) +{ + string str = (string)value; + integer where = llSubStringIndex(str, "."); + return(llGetSubString(str, 0, where + 2)); +} // clip + + +integer NamedLinkFind(string sName) +{ + // locate a linked prim + integer i; + integer m = llGetNumberOfPrims(); + integer ret= LINK_INVALID; + + for (i = 1; i <= m; i++) + { + + if (llGetLinkName(i) == sName) + { + ret = i; + } + + } + + return ret; +} // NamedLinkFind + + +key ScanForAgentGone() +{ + key kLinkID; + + integer iIndex; + integer iRiderRollCount = llGetListLength(glRidersAboard); + + // Scan Rider roll for ID missing from linked objects beyond + // original object linkset. Assume any missing are AGENT + list lRiderLinks = []; + + for (iIndex = giLinkCountRiderless + 1; iIndex <= + giLinkCountCurrent; ++iIndex) + { + kLinkID = llGetLinkKey(iIndex); + lRiderLinks = lRiderLinks + (list)kLinkID; + } + + for (iIndex = 0; iIndex < iRiderRollCount; ++iIndex) + { + kLinkID = llList2Key(glRidersAboard, iIndex); + + if (llListFindList(lRiderLinks, [kLinkID]) == INDEX_INVALID) + { + return(kLinkID); + } + + } // for all high links (sitting atatars) + + return(NULL_KEY); +} // ScanForAgentGone + +//==== Utility Routines ============================== ====// +//===================================================================// + + +//===================================================================// +//==== Locomotive-Specific Values & Code =========== ====// + +// This code subsection defines a list of locomotive features encoded +// as single-bit values into an integer. The feature set controls +// which buttons are included in the Engineer's Menu and some aspects +// of the locomotive's operation. + +integer FEATURE_SOUND_ENGINE = 0x00000001; +integer FEATURE_SOUND_WHEELS = 0x00000002; +integer FEATURE_SOUND_STEAM = 0x00000004; +integer FEATURE_SOUND_RESERVED = 0x00000008; + +integer FEATURE_ALERT_BELL = 0x00000010; +integer FEATURE_ALERT_WHISTLE = 0x00000020; +integer FEATURE_ALERT_HORN = 0x00000040; +integer FEATURE_ALERT_RESERVED = 0x00000080; + +integer FEATURE_LIGHTS_HEADLAMPS = 0x00000100; +integer FEATURE_LIGHTS_TAILLAMPS = 0x00000200; +integer FEATURE_LIGHTS_RUNNING = 0x00000400; +integer FEATURE_LIGHTS_RESERVED = 0x00000800; + +integer FEATURE_CONTROL_SMOKE = 0x00010000; + +integer FEATURE_MOTION_NONPHYSICAL = 0x00100000; +integer FEATURE_MOTION_PHYSICAL = 0x00200000; +integer FEATURE_MOTION_FLIP = 0x00400000; +integer FEATURE_MOTION_REVERSE = 0x00800000; + +integer FEATURE_DISPLACE_RERAIL = 0x01000000; +integer FEATURE_DISPLACE_LEFT = 0x02000000; +integer FEATURE_DISPLACE_RIGHT = 0x04000000; + + +// Compute later due to LSL limitation +rotation ROTATION_FLIP = ZERO_ROTATION; + + +// This value defines the 'standard' featureset implemented in the VRC +// Opensource Locomotive Script. TO remove a feature from a particular +// build, Boolean (bitwise) AND the NOT of the feature code with the +// FEATURESET_DEFAULT value. (See "VRC 2010 Train" entry in +// configuration table below) + +// Compute at initialization time due to LSL limitation. Cannot include +// in table due to LSL design. Instead, table can include a SINGLE +// feature to be REMOVED from FEATURESET_DEFAULT +integer FEATURESET_DEFAULT = 0; + + +// This code subsection defines a list-based table of locomotive +// builds identified by the first words in their ObjectName. Several +// configuration values are preset in the table for selection by build +// name +// +// Any build found in the table at script initialization time will be +// assigned the values following its BUILD_TAG. +// +// Any build not identified in the table will be assigned the default +// values. + +integer UNIT_BUILD_TAG_IDX = 0; +integer UNIT_BUILD_GUIDE_OFFSET_IDX = 1; +integer UNIT_BUILD_SIT_OFFSET_IDX = 2; +integer UNIT_BUILD_SIT_ROTATION_IDX = 3; +integer UNIT_BUILD_SPEED_MINIMUM_IDX = 4; +integer UNIT_BUILD_SPEED_MAXIMUM_IDX = 5; +integer UNIT_BUILD_SPEED_INCREMENT_IDX = 6; +integer UNIT_BUILD_FEATURE_ADJUST_IDX = 7; + +integer UNIT_BUILD_TABLE_STRIDE = 8; + +// string sBuildTag, vector vGuideOffset, vector vSitOffset, +// rotation rSitRotation +list glUnitConfiguration = [ + + // DEFAULT BUILD - probably won't work well for ANY build + "DEFAULT BUILD", <0.00, 0.00, 0.500>, <-4.000, -0.500, 1.000>, + ZERO_ROTATION, 0.05, 8.00, 0.05, 0, + + // Desmond Shang + "VRC Hobo Train", <0.00, 0.00, 0.585>, <-3.50816, -0.500, 1.000>, + <-0.00876, -0.00003, 0.00000, 0.99996>, 0.0050, 0.1000, 0.0050, + 0, + + // Lotek Ixtar +// "NS2200", <0.00, 0.00, 0.720>, <-3.88150, -0.73620, 1.51784>, + "[ix] NS 2201", <0.00, 0.00, 0.625>, + <-3.594925, -1.217773, 1.880998>, + <0.00000, 0.00000, 0.00000, -1.00000>, + 0.005, 0.05, 0.005, + 0x10034, + + // Veryfluffy Wingtips + "Fluffies Diesel", <0.00, 0.00, 0.910>, <-1.95, 0.850, 0.950>, + ZERO_ROTATION, 0.25, 10.00, 0.25, 0, + + // Garden Mole + "Great Western 4500", <0.00, 0.00, 0.917>, + <-3.50816, -0.500, 1.475>, ZERO_ROTATION, 0.10, 8.00, 0.10, + 0 + + ]; + + +//==== Operator Preference Subsection ============== ====// + +// This feature may be unacceptable at higher speeds or if subject to +// motion sickness, so it is disable by default + +//integer CAMERA_DYNAMICS_ENABLED = TRUE; +integer CAMERA_DYNAMICS_ENABLED = FALSE; + +//==== Operator Preference Subsection ================ ====// + + +// CAUTION: Be VERY careful when adjusting these values - they are +// set to meet SLRR published track layout standards and +// have been thoroughly tested on the SLRR + +// Scan radius in meters +float SENSOR_SEEK_RANGE_DEFAULT = 20.00; + +// Scan half-angle in degrees. Arc swept is this amount on either +// side of X-axis of sensor prim +float SENSOR_SEEK_ARC_DEFAULT = 25.00; + + +string OBJECT_DESCRIPTION_TAG = "Script Version "; +string VERSION_PREFIX_TAG = " Opensource V"; +string ENGINEER_MENU_INSTRUCTION = "Touch train for Engineer's Menu"; + +string SIT_TEXT_OPERATOR = "Engineer"; +string SIT_TEXT_PASSENGER = "Passenger"; + +// Buttons for Engineer's menu for all builds and script revisions +string BTN_START = "Start"; +string BTN_IDLE = "Idle"; +string BTN_STOP = "Stop"; +string BTN_FASTER = "Faster"; +string BTN_SLOWER = "Slower"; +string BTN_FLIP = "Flip"; +string BTN_HELP = "Help"; + +// Buttons for Engineer's menu constructed according to featureset +// Not all buttons or related functionality are implemented in a given +// revision of this script +string BTN_BLANK = " "; +string BTN_BELL = "Bell"; +string BTN_WHISTLE = "Whistle"; +string BTN_HORN = "Horn"; +string BTN_HEADLAMP = "Headlamp"; +string BTN_RERAIL = "Rerail"; +string BTN_NONPHYSICAL = "NONPhysical"; +string BTN_PHYSICAL = "Physical"; + +string BTN_SMOKE = "Smoke"; + + +// Name the smoke-emitting prim SMOKE_PRIM_NAME +string SMOKE_PRIM_NAME = "Smoke"; +integer SMOKE_OFF = 0; +integer SMOKE_IDLE = 1; +integer SMOKE_RUN = 2; + + +integer SIT_ALLOW = TRUE; +integer SIT_DENY = FALSE; + +integer ENGINE_STATUS_OFF = 0; +integer ENGINE_STATUS_IDLING = 1; +integer ENGINE_STATUS_ROLLING = 2; + +// Name the Engineer's display prim ENGINEERDISPLAY_PRIM_NAME +string ENGINEERDISPLAY_PRIM_NAME = "EngineerDisplay"; + +string gsLocomotiveNameTag = ""; +integer giLocomotiveFeatureSet = 0; +integer giLocomotiveFeatureAdjust = 0; +vector gvUnitToGuideOffset = ZERO_VECTOR; +vector gvSitOffset = ZERO_VECTOR; +rotation grSitOrientation = ZERO_ROTATION; +integer giEngineStatus = 0; +integer giNoHitsCount = 0; + +// Default for SLRR ROW +string gsSensorTargetName = "Guide"; + +float gfSensorSeekRange = 20.00; +float gfSensorScanArc = 0; +integer giTravelDirection = 0; +integer giFlipRequest = 0; +vector gvWaypointPrior = ZERO_VECTOR; + +float gfSpeedPrior = 0.00; +float gfSpeedCurrent = 0.00; +float gfSpeedMinimum = 0.00; +float gfSpeedMaximum = 0.00; +float gfSpeedIncrement = 0.00; + +integer giStatusPhysical = TRUE; +integer giFlagPhantom = TRUE; +string gsStatusDisplayPrior = ""; + +integer giRerailRequest = TRUE; + +string gsDisplayPrimName = "EngineerDisplay"; +integer giDisplayPrimLink = -32768; + +integer giLinkCountRiderless = 0; +integer giLinkCountPrior = 0; +integer giLinkCountCurrent = 0; +integer giLinkGained = FALSE; +string gsRegionCurrentName; +vector gvRegionCurrentCorner; +integer giSmokeEnabled = TRUE; +integer giSmokeOn = TRUE; +integer giDialogChannel = PUBLIC_CHANNEL; +key gkOwnerID = NULL_KEY; +key gkOperatorID = NULL_KEY; +key gkPassengerID = NULL_KEY; +integer giOperatorSeated = FALSE; +integer giCameraDynamics = FALSE; + +// Table of all agents seated +list glRidersAboard =[]; + +// Default Buttonset in all menu versions. At initialization time +// build-specific feature buttons may be added as indiczted by +// giFeatureSet +list glMenuOperatorDefault = [ + "HELP", + "FASTER", "SLOWER", "FLIP", + "START", "IDLE", "STOP" + ]; + +list glMenuOperator = []; + + +// These are activated if set in Operator Preference section +list glCameraParameters = [ + CAMERA_ACTIVE, TRUE, + + CAMERA_BEHINDNESS_ANGLE, 0.00, + CAMERA_BEHINDNESS_LAG, 0.50, + CAMERA_DISTANCE, 7.00, + CAMERA_PITCH, 15.00, + + // CAMERA_FOCUS, + CAMERA_FOCUS_LAG, 0.50, + CAMERA_FOCUS_LOCKED, FALSE, + CAMERA_FOCUS_THRESHOLD, 0.00, + + // CAMERA_POSITION, + CAMERA_POSITION_LAG, 0.50, + CAMERA_POSITION_LOCKED, FALSE, + CAMERA_POSITION_THRESHOLD, 0.05, + + CAMERA_FOCUS_OFFSET, <0.00, 0.00, 1.00> + ]; + + +//===================================================================// +//==== Locomotive-Specific Routines == ==================// + +list LocomotiveMenuCreateFromFeatureset(integer iFeatureSet) +{ + list lMenu = []; + + // llOwnerSay("$00F0: FeatureSet=" + Int2Hex(iFeatureSet, 8)); + + if (iFeatureSet & FEATURE_CONTROL_SMOKE) + { + lMenu = BTN_SMOKE + lMenu; + } + + if (iFeatureSet & FEATURE_ALERT_BELL) + { + lMenu = BTN_BELL + lMenu; + } + + if (iFeatureSet & FEATURE_ALERT_HORN) + { + lMenu = BTN_HORN + lMenu; + } + + // Temporary fix to accomodate an exception. Later + // version will provide more flexible menu construction + else + { + lMenu = BTN_BLANK + lMenu; + } + + if (iFeatureSet & FEATURE_ALERT_WHISTLE) + { + lMenu = BTN_WHISTLE + lMenu; + } + + if (iFeatureSet & FEATURE_LIGHTS_HEADLAMPS) + { + lMenu = BTN_HEADLAMP + lMenu; + } + + if (~llGetListLength(glMenuOperator)) lMenu = lMenu + glMenuOperator; + // llOwnerSay("Menu=" + llList2CSV(lMenu)); + + return(lMenu); +} // LocomotiveMenuCreateFromFeatureset + + +LocomotiveBuildConfigure() +{ + integer iBuild; + integer iBuildItems = llGetListLength(glUnitConfiguration); + integer iBuildIndex = INDEX_INVALID; + string sBuildTag; + string sBuildName = llGetObjectName(); + + // Set to a default feature set, from which a single feature may later be removed + integer giLocomotiveFeatureSet = FEATURE_SOUND_ENGINE | + FEATURE_SOUND_WHEELS | FEATURE_SOUND_STEAM | FEATURE_ALERT_BELL | + FEATURE_ALERT_WHISTLE | FEATURE_ALERT_HORN | + FEATURE_LIGHTS_HEADLAMPS | FEATURE_LIGHTS_TAILLAMPS | + FEATURE_CONTROL_SMOKE | FEATURE_MOTION_PHYSICAL; +// FEATURE_CONTROL_SMOKE | FEATURE_MOTION_NONPHYSICAL; + + for (iBuild = 0; iBuild < iBuildItems; iBuild = iBuild + + UNIT_BUILD_TABLE_STRIDE) + { + sBuildTag = llList2String(glUnitConfiguration, iBuild + + UNIT_BUILD_TAG_IDX); + + if (llSubStringIndex(sBuildName, sBuildTag) == 0) + { + iBuildIndex = iBuild; + gsLocomotiveNameTag = sBuildTag; + } + + } // for all builds in table + + + if (iBuildIndex == INDEX_INVALID) + { + iBuildIndex = 0; + } + + gvUnitToGuideOffset = llList2Vector(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_GUIDE_OFFSET_IDX); + gvSitOffset = llList2Vector(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SIT_OFFSET_IDX); + grSitOrientation = llList2Rot(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SIT_ROTATION_IDX); + gfSpeedMinimum = llList2Float(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SPEED_MINIMUM_IDX); + gfSpeedIncrement = llList2Float(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SPEED_INCREMENT_IDX); + gfSpeedMaximum = llList2Float(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SPEED_MAXIMUM_IDX); + giLocomotiveFeatureAdjust = llList2Integer(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_FEATURE_ADJUST_IDX); +/* + llOwnerSay("$0100: Buildname=" + sBuildName + + " Build=" + (string)iBuildIndex + + ":" + gsLocomotiveNameTag + + " FeatureSet=" + Int2Hex(giLocomotiveFeatureSet, 8) + + " FeatureAdjust=" + Int2Hex(giLocomotiveFeatureAdjust, 8) + + " AdjustedFeatureSet=" + Int2Hex(giLocomotiveFeatureSet & + (~giLocomotiveFeatureAdjust), 8)); +*/ + // Adjust the default featureset by removing any single + // unwanted feature. Need greater flexibilty here. + giLocomotiveFeatureSet = giLocomotiveFeatureSet & + (~giLocomotiveFeatureAdjust); + glMenuOperator = LocomotiveMenuCreateFromFeatureset( + giLocomotiveFeatureSet); + + // llOwnerSay("$0110: CONFIG=" + (string)gvUnitToGuideOffset + ", " + + // (string)gvSitOffset + ", " + (string)grSitOrientation + + // ", " + (string)gfSpeedMinimum + ", " + + // (string)gfSpeedMaximum + ", " + (string)gfSpeedIncrement); + +} // LocomotiveBuildConfigure + + +string LocomotiveHelpBuild() +{ + string sHelpString = ""; + + sHelpString += "\n Page Up = Stopped: Idle->Running->Faster"; + sHelpString += "\n Page Down = Running: Slower->Idle->Stop"; + sHelpString += "\n Shift + Right Arrow = Instant Idle/Resume"; + // sHelpString += "\nDown Arrow = Reverse"; + sHelpString += "\n Shift + Left Arrow = Flip train around"; + return(sHelpString); +} // LocomotiveHelpBuild + + +LocomotiveDisplaySet(string str, vector color) +{ + + if (gsStatusDisplayPrior != str) + { + gsStatusDisplayPrior = str; + llSetLinkPrimitiveParamsFast(giDisplayPrimLink, [ + PRIM_TEXT, gsStatusDisplayPrior, color, 1.0]); + } + +} // LocomotiveDisplaySet + + +LocomotiveDisplayUpdate() +{ + vector color = <0.5, 1.0, 0.5>; + string str = "Speed: " + clip(gfSpeedCurrent*1000) + "\n"; + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + str += "\nRunning"; + } + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + str += "\nIdling"; + } + else + { + str = llGetObjectName() + "\n\n"; + str += "\nStopped"; + color = <0.5, 0.5, 1.0>; + } + + if (llGetStatus(STATUS_PHANTOM)) + { + str += "\nPhantom"; + } + else + { + str += "\nNOT Phantom!!"; + } + + str += "\nFollowing '" + gsSensorTargetName + "'"; + + if (giLinkCountCurrent != giLinkCountRiderless) + { + str += "\n \n \n" + ENGINEER_MENU_INSTRUCTION; + } + + LocomotiveDisplaySet(str, color); +} // LocomotiveDisplayUpdate + + +integer LocomotiveRiderAboard(key gkRiderID) +{ + return(NamedLinkFind(llKey2Name(gkRiderID)) != LINK_INVALID); +} // LocomotiveRiderAboard + + +LocomotivePassengerRejection(key kAgentID, integer iSitAllow) +{ + + if (iSitAllow) + { + llInstantMessage(kAgentID, "\nSorry, " + llKey2Name(kAgentID) + + ", only the Owner may operate this locomotive."); + } + else + { + llInstantMessage(kAgentID, "\nSorry, " + llKey2Name(kAgentID) + + ", only the Owner may operate this locomotive." + + "\nAnd the engineer needs to climb aboard first"); + llUnSit(kAgentID); + } + +} // LocomotivePassengerRejection + + +LocomotiveMoveTo(vector vWaypointNext, rotation rAttitudeNext) +{ + + if (llGetStatus(STATUS_PHYSICS)) + { + llRotLookAt(rAttitudeNext, 1 / llVecMag(llGetVel()), 1); + llMoveToTarget( vWaypointNext, 1.0); + } + else + { + llSetLinkPrimitiveParamsFast(LINK_ROOT, + [PRIM_POSITION, vWaypointNext, + PRIM_ROTATION, rAttitudeNext]); + } + +} // LocomotiveMoveTo + +// Opensim sim-crossing compatibility +LocomotiveMoveToSlow(vector vWaypointNext, rotation rAttitudeNext) +{ + if (llGetStatus(STATUS_PHYSICS)) { + llRotLookAt(rAttitudeNext, 1 / llVecMag(llGetVel()), 1); + llMoveToTarget( vWaypointNext, 1.0); + } else { + llSetPos(vWaypointNext); + llSetRot(rAttitudeNext); + } +} + +LocomotiveFlip() +{ + giStatusPhysical = llGetStatus(STATUS_PHYSICS); + + if (giStatusPhysical) + { + llSetStatus(STATUS_PHYSICS,FALSE); + } + + LocomotiveDisplaySet("reverse", <1.0, 0.5, 0.5>); + giFlipRequest = FALSE; +// llSetRot(ROTATION_FLIP * llGetRot() ); + llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_ROTATION, (ROTATION_FLIP*llGetRot()) ]); + giTravelDirection = (giTravelDirection + 1) % 2; + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + llSleep(0.5); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + } + + if (giStatusPhysical) + { + llSetStatus(STATUS_PHYSICS,TRUE); + } + +} // LocomotiveFlip + + +float LocomotiveSpeedUpdate(float fSpeedDelta) +{ + // llOwnerSay("SD=" + (string)fSpeedDelta); + + if (fSpeedDelta > 0.00) + { + + if ( (gfSpeedCurrent + fSpeedDelta) <= gfSpeedMaximum) + { + gfSpeedCurrent += fSpeedDelta; + } + + } + + else + { + + if ( (gfSpeedCurrent + fSpeedDelta) > 0) + { + gfSpeedCurrent += fSpeedDelta; + } + + } + + return(gfSpeedCurrent); +} // LocomotiveSpeedUpdate + + + +LocomotiveSpeedUp() +{ + + if (giEngineStatus == ENGINE_STATUS_OFF) + { + LocomotiveIdle(); + } + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + LocomotiveRun(gfSpeedMinimum); + } + else if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + LocomotiveSpeedUpdate(gfSpeedIncrement); + } + +} // LocomotiveSpeedUp + + +LocomotiveSlowDown() +{ + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + + if (gfSpeedCurrent > gfSpeedMinimum) + { + LocomotiveSpeedUpdate(-gfSpeedIncrement); + } + else + { + LocomotiveIdle(); + } + + } + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + LocomotiveStop(); + } + +} // LocomotiveSlowDown + + +LocomotiveIdle() +{ + llMessageLinked(LINK_SET, 99, "idle", (key)NAME_WILDCARD); + giSmokeOn = LocomotiveSmoke(SMOKE_IDLE); + llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_POSITION,llGetPos()+<-0.01,0.0,0.0>]); + giEngineStatus = ENGINE_STATUS_IDLING; + gfSpeedPrior = gfSpeedCurrent; + gfSpeedCurrent = 0.00; +// llVolumeDetect(TRUE); + giFlagPhantom = TRUE; +} // LocomotiveIdle + + +LocomotiveRun(float fSpeedStartup) +{ + llMessageLinked(LINK_SET, 99, "run", (key)NAME_WILDCARD); + giSmokeOn = LocomotiveSmoke(SMOKE_RUN); + llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_POSITION,llGetPos()+<-0.01,0.0,0.0>]); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + giEngineStatus = ENGINE_STATUS_ROLLING; + gfSpeedCurrent = fSpeedStartup; +// llVolumeDetect(TRUE); + giFlagPhantom = TRUE; +} // LocomotiveRun + + +LocomotiveStop() +{ + gfSpeedCurrent = 0.00; + llMessageLinked(LINK_SET, -1, "reset", (key)NAME_WILDCARD); + giSmokeOn = LocomotiveSmoke(SMOKE_OFF); + giStatusPhysical = llGetStatus(STATUS_PHYSICS); + llSetStatus(STATUS_PHYSICS, FALSE); + llSensorRemove(); + giEngineStatus = ENGINE_STATUS_OFF; + giFlagPhantom = FALSE; +// llVolumeDetect(FALSE); + llSetStatus(STATUS_PHANTOM, TRUE); +} // LocomotiveStop + + +integer LocomotiveSmoke(integer iSmokeLevel) +{ + integer iSmokeNew = TRUE; + + if (giSmokeEnabled) + { + llMessageLinked(LINK_ALL_CHILDREN, iSmokeLevel, + SMOKE_PRIM_NAME, NULL_KEY); + } + else + { + llMessageLinked(LINK_ALL_CHILDREN, SMOKE_OFF, + SMOKE_PRIM_NAME, NULL_KEY); + } + + if (iSmokeLevel == SMOKE_OFF) + { + iSmokeNew = FALSE; + } + + return(iSmokeNew); +} // LocomotiveSmoke + +//==== Locomotive-Specific Routines == ====================// + + +ScriptInitialize() +{ + ROTATION_FLIP = llEuler2Rot(<0.00, 0.00, 180.00> * DEG_TO_RAD); + + integer iLinkCount = llGetNumberOfPrims(); + + // TO clear any existing sit targets, uncomment the code below: + // + // integer iIndex; + // for (iIndex = 0; iIndex > iLinkCount; iIndex++) + // { + // llSetText(".", COLOR_BLACK, 0.00); + // llSitTarget(ZERO_VECTOR, ZERO_ROTATION); + // } + + UnsitAllAgents(); + llSetClickAction(CLICK_ACTION_SIT); + giLinkCountRiderless = PrimCount(); + giLinkCountCurrent = llGetNumberOfPrims(); + giLinkCountPrior = giLinkCountCurrent; + gsRegionCurrentName = llGetRegionName(); + gvRegionCurrentCorner = llGetRegionCorner(); + gfSensorScanArc = SENSOR_SEEK_ARC_DEFAULT * DEG_TO_RAD; + gkOwnerID = llGetOwner(); + llMessageLinked(LINK_SET, -1, "reset", (key)NAME_WILDCARD); + llSetBuoyancy(1.0); + llStopMoveToTarget(); + llStopLookAt(); + llSetText(" ",<1.00, 1.00, 1.00>, 1.00); + + llSetStatus(STATUS_PHANTOM,TRUE); + giDisplayPrimLink = NamedLinkFind(gsDisplayPrimName); + // llOwnerSay("Display=" + (string)giDisplayPrimLink + " " + + // llGetLinkName(giDisplayPrimLink)); + LocomotiveDisplaySet("-----", <0.50, 0.50, 1.00>); + + llSetObjectDesc(""); + + // For development and testing + // llSetObjectName(SET TO BUILD NAME TO TEST AGAINST); + + glMenuOperator = glMenuOperatorDefault; + LocomotiveBuildConfigure(); + llSetObjectName(gsLocomotiveNameTag + VERSION_PREFIX_TAG + + gsScriptVersion); + llSetSitText(SIT_TEXT_OPERATOR); + llSitTarget(gvSitOffset, grSitOrientation); + llSetCameraAtOffset(<3.00, 0.00, 1.50>); + llSetCameraEyeOffset(<-8.00, 0.00, 5.00>); + LocomotiveDisplayUpdate(); + + key agent = llAvatarOnSitTarget(); + + if (agent != NULL_KEY) + { + llRequestPermissions(agent, PERMISSION_TAKE_CONTROLS); +// llVolumeDetect(TRUE); + } + +} // ScriptInitialize + + + +default +{ + + state_entry() + { + + if (gkOperatorID == NULL_KEY) + { + ScriptInitialize(); + } + + if (giRerailRequest) + { + state Rerail; + } + + giDialogChannel = -llGetUnixTime(); + llListen(giDialogChannel, "", "", ""); + giSmokeOn = LocomotiveSmoke(SMOKE_OFF); + LocomotiveDisplayUpdate(); + } // state_entry + + + // Only recognizes first Toucher + touch_start(integer iTouching) + { + + if (giLinkCountCurrent > giLinkCountRiderless) + { + + if (llDetectedKey(0) == gkOperatorID) + { + string sPrompt = "\nSmoke "; + + if (giSmokeEnabled) + { + sPrompt += "ENABLED"; + } + else + { + sPrompt += "DISABLED"; + } + + sPrompt += "\n\nOperate locomotive"; + llDialog(gkOperatorID, sPrompt, + glMenuOperator, giDialogChannel); + } +// else +// { +// LocomotivePassengerRejection(llDetectedKey(0), +// SIT_ALLOW); +// } + + } + + } // touch_start + + + changed(integer iChanged) + { + + if (iChanged & CHANGED_REGION) + { + gsRegionCurrentName = llGetRegionName(); + gvRegionCurrentCorner = llGetRegionCorner(); + LocomotiveRun(gfSpeedCurrent); + LocomotiveDisplayUpdate(); + } + + if (iChanged & CHANGED_LINK) + { + key kRiderID = NULL_KEY; + + giLinkCountCurrent = llGetNumberOfPrims(); + + // Gained a link + if (giLinkCountCurrent > giLinkCountPrior) + { + giLinkGained = TRUE; + giLinkCountPrior = giLinkCountCurrent; + kRiderID = llGetLinkKey(giLinkCountCurrent); + glRidersAboard = glRidersAboard + (list)kRiderID; + + // If driver needed. + if (gkOperatorID == NULL_KEY) + { + + // owner only can ride + if (kRiderID == gkOwnerID) + { + // seat as driver + gkOperatorID = kRiderID; + llSetSitText(SIT_TEXT_PASSENGER); + llSetClickAction(CLICK_ACTION_TOUCH); + llVolumeDetect(TRUE); + llRequestPermissions(gkOperatorID, + PERMISSION_TRIGGER_ANIMATION | + PERMISSION_TAKE_CONTROLS | + PERMISSION_CONTROL_CAMERA); + } + else + { + LocomotivePassengerRejection(kRiderID, + SIT_DENY); + } + } + + // else seat as passenger + else + { + gkPassengerID = llGetLinkKey(giLinkCountCurrent); + // seat passenger at the right spot + llSetLinkPrimitiveParamsFast(giLinkCountCurrent, [ PRIM_POS_LOCAL, gvSitOffset+<.5,1,0.4>, PRIM_ROT_LOCAL, grSitOrientation ]); +// llSetCameraAtOffset(<3.00, 0.00, 1.50>); +// llSetCameraEyeOffset(<-8.00, 0.00, 5.00>); + llSetSitText(SIT_TEXT_OPERATOR); + llInstantMessage(gkPassengerID, "\nWelcome aboard, " + + llKey2Name(gkPassengerID) + "!" + + "\nYou can get your own train at:" + + "\n (todo: put hg address here)"); + } + + } // gained a link + + // Lost a link + else + { + giLinkGained = FALSE; + giLinkCountPrior = giLinkCountCurrent; + + // Scan the rider roll to see who is no longer linked + kRiderID = ScanForAgentGone(); + integer iRider = llListFindList(glRidersAboard, + (list)kRiderID); + + if (iRider != INDEX_INVALID) + { + glRidersAboard = llDeleteSubList(glRidersAboard, + iRider, iRider); + } + + // Engineer unsat + if (kRiderID == gkOperatorID) + { + giOperatorSeated = FALSE; + gkOperatorID = NULL_KEY; + llStopAnimation("sit"); + + if (llGetPermissions() & PERMISSION_TAKE_CONTROLS) + { + llReleaseControls(); + } + + if ( (giCameraDynamics) && + (llGetPermissions() & PERMISSION_CONTROL_CAMERA) ) + { + llClearCameraParams(); + } + + // Just leaving this commented here, as a warning. + // The following triggers a crash of the avatar after unseating: + llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, 0)); + + llSetClickAction(CLICK_ACTION_SIT); + llSetSitText(SIT_TEXT_OPERATOR); + llSitTarget(gvSitOffset, grSitOrientation); + // set camera for when engineer reseats + llSetCameraAtOffset(<3.00, 0.00, 1.50>); + llSetCameraEyeOffset(<-8.00, 0.00, 5.00>); + } // Driver dismounted + + // Passenger dismounted + else + { + + if (gkOperatorID == NULL_KEY) + { + llSetSitText(SIT_TEXT_OPERATOR); + } + else + { + llSetSitText(SIT_TEXT_PASSENGER); + } + + gkPassengerID = NULL_KEY; + } // Passenger dismounted + + + } // lost a link + + // llOwnerSay("$1890: Riderless=" + + // (string)giLinkCountRiderless + + // " Prior=" + (string)giLinkCountPrior + + // " Current=" + (string)giLinkCountCurrent); + + // Force any count errors our way + if (giLinkCountCurrent < giLinkCountRiderless) + { + giLinkCountCurrent = giLinkCountRiderless; + } + + // If no-one aboard, stop locomotive + if ( ((giEngineStatus == ENGINE_STATUS_ROLLING) || + (giEngineStatus == ENGINE_STATUS_IDLING)) && + (giLinkCountCurrent == giLinkCountRiderless)) + { + LocomotiveStop(); + llSetSitText(SIT_TEXT_OPERATOR); + llSetClickAction(CLICK_ACTION_SIT); + } + + LocomotiveDisplayUpdate(); + } // CHANGED_LINK + + } // changed + + + run_time_permissions(integer iPermissions) + { + + if (iPermissions & PERMISSION_TAKE_CONTROLS) + { + // llTakeControls(CONTROL_UP | CONTROL_DOWN | CONTROL_FWD | + // CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT, + // TRUE, FALSE); + llTakeControls(CONTROL_UP | CONTROL_DOWN | CONTROL_LEFT | + CONTROL_RIGHT, TRUE, FALSE); + llInstantMessage(llAvatarOnSitTarget(), + "\n" + llKey2Name(llAvatarOnSitTarget()) + + ", " + ENGINEER_MENU_INSTRUCTION + + LocomotiveHelpBuild()); + } + + if ( (iPermissions & PERMISSION_TRIGGER_ANIMATION) && + (gkOperatorID != NULL_KEY) ) + { + llStopAnimation("sit"); + llStartAnimation(llGetInventoryName( + INVENTORY_ANIMATION, 0)); + llSetClickAction(CLICK_ACTION_TOUCH); + } + + if ( (giCameraDynamics) && + (iPermissions & PERMISSION_CONTROL_CAMERA) ) + { + llSetCameraParams(glCameraParameters); + } + + } // run_time_permissions + + + control(key kID, integer level, integer edge) + { + + if (edge & level & CONTROL_UP) + { + LocomotiveSpeedUp(); + } // CONTROL_UP + + + // if (edge & level & CONTROL_BACK) + // { + // Reserved for reverse/forward (as opposed to Flip + // } + + + if (edge & level & CONTROL_DOWN) + { + LocomotiveSlowDown(); + } // CONTROL_DOWN + + + if (edge & level & CONTROL_LEFT) + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + giFlipRequest=TRUE; + } + else + { + LocomotiveFlip(); + } + + } // CONTROL_LEFT + + + if (edge & level & CONTROL_RIGHT) + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + LocomotiveIdle(); + } + + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + LocomotiveRun(gfSpeedPrior); + } + + } // CONTROL_ROT_RIGHT + + LocomotiveDisplayUpdate(); + } // control + + + listen(integer iChannel, string sName, key kID, string sMessage) + { + + if ((sMessage == "Stop") && + ((giEngineStatus == ENGINE_STATUS_IDLING) || + giEngineStatus == ENGINE_STATUS_ROLLING) ) + { + LocomotiveStop(); + } // Stop + + + else if ( (sMessage == "Idle") && + ((giEngineStatus == ENGINE_STATUS_OFF) || + (giEngineStatus == ENGINE_STATUS_ROLLING)) ) + { + LocomotiveIdle(); + } // Idle + + + else if ((sMessage == "Start") && (giEngineStatus != + ENGINE_STATUS_ROLLING)) + { + LocomotiveRun(gfSpeedMinimum); + } // Start + + + else if (sMessage == "Flip") + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + giFlipRequest = TRUE; + } + else + { + LocomotiveFlip(); + } + + } // Flip + + + else if (sMessage == "Rerail") + { + state Rerail; + } // Rerail + + + else if (sMessage == "Faster") + { + LocomotiveSpeedUp(); + } // Faster + + + else if (sMessage == "Slower") + { + LocomotiveSlowDown(); + } // Slower + + else if (sMessage == "Smoke") + { + giSmokeEnabled = !giSmokeEnabled; + giSmokeOn = LocomotiveSmoke(giEngineStatus); + } // Smoke + + else if (sMessage == "Notecard") + { + llWhisper(PUBLIC_CHANNEL, "No Notecard available yet"); + } // Notecard + + else if (sMessage == "Help") + { + llOwnerSay(LocomotiveHelpBuild()); + } // Help + + else if ( (sMessage == "Bell") || (sMessage == "Horn") || + (sMessage == "Whistle") || (sMessage == "Headlamp") ) + { + llMessageLinked(LINK_SET, 0, sMessage, sMessage); + } // Bell | Horn | Whistle + + giStatusPhysical = llGetStatus(STATUS_PHYSICS); + LocomotiveDisplayUpdate(); + } // listen + + + sensor(integer iSensed) + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + integer index = 0; + giNoHitsCount = 0; + + if (giFlipRequest) + { + LocomotiveFlip(); + return; + } + + if (llGetPos() == llDetectedPos(0) + gvUnitToGuideOffset) + { + + if (iSensed > 1) + { + index++; + } + else + { + vector next = llGetPos() + <15.00, 0.00, 0.00> * + llGetRot(); + + if (next.x < 1.00 || next.x > 255.00 + || next.y < 1.00 || next.y > 255.00) + { + LocomotiveDisplaySet("sim crossing 1", + <0.50, 0.50, 1.00>); + LocomotiveMoveTo(llGetPos() + + <2.00, 0.00, 0.00> * llGetRot(), + llGetRot()); + } + else + { + LocomotiveFlip(); + LocomotiveDisplayUpdate(); + return; + } + + LocomotiveDisplayUpdate(); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + return; + } + } + + rotation rHeadingNext = llDetectedRot(0); + + // Reverse Target Heading if travelling opposite to Guide + // X-axis polarity + if (giTravelDirection) + { + rHeadingNext = ROTATION_FLIP * rHeadingNext; + } + + // If angle between unit and next Guide greater than acceptable + // flip Target Heading 180 degrees + if (llFabs(llAngleBetween(rHeadingNext, llGetRot())) > PI_BY_TWO) + { + rHeadingNext = ROTATION_FLIP * rHeadingNext; + } + + + vector vPositionCurrent = llGetPos(); + vector vWaypointNext = llDetectedPos(index) + gvUnitToGuideOffset; + + if (llVecDist(vPositionCurrent, gvWaypointPrior) < + llVecDist(vPositionCurrent, vWaypointNext)) + { + rHeadingNext = llGetRot(); + } + else + { + gvWaypointPrior = vWaypointNext; + } + + vector vPositionNext = vPositionCurrent + (llVecNorm(vWaypointNext - + vPositionCurrent) * gfSpeedCurrent); + LocomotiveMoveTo(vPositionNext, rHeadingNext); + LocomotiveDisplayUpdate(); + + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + } // if running + + } // sensor + + + no_sensor() + { + + if (++giNoHitsCount > 4) + { + llResetScript(); + } + else + { + gsRegionCurrentName = llGetRegionName(); + LocomotiveDisplayUpdate(); + vector vPositionNext = llGetPos() + <15.00, 0.00, 0.00> * + llGetRot(); + + if (vPositionNext.x < 1.00 || vPositionNext.x > 255.00 + || vPositionNext.y < 1.00 || vPositionNext.y > 255.00) + { + LocomotiveDisplaySet("sim crossing 2", + <0.50, 0.50, 1.00>); + LocomotiveMoveToSlow(llGetPos() + <5.00, 0.00, 0.00> * + llGetRot(), + llGetRot()); + } + else + { + LocomotiveFlip(); + LocomotiveDisplayUpdate(); + return; + } + + LocomotiveDisplayUpdate(); + + // Second Life: + + // Give llSensor a rest + //llSleep(1.00); + + //llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + // gfSensorSeekRange, gfSensorScanArc); + + // XEngine in OpenSim doesn't like llSleep so we use + // a timer and do the llSensor there instead: + + llSetTimerEvent(1.0); + + } // if still seeking Guide + + } // no_sensor + + timer() + { + llSetTimerEvent(0); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + } + + on_rez(integer iRezParameter) + { + llResetScript(); + } // on_rez + + +} // state default + + + +state Rerail +{ + + state_entry() + { + LocomotiveDisplayUpdate(); + giRerailRequest = FALSE; + + // Note full-sphere sensor scan angle when seeking ROW + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, PI); + gfSensorSeekRange = SENSOR_SEEK_RANGE_DEFAULT; + } // state_entry + + + sensor(integer iSensed) + { + // In theory, the first hit reported will always be the + // closest scan target detected + LocomotiveMoveTo(llDetectedPos(0) + gvUnitToGuideOffset, + llDetectedRot(0)); + state default; + } // sensor + + + no_sensor() + { + llOwnerSay("Scan target '" + gsSensorTargetName + + "' not within " + + (string)llRound(gfSensorSeekRange) + "m"); + state default; + } // no_sensor + +} // state Rerail + +//==== Locomotive-Specific Values & Code ============= ====// +//===================================================================// + + +// [VRCLocomotiveOpensourceScript] \ No newline at end of file diff --git a/OpensimTrain/Opensim Train/Train/Readme.txt b/OpensimTrain/Opensim Train/Train/Readme.txt new file mode 100644 index 00000000..58be64b2 --- /dev/null +++ b/OpensimTrain/Opensim Train/Train/Readme.txt @@ -0,0 +1,5 @@ + This script works under DREAMGRID! It lives in the root prim of the engine, which in my test case was a box prim of size <9.00000, 1.20000, 0.75029>. The image below shows this moves out from its usual position + +The track I used was some mesh track I found elsewhere, all named "Guide". The reason I've been using it is that as mesh it was easy to just change the size and direction of it when building track. I'm glad this works BETWEEN regions, but you have to be driving it to make anything happen. Ultimately I want to have a "guided tour" vehicle running under the same principles but with a much expanded route across several regions. The old guided tour scripts were OK but couldn't go outside a single region. + +~ Laura \ No newline at end of file diff --git a/OpensimTrain/OpensimTrain.sol b/OpensimTrain/OpensimTrain.sol new file mode 100644 index 00000000..1a8a610c --- /dev/null +++ b/OpensimTrain/OpensimTrain.sol @@ -0,0 +1,3 @@ + + +