public void Update(WorldLocation playerLocation, int approachDist, int scaredDist) { if (state == State.Idle1) { if (Simulator.Random.Next(10) == 0) { state = State.Idle2; } } else if (state == State.Idle2) { if (Simulator.Random.Next(5) == 0) { state = State.Idle1; } } if (!WorldLocation.Within(Location, playerLocation, scaredDist) && state < State.LookLeft) { if (WorldLocation.Within(Location, playerLocation, approachDist) && state < State.LookLeft) { state = State.LookRight; } } if (WorldLocation.Within(Location, playerLocation, scaredDist) && state == State.LookRight || state == State.LookLeft) { state = State.Scared; } }
public void WorldLocationDistanceZeroTest() { WorldLocation location1 = new WorldLocation(); WorldLocation location2 = new WorldLocation(); Assert.IsTrue(WorldLocation.Within(location1, location2, 0)); Assert.AreEqual(0, WorldLocation.GetDistanceSquared(location1, location2)); Assert.AreEqual(Microsoft.Xna.Framework.Vector3.Zero, WorldLocation.GetDistance(location1, location2)); Assert.AreEqual(Microsoft.Xna.Framework.Vector2.Zero, WorldLocation.GetDistance2D(location1, location2)); }
public void WorldLocationDistanceTest() { WorldLocation location1 = new WorldLocation(); WorldLocation location2 = new WorldLocation(1, -1, Microsoft.Xna.Framework.Vector3.Zero); Assert.AreEqual(2048 * 2048 + 2048 * 2048, WorldLocation.GetDistanceSquared(location1, location2)); Assert.IsTrue(WorldLocation.Within(location1, location2, (float)Math.Sqrt(2048 * 2048 * 2) + 1)); Assert.AreEqual(new Microsoft.Xna.Framework.Vector3(2048, 0, -2048), WorldLocation.GetDistance(location1, location2)); Assert.AreEqual(new Microsoft.Xna.Framework.Vector2(2048, -2048), WorldLocation.GetDistance2D(location1, location2)); }
public void Horn() { var playerLocation = Simulator.PlayerLocomotive.WorldPosition.WorldLocation; foreach (var haz in Hazards) { if (WorldLocation.Within(haz.Value.Location, playerLocation, hornDist)) { haz.Value.state = Hazard.State.LookLeft; } } }
public void Horn() { WorldLocation playerLocation = Simulator.Instance.PlayerLocomotive.WorldPosition.WorldLocation; foreach (KeyValuePair <int, Hazard> item in hazards) { if (WorldLocation.Within(item.Value.Location, playerLocation, hornDist)) { item.Value.State = Hazard.HazardState.LookLeft; } } }
/// <summary> /// CheckTrainOnTurntable: checks if actual player train is on turntable /// </summary> public bool CheckTrainOnMovingTable(Train train) { if (train == null) { return(false); } string tableType = this is TurnTable?Simulator.Catalog.GetString("turntable") : Simulator.Catalog.GetString("transfertable"); int trainIndex = (TrainsOnMovingTable as List <TrainOnMovingTable>)?.FindIndex(x => x.Train.Number == train.Number) ?? -1; if (WorldLocation.Within(train.FrontTDBTraveller.WorldLocation, WorldPosition.WorldLocation, Length / 2)) { if (trainIndex == -1 || !TrainsOnMovingTable[trainIndex].FrontOnBoard) { if (trainIndex == -1) { TrainOnMovingTable trainOnTurntable = new TrainOnMovingTable(train); trainIndex = TrainsOnMovingTable.Count; TrainsOnMovingTable.Add(trainOnTurntable); } if (!TrainsOnMovingTable[trainIndex].BackOnBoard) { // check if turntable aligned with train bool aligned = CheckMovingTableAligned(train, true); if (!aligned) { TrainsOnMovingTable[trainIndex].SetFrontState(true); Simulator.Instance.Confirmer.Warning(Simulator.Catalog.GetString("Train slipped into non aligned {0}", tableType)); train.SetTrainOutOfControl(OutOfControlReason.SlippedIntoTurnTable); train.SpeedMpS = 0; foreach (TrainCar car in train.Cars) { car.SpeedMpS = 0; } return(false); } } if (SendNotifications) { Simulator.Instance.Confirmer.Information(Simulator.Catalog.GetString("Train front on {0}", tableType)); } } TrainsOnMovingTable[trainIndex].SetFrontState(true); } else if (trainIndex != -1 && TrainsOnMovingTable[trainIndex].FrontOnBoard) { if (SendNotifications) { Simulator.Instance.Confirmer.Information(Simulator.Catalog.GetString("Train front outside {0}", tableType)); } if (TrainsOnMovingTable[trainIndex].BackOnBoard) { TrainsOnMovingTable[trainIndex].SetFrontState(false); } else { TrainsOnMovingTable.RemoveAt(trainIndex); trainIndex = -1; } } if (WorldLocation.Within(train.RearTDBTraveller.WorldLocation, WorldPosition.WorldLocation, Length / 2)) { if (trainIndex == -1 || !TrainsOnMovingTable[trainIndex].BackOnBoard) { if (trainIndex == -1) { TrainOnMovingTable trainOnTurntable = new TrainOnMovingTable(train); trainIndex = TrainsOnMovingTable.Count; TrainsOnMovingTable.Add(trainOnTurntable); } if (!TrainsOnMovingTable[trainIndex].FrontOnBoard) { // check if turntable aligned with train bool aligned = CheckMovingTableAligned(train, false); if (!aligned) { TrainsOnMovingTable[trainIndex].SetBackState(true); Simulator.Instance.Confirmer.Warning(Simulator.Catalog.GetString("Train slipped into non aligned {0}", tableType)); train.SetTrainOutOfControl(OutOfControlReason.SlippedIntoTurnTable); train.SpeedMpS = 0; foreach (TrainCar car in train.Cars) { car.SpeedMpS = 0; } return(false); } } Simulator.Instance.Confirmer.Information(Simulator.Catalog.GetString("Train rear on {0}", tableType)); } TrainsOnMovingTable[trainIndex].SetBackState(true); } else if (trainIndex != -1 && TrainsOnMovingTable[trainIndex].BackOnBoard) { if (SendNotifications) { Simulator.Instance.Confirmer.Information(Simulator.Catalog.GetString("Train rear outside {0}", tableType)); } if (TrainsOnMovingTable[trainIndex].FrontOnBoard) { TrainsOnMovingTable[trainIndex].SetBackState(false); } else { TrainsOnMovingTable.RemoveAt(trainIndex); trainIndex = -1; } } if (Simulator.Instance.ActivityRun != null && !train.IsPathless && train.TrainType != TrainType.Static && trainIndex != -1 && TrainsOnMovingTable[trainIndex].FrontOnBoard && TrainsOnMovingTable[trainIndex].BackOnBoard && train.SpeedMpS <= 0.1f && train.ControlMode != TrainControlMode.Manual && train.TCRoute.ActiveSubPath == train.TCRoute.TCRouteSubpaths.Count - 1 && train.TCRoute.TCRouteSubpaths[train.TCRoute.ActiveSubPath].Count > 1 && (train.PresentPosition[Direction.Forward].RouteListIndex == train.TCRoute.TCRouteSubpaths[train.TCRoute.ActiveSubPath].Count - 2 || train.PresentPosition[Direction.Backward].RouteListIndex == train.TCRoute.TCRouteSubpaths[train.TCRoute.ActiveSubPath].Count - 2)) { train.IsPathless = true; } return(false); }
void UpdateCrossings(Train train, float elapsedTime) { var speedMpS = train.SpeedMpS; var absSpeedMpS = Math.Abs(speedMpS); var maxSpeedMpS = train.AllowedMaxSpeedMpS; var minCrossingActivationSpeed = 5.0f; //5.0MpS is equalivalent to 11.1mph. This is the estimated min speed that MSTS uses to activate the gates when in range. bool validTrain = false; bool validStaticConsist = false; //var stopTime = elapsedTime; // This has been set up, but it is not being used in the code. //stopTime = 0; // We only care about crossing items which are: // a) Grouped properly. // b) Within the maximum activation distance of front/rear of the train. // Separate tests are performed for present speed and for possible maximum speed to avoid anomolies if train accelerates. // Special test is also done to check on section availability to avoid closure beyond signal at danger. foreach (var crossing in TrackCrossingItems.Values.Where(ci => ci.CrossingGroup != null)) { var predictedDist = crossing.CrossingGroup.WarningTime * absSpeedMpS; var maxPredictedDist = crossing.CrossingGroup.WarningTime * (maxSpeedMpS - absSpeedMpS) / 2; // added distance if train accelerates to maxspeed var minimumDist = crossing.CrossingGroup.MinimumDistance; var totalDist = predictedDist + minimumDist + 1; var totalMaxDist = predictedDist + maxPredictedDist + minimumDist + 1; var reqDist = 0f; // actual used distance var hornReqDist = 0f; // used distance for horn blow var adjustDist = 0f; // The first 2 tests are critical for STATIC CONSISTS, but at the same time should be mandatory since there should always be checks for any null situation. // The first 2 tests cover a situation where a STATIC consist is found on a crossing attached to a road where other crossings are attached to the same road. // These tests will only allow the activation of the crossing that should be activated but prevent the actvation of the other crossings which was the issue. // The source of the issue is not known yet since this only happens with STATIC consists. // The purpose of this test is to validate the static consist that is within vicinity of the crossing. if (train.TrainType == Train.TRAINTYPE.STATIC) { // An issue was found where a road sharing more than one crossing would have all crossings activated instead of the one crossing when working with STATIC consists. // The test below corrects this, but the source of the issue is not understood. if (!WorldLocation.Within(crossing.Location, train.FrontTDBTraveller.WorldLocation, (minimumDist + (train.Length / 2))) && !WorldLocation.Within(crossing.Location, train.RearTDBTraveller.WorldLocation, (minimumDist + (train.Length / 2)))) { continue; } if (WorldLocation.Within(crossing.Location, train.FrontTDBTraveller.WorldLocation, (minimumDist + (train.Length / 2))) || WorldLocation.Within(crossing.Location, train.RearTDBTraveller.WorldLocation, (minimumDist + (train.Length / 2)))) { foreach (var scar in train.Cars) { if (WorldLocation.Within(crossing.Location, scar.WorldPosition.WorldLocation, minimumDist)) { validStaticConsist = true; } } } } if ((train.TrainType != Train.TRAINTYPE.STATIC) && WorldLocation.Within(crossing.Location, train.FrontTDBTraveller.WorldLocation, totalDist) || WorldLocation.Within(crossing.Location, train.RearTDBTraveller.WorldLocation, totalDist)) { validTrain = true; reqDist = totalDist; hornReqDist = Math.Min(totalDist, 80.0f); } else if ((train.TrainType != Train.TRAINTYPE.STATIC) && WorldLocation.Within(crossing.Location, train.FrontTDBTraveller.WorldLocation, totalMaxDist) || WorldLocation.Within(crossing.Location, train.RearTDBTraveller.WorldLocation, totalMaxDist)) { validTrain = true; reqDist = totalMaxDist; hornReqDist = Math.Min(totalMaxDist, 80.0f); } if ((train.TrainType == Train.TRAINTYPE.STATIC) && !validStaticConsist && !crossing.StaticConsists.Contains(train)) { continue; } if ((train.TrainType != Train.TRAINTYPE.STATIC) && !validTrain && !crossing.Trains.Contains(train)) { continue; } // Distances forward from the front and rearwards from the rear. var frontDist = crossing.DistanceTo(train.FrontTDBTraveller, reqDist); if (frontDist < 0 && train.TrainType != Train.TRAINTYPE.STATIC) { frontDist = -crossing.DistanceTo(new Traveller(train.FrontTDBTraveller, Traveller.TravellerDirection.Backward), reqDist + train.Length); if (frontDist > 0) { // Train cannot find crossing. crossing.RemoveTrain(train); continue; } } var rearDist = -frontDist - train.Length; if (train is AITrain && frontDist <= hornReqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { // Add generic actions if needed ((AITrain)train).AuxActionsContain.CheckGenActions(this.GetType(), crossing.Location, rearDist, frontDist, crossing.TrackIndex); } // The tests below is to allow the crossings operate like the crossings under MSTS // Tests as follows // Train speed is 0. This was the initial issue that was found under one the MSTS activities. Activity should start without gates being activated. // There are 2 tests for train speed between 0 and 5.0MpS(11.1mph). Covering forward movement and reverse movement. // The last 2 tests is for testing trains running at line speed, forward or reverse. // The crossing only becomes active if the train has been added to the list such as crossing.AddTrain(train). // Note: With the conditions below, OR's crossings operates like the crossings in MSTS, with exception to the simulation of the timout below. // MSTS did not simulate a timeout, I introduced a simple timout using speedMpS. // Depending upon future development in this area, it would probably be best to have the current operation in its own class followed by any new region specific operations. // Recognizing static consists at crossings. if ((train.TrainType == Train.TRAINTYPE.STATIC) && validStaticConsist) { // This process is to raise the crossing gates if a loose consist rolls through the crossing. if (speedMpS > 0) { frontDist = crossing.DistanceTo(train.FrontTDBTraveller, minimumDist); rearDist = crossing.DistanceTo(train.RearTDBTraveller, minimumDist); if (frontDist < 0 && rearDist < 0) { crossing.RemoveTrain(train); } } //adjustDist is used to allow static cars to be placed closer to the crossing without activation. // One example would be industry sidings with a crossing nearby. This was found in a custom route. if (minimumDist >= 20) { adjustDist = minimumDist - 13.5f; } else if (minimumDist < 20) { adjustDist = minimumDist - 6.5f; } frontDist = crossing.DistanceTo(train.FrontTDBTraveller, adjustDist); rearDist = crossing.DistanceTo(train.RearTDBTraveller, adjustDist); // Static consist passed the crossing. if (frontDist < 0 && rearDist < 0) { rearDist = crossing.DistanceTo(new Traveller(train.RearTDBTraveller, Traveller.TravellerDirection.Backward), adjustDist); } // Testing distance before crossing if (frontDist > 0 && frontDist <= adjustDist) { crossing.AddTrain(train); } // Testing to check if consist is straddling the crossing. else if (frontDist < 0 && rearDist > 0) { crossing.AddTrain(train); } // This is an odd test because a custom route has a particular // crossing object placement that is creating different results. // Testing to check if consist is straddling the crossing. else if (frontDist < 0 && rearDist < 0) { crossing.AddTrain(train); } // Testing distance when past crossing. else if (rearDist <= adjustDist && rearDist > 0) { crossing.AddTrain(train); } else { crossing.RemoveTrain(train); } } // Train is stopped. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER || train.TrainType == Train.TRAINTYPE.REMOTE) && Math.Abs(speedMpS) <= Simulator.MaxStoppedMpS && frontDist <= reqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { // First test is to simulate a timeout if a train comes to a stop before minimumDist if (frontDist > minimumDist && Simulator.Trains.Contains(train)) { crossing.RemoveTrain(train); } // This test is to factor in the train sitting on the crossing at the start of the activity. else { crossing.AddTrain(train); } } // Train is travelling toward crossing below 11.1mph. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER || train.TrainType == Train.TRAINTYPE.STATIC || train.TrainType == Train.TRAINTYPE.REMOTE) && speedMpS > 0 && speedMpS <= minCrossingActivationSpeed && frontDist <= reqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { // This will allow a slow train to approach to the crossing's minmum distance without activating the crossing. if (frontDist <= minimumDist + 65f) // Not all crossing systems operate the same so adding an additional 65 meters is only an option to improve operation. { crossing.AddTrain(train); } } // Checking for reverse movement when train is approaching crossing while travelling under 11.1mph. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER) && speedMpS < 0 && absSpeedMpS <= minCrossingActivationSpeed && rearDist <= reqDist && (train.ReservedTrackLengthM <= 0 || rearDist < train.ReservedTrackLengthM) && frontDist <= minimumDist) { // This will allow a slow train to approach a crossing to a certain point without activating the system. // First test covers front of train clearing crossing. // Second test covers rear of train approaching crossing. if (frontDist > 9.5) // The value of 9.5 which is within minimumDist is used to test against frontDist to give the best possible distance the gates should deactivate. { crossing.RemoveTrain(train); } else if (rearDist <= minimumDist + 65f) // Not all crossing systems operate the same so adding an additional 65 meters is only an option to improve operation. { crossing.AddTrain(train); } } // Checking for reverse movement through crossing when train is travelling above 11.1mph. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER || train.TrainType == Train.TRAINTYPE.REMOTE) && speedMpS < 0 && absSpeedMpS > minCrossingActivationSpeed && rearDist <= reqDist && (train.ReservedTrackLengthM <= 0 || rearDist < train.ReservedTrackLengthM) && frontDist <= minimumDist) { crossing.AddTrain(train); } // Player train travelling in forward direction above 11.1mph will activate the crossing. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER || train.TrainType == Train.TRAINTYPE.REMOTE) && speedMpS > 0 && speedMpS > minCrossingActivationSpeed && frontDist <= reqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { crossing.AddTrain(train); } else { crossing.RemoveTrain(train); } } }
/// <summary> /// CheckTrainOnTurntable: checks if actual player train is on turntable /// </summary> /// public bool CheckTrainOnMovingTable(Train train) { var thisTableType = this is Turntable?Simulator.Catalog.GetString("turntable") : Simulator.Catalog.GetString("transfertable"); var trainIndex = TrainsOnMovingTable.FindIndex(x => x.Train.Number == train.Number); if (WorldLocation.Within(train.FrontTDBTraveller.WorldLocation, WorldPosition.WorldLocation, Length / 2)) { if (trainIndex == -1 || !TrainsOnMovingTable[trainIndex].FrontOnBoard) { if (trainIndex == -1) { var trainOnTurntable = new TrainOnMovingTable(train, Simulator); trainIndex = TrainsOnMovingTable.Count; TrainsOnMovingTable.Add(trainOnTurntable); } if (!TrainsOnMovingTable[trainIndex].BackOnBoard) { // check if turntable aligned with train var isAligned = CheckMovingTableAligned(train, true); if (!isAligned) { TrainsOnMovingTable[trainIndex].SetFrontState(true); Simulator.Confirmer.Warning(Simulator.Catalog.GetStringFmt("Train slipped into non aligned {0}", thisTableType)); train.SetTrainOutOfControl(Train.OUTOFCONTROL.SLIPPED_INTO_TURNTABLE); train.SpeedMpS = 0; foreach (var car in train.Cars) { car.SpeedMpS = 0; } return(false); } } if (SendNotifications) { Simulator.Confirmer.Information(Simulator.Catalog.GetStringFmt("Train front on {0}", thisTableType)); } } TrainsOnMovingTable[trainIndex].SetFrontState(true); } else { if (trainIndex != -1 && TrainsOnMovingTable[trainIndex].FrontOnBoard) { if (SendNotifications) { Simulator.Confirmer.Information(Simulator.Catalog.GetStringFmt("Train front outside {0}", thisTableType)); } if (TrainsOnMovingTable[trainIndex].BackOnBoard) { TrainsOnMovingTable[trainIndex].SetFrontState(false); } else { TrainsOnMovingTable.RemoveAt(trainIndex); trainIndex = -1; } } } if (WorldLocation.Within(train.RearTDBTraveller.WorldLocation, WorldPosition.WorldLocation, Length / 2)) { if (trainIndex == -1 || !TrainsOnMovingTable[trainIndex].BackOnBoard) { if (trainIndex == -1) { var trainOnTurntable = new TrainOnMovingTable(train, Simulator); trainIndex = TrainsOnMovingTable.Count; TrainsOnMovingTable.Add(trainOnTurntable); } if (!TrainsOnMovingTable[trainIndex].FrontOnBoard) { // check if turntable aligned with train var isAligned = CheckMovingTableAligned(train, false); if (!isAligned) { TrainsOnMovingTable[trainIndex].SetBackState(true); Simulator.Confirmer.Warning(Simulator.Catalog.GetStringFmt("Train slipped into non aligned {0}", thisTableType)); train.SetTrainOutOfControl(Train.OUTOFCONTROL.SLIPPED_INTO_TURNTABLE); train.SpeedMpS = 0; foreach (var car in train.Cars) { car.SpeedMpS = 0; } return(false); } } Simulator.Confirmer.Information(Simulator.Catalog.GetStringFmt("Train rear on {0}", thisTableType)); } TrainsOnMovingTable[trainIndex].SetBackState(true); } else { if (trainIndex != -1 && TrainsOnMovingTable[trainIndex].BackOnBoard) { if (SendNotifications) { Simulator.Confirmer.Information(Simulator.Catalog.GetStringFmt("Train rear outside {0}", thisTableType)); } if (TrainsOnMovingTable[trainIndex].FrontOnBoard) { TrainsOnMovingTable[trainIndex].SetBackState(false); } else { TrainsOnMovingTable.RemoveAt(trainIndex); trainIndex = -1; } } } if (Simulator.ActivityRun != null && !train.IsPathless && train.TrainType != Train.TRAINTYPE.STATIC && trainIndex != -1 && TrainsOnMovingTable[trainIndex].FrontOnBoard && TrainsOnMovingTable[trainIndex].BackOnBoard && train.SpeedMpS <= 0.1f && train.ControlMode != Train.TRAIN_CONTROL.MANUAL && train.TCRoute.activeSubpath == train.TCRoute.TCRouteSubpaths.Count - 1 && train.TCRoute.TCRouteSubpaths[train.TCRoute.activeSubpath].Count > 1 && (train.PresentPosition[0].RouteListIndex == train.TCRoute.TCRouteSubpaths[train.TCRoute.activeSubpath].Count - 2 || train.PresentPosition[1].RouteListIndex == train.TCRoute.TCRouteSubpaths[train.TCRoute.activeSubpath].Count - 2)) // Activity mode, train with path is at end of it and is being rotated on the turntable { train.IsPathless = true; } return(false); }
void UpdateCrossings(Train train, float elapsedTime) { var speedMpS = train.SpeedMpS; var absSpeedMpS = Math.Abs(speedMpS); var maxSpeedMpS = train.AllowedMaxSpeedMpS; var minCrossingActivationSpeed = 5.0f; //5.0MpS is equalivalent to 11.1mph. This is the estimated min speed that MSTS uses to activate the gates when in range. bool validTrain = false; //var stopTime = elapsedTime; // This has been set up, but it is not being used in the code. //stopTime = 0; // We only care about crossing items which are: // a) Grouped properly. // b) Within the maximum activation distance of front/rear of the train. // Separate tests are performed for present speed and for possible maximum speed to avoid anomolies if train accelerates. // Special test is also done to check on section availability to avoid closure beyond signal at danger. foreach (var crossing in TrackCrossingItems.Values.Where(ci => ci.CrossingGroup != null)) { var predictedDist = crossing.CrossingGroup.WarningTime * absSpeedMpS; var maxPredictedDist = crossing.CrossingGroup.WarningTime * (maxSpeedMpS - absSpeedMpS) / 2; // added distance if train accelerates to maxspeed var minimumDist = crossing.CrossingGroup.MinimumDistance; var totalDist = predictedDist + minimumDist + 1; var totalMaxDist = predictedDist + maxPredictedDist + minimumDist + 1; var reqDist = 0f; // actual used distance var hornReqDist = 0f; // used distance for horn blow if (WorldLocation.Within(crossing.Location, train.FrontTDBTraveller.WorldLocation, totalDist) || WorldLocation.Within(crossing.Location, train.RearTDBTraveller.WorldLocation, totalDist)) { validTrain = true; reqDist = totalDist; hornReqDist = Math.Min(totalDist, 80.0f); } else if (WorldLocation.Within(crossing.Location, train.FrontTDBTraveller.WorldLocation, totalMaxDist) || WorldLocation.Within(crossing.Location, train.RearTDBTraveller.WorldLocation, totalMaxDist)) { validTrain = true; reqDist = totalMaxDist; hornReqDist = Math.Min(totalMaxDist, 80.0f); } if (!validTrain && !crossing.Trains.Contains(train)) { continue; } // Distances forward from the front and rearwards from the rear. var frontDist = crossing.DistanceTo(train.FrontTDBTraveller, reqDist); if (frontDist < 0) { frontDist = -crossing.DistanceTo(new Traveller(train.FrontTDBTraveller, Traveller.TravellerDirection.Backward), reqDist + train.Length); if (frontDist > 0) { // Train cannot find crossing. crossing.RemoveTrain(train); continue; } } var rearDist = -frontDist - train.Length; if (train is AITrain && frontDist <= hornReqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { // Add generic actions if needed ((AITrain)train).AuxActionsContain.CheckGenActions(this.GetType(), crossing.Location, rearDist, frontDist, crossing.TrackIndex); } // The tests below is to allow the crossings operate like the crossings under MSTS // Tests as follows // Train speed is 0. This was the initial issue that was found under one the MSTS activities. Activity should start without gates being activated. // There are 2 tests for train speed between 0 and 5.0MpS(11.1mph). Covering forward movement and reverse movement. // The last 2 tests is for testing trains running at line speed, forward or reverse. // The crossing only becomes active if the train has been added to the list such as crossing.AddTrain(train). // Note: With the conditions below, OR's crossings operates like the crossings in MSTS, with exception to the simulation of the timout below. // MSTS did not simulate a timeout, I introduced a simple timout using speedMpS. // Depending upon future development in this area, it would probably be best to have the current operation in its own class followed by any new region specific operations. // Train is stopped. if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER) && speedMpS == 0 && frontDist <= reqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { // First test is to simulate a timeout if a train comes to a stop before minimumDist if (frontDist > minimumDist && Simulator.Trains.Contains(train)) { crossing.RemoveTrain(train); } } // Train is travelling toward crossing below 11.1mph. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER) && speedMpS > 0 && speedMpS <= minCrossingActivationSpeed && frontDist <= reqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { // This will allow a slow train to approach to the crossing's minmum distance without activating the crossing. if (frontDist <= minimumDist + 60f) // Not all crossing systems operate the same so adding an additional 60 meters is only an option to improve operation. { crossing.AddTrain(train); } } // Checking for reverse movement when train is approaching crossing while travelling under 11.1mph. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER) && speedMpS < 0 && absSpeedMpS <= minCrossingActivationSpeed && rearDist <= reqDist && (train.ReservedTrackLengthM <= 0 || rearDist < train.ReservedTrackLengthM) && frontDist <= minimumDist) { // This will allow a slow train to approach a crossing to a certain point without activating the system. // First test covers front of train clearing crossing. // Second test covers rear of train approaching crossing. if (frontDist > 2.5) // The value of 2.5 which is within minimumDist is used to test against frontDist to give the best possible distance the gates should deactivate. { crossing.RemoveTrain(train); } else if (rearDist <= minimumDist + 60f) // Not all crossing systems operate the same so adding an additional 60 meters is only an option to improve operation. { crossing.AddTrain(train); } } // Checking for reverse movement through crossing when train is travelling above 11.1mph. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER) && speedMpS < 0 && absSpeedMpS > minCrossingActivationSpeed && rearDist <= reqDist && (train.ReservedTrackLengthM <= 0 || rearDist < train.ReservedTrackLengthM) && frontDist <= minimumDist) { crossing.AddTrain(train); } // Player train travelling in forward direction above 11.1mph will activate the crossing. else if ((train is AITrain || train.TrainType == Train.TRAINTYPE.PLAYER) && speedMpS > 0 && speedMpS > minCrossingActivationSpeed && frontDist <= reqDist && (train.ReservedTrackLengthM <= 0 || frontDist < train.ReservedTrackLengthM) && rearDist <= minimumDist) { crossing.AddTrain(train); } else { crossing.RemoveTrain(train); } } }