public ScriptedBrakeController(ScriptedBrakeController controller, MSTSLocomotive locomotive) { Simulator = locomotive.Simulator; Locomotive = locomotive; ScriptName = controller.ScriptName; MaxPressurePSI = controller.MaxPressurePSI; ReleaseRatePSIpS = controller.ReleaseRatePSIpS; QuickReleaseRatePSIpS = controller.QuickReleaseRatePSIpS; ApplyRatePSIpS = controller.ApplyRatePSIpS; EmergencyRatePSIpS = controller.EmergencyRatePSIpS; FullServReductionPSI = controller.FullServReductionPSI; MinReductionPSI = controller.MinReductionPSI; CurrentValue = controller.CurrentValue; MinimumValue = controller.MinimumValue; MaximumValue = controller.MaximumValue; StepSize = controller.StepSize; controller.Notches.ForEach( (item) => { Notches.Add(new MSTSNotch(item)); } ); Initialize(); }
public DistributedPowerInterfaceRenderer(Viewer viewer, MSTSLocomotive locomotive, CVCScreen control, CabShader shader) : base(viewer, locomotive, control, shader) { Position.X = (float)Control.PositionX; Position.Y = (float)Control.PositionY; DPI = new DistributedPowerInterface((int)Control.Height, (int)Control.Width, locomotive, viewer, control); }
public ScriptedTrainControlSystem(MSTSLocomotive locomotive) { Locomotive = locomotive; Simulator = Locomotive.Simulator; PowerAuthorization = true; }
public MasterKey(MSTSLocomotive locomotive) { Locomotive = locomotive; Timer = new Timer(Locomotive); Timer.Setup(DelayS); }
public CircularSpeedGaugeRenderer(Viewer viewer, MSTSLocomotive locomotive, CabViewDigitalControl control, CabShader shader) : base(viewer, locomotive, control, shader) { // Height is adjusted to keep compatibility driverMachineInterface = new DriverMachineInterface((int)(Control.Bounds.Width * 640 / 280), (int)(Control.Bounds.Height * 480 / 300), locomotive, viewer, control); viewer.UserCommandController.AddEvent(CommonUserCommand.AlternatePointerPressed, MouseRightButtonPressed); viewer.UserCommandController.AddEvent(CommonUserCommand.AlternatePointerReleased, MouseRightButtonReleased); }
public DriverMachineInterface(float height, float width, MSTSLocomotive locomotive, Viewer viewer, CabViewControl control) { if (control is CVCScreen) { CurrentDMIMode = DMIMode.FullSize; if ((control as CVCScreen).CustomParameters.TryGetValue("mode", out string mode)) { if (mode == "planningarea") { CurrentDMIMode = DMIMode.PlanningArea; } else if (mode == "speedarea") { CurrentDMIMode = DMIMode.SpeedArea; } } } else { CurrentDMIMode = DMIMode.GaugeOnly; } switch (CurrentDMIMode) { case DMIMode.GaugeOnly: Width = 280; Height = 300; break; case DMIMode.FullSize: Width = 640; Height = 480; break; case DMIMode.PlanningArea: case DMIMode.SpeedArea: Width = 334; Height = 480; break; } Viewer = viewer; Locomotive = locomotive; Scale = Math.Min(width / Width, height / Height); if (Scale < 0.5) { MipMapScale = 2; } else { MipMapScale = 1; } Shader = new DriverMachineInterfaceShader(Viewer.GraphicsDevice); ETCSDefaultWindow = new ETCSDefaultWindow(this, control); ETCSDefaultWindow.Visible = true; AddToLayout(ETCSDefaultWindow, Point.Zero); ActiveWindow = ETCSDefaultWindow; }
public AbstractPowerSupply(MSTSLocomotive locomotive) { Locomotive = locomotive; Simulator = locomotive.Simulator; State = PowerSupplyState.PowerOff; AuxiliaryState = PowerSupplyState.PowerOff; PowerOnDelayS = 0; AuxPowerOnDelayS = 0; }
public ScriptedTrainControlSystem(MSTSLocomotive locomotive) { Locomotive = locomotive; Simulator = Locomotive.Simulator; PowerAuthorization = true; CircuitBreakerClosingOrder = false; CircuitBreakerOpeningOrder = false; TractionAuthorization = true; }
public ScriptedBrakeController(MSTSLocomotive locomotive) { Simulator = locomotive.Simulator; Locomotive = locomotive; MaxPressurePSI = 90; ReleaseRatePSIpS = 5; QuickReleaseRatePSIpS = 10; ApplyRatePSIpS = 2; EmergencyRatePSIpS = 10; FullServReductionPSI = 26; MinReductionPSI = 6; }
public ScriptedLocomotivePowerSupply(MSTSLocomotive locomotive) { Locomotive = locomotive; BatterySwitch = new BatterySwitch(Locomotive); MasterKey = new MasterKey(Locomotive); ElectricTrainSupplySwitch = new ElectricTrainSupplySwitch(Locomotive); MainPowerSupplyState = PowerSupplyState.PowerOff; AuxiliaryPowerSupplyState = PowerSupplyState.PowerOff; LowVoltagePowerSupplyState = PowerSupplyState.PowerOff; CabPowerSupplyState = PowerSupplyState.PowerOff; }
public DriverMachineInterfaceRenderer(Viewer viewer, MSTSLocomotive locomotive, CVCScreen control, CabShader shader) : base(viewer, locomotive, control, shader) { Position.X = (float)Control.PositionX; Position.Y = (float)Control.PositionY; if ((int)Control.Height == 102 && (int)Control.Width == 136) { // Hack for ETR400 cab, which was built with a bugged size calculation of digital displays Control.Height *= 0.75f; Control.Width *= 0.75f; } DMI = new DriverMachineInterface((int)Control.Height, (int)Control.Width, locomotive, viewer, control); }
public CabViewCircularSpeedGaugeRenderer(Viewer viewer, MSTSLocomotive locomotive, CVCDigital control, CabShader shader) : base(viewer, locomotive, control, shader) { CircularSpeedGauge = new CircularSpeedGauge( (int)Control.Width, (int)Control.Height, (int)Control.MaxValue, Control.Units == CABViewControlUnits.KM_PER_HOUR, true, Control.MaxValue == 240 || Control.MaxValue == 260, (int)Control.MinValue, Locomotive, Viewer ); }
public override void Update(float elapsedClockSeconds) { MSTSLocomotive lead = (MSTSLocomotive)Car.Train.LeadLocomotive; float demandedAutoCylPressurePSI = 0; // Only allow EP brake tokens to operate if car is connected to an EP system if (lead == null || !(lead.BrakeSystem is EPBrakeSystem)) { HoldingValve = ValveState.Release; base.Update(elapsedClockSeconds); return; } // process valid EP brake tokens if (BrakeLine3PressurePSI >= 1000f || Car.Train.BrakeLine4 < 0) { HoldingValve = ValveState.Release; } else if (Car.Train.BrakeLine4 == 0) { HoldingValve = ValveState.Lap; } else { demandedAutoCylPressurePSI = Math.Min(Math.Max(Car.Train.BrakeLine4, 0), 1) * MaxCylPressurePSI; HoldingValve = AutoCylPressurePSI <= demandedAutoCylPressurePSI ? ValveState.Lap : ValveState.Release; } base.Update(elapsedClockSeconds); // Allow processing of other valid tokens if (AutoCylPressurePSI < demandedAutoCylPressurePSI && !Car.WheelBrakeSlideProtectionActive) { float dp = elapsedClockSeconds * MaxApplicationRatePSIpS; if (BrakeLine2PressurePSI - dp * AuxBrakeLineVolumeRatio / AuxCylVolumeRatio < AutoCylPressurePSI + dp) { dp = (BrakeLine2PressurePSI - AutoCylPressurePSI) / (1 + AuxBrakeLineVolumeRatio / AuxCylVolumeRatio); } if (dp > demandedAutoCylPressurePSI - AutoCylPressurePSI) { dp = demandedAutoCylPressurePSI - AutoCylPressurePSI; } BrakeLine2PressurePSI -= dp * AuxBrakeLineVolumeRatio / AuxCylVolumeRatio; AutoCylPressurePSI += dp; } }
public ScriptedBrakeController(MSTSLocomotive locomotive) { Simulator = locomotive.Simulator; Locomotive = locomotive; MaxPressurePSI = 90; MaxOverchargePressurePSI = 95; ReleaseRatePSIpS = 5; QuickReleaseRatePSIpS = 10; OverchargeEliminationRatePSIpS = 0.036f; ApplyRatePSIpS = 2; SlowApplicationRatePSIpS = 1; EmergencyRatePSIpS = 10; FullServReductionPSI = 26; MinReductionPSI = 6; }
//[CallOnThread("Loader")] public CabViewCircularSpeedGaugeRenderer(Viewer viewer, MSTSLocomotive locomotive, CabViewDigitalControl control, CabShader shader) : base(viewer, locomotive, control, shader) { CircularSpeedGauge = new CircularSpeedGauge( control.Bounds.Width, Control.Bounds.Height, (int)Control.ScaleRangeMax, Control.ControlUnit == CabViewControlUnit.Km_Per_Hour, true, Control.ScaleRangeMax == 240 || Control.ScaleRangeMax == 260, (int)Control.ScaleRangeMin, Locomotive, Viewer, shader ); }
public CircularSpeedGauge(int width, int height, int maxSpeed, bool unitMetric, bool unitVisible, bool dialQuarterLines, int maxVisibleScale, MSTSLocomotive locomotive, Viewer viewer, CabShader shader) { UnitVisible = unitVisible; SetUnit(unitMetric); DialQuarterLines = dialQuarterLines; MaxSpeed = maxSpeed; MaxVisibleScale = maxVisibleScale; Viewer = viewer; Locomotive = locomotive; cabShader = shader; SizeTo(width, height); SetRange(MaxSpeed); CurrentSpeed = new TextPrimitive[3]; for (var i = 0; i < CurrentSpeed.Length; i++) { CurrentSpeed[i] = new TextPrimitive(new Point(CurrentSpeedPosition[i], CurrentSpeedPosition[3]), Color.Black, "0", FontCurrentSpeed); } ReleaseSpeed = new TextPrimitive(ReleaseSpeedPosition, ColorGrey, String.Empty, FontReleaseSpeed); Shader = new DriverMachineInterfaceShader(Viewer.RenderProcess.GraphicsDevice); if (NeedleTextureData == null) { NeedleTextureData = new Color[128 * 16]; // Needle texture is according to ETCS specification for (var v = 0; v < 128; v++) { for (var u = 0; u < 16; u++) { NeedleTextureData[u + 16 * v] = ( v <= 15 && 5 < u && u < 9 || 15 < v && v <= 23 && 5f - (float)(v - 15) / 8f * 3f < u && u < 9f + (float)(v - 15) / 8f * 3f || 23 < v && v < 82 && 2 < u && u < 12 ) ? Color.White : Color.Transparent; } } } }
public void Initialize() { if (!Activated) { if (ScriptName != null && ScriptName != "Automatic") { var pathArray = new string[] { Path.Combine(Path.GetDirectoryName(Locomotive.WagFilePath), "Script") }; Script = Simulator.ScriptManager.Load(pathArray, ScriptName) as CircuitBreaker; } if (Script == null) { Script = new AutomaticCircuitBreaker() as CircuitBreaker; } Script.ClockTime = () => (float)Simulator.ClockTime; Script.GameTime = () => (float)Simulator.GameTime; Script.DistanceM = () => Locomotive.DistanceM; Script.CurrentState = () => State; Script.CurrentPantographState = () => Locomotive.Pantographs.State; Script.CurrentPowerSupplyState = () => Locomotive.PowerSupply.State; Script.TCSCloseAuthorization = () => { MSTSLocomotive locomotive = Locomotive.Train.LeadLocomotive as MSTSLocomotive; if (locomotive != null) { return(locomotive.TrainControlSystem.PowerAuthorization); } else { return(false); } }; Script.DriverCloseAuthorization = () => DriverCloseAuthorization; Script.ClosingDelayS = () => DelayS; Script.SetCurrentState = (value) => State = value; Script.SetDriverCloseAuthorization = (value) => DriverCloseAuthorization = value; Script.Initialize(); Activated = true; } }
public DistributedPowerInterface(float height, float width, MSTSLocomotive locomotive, Viewer viewer, CabViewControl control) { Viewer = viewer; Locomotive = locomotive; Scale = Math.Min(width / Width, height / Height); if (Scale < 0.5) { MipMapScale = 2; } else { MipMapScale = 1; } Shader = new DriverMachineInterfaceShader(Viewer.GraphicsDevice); DPDefaultWindow = new DPDefaultWindow(this, control); DPDefaultWindow.Visible = true; AddToLayout(DPDefaultWindow, Point.Zero); ActiveWindow = DPDefaultWindow; }
public override void Initialize(bool handbrakeOn, float maxPressurePSI, float fullServPressurePSI, bool immediateRelease) { // reducing size of Emergency Reservoir for short (fake) cars if (Car.Simulator.Settings.CorrectQuestionableBrakingParams && Car.CarLengthM <= 1) { EmergResVolumeM3 = Math.Min(0.02f, EmergResVolumeM3); } BrakeLine1PressurePSI = Car.Train.BrakeLine1PressurePSIorInHg; BrakeLine2PressurePSI = Car.Train.BrakeLine2PressurePSI; BrakeLine3PressurePSI = 0; AuxResPressurePSI = maxPressurePSI > BrakeLine1PressurePSI ? maxPressurePSI : BrakeLine1PressurePSI; if ((Car as MSTSWagon).EmergencyReservoirPresent || maxPressurePSI > 0) { EmergResPressurePSI = maxPressurePSI; } FullServPressurePSI = fullServPressurePSI; AutoCylPressurePSI = immediateRelease ? 0 : Math.Min((maxPressurePSI - BrakeLine1PressurePSI) * AuxCylVolumeRatio, MaxCylPressurePSI); TripleValveState = ValveState.Lap; HoldingValve = ValveState.Release; HandbrakePercent = handbrakeOn & (Car as MSTSWagon).HandBrakePresent ? 100 : 0; SetRetainer(RetainerSetting.Exhaust); MSTSLocomotive loco = Car as MSTSLocomotive; if (loco != null) { loco.MainResPressurePSI = loco.MaxMainResPressurePSI; } if (EmergResVolumeM3 > 0 && EmergAuxVolumeRatio > 0 && BrakePipeVolumeM3 > 0) { AuxBrakeLineVolumeRatio = EmergResVolumeM3 / EmergAuxVolumeRatio / BrakePipeVolumeM3; } else { AuxBrakeLineVolumeRatio = 3.1f; } //FullCylAirConsumedM3 = MaxCylPressurePSI * MaxBrakeForceN * 0.00000059733491f; //an average volume (M3) of air used in brake cylinder for 1 N brake force.; CylVolumeM3 = EmergResVolumeM3 / EmergAuxVolumeRatio / AuxCylVolumeRatio; }
void buttonTogglePower_Click(Control arg1, Point arg2) { if ((Viewer.PlayerTrain.Cars[CarPosition] is MSTSElectricLocomotive) || (Viewer.PlayerTrain.Cars[CarPosition] is MSTSDieselLocomotive)) { MSTSLocomotive locomotive = Viewer.PlayerTrain.Cars[CarPosition] as MSTSLocomotive; new PowerCommand(Viewer.Log, locomotive, !locomotive.LocomotivePowerSupply.MainPowerSupplyOn); if (locomotive.LocomotivePowerSupply.MainPowerSupplyOn) { Viewer.Simulator.Confirmer.Information(Viewer.Catalog.GetString("Power OFF command sent")); } else { Viewer.Simulator.Confirmer.Information(Viewer.Catalog.GetString("Power ON command sent")); } } else { Viewer.Simulator.Confirmer.Warning(Viewer.Catalog.GetString("No power command for this type of car!")); } }
public DriverMachineInterface(float height, float width, MSTSLocomotive locomotive, Viewer viewer, CabViewControl control) { Viewer = viewer; Locomotive = locomotive; Scale = Math.Min(width / Width, height / Height); if (Scale < 0.5) { MipMapScale = 2; } else { MipMapScale = 1; } GaugeOnly = control is CVCDigital; Shader = new DriverMachineInterfaceShader(Viewer.GraphicsDevice); ETCSDefaultWindow = new ETCSDefaultWindow(this, control); ETCSDefaultWindow.Visible = true; AddToLayout(ETCSDefaultWindow, Point.Zero); ActiveWindow = ETCSDefaultWindow; }
void buttonToggleMU_Click(Control arg1, Point arg2) { if ((Viewer.PlayerTrain.Cars[CarPosition] is MSTSElectricLocomotive) || (Viewer.PlayerTrain.Cars[CarPosition] is MSTSDieselLocomotive)) { MSTSLocomotive locomotive = Viewer.PlayerTrain.Cars[CarPosition] as MSTSLocomotive; new ToggleMUCommand(Viewer.Log, locomotive, locomotive.RemoteControlGroup < 0); if (locomotive.RemoteControlGroup >= 0) { Viewer.Simulator.Confirmer.Information(Viewer.Catalog.GetString("MU signal connected")); } else { Viewer.Simulator.Confirmer.Information(Viewer.Catalog.GetString("MU signal disconnected")); } } else { Viewer.Simulator.Confirmer.Warning(Viewer.Catalog.GetString("No MU command for this type of car!")); } }
/// Propagate brake pressure protected void PropagateBrakePressure(double elapsedClockSeconds) { if (LeadLocomotiveIndex >= 0) { MSTSLocomotive lead = (MSTSLocomotive)Cars[LeadLocomotiveIndex]; if (lead.TrainBrakeController != null) { (double pressurePSI, double epControllerState) = lead.TrainBrakeController.UpdatePressure(EqualReservoirPressurePSIorInHg, BrakeLine4, elapsedClockSeconds); EqualReservoirPressurePSIorInHg = (float)pressurePSI; BrakeLine4 = (float)epControllerState; } if (lead.EngineBrakeController != null) { BrakeLine3PressurePSI = (float)lead.EngineBrakeController.UpdateEngineBrakePressure(BrakeLine3PressurePSI, elapsedClockSeconds); } lead.BrakeSystem.PropagateBrakePressure(elapsedClockSeconds); } else if (TrainType == TrainType.Static) { // Propagate brake pressure of locomotiveless static consists in the advanced way, // to allow proper shunting operations. Cars[0].BrakeSystem.PropagateBrakePressure(elapsedClockSeconds); } else { // Propagate brake pressure of AI trains simplified /// AI trains simplyfied brake control is done by setting their Train.BrakeLine1PressurePSIorInHg, /// that is propagated promptly to each car directly. foreach (TrainCar car in Cars) { car.BrakeSystem.BrakeLine1PressurePSI = car.BrakeSystem.InternalPressure(EqualReservoirPressurePSIorInHg); car.BrakeSystem.BrakeLine2PressurePSI = BrakeLine2PressurePSI; car.BrakeSystem.BrakeLine3PressurePSI = 0; } } }
/// <summary> /// Retrieve a formatted list <see cref="ListLabel"/>s to be displayed as an in-browser Track Monitor. /// </summary> /// <param name="viewer">The Viewer to read train data from.</param> /// <returns>A list of <see cref="ListLabel"/>s, one per row of the popup.</returns> public static IEnumerable <ListLabel> TrainDrivingDisplayList(this Viewer viewer, bool normalTextMode = true) { bool useMetric = viewer.MilepostUnitsMetric; var labels = new List <ListLabel>(); void AddLabel(ListLabel label) { CheckLabel(ref label, normalTextMode); labels.Add(label); } void AddSeparator() => AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Sprtr"), }); TrainCar trainCar = viewer.PlayerLocomotive; Train train = trainCar.Train; string trainBrakeStatus = trainCar.GetTrainBrakeStatus(); string dynamicBrakeStatus = trainCar.GetDynamicBrakeStatus(); string engineBrakeStatus = trainCar.GetEngineBrakeStatus(); MSTSLocomotive locomotive = (MSTSLocomotive)trainCar; string locomotiveStatus = locomotive.GetStatus(); bool combinedControlType = locomotive.CombinedControlType == MSTSLocomotive.CombinedControl.ThrottleDynamic; bool showMUReverser = Math.Abs(train.MUReverserPercent) != 100f; bool showRetainers = train.RetainerSetting != RetainerSetting.Exhaust; bool stretched = train.Cars.Count > 1 && train.NPull == train.Cars.Count - 1; bool bunched = !stretched && train.Cars.Count > 1 && train.NPush == train.Cars.Count - 1; Train.TrainInfo trainInfo = train.GetTrainInfo(); // First Block // Client and server may have a time difference. AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Time"), LastCol = FormatStrings.FormatTime(viewer.Simulator.ClockTime + (MultiPlayer.MPManager.IsClient() ? MultiPlayer.MPManager.Instance().serverTimeDifference : 0)), }); if (viewer.Simulator.IsReplaying) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Replay"), LastCol = FormatStrings.FormatTime(viewer.Log.ReplayEndsAt - viewer.Simulator.ClockTime), }); } Color speedColor; if (locomotive.SpeedMpS < trainInfo.allowedSpeedMpS - 1f) { speedColor = Color.White; } else if (locomotive.SpeedMpS < trainInfo.allowedSpeedMpS) { speedColor = Color.PaleGreen; } else if (locomotive.SpeedMpS < trainInfo.allowedSpeedMpS + 5f) { speedColor = Color.Orange; } else { speedColor = Color.OrangeRed; } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Speed"), LastCol = $"{FormatStrings.FormatSpeedDisplay(locomotive.SpeedMpS, useMetric)}{ColorCode[speedColor]}", }); // Gradient info if (normalTextMode) { float gradient = -trainInfo.currentElevationPercent; const float minSlope = 0.00015f; string gradientIndicator; if (gradient < -minSlope) { gradientIndicator = $"{gradient:F1}%{Symbols.GradientDown}{ColorCode[Color.LightSkyBlue]}"; } else if (gradient > minSlope) { gradientIndicator = $"{gradient:F1}%{Symbols.GradientUp}{ColorCode[Color.Yellow]}"; } else { gradientIndicator = $"{gradient:F1}%"; } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Gradient"), LastCol = gradientIndicator, }); } // Separator AddSeparator(); // Second block // Direction { UserCommand?reverserCommand = GetPressedKey(UserCommand.ControlBackwards, UserCommand.ControlForwards); string reverserKey; bool moving = Math.Abs(trainCar.SpeedMpS) > 1; bool nonSteamEnd = trainCar.EngineType != TrainCar.EngineTypes.Steam && trainCar.Direction == Direction.N && (trainCar.ThrottlePercent >= 1 || moving); bool steamEnd = locomotive is MSTSSteamLocomotive steamLocomotive2 && steamLocomotive2.CutoffController.MaximumValue == Math.Abs(train.MUReverserPercent / 100); if (reverserCommand != null && (nonSteamEnd || steamEnd)) { reverserKey = Symbols.End + ColorCode[Color.Yellow]; } else if (reverserCommand == UserCommand.ControlBackwards) { reverserKey = Symbols.ArrowDown + ColorCode[Color.Yellow]; } else if (reverserCommand == UserCommand.ControlForwards) { reverserKey = Symbols.ArrowUp + ColorCode[Color.Yellow]; } else { reverserKey = ""; } string reverserIndicator = showMUReverser ? $"{Round(Math.Abs(train.MUReverserPercent))}% " : ""; AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString(locomotive.EngineType == TrainCar.EngineTypes.Steam ? "Reverser" : "Direction"), LastCol = $"{reverserIndicator}{FormatStrings.Catalog.GetParticularString("Reverser", GetStringAttribute.GetPrettyName(locomotive.Direction))}", KeyPressed = reverserKey, SymbolCol = reverserKey, }); } // Throttle { UserCommand?throttleCommand = GetPressedKey(UserCommand.ControlThrottleDecrease, UserCommand.ControlThrottleIncrease); string throttleKey; bool upperLimit = throttleCommand == UserCommand.ControlThrottleIncrease && locomotive.ThrottleController.MaximumValue == trainCar.ThrottlePercent / 100; bool lowerLimit = throttleCommand == UserCommand.ControlThrottleDecrease && trainCar.ThrottlePercent == 0; if (locomotive.DynamicBrakePercent < 1 && (upperLimit || lowerLimit)) { throttleKey = Symbols.End + ColorCode[Color.Yellow]; } else if (locomotive.DynamicBrakePercent > -1) { throttleKey = Symbols.EndLower + ColorCode[Color.Yellow]; } else if (throttleCommand == UserCommand.ControlThrottleIncrease) { throttleKey = Symbols.ArrowUp + ColorCode[Color.Yellow]; } else if (throttleCommand == UserCommand.ControlThrottleDecrease) { throttleKey = Symbols.ArrowDown + ColorCode[Color.Yellow]; } else { throttleKey = ""; } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString(locomotive is MSTSSteamLocomotive ? "Regulator" : "Throttle"), LastCol = $"{Round(locomotive.ThrottlePercent)}%", KeyPressed = throttleKey, SymbolCol = throttleKey, }); } // Cylinder Cocks if (locomotive is MSTSSteamLocomotive steamLocomotive) { string cocksIndicator, cocksKey; if (steamLocomotive.CylinderCocksAreOpen) { cocksIndicator = Viewer.Catalog.GetString("Open") + ColorCode[Color.Orange]; cocksKey = Symbols.ArrowToRight + ColorCode[Color.Yellow]; } else { cocksIndicator = Viewer.Catalog.GetString("Closed") + ColorCode[Color.White]; cocksKey = ""; } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Cylinder cocks"), LastCol = cocksIndicator, KeyPressed = cocksKey, SymbolCol = cocksKey, }); } // Sander if (locomotive.GetSanderOn()) { bool sanderBlocked = locomotive.AbsSpeedMpS > locomotive.SanderSpeedOfMpS; string sanderKey = Symbols.ArrowToRight + ColorCode[Color.Yellow]; AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Sander"), LastCol = sanderBlocked ? Viewer.Catalog.GetString("Blocked") + ColorCode[Color.OrangeRed] : Viewer.Catalog.GetString("On") + ColorCode[Color.Orange], KeyPressed = sanderKey, SymbolCol = sanderKey, }); } else { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Sander"), LastCol = Viewer.Catalog.GetString("Off"), KeyPressed = "", SymbolCol = "", }); } AddSeparator(); // Train Brake multi-lines // TODO: A better algorithm //var brakeStatus = Owner.Viewer.PlayerLocomotive.GetTrainBrakeStatus(); //steam loco string brakeInfoValue = ""; int index = 0; if (trainBrakeStatus.Contains(Viewer.Catalog.GetString("EQ"))) { string brakeKey; switch (GetPressedKey(UserCommand.ControlTrainBrakeDecrease, UserCommand.ControlTrainBrakeIncrease)) { case UserCommand.ControlTrainBrakeDecrease: brakeKey = Symbols.ArrowDown + ColorCode[Color.Yellow]; break; case UserCommand.ControlTrainBrakeIncrease: brakeKey = Symbols.ArrowUp + ColorCode[Color.Yellow]; break; default: brakeKey = ""; break; } brakeInfoValue = trainBrakeStatus.Substring(0, trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("EQ"))).TrimEnd(); AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Train brake"), LastCol = $"{brakeInfoValue}{ColorCode[Color.Cyan]}", KeyPressed = brakeKey, SymbolCol = brakeKey, }); index = trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("EQ")); brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("BC")) - index).TrimEnd(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); if (trainBrakeStatus.Contains(Viewer.Catalog.GetString("EOT"))) { int indexOffset = Viewer.Catalog.GetString("EOT").Length + 1; index = trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("BC")); brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("EOT")) - index).TrimEnd(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); index = trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("EOT")) + indexOffset; brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.Length - index).TrimStart(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); } else { index = trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("BC")); brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.Length - index).TrimEnd(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); } } else if (trainBrakeStatus.Contains(Viewer.Catalog.GetString("Lead"))) { int indexOffset = Viewer.Catalog.GetString("Lead").Length + 1; brakeInfoValue = trainBrakeStatus.Substring(0, trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("Lead"))).TrimEnd(); AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Train brake"), LastCol = $"{brakeInfoValue}{ColorCode[Color.Cyan]}", }); index = trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("Lead")) + indexOffset; if (trainBrakeStatus.Contains(Viewer.Catalog.GetString("EOT"))) { brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("EOT")) - index).TrimEnd(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); index = trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("EOT")) + indexOffset; brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.Length - index).TrimEnd(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); } else { brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.Length - index).TrimEnd(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); } } else if (trainBrakeStatus.Contains(Viewer.Catalog.GetString("BC"))) { brakeInfoValue = trainBrakeStatus.Substring(0, trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("BC"))).TrimEnd(); AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Train brake"), LastCol = $"{brakeInfoValue}{ColorCode[Color.Cyan]}", }); index = trainBrakeStatus.IndexOf(Viewer.Catalog.GetString("BC")); brakeInfoValue = trainBrakeStatus.Substring(index, trainBrakeStatus.Length - index).TrimEnd(); AddLabel(new ListLabel { LastCol = brakeInfoValue, }); } if (showRetainers) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Retainers"), LastCol = $"{train.RetainerPercent} {Viewer.Catalog.GetString(GetStringAttribute.GetPrettyName(train.RetainerSetting))}", }); } if (engineBrakeStatus.Contains(Viewer.Catalog.GetString("BC"))) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Engine brake"), LastCol = engineBrakeStatus.Substring(0, engineBrakeStatus.IndexOf("BC")) + ColorCode[Color.Cyan], }); index = engineBrakeStatus.IndexOf(Viewer.Catalog.GetString("BC")); brakeInfoValue = engineBrakeStatus.Substring(index, engineBrakeStatus.Length - index).TrimEnd(); AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString(""), LastCol = $"{brakeInfoValue}{ColorCode[Color.White]}", }); } else { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Engine brake"), LastCol = $"{engineBrakeStatus}{ColorCode[Color.Cyan]}", }); } if (dynamicBrakeStatus != null && locomotive.IsLeadLocomotive()) { if (locomotive.DynamicBrakePercent >= 0) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Dynamic brake"), LastCol = locomotive.DynamicBrake ? dynamicBrakeStatus : Viewer.Catalog.GetString("Setup") + ColorCode[Color.Cyan], }); } else { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Dynamic brake"), LastCol = Viewer.Catalog.GetString("Off"), }); } } AddSeparator(); if (locomotiveStatus != null) { foreach (string data in locomotiveStatus.Split('\n').Where((string d) => !string.IsNullOrWhiteSpace(d))) { string[] parts = data.Split(new string[] { " = " }, 2, StringSplitOptions.None); string keyPart = parts[0]; string valuePart = parts?[1]; if (Viewer.Catalog.GetString(keyPart).StartsWith(Viewer.Catalog.GetString("Boiler pressure"))) { MSTSSteamLocomotive steamLocomotive2 = (MSTSSteamLocomotive)locomotive; float bandUpper = steamLocomotive2.PreviousBoilerHeatOutBTUpS * 1.025f; // find upper bandwidth point float bandLower = steamLocomotive2.PreviousBoilerHeatOutBTUpS * 0.975f; // find lower bandwidth point - gives a total 5% bandwidth string heatIndicator; if (steamLocomotive2.BoilerHeatInBTUpS > bandLower && steamLocomotive2.BoilerHeatInBTUpS < bandUpper) { heatIndicator = $"{Symbols.SmallDiamond}{ColorCode[Color.White]}"; } else if (steamLocomotive2.BoilerHeatInBTUpS < bandLower) { heatIndicator = $"{Symbols.SmallArrowDown}{ColorCode[Color.Cyan]}"; } else if (steamLocomotive2.BoilerHeatInBTUpS > bandUpper) { heatIndicator = $"{Symbols.SmallArrowUp}{ColorCode[Color.Orange]}"; } else { heatIndicator = ColorCode[Color.White]; } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Boiler pressure"), LastCol = Viewer.Catalog.GetString(valuePart), SymbolCol = heatIndicator, }); } else if (!normalTextMode && Viewer.Catalog.GetString(parts[0]).StartsWith(Viewer.Catalog.GetString("Fuel levels"))) { AddLabel(new ListLabel { FirstCol = keyPart.EndsWith("?") || keyPart.EndsWith("!") ? Viewer.Catalog.GetString(keyPart.Substring(0, keyPart.Length - 3)) : Viewer.Catalog.GetString(keyPart), LastCol = valuePart.Length > 1 ? Viewer.Catalog.GetString(valuePart.Replace(" ", string.Empty)) : "", }); } else if (keyPart.StartsWith(Viewer.Catalog.GetString("Gear"))) { string gearKey; switch (GetPressedKey(UserCommand.ControlGearDown, UserCommand.ControlGearUp)) { case UserCommand.ControlGearDown: gearKey = Symbols.ArrowDown + ColorCode[Color.Yellow]; break; case UserCommand.ControlGearUp: gearKey = Symbols.ArrowUp + ColorCode[Color.Yellow]; break; default: gearKey = ""; break; } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString(keyPart), LastCol = valuePart != null ? Viewer.Catalog.GetString(valuePart) : "", KeyPressed = gearKey, SymbolCol = gearKey, }); } else if (parts.Contains(Viewer.Catalog.GetString("Pantographs"))) { string pantoKey; switch (GetPressedKey(UserCommand.ControlPantograph1)) { case UserCommand.ControlPantograph1: string arrow = parts[1].StartsWith(Viewer.Catalog.GetString("Up")) ? Symbols.ArrowUp : Symbols.ArrowDown; pantoKey = arrow + ColorCode[Color.Yellow]; break; default: pantoKey = ""; break; } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString(keyPart), LastCol = valuePart != null ? Viewer.Catalog.GetString(valuePart) : "", KeyPressed = pantoKey, SymbolCol = pantoKey, }); } else if (parts.Contains(Viewer.Catalog.GetString("Engine"))) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString(keyPart), LastCol = valuePart != null ? $"{Viewer.Catalog.GetString(valuePart)}{ColorCode[Color.White]}" : "", }); } else { AddLabel(new ListLabel { FirstCol = keyPart.EndsWith("?") || keyPart.EndsWith("!") ? Viewer.Catalog.GetString(keyPart.Substring(0, keyPart.Length - 3)) : Viewer.Catalog.GetString(keyPart), LastCol = valuePart != null ? Viewer.Catalog.GetString(valuePart) : "", }); } } } AddSeparator(); if (normalTextMode) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("FPS"), LastCol = $"{Math.Floor(viewer.RenderProcess.FrameRate.SmoothedValue)}", }); } // Messages // Autopilot bool autopilot = locomotive.Train.TrainType == Train.TRAINTYPE.AI_PLAYERHOSTING; AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Autopilot"), LastCol = autopilot ? Viewer.Catalog.GetString("On") + ColorCode[Color.Yellow] : Viewer.Catalog.GetString("Off"), }); // Grate limit if (locomotive is MSTSSteamLocomotive steamLocomotive1) { if (steamLocomotive1.IsGrateLimit && steamLocomotive1.GrateCombustionRateLBpFt2 > steamLocomotive1.GrateLimitLBpFt2) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Grate limit"), LastCol = Viewer.Catalog.GetString("Exceeded") + ColorCode[Color.OrangeRed], }); } else { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Grate limit") + ColorCode[Color.Black], LastCol = Viewer.Catalog.GetString("Normal") + ColorCode[Color.Black], }); } } else { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Grate limit") + ColorCode[Color.Black], LastCol = Viewer.Catalog.GetString("-") + ColorCode[Color.Black], }); } // Wheel if (train.IsWheelSlip) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Wheel"), LastCol = Viewer.Catalog.GetString("slip") + ColorCode[Color.OrangeRed], }); } else if (train.IsWheelSlipWarninq) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Wheel"), LastCol = Viewer.Catalog.GetString("slip warning") + ColorCode[Color.Yellow], }); } else if (train.IsBrakeSkid) { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Wheel"), LastCol = Viewer.Catalog.GetString("skid") + ColorCode[Color.OrangeRed], }); } else { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Wheel") + ColorCode[Color.Black], LastCol = Viewer.Catalog.GetString("Normal") + ColorCode[Color.Black], }); } // Doors var wagon = (MSTSWagon)locomotive; if (wagon.DoorLeftOpen || wagon.DoorRightOpen) { var status = new List <string>(); bool flipped = locomotive.GetCabFlipped(); if (wagon.DoorLeftOpen) { status.Add(Viewer.Catalog.GetString(Viewer.Catalog.GetString(flipped ? "Right" : "Left"))); } if (wagon.DoorRightOpen) { status.Add(Viewer.Catalog.GetString(Viewer.Catalog.GetString(flipped ? "Left" : "Right"))); } AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Doors open"), LastCol = string.Join(" ", status) + ColorCode[locomotive.AbsSpeedMpS > 0.1f ? Color.OrangeRed : Color.Yellow], }); } else { AddLabel(new ListLabel { FirstCol = Viewer.Catalog.GetString("Doors open") + ColorCode[Color.Black], LastCol = Viewer.Catalog.GetString("Closed") + ColorCode[Color.Black], }); } AddLabel(new ListLabel()); return(labels); }
public ToggleMUCommand(CommandLog log, MSTSLocomotive receiver, bool toState) : base(log, toState) { Receiver = receiver; Redo(); }
public ScriptedBrakeController Clone(MSTSLocomotive locomotive) { return(new ScriptedBrakeController(this, locomotive)); }
public override void CorrectMaxCylPressurePSI(MSTSLocomotive loco) { }
public override void Update(float elapsedClockSeconds) { MSTSLocomotive lead = (MSTSLocomotive)Car.Train.LeadLocomotive; float BrakemanBrakeSettingValue = 0; float EngineBrakeSettingValue = 0; ManualBrakingDesiredFraction = 0; SteamBrakeCompensation = 1.0f; // Process manual braking on all cars if (lead != null) { BrakemanBrakeSettingValue = lead.BrakemanBrakeController.CurrentValue; } ManualBrakingDesiredFraction = BrakemanBrakeSettingValue * ManualMaxBrakeValue; if (ManualBrakingCurrentFraction < ManualBrakingDesiredFraction) { ManualBrakingCurrentFraction += ManualMaxApplicationRateValuepS; if (ManualBrakingCurrentFraction > ManualBrakingDesiredFraction) { ManualBrakingCurrentFraction = ManualBrakingDesiredFraction; } } else if (ManualBrakingCurrentFraction > ManualBrakingDesiredFraction) { ManualBrakingCurrentFraction -= ManualReleaseRateValuepS; if (ManualBrakingCurrentFraction < 0) { ManualBrakingCurrentFraction = 0; } } BrakeForceFraction = ManualBrakingCurrentFraction / ManualMaxBrakeValue; // If car is a locomotive or tender, then process engine brake if (Car.WagonType == MSTSWagon.WagonTypes.Engine || Car.WagonType == MSTSWagon.WagonTypes.Tender) // Engine brake { if (lead != null) { EngineBrakeSettingValue = lead.EngineBrakeController.CurrentValue; if (lead.SteamEngineBrakeFitted) { LocomotiveSteamBrakeFitted = true; EngineBrakeDesiredFraction = EngineBrakeSettingValue * lead.MaxBoilerPressurePSI; } else { EngineBrakeDesiredFraction = EngineBrakeSettingValue * ManualMaxBrakeValue; } if (EngineBrakingCurrentFraction < EngineBrakeDesiredFraction) { EngineBrakingCurrentFraction += elapsedClockSeconds * lead.EngineBrakeController.ApplyRatePSIpS; if (EngineBrakingCurrentFraction > EngineBrakeDesiredFraction) { EngineBrakingCurrentFraction = EngineBrakeDesiredFraction; } } else if (EngineBrakingCurrentFraction > EngineBrakeDesiredFraction) { EngineBrakingCurrentFraction -= elapsedClockSeconds * lead.EngineBrakeController.ReleaseRatePSIpS; if (EngineBrakingCurrentFraction < 0) { EngineBrakingCurrentFraction = 0; } } if (lead.SteamEngineBrakeFitted) { SteamBrakeCompensation = lead.BoilerPressurePSI / lead.MaxBoilerPressurePSI; SteamBrakePressurePSI = EngineBrakeSettingValue * SteamBrakeCompensation * lead.MaxBoilerPressurePSI; SteamBrakeCylinderPressurePSI = EngineBrakingCurrentFraction * SteamBrakeCompensation; // For display purposes BrakeForceFraction = EngineBrakingCurrentFraction / lead.MaxBoilerPressurePSI; // Manual braking value overwritten by engine calculated value } else { BrakeForceFraction = EngineBrakingCurrentFraction / ManualMaxBrakeValue; } } } float f; if (!Car.BrakesStuck) { f = Car.MaxBrakeForceN * Math.Min(BrakeForceFraction, 1); if (f < Car.MaxHandbrakeForceN * HandbrakePercent / 100) { f = Car.MaxHandbrakeForceN * HandbrakePercent / 100; } } else { f = Math.Max(Car.MaxBrakeForceN, Car.MaxHandbrakeForceN / 2); } Car.BrakeRetardForceN = f * Car.BrakeShoeRetardCoefficientFrictionAdjFactor; // calculates value of force applied to wheel, independent of wheel skid if (Car.BrakeSkid) // Test to see if wheels are skiding to excessive brake force { Car.BrakeForceN = f * Car.SkidFriction; // if excessive brakeforce, wheel skids, and loses adhesion } else { Car.BrakeForceN = f * Car.BrakeShoeCoefficientFrictionAdjFactor; // In advanced adhesion model brake shoe coefficient varies with speed, in simple model constant force applied as per value in WAG file, will vary with wheel skid. } }
/// Initializes brakes also if Speed != 0; directly used by keyboard command internal void UnconditionalInitializeBrakes() { if (simulator.Settings.SimpleControlPhysics && LeadLocomotiveIndex >= 0) // If brake and control set to simple, and a locomotive present, then set all cars to same brake system as the locomotive { MSTSLocomotive lead = (MSTSLocomotive)Cars[LeadLocomotiveIndex]; if (lead.TrainBrakeController != null) { foreach (MSTSWagon car in Cars) { if (lead.CarBrakeSystemType != car.CarBrakeSystemType) // Test to see if car brake system is the same as the locomotive { // If not, change so that they are compatible car.CarBrakeSystemType = lead.CarBrakeSystemType; if (lead.BrakeSystem is VacuumSinglePipe) { car.MSTSBrakeSystem = new VacuumSinglePipe(car); } else if (lead.BrakeSystem is AirTwinPipe) { car.MSTSBrakeSystem = new AirTwinPipe(car); } else if (lead.BrakeSystem is AirSinglePipe) { car.MSTSBrakeSystem = new AirSinglePipe(car); // if emergency reservoir has been set on lead locomotive then also set on trailing cars if (lead.EmergencyReservoirPresent) { car.EmergencyReservoirPresent = lead.EmergencyReservoirPresent; } } else if (lead.BrakeSystem is EPBrakeSystem) { car.MSTSBrakeSystem = new EPBrakeSystem(car); } else if (lead.BrakeSystem is SingleTransferPipe) { car.MSTSBrakeSystem = new SingleTransferPipe(car); } else { throw new Exception("Unknown brake type"); } car.MSTSBrakeSystem.InitializeFromCopy(lead.BrakeSystem); Trace.TraceInformation("Car and Locomotive Brake System Types Incompatible on Car {0} - Car brakesystem type changed to {1}", car.CarID, car.CarBrakeSystemType); } } } } simulator.Confirmer?.Confirm(CabControl.InitializeBrakes, CabSetting.Off); float maxPressurePSI = 90; float fullServPressurePSI = 64; if (FirstCar?.BrakeSystem is VacuumSinglePipe) { maxPressurePSI = 21; fullServPressurePSI = 16; } if (LeadLocomotiveIndex >= 0) { MSTSLocomotive lead = (MSTSLocomotive)Cars[LeadLocomotiveIndex]; if (lead.TrainBrakeController != null) { maxPressurePSI = lead.TrainBrakeController.MaxPressurePSI; fullServPressurePSI = lead.BrakeSystem is VacuumSinglePipe ? 16 : maxPressurePSI - lead.TrainBrakeController.FullServReductionPSI; EqualReservoirPressurePSIorInHg = Math.Min(maxPressurePSI, EqualReservoirPressurePSIorInHg); (double pressurePSI, double epControllerState) = lead.TrainBrakeController.UpdatePressure(EqualReservoirPressurePSIorInHg, BrakeLine4, 1000); BrakeLine4 = (float)epControllerState; EqualReservoirPressurePSIorInHg = (float)Math.Max(pressurePSI, fullServPressurePSI); } if (lead.EngineBrakeController != null) { BrakeLine3PressurePSI = (float)lead.EngineBrakeController.UpdateEngineBrakePressure(BrakeLine3PressurePSI, 1000); } if (lead.DynamicBrakeController != null) { MUDynamicBrakePercent = lead.DynamicBrakeController.Update(1000) * 100; if (MUDynamicBrakePercent == 0) { MUDynamicBrakePercent = -1; } } BrakeLine2PressurePSI = lead.MaximumMainReservoirPipePressurePSI; ConnectBrakeHoses(); } else { EqualReservoirPressurePSIorInHg = BrakeLine2PressurePSI = BrakeLine3PressurePSI = 0; // Initialize static consists airless for allowing proper shunting operations, // but set AI trains pumped up with air. if (TrainType == TrainType.Static) { maxPressurePSI = 0; } BrakeLine4 = -1; } foreach (TrainCar car in Cars) { car.BrakeSystem.Initialize(LeadLocomotiveIndex < 0, maxPressurePSI, fullServPressurePSI, false); } }
public abstract bool IsBraking(); // return true if the wagon is braking above a certain threshold public abstract void CorrectMaxCylPressurePSI(MSTSLocomotive loco); // corrects max cyl pressure when too high