/// <summary>Is called every frame.</summary> /// <param name="data">The data.</param> /// <param name="blocking">Whether the device is blocked or will block subsequent devices.</param> internal override void Elapse(ElapseData data, ref bool blocking) { //Is rain and windscreen wipers enabled? if (this.Enabled) { //First pull a random unlit drop from our array var unuseddrops = new List <int>(); unuseddrops.Clear(); int count = 0; int unusedlength = 0; foreach (bool x in droparray) { count++; if (x == false) { //If this drop is not yet lit, add it's index to the unused drops unuseddrops.Add(count); unusedlength++; } } if (count == 0) { //Nothing to pick nextdrop = -1; } else { //Pick a random drop from the unused drops nextdrop = unuseddrops[Plugin.Random.Next(0, unusedlength - 1)]; } //If we're raining, if (israining == true) { //Generate a random drop interval var dev = (int)(0.4 * 2000 / rainintensity); int dropinterval = (2000 / rainintensity) + (Plugin.Random.Next(dev, dev * 2)); droptimer += (int)data.ElapsedTime.Milliseconds; //If we're past the drop interval, try to add a drop if (droptimer > dropinterval && nextdrop != -1) { droptimer = 0; droparray[nextdrop] = true; //Play random drop sound if (dropsound1 != -1) { SoundManager.Play((dropsound1 + Plugin.Random.Next(0, dropsound2)), 1.0, 1.0, false); } } else if (droptimer > dropinterval && nextdrop == -1) { //Reset timer and play random drop sound droptimer = 0; if (dropsound1 != -1) { SoundManager.Play((dropsound1 + Plugin.Random.Next(0, dropsound2)), 1.0, 1.0, false); } } } //This section of code moves our windscreen wipers if (currentwiperposition > 0 && currentwiperposition < 100) { //Always move wiper if not at rest- No need to set direction movewiper(data.ElapsedTime.Milliseconds); wiperheldtimer = 0.0; } else if (currentwiperposition == 0 && currentwiperposition == wiperholdposition) { //Set new direction wiperdirection = 1; if (wiperspeed == 0) { heldwipers = true; } else if (wiperspeed == 1) { wiperheldtimer += data.ElapsedTime.Milliseconds; if (wiperheldtimer > wiperdelay) { heldwipers = false; wiperheldtimer = 0.0; } else { heldwipers = true; } } else { heldwipers = false; } if (heldwipers == false) { movewiper(data.ElapsedTime.Milliseconds); } } else if (currentwiperposition == 0 && currentwiperposition != wiperholdposition) { //Set new direction wiperdirection = 1; movewiper(data.ElapsedTime.Milliseconds); } else if (currentwiperposition == 100 && currentwiperposition != wiperholdposition) { //Set new direction wiperdirection = -1; movewiper(data.ElapsedTime.Milliseconds); } else if (currentwiperposition == 100 && currentwiperposition == wiperholdposition) { //Set new direction wiperdirection = -1; if (wiperspeed == 0) { heldwipers = true; } else if (wiperspeed == 1) { wiperheldtimer += data.ElapsedTime.Milliseconds; if (wiperheldtimer > wiperdelay) { heldwipers = false; wiperheldtimer = 0.0; } else { heldwipers = true; } } else { heldwipers = false; } if (heldwipers == false) { movewiper(data.ElapsedTime.Milliseconds); } } else { //Set new direction wiperdirection = 1; movewiper(data.ElapsedTime.Milliseconds); } //This section of code plays the wiper sounds { int sound; //Figure out if we should play the wetwipe or the drywipe sound if ((double)unusedlength / (double)droparray.Length > 0.8 && wetwipesound != 1) { sound = drywipesound; } else { sound = wetwipesound; } if (currentwiperposition == 1 && wiperdirection == 1) { if (wiperholdposition == 0) { if (wipersoundbehaviour == 0) { SoundManager.Play(sound, 1.0, 1.0, false); } } else { if (wipersoundbehaviour != 0) { SoundManager.Play(sound, 1.0, 1.0, false); } } } else if (currentwiperposition == 99 && wiperdirection == -1) { if (wiperholdposition == 0) { if (wipersoundbehaviour != 0) { SoundManager.Play(sound, 1.0, 1.0, false); } } else { if (wipersoundbehaviour == 0) { SoundManager.Play(sound, 1.0, 1.0, false); } } } } //This section of code should delete drops if (heldwipers == false) { //int dropremove = Math.Min(49, (int)(currentwiperposition / (100 / numberofdrops))); int dropremove = Math.Min(numberofdrops - 1, (int)(currentwiperposition / (100.0 / numberofdrops))); droparray[dropremove] = false; } //Light Windscreen Drops { int i = 0; foreach (bool x in droparray) { i++; if (x == true) { this.Train.Panel[(i + dropstartindex - 1)] = 1; } else { this.Train.Panel[(i + dropstartindex - 1)] = 0; } } } //Animate Windscreen Wiper if (wiperindex != -1) { this.Train.Panel[wiperindex] = currentwiperposition; } //Animate Windscreen Wiper Switch if (wiperswitchindex != -1) { this.Train.Panel[wiperswitchindex] = wiperspeed; } } }
/// <summary>Is called every frame.</summary> /// <param name="data">The data.</param> /// <param name="blocking">Whether the device is blocked or will block subsequent devices.</param> internal override void Elapse(ElapseData data, ref bool blocking) { if (this.Enabled) { if (this.SafetyState != SafetyStates.Isolated) { if (this.suppressionactive) { /* Cancel any suppression which is in effect, if the train is not within range of the last suppression magnet location */ if (Train.CurrentSpeed > 0) { if (Train.TrainLocation > this.suppressionlocation + 2) { this.suppressionlocation = 0; this.suppressionactive = false; } } else { if (Train.TrainLocation < this.suppressionlocation - 2) { this.suppressionlocation = 0; this.suppressionactive = false; } } } else if (this.MySafetyState == SafetyStates.Primed) { /* An AWS magnet south pole has primed the AWS */ this.SunflowerState = SunflowerStates.Clear; this.detectiontimer = this.detectiontimer + (int)data.ElapsedTime.Milliseconds; if (this.detectiontimer > 1000) { /* No north pole has been detected within the timeout period, so reset the detection timer and issue an AWS warning */ this.detectiontimer = 0; this.MySafetyState = SafetyStates.CancelTimerActive; } } else if (this.MySafetyState == SafetyStates.Clear) { /* The AWS indicates a clear signal */ this.Reset(); this.SunflowerState = SunflowerStates.Clear; if (this.ClearSound != -1) { SoundManager.Play(ClearSound, 1.0, 1.0, false); } if (this.WarningSound != -1) { SoundManager.Stop(WarningSound); } } else if (this.MySafetyState == SafetyStates.CancelTimerActive) { /* An AWS warning has been issued */ if (this.WarningSound != -1) { SoundManager.Play(WarningSound, 1.0, 1.0, true); } this.SunflowerState = SunflowerStates.Clear; this.canceltimer -= (int)data.ElapsedTime.Milliseconds; if (this.canceltimer < 0) { this.canceltimer = 0; this.MySafetyState = SafetyStates.CancelTimerExpired; } } else if (this.MySafetyState == SafetyStates.WarningAcknowledged) { /* An AWS warning was acknowledged in time */ if (this.WarningSound != -1) { SoundManager.Stop(WarningSound); } this.Reset(); } else if (this.MySafetyState == SafetyStates.CancelTimerExpired) { /* An AWS warning was not acknowledged in time */ if (this.WarningSound != -1) { SoundManager.Play(WarningSound, 1.0, 1.0, true); } if (Train.TPWS.Enabled) { Train.TPWS.IssueBrakeDemand(); } else { if (Train.TractionManager.PowerCutoffDemanded == false) { Train.TractionManager.DemandPowerCutoff("Power cutoff was demanded by the AWS due to a warning not being acknowledged in time"); } if (Train.TractionManager.CurrentInterventionBrakeNotch != this.Train.Specs.BrakeNotches + 1) { Train.TractionManager.DemandBrakeApplication(this.Train.Specs.BrakeNotches + 1, "Emergency brakes were demanded by the AWS due to a warning not being acknowledged in time"); } } } else if (this.MySafetyState == SafetyStates.SelfTest) { blinktimer += (int)data.ElapsedTime.Milliseconds; if (blinktimer > 1200) { if (Train.AWS.WarningSound != -1) { if (startuphorntriggered == false) { SoundManager.Play(WarningSound, 1.0, 1.0, true); startuphorntriggered = true; } } } else if (blinktimer > 1000) { this.SunflowerState = SunflowerStates.Clear; } else if (blinktimer > 400) { this.SunflowerState = SunflowerStates.Warn; } } else if (this.MySafetyState == SafetyStates.TPWSAWSBrakeDemandIssued) { /* The TPWS issued an AWS Brake Demand due to the AWS not being acknowledged in time */ if (TPWSWarningSound != -1) { SoundManager.Play(TPWSWarningSound, 1.0, 1.0, true); } } } else if (this.SafetyState == SafetyStates.Isolated) { if (WarningSound != -1) { if (SoundManager.IsPlaying(WarningSound)) { SoundManager.Stop(WarningSound); } } if (TPWSWarningSound != -1) { if (SoundManager.IsPlaying(TPWSWarningSound)) { SoundManager.Stop(TPWSWarningSound); } } this.canceltimer = (int)this.canceltimeout; this.SunflowerState = SunflowerStates.Warn; } /* Set the state of the AWS Sunflower instrument */ if (awsindicator != -1) { if (this.SunflowerState == SunflowerStates.Warn) { this.Train.Panel[awsindicator] = 1; } else { this.Train.Panel[awsindicator] = 0; } } } //Set the state of the cancel button panel index //As this is a physical button, it can be pressed at any time if (CancelButtonIndex != -1) { if (CancelButtonPressed) { this.Train.Panel[CancelButtonIndex] = 1; } else { this.Train.Panel[CancelButtonIndex] = 0; } } }
/// <summary>Is called every frame.</summary> /// <param name="data">The data.</param> /// <param name="blocking">Whether the device is blocked or will block subsequent devices.</param> internal override void Elapse(ElapseData data, ref bool blocking) { //Required to reset the max notch before each frame this.Train.TractionManager.SetMaxPowerNotch(this.Train.Specs.PowerNotches, false); //Check we've got a maximum temperature and a heating part if (overheat != 0 && heatingpart != 0) { this.heatingtimer += data.ElapsedTime.Milliseconds; if (heatingpart == 0 || overheat == 0) { //No heating part or overheat temperature not set this.temperature = 0.0; this.heatingtimer = 0.0; } else if (heatingpart == 1) { //Heats based upon power notch if (this.heatingtimer > 1000) { this.heatingtimer = 0.0; if (Train.Handles.PowerNotch == 0) { currentheat = HeatingRates[0]; } else if (Train.Handles.PowerNotch < HeatingRates.Length) { currentheat = HeatingRates[Train.Handles.PowerNotch]; } else { currentheat = HeatingRates[HeatingRates.Length - 1]; } temperature += currentheat; } } else { //Heats based upon RPM- Not on an electric loco! this.temperature = 0.0; this.heatingtimer = 0.0; } //Keep temperature below max & above zero if (temperature > overheat) { temperature = overheat; if (overheatresult == 1 && Train.TractionManager.EngineOverheated == false) { DemandPowerCutoff("Power cutoff was demanded due to the electric engine overheating"); Train.TractionManager.EngineOverheated = true; } } else if (temperature < overheat && temperature > 0) { if (BreakerTripped == false && ((FrontPantograph.State == PantographStates.Disabled && RearPantograph.State == PantographStates.OnService) || (RearPantograph.State == PantographStates.Disabled && FrontPantograph.State == PantographStates.OnService))) { ResetPowerCutoff("Power cutoff was released due to the electric engine temperature returning to safe levels"); } Train.TractionManager.EngineOverheated = false; } else if (temperature < 0) { temperature = 0; } } { //If we're in a power gap, check whether we have a pickup available if (PowerGap) { int new_power; //First check to see whether the first pickup is in the neutral section if (Train.TrainLocation - PickupLocations[0] > firstmagnet && Train.TrainLocation - PickupLocations[0] < nextmagnet) { //Cycle through the other pickups int j = 0; foreach (int t in PickupLocations) { if (Train.TrainLocation - t < firstmagnet) { j++; } } switch (PowerGapBehaviour) { case PowerGapBehaviour.ProportionalReduction: //Reduce max throttle by percentage of how many pickups are in the gap double throttlemultiplier = (double)j / (double)PickupLocations.Length; new_power = (int)(this.Train.Specs.PowerNotches * throttlemultiplier); this.Train.TractionManager.SetMaxPowerNotch(new_power, false); //data.Handles.PowerNotch = new_power; break; case PowerGapBehaviour.InactiveAny: //Kill traction power if any pickup is on the gap if (j != 0 && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to a neutral gap in the overhead line"); } break; case PowerGapBehaviour.InactiveAll: if (j == PickupLocations.Length && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to a neutral gap in the overhead line"); } break; } } //Now, check to see if the last pickup is in the neutral section else if (Train.TrainLocation - PickupLocations[PickupLocations.Length - 1] > firstmagnet && Train.TrainLocation - PickupLocations[PickupLocations.Length - 1] < nextmagnet) { //Cycle through the other pickups int j = 0; foreach (int t in PickupLocations) { if (Train.TrainLocation - t < firstmagnet) { j++; } } switch (PowerGapBehaviour) { case PowerGapBehaviour.ProportionalReduction: //Reduce max throttle by percentage of how many pickups are in the gap double throttlemultiplier = (double)j / (double)PickupLocations.Length; new_power = (int)(this.Train.Specs.PowerNotches * throttlemultiplier); this.Train.TractionManager.SetMaxPowerNotch(new_power, false); //data.Handles.PowerNotch = new_power; break; case PowerGapBehaviour.InactiveAny: //Kill traction power if any pickup is on the gap if (j != 0 && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to a neutral gap in the overhead line"); } break; case PowerGapBehaviour.InactiveAll: //Kill traction power when all pickups are on the gap if (j == PickupLocations.Length && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to a neutral gap in the overhead line"); } break; } } //Neither the first or last pickups are in the power gap, reset the power //However also check that the breaker has not been tripped by a UKTrainSys beacon else if (nextmagnet != 0 && (Train.TrainLocation - PickupLocations[PickupLocations.Length - 1]) > nextmagnet && (Train.TrainLocation - PickupLocations[0]) > nextmagnet) { PowerGap = false; if (LegacyPowerCut == true) { //Reset legacy power cutoff state and retrip breaker Train.ElectricEngine.TripBreaker(); LegacyPowerCut = false; } if (BreakerTripped == false && ((FrontPantograph.State == PantographStates.Disabled && RearPantograph.State == PantographStates.OnService) || (RearPantograph.State == PantographStates.Disabled && FrontPantograph.State == PantographStates.OnService))) { ResetPowerCutoff("Power cutoff was released due to leaving the neutral gap"); } } //If the final pickup has passed the UKTrainSys standard power gap location else if (Train.TrainLocation - PickupLocations[PickupLocations.Length - 1] < lastmagnet) { PowerGap = false; } } //This section of code handles a UKTrainSys compatible ACB/VCB // //If the ACB/VCB has tripped, always demand power cutoff if (BreakerTripped == true && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to the ACB/VCB state"); } //If we're in a power gap, also always demand power cutoff else if (BreakerTripped == false && PowerGap == true && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to a neutral gap in the overhead line"); } //If the ACB/VCB has now been reset with a pantograph available & we're not in a powergap reset traction power if (BreakerTripped == false && PowerGap == false && (FrontPantograph.State == PantographStates.OnService || RearPantograph.State == PantographStates.OnService)) { ResetPowerCutoff("Power cutoff was released due to the availability of overhead power"); } } { //This section of code handles raising the pantographs and the alarm state // //If both pantographs are lowered or disabled, then there are no line volts if ((FrontPantograph.State == PantographStates.Lowered || FrontPantograph.State == PantographStates.Disabled) && (RearPantograph.State == PantographStates.Lowered || RearPantograph.State == PantographStates.Disabled) && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to no available pantographs"); PowerGap = true; } //If the powergap behaviour cuts power when *any* pantograph is disabled / lowered // //Line volts is lit, but power is still cut off else if (FrontPantograph.State != PantographStates.Disabled && RearPantograph.State != PantographStates.Disabled && (FrontPantograph.State != PantographStates.OnService || RearPantograph.State != PantographStates.OnService) && PowerGapBehaviour == PowerGapBehaviour.InactiveAny && Train.TractionManager.PowerCutoffDemanded == false) { DemandPowerCutoff("Power cutoff was demanded due to no available pantographs"); } //Pantographs //One pantograph has been raised, and the line volts indicator has lit, but the timer is active //Power cutoff is still in force FrontPantograph.Update(data.ElapsedTime.Milliseconds); RearPantograph.Update(data.ElapsedTime.Milliseconds); if ((FrontPantograph.State == PantographStates.RaisedTimer && RearPantograph.State != PantographStates.OnService) || (RearPantograph.State == PantographStates.RaisedTimer && FrontPantograph.State != PantographStates.OnService)) { PowerGap = false; DemandPowerCutoff(null); } if (Train.CurrentSpeed > AutomaticPantographLowerSpeed) { //Automatic pantograph lowering switch (PantographLoweringMode) { case AutomaticPantographLoweringModes.LowerAll: //In lower all mode, we don't need to check anything if (FrontPantograph.State == PantographStates.OnService || FrontPantograph.State == PantographStates.RaisedTimer || FrontPantograph.State == PantographStates.VCBReady) { FrontPantograph.Lower(true); } if (RearPantograph.State == PantographStates.OnService || RearPantograph.State == PantographStates.RaisedTimer || RearPantograph.State == PantographStates.VCBReady) { RearPantograph.Lower(true); } Train.DebugLogger.LogMessage("Automatically lowered all pantographs due to reaching the setspeed."); break; case AutomaticPantographLoweringModes.LowerFront: if (RearPantograph.State == PantographStates.OnService) { if (FrontPantograph.State == PantographStates.OnService || FrontPantograph.State == PantographStates.RaisedTimer || FrontPantograph.State == PantographStates.VCBReady) { FrontPantograph.Lower(true); Train.DebugLogger.LogMessage("Automatically lowered the front pantograph due to reaching the setspeed."); } } break; case AutomaticPantographLoweringModes.LowerRear: if (FrontPantograph.State == PantographStates.OnService) { if (RearPantograph.State == PantographStates.OnService || RearPantograph.State == PantographStates.RaisedTimer || RearPantograph.State == PantographStates.VCBReady) { RearPantograph.Lower(true); Train.DebugLogger.LogMessage("Automatically lowered the rear pantograph due to reaching the setspeed."); } } break; case AutomaticPantographLoweringModes.LowerFrontRegardless: if (FrontPantograph.State == PantographStates.OnService || FrontPantograph.State == PantographStates.RaisedTimer || FrontPantograph.State == PantographStates.VCBReady) { FrontPantograph.Lower(true); Train.DebugLogger.LogMessage("Automatically lowered the front pantograph due to reaching the setspeed."); } break; case AutomaticPantographLoweringModes.LowerRearRegardless: if (RearPantograph.State == PantographStates.OnService || RearPantograph.State == PantographStates.RaisedTimer || RearPantograph.State == PantographStates.VCBReady) { RearPantograph.Lower(true); Train.DebugLogger.LogMessage("Automatically lowered the rear pantograph due to reaching the setspeed."); } break; } } } //This section of code runs the power notch loop sound if (powerloopsound != -1 && data.Handles.PowerNotch != 0) { if (BreakerTripped == false) { //Start the timer powerlooptimer += data.ElapsedTime.Milliseconds; if (powerlooptimer > powerlooptime && powerloop == false) { //Start playback and reset our conditions powerloop = true; SoundManager.Play(powerloopsound, 1.0, 1.0, true); } else if (powerloop == false) { SoundManager.Stop(powerloopsound); } } else { SoundManager.Stop(powerloopsound); } } else if (powerloopsound != -1 && this.Train.Handles.PowerNotch == 0) { SoundManager.Stop(powerloopsound); } //This section of code runs the breaker loop sound if (breakerloopsound != -1 && BreakerTripped == false) { if (!PowerGap && SoundManager.IsPlaying(breakerloopsound) == false) { breakerlooptimer += data.ElapsedTime.Milliseconds; if (breakerlooptimer > breakerlooptime) { SoundManager.Play(breakerloopsound, 1.0, 1.0, true); breakerlooptimer = 0.0; } } } else if (breakerloopsound != -1 && BreakerTripped == true) { SoundManager.Stop(breakerloopsound); breakerlooptimer = 0.0; } //Panel Indicies { //Ammeter if (Ammeter.PanelIndex != -1) { if (PowerGap == true || BreakerTripped == true || Train.TractionManager.PowerCutoffDemanded == true) { this.Train.Panel[Ammeter.PanelIndex] = 0; } else { this.Train.Panel[Ammeter.PanelIndex] = Ammeter.GetCurrentValue(); } } //Line Volts indicator if (powerindicator != -1) { if (!PowerGap) { this.Train.Panel[powerindicator] = 1; } else { this.Train.Panel[powerindicator] = 0; } } //ACB/VCB Breaker Indicator if (breakerindicator != -1) { if (!BreakerTripped) { this.Train.Panel[breakerindicator] = 1; } else { this.Train.Panel[breakerindicator] = 0; } } if (thermometer != -1) { this.Train.Panel[(thermometer)] = (int)temperature; } if (overheatindicator != -1) { if (temperature > overheatwarn) { this.Train.Panel[(overheatindicator)] = 1; } else { this.Train.Panel[(overheatindicator)] = 0; } } //Pantograph Indicators if (FrontPantograph.PanelIndex != -1) { if (FrontPantograph.Raised() == true) { this.Train.Panel[FrontPantograph.PanelIndex] = 1; } else { this.Train.Panel[FrontPantograph.PanelIndex] = 0; } } if (RearPantograph.PanelIndex != -1) { if (RearPantograph.Raised() == true) { this.Train.Panel[RearPantograph.PanelIndex] = 1; } else { this.Train.Panel[RearPantograph.PanelIndex] = 0; } } } //Sounds { if (overheatalarm != -1) { if (temperature > overheatalarm) { SoundManager.Play(overheatalarm, 1.0, 1.0, true); } else { SoundManager.Stop(overheatalarm); } } } //Pass information to the advanced driving window if (AdvancedDriving.CheckInst != null) { TractionManager.debuginformation[14] = Convert.ToString(FrontPantograph.State); TractionManager.debuginformation[15] = Convert.ToString(RearPantograph.State); TractionManager.debuginformation[16] = Convert.ToString(!BreakerTripped); TractionManager.debuginformation[17] = Convert.ToString(!PowerGap); } }