using System; using OpenBveApi.Runtime; namespace OpenbveFcmbTrainPlugin { /// A device controlling the door operation. internal class DoorClosingSound : Device { /// Whether the door closing sound is required to be played to close the doors. private readonly bool RequireDoorClosingSound; /// The duration of the door closing sound, in seconds. private readonly double DoorClosingSoundDuration; /// The timeout after which the door closing sound needs to be played again, in seconds. private readonly double DoorClosingSoundTimeout; /// The counter for the door closing sound. private double DoorClosingSoundCounter; /// Whether the AI should trigger the door closing sound. private bool AiTriggerDoorClosingSound; /// The index of the door closing sound. private readonly int DoorClosingSoundIndex; /// The current time. private Time CurrentTime = new Time(0); /// The time when the doors last opened. private Time DoorOpenTime = new Time(0); /// Whether the door closing sound is required to be played to close the doors. /// The delay before the brakes are applied, in seconds. /// The duration of the door closing sound, in seconds. /// The timeout after which the door closing sound needs to be played again, in seconds. /// The index of the door closing sound. internal DoorClosingSound(bool requireClosingSound, double closingSoundDuration, double closingSoundTimeout, int closingSoundIndex) { RequireDoorClosingSound = requireClosingSound; DoorClosingSoundDuration = closingSoundDuration; DoorClosingSoundTimeout = closingSoundTimeout; DoorClosingSoundIndex = closingSoundIndex; } /// Is called when the device state should be updated. /// The current train. /// The current route. /// Whether the device should initialize. /// The time elapsed since the previous call. internal override void Update(Train train, Route route, bool init, Time elapsedTime) { // Update current time CurrentTime = route.CurrentTime; if (init) { // Set door closing counter to value higher than maximum where doors are allowed to close DoorClosingSoundCounter = DoorClosingSoundDuration + DoorClosingSoundTimeout; } // Update door closing sound counter DoorClosingSoundCounter += elapsedTime.Seconds; // Lock and unlock doors depending on whether the door closing sound has been played if (RequireDoorClosingSound && train.DoorState != DoorStates.None) { if (DoorClosingSoundCounter > DoorClosingSoundDuration + DoorClosingSoundTimeout) { switch (train.DoorState) { case DoorStates.Both: RequestedDoorInterlock = DoorInterlockStates.Locked; break; case DoorStates.Left: RequestedDoorInterlock = DoorInterlockStates.Right; break; case DoorStates.Right: RequestedDoorInterlock = DoorInterlockStates.Left; break; } } else if (DoorClosingSoundCounter > DoorClosingSoundDuration) { switch (train.DoorState) { case DoorStates.Both: RequestedDoorInterlock = DoorInterlockStates.Unlocked; break; case DoorStates.Left: RequestedDoorInterlock = DoorInterlockStates.Left; break; case DoorStates.Right: RequestedDoorInterlock = DoorInterlockStates.Right; break; } } } else { RequestedDoorInterlock = DoorInterlockStates.Unlocked; } // Check if AI needs to trigger the door closing sound if (train.DoorState != DoorStates.None && DoorClosingSoundCounter > DoorClosingSoundDuration + DoorClosingSoundTimeout) { if (route.CurrentStation.Type == StationType.Normal || route.CurrentStation.Type == StationType.RequestStop) { // The current station is an intermediate stop if (route.CurrentStation.DepartureTime < 0) { // The current station has dwell time, not a specific departure time // Calculate departure time from the time when the doors opened AiTriggerDoorClosingSound |= CurrentTime.Seconds >= DoorOpenTime.Seconds + (route.CurrentStation.StopTime - DoorClosingSoundDuration); } else { // Use departure time - door closing sound duration AiTriggerDoorClosingSound |= CurrentTime.Seconds >= route.CurrentStation.DepartureTime - DoorClosingSoundDuration; } } else { // The doors have been opened between stations if (route.CurrentStation.StopPosition - route.CurrentStation.BackwardTolerance > train.State.Location) { AiTriggerDoorClosingSound = true; } else { AiTriggerDoorClosingSound = false; } } } else { AiTriggerDoorClosingSound = false; } } /// Is called when the state of a key changes. /// The key. /// Whether the key is pressed or released. /// The current train. internal override void KeyChange(VirtualKeys key, bool pressed, Train train) { if (pressed) { switch (key) { case VirtualKeys.F: if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) { // Play the door closing sound and reset timeout DoorClosingSoundCounter = 0; if (!SoundManager.Playing(DoorClosingSoundIndex)) { SoundManager.Play(DoorClosingSoundIndex, 1, 1, false); } } break; } } } /// Is called when the state of the doors changes. /// The old state of the doors. /// The new state of the doors. internal override void DoorChange(DoorStates oldState, DoorStates newState) { // Set arrival time when doors open if (oldState == DoorStates.None && newState != DoorStates.None) { DoorOpenTime = CurrentTime; } // Set door closing counter to value higher than maximum where doors are allowed to close // This effectively locks the doors when they open DoorClosingSoundCounter = DoorClosingSoundDuration + DoorClosingSoundTimeout; } /// Is called when the device should perform the AI. /// The AI data. /// The current train. /// The current route. internal override void PerformAI(AIData data, Train train, Route route) { if (AiTriggerDoorClosingSound) { KeyChange(VirtualKeys.F, true, train); data.Response = AIResponse.Short; KeyChange(VirtualKeys.F, false, train); AiTriggerDoorClosingSound = false; } } } }