/// <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); //If reverser is put into neutral when moving, block the gears if (reversercontrol != 0 && Train.CurrentSpeed > 0 && Train.Handles.Reverser == 0) { Train.DieselEngine.gearsblocked = true; } if (!nogears) { //Set gear ratio for current gear if (CurrentGear == 0) { gearratio = 0; } else if (CurrentGear <= geararray.Length) { gearratio = geararray[CurrentGear - 1]; } else { gearratio = geararray[geararray.Length - 1]; } //Set fade in ratio for current gear if (CurrentGear == 0) { fadeinratio = 0; } else if (CurrentGear <= gearfadeinarray.Length) { fadeinratio = gearfadeinarray[CurrentGear - 1]; } else { fadeinratio = gearfadeinarray[gearfadeinarray.Length - 1]; } //Set fade out ratio for current gear if (CurrentGear == 0) { fadeoutratio = 0; } else if (CurrentGear >= gearfadeoutarray.Length) { fadeoutratio = gearfadeoutarray[CurrentGear - 1]; } else { fadeoutratio = gearfadeoutarray[gearfadeoutarray.Length - 1]; } //If the fade in and fade out ratios would make this gear not work, set them both to zero if (fadeinratio + fadeoutratio >= 1000) { fadeinratio = 0; fadeoutratio = 0; } //Set current revolutions per minute currentrevs = Math.Max(0, Math.Min(1000, Train.CurrentSpeed * gearratio)); //Now calculate the maximumum power notch int power_limit; if (currentrevs < fadeinratio) { power_limit = (int)((float)currentrevs / fadeinratio * this.Train.Specs.PowerNotches); } else if (currentrevs > 1000 - fadeoutratio) { power_limit = (int)(this.Train.Specs.PowerNotches - (float)(currentrevs - (1000 - fadeoutratio)) / fadeoutratio * this.Train.Specs.PowerNotches); } else { power_limit = this.Train.Specs.PowerNotches; } //Next we need to set the gears //Manual gears are handled in the KeyUp function //Automatic gears are handled here if (this.Train.TractionManager.AutomaticAdvancedFunctions == true) { if (Train.DieselEngine.gearsblocked == true) { power_limit = 0; //Stop, drop to N with no power applied and the gears will unblock if (Train.CurrentSpeed == 0 && Train.Handles.Reverser == 0 && Train.Handles.PowerNotch == 0) { Train.DieselEngine.gearsblocked = false; } } //Test if all handles are in a position for a gear to be activated if (Train.Handles.Reverser != 0 && Train.Handles.PowerNotch != 0 && Train.Handles.BrakeNotch == 0) { gearplayed = false; //If we aren't in gear & gears aren't blocked if (CurrentGear == 0 && Train.DieselEngine.gearsblocked == false) { CurrentGear = 1; gearchange(); Train.DieselEngine.gearloop = false; Train.DieselEngine.gearlooptimer = 0.0; } if (currentrevs > Math.Min((2000 - fadeoutratio) / 2, 800) && CurrentGear < totalgears - 1) { CurrentGear++; gearchange(); Train.DieselEngine.gearloop = false; Train.DieselEngine.gearlooptimer = 0.0; } //Change down else if (currentrevs < Math.Max(fadeinratio / 2, 200) && CurrentGear > 1) { CurrentGear--; gearchange(); Train.DieselEngine.gearloop = false; Train.DieselEngine.gearlooptimer = 0.0; } } //If we're stopped with the power off, drop out of gear else if (Train.Handles.Reverser == 0 && Train.Handles.PowerNotch == 0) { CurrentGear = 0; if (gearplayed == false) { gearchange(); gearplayed = true; } } } //Finally set the power notch if (CurrentGear != 0) { this.Train.TractionManager.SetMaxPowerNotch(Math.Min(power_limit, this.Train.Handles.PowerNotch), false); //data.Handles.PowerNotch = Math.Min(power_limit, this.Train.Handles.PowerNotch); } else { this.Train.TractionManager.SetMaxPowerNotch(0, false); } //If revving the engine is allowed in neutral if (allowneutralrevs == 1 && (CurrentGear == 0 || Train.Handles.Reverser == 0)) { currentrevs = (int)Math.Abs((1000 / Train.Specs.PowerNotches) * Train.Handles.PowerNotch * 0.9); //Play sounds based upon revs state if (revsupsound != -1 && revsdownsound != -1 && motorsound != -1) { double pitch = (double)Train.Handles.PowerNotch / (double)Train.Specs.PowerNotches; if (currentrevs > 0 && currentrevs > previousrevs) { SoundManager.Play(motorsound, 0.8, pitch, true); SoundManager.Play(revsupsound, 1.0, 1.0, false); SoundManager.Stop(revsdownsound); } else if (currentrevs >= 0 && currentrevs < previousrevs) { SoundManager.Play(motorsound, 0.8, pitch, true); SoundManager.Play(revsdownsound, 1.0, 1.0, false); SoundManager.Stop(revsupsound); } else if (currentrevs == 0) { SoundManager.Stop(revsupsound); SoundManager.Stop(revsdownsound); SoundManager.Stop(motorsound); } previousrevs = currentrevs; } } //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 = heatingarray[0]; } else if (Train.Handles.PowerNotch < heatingarray.Length) { currentheat = heatingarray[Train.Handles.PowerNotch]; } else { currentheat = heatingarray[heatingarray.Length - 1]; } temperature += currentheat; } } else { //Heats based upon RPM int revspercentage = currentrevs / 100; if (this.heatingtimer > 1000) { this.heatingtimer = 0.0; if (revspercentage == 0) { currentheat = heatingarray[0]; } else if (revspercentage < heatingarray.Length) { currentheat = heatingarray[revspercentage]; } else { currentheat = heatingarray[heatingarray.Length - 1]; } temperature += currentheat; } } //Keep temperature below max & above zero if (temperature > overheat) { temperature = overheat; if (overheatresult == 1 && Train.TractionManager.EngineOverheated == false) { Train.TractionManager.DemandPowerCutoff("Power cutoff was demanded due to the diesel engine overheating"); Train.TractionManager.EngineOverheated = true; } } else if (temperature < overheat && temperature > 0) { Train.TractionManager.ResetPowerCutoff(); Train.TractionManager.EngineOverheated = false; } else if (temperature < 0) { temperature = 0; } } } //This section of code uses fuel fuelusetimer += data.ElapsedTime.Milliseconds; if (fuelusetimer > 1000) { fuelusetimer = 0.0; if (data.Handles.PowerNotch < fuelarray.Length) { fuel -= fuelarray[data.Handles.PowerNotch]; } else { fuel -= fuelarray[fuelarray.Length - 1]; } if (fuel <= 0) { fuel = 0; } } //This section of code fills our fuel tanks if (fuelling == true) { fuellingtimer += data.ElapsedTime.Milliseconds; if (fuellingtimer > 1000) { fuellingtimer = 0.0; fuel += (int)fuelfillspeed; } if (fuel > fuelcapacity) { fuel = (int)fuelcapacity; } } //This section of code runs the gear loop sound if (gearloopsound != -1 && CurrentGear != 0) { //Start the timer gearlooptimer += data.ElapsedTime.Milliseconds; if (gearlooptimer > gearlooptime && gearloop == false) { //Start playback and reset our conditions gearloop = true; SoundManager.Play(gearloopsound, 1.0, 1.0, true); } else if (gearloop == false) { SoundManager.Stop(gearloopsound); } } else if (gearloopsound != -1 && CurrentGear == 0) { SoundManager.Stop(gearloopsound); } { //Panel Variables //Ammeter if (Ammeter.PanelIndex != -1) { if (Train.TractionManager.PowerCutoffDemanded == true) { this.Train.Panel[Ammeter.PanelIndex] = 0; } else { this.Train.Panel[Ammeter.PanelIndex] = Ammeter.GetCurrentValue(); } } if (!nogears) { if (gearindicator != -1) { this.Train.Panel[(gearindicator)] = CurrentGear; } if (tachometer != -1) { this.Train.Panel[(tachometer)] = currentrevs; } } if (automaticindicator != -1) { if (this.Train.TractionManager.AutomaticAdvancedFunctions == false) { this.Train.Panel[(automaticindicator)] = 0; } else { this.Train.Panel[(automaticindicator)] = 1; } } 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; } } if (fuelindicator != -1) { this.Train.Panel[(fuelindicator)] = fuel; } if (fuelfillindicator != -1) { if (fuelling == true) { this.Train.Panel[(fuelfillindicator)] = 1; } } } { //Sounds if (overheatalarm != -1) { if (temperature > overheatwarn) { SoundManager.Play(overheatalarm, 1.0, 1.0, true); } else { SoundManager.Stop(overheatalarm); } } } }
/// <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); } }