From 0d696fb381bbfe6c186b0424d2e0227a9a83a981 Mon Sep 17 00:00:00 2001 From: Marc Riera Date: Thu, 12 Dec 2024 22:30:44 +0100 Subject: [PATCH] Doors: Split into 3 devices --- OpenbveFcmbTrainPlugin.sln | 2 +- src/Devices/DoorClosingSound.cs | 187 ++++++++++++++++++++ src/Devices/{Doors.cs => DoorSelection.cs} | 188 ++------------------- src/Devices/DoorTractionCut.cs | 21 +++ src/Managers/ConfigManager.cs | 48 ++++-- src/OpenbveFcmbTrainPlugin.csproj | 4 +- src/Train/Train.cs | 16 +- 7 files changed, 274 insertions(+), 192 deletions(-) create mode 100644 src/Devices/DoorClosingSound.cs rename src/Devices/{Doors.cs => DoorSelection.cs} (56%) create mode 100644 src/Devices/DoorTractionCut.cs diff --git a/OpenbveFcmbTrainPlugin.sln b/OpenbveFcmbTrainPlugin.sln index 97200f0..ef17bd7 100644 --- a/OpenbveFcmbTrainPlugin.sln +++ b/OpenbveFcmbTrainPlugin.sln @@ -1,7 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenbveFcmbTrainPlugin", "src/OpenbveFcmbTrainPlugin.csproj", "{9197FFA4-D95A-410C-A705-4E7CD187546A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenbveFcmbTrainPlugin", "src\OpenbveFcmbTrainPlugin.csproj", "{9197FFA4-D95A-410C-A705-4E7CD187546A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Devices/DoorClosingSound.cs b/src/Devices/DoorClosingSound.cs new file mode 100644 index 0000000..e3b5527 --- /dev/null +++ b/src/Devices/DoorClosingSound.cs @@ -0,0 +1,187 @@ +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; + } + // TODO: AI gets stuck if doors open between stations, check distance to stop point + } + 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; + } + } + + /// 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; + } + } + } +} + diff --git a/src/Devices/Doors.cs b/src/Devices/DoorSelection.cs similarity index 56% rename from src/Devices/Doors.cs rename to src/Devices/DoorSelection.cs index 12312e1..2249f25 100644 --- a/src/Devices/Doors.cs +++ b/src/Devices/DoorSelection.cs @@ -4,7 +4,7 @@ using OpenBveApi.Runtime; namespace OpenbveFcmbTrainPlugin { /// A device controlling the door operation. - internal class Doors : Device + internal class DoorSelection : Device { /// Whether the left doors are closing. private bool LeftDoorsClosing; @@ -12,48 +12,6 @@ namespace OpenbveFcmbTrainPlugin /// Whether the right doors are closing. private bool RightDoorsClosing; - /// Whether the door closing sound is required to be played to close the doors. - private bool RequireDoorClosingSound; - - /// The duration of the door closing sound, in seconds. - private double DoorClosingSoundDuration; - - /// The timeout after which the door closing sound needs to be played again, in seconds. - private double DoorClosingSoundTimeout; - - /// The counter for the door closing sound. - private double DoorClosingSoundCounter; - - /// Whether to trigger a door unlock. - private bool TriggerDoorUnlock; - - /// Whether to trigger a door lock. - private bool TriggerDoorLock; - - /// Whether the AI should trigger the door closing sound. - private bool AiTriggerDoorClosingSound; - - /// The index of the door closing sound. - private 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 Doors(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. @@ -62,102 +20,22 @@ namespace OpenbveFcmbTrainPlugin /// 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) { - if (!RequireDoorClosingSound) + // Set the selection state of the doors during initialization + switch (train.DoorState) { - // Set the selection state of the doors during initialization - switch (train.DoorState) - { - case DoorStates.None: - RequestedDoorInterlock = DoorInterlockStates.Locked; - break; - case DoorStates.Both: - RequestedDoorInterlock = DoorInterlockStates.Unlocked; - break; - case DoorStates.Left: - case DoorStates.Right: - RequestedDoorInterlock = (DoorInterlockStates)train.DoorState; - break; - } + case DoorStates.None: + RequestedDoorInterlock = DoorInterlockStates.Locked; + break; + case DoorStates.Both: + RequestedDoorInterlock = DoorInterlockStates.Unlocked; + break; + case DoorStates.Left: + case DoorStates.Right: + RequestedDoorInterlock = (DoorInterlockStates)train.DoorState; + break; } - // Set door closing counter to value higher than maximum where doors are allowed to close - DoorClosingSoundCounter = DoorClosingSoundDuration + DoorClosingSoundTimeout; - } - - // Cut power when the doors are open - RequestedPowerNotch = (train.DoorState != DoorStates.None) ? 0 : -1; - - // Update door closing sound counter - DoorClosingSoundCounter += elapsedTime.Seconds; - - // Unlock doors for closing if the door closing sound has been played - if (RequireDoorClosingSound) - { - if (DoorClosingSoundCounter > DoorClosingSoundDuration && TriggerDoorUnlock) - { - switch (train.DoorState) - { - case DoorStates.Both: - RequestedDoorInterlock = DoorInterlockStates.Unlocked; - break; - case DoorStates.Left: - RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Right ? DoorInterlockStates.Unlocked : DoorInterlockStates.Left; - break; - case DoorStates.Right: - RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Left ? DoorInterlockStates.Unlocked : DoorInterlockStates.Right; - break; - } - TriggerDoorUnlock = false; - TriggerDoorLock = true; - } - else if (DoorClosingSoundCounter > DoorClosingSoundDuration + DoorClosingSoundTimeout && TriggerDoorLock) - { - switch (train.DoorState) - { - case DoorStates.Both: - RequestedDoorInterlock = DoorInterlockStates.Locked; - break; - case DoorStates.Left: - RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Unlocked ? DoorInterlockStates.Right : DoorInterlockStates.Locked; - break; - case DoorStates.Right: - RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Unlocked ? DoorInterlockStates.Left : DoorInterlockStates.Locked; - break; - } - TriggerDoorLock = false; - } - } - - // 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 - { - AiTriggerDoorClosingSound = false; - } - } - else - { - AiTriggerDoorClosingSound = false; } // Update panel variables for door selection state @@ -175,18 +53,6 @@ namespace OpenbveFcmbTrainPlugin { switch (key) { - case VirtualKeys.F: - if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) - { - // Play the door closing sound and reset timeout - DoorClosingSoundCounter = 0; - TriggerDoorUnlock = true; - if (!SoundManager.Playing(DoorClosingSoundIndex)) - { - SoundManager.Play(DoorClosingSoundIndex, 1, 1, false); - } - } - break; case VirtualKeys.G: // Change the selection state of the left doors if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) @@ -241,7 +107,7 @@ namespace OpenbveFcmbTrainPlugin if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) { // Unselect doors automatically when closing - if ((train.DoorState & DoorStates.Left) > 0 && !LeftDoorsClosing && (RequestedDoorInterlock == DoorInterlockStates.Left || RequestedDoorInterlock == DoorInterlockStates.Unlocked)) + if ((train.DoorState & DoorStates.Left) > 0 && !LeftDoorsClosing && (train.DoorInterlockState == DoorInterlockStates.Left || train.DoorInterlockState == DoorInterlockStates.Unlocked)) { LeftDoorsClosing = true; RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Left ? DoorInterlockStates.Locked : DoorInterlockStates.Right; @@ -256,7 +122,7 @@ namespace OpenbveFcmbTrainPlugin if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) { // Unselect doors automatically when closing - if ((train.DoorState & DoorStates.Right) > 0 && !RightDoorsClosing && (RequestedDoorInterlock == DoorInterlockStates.Right || RequestedDoorInterlock == DoorInterlockStates.Unlocked)) + if ((train.DoorState & DoorStates.Right) > 0 && !RightDoorsClosing && (train.DoorInterlockState == DoorInterlockStates.Right || train.DoorInterlockState == DoorInterlockStates.Unlocked)) { RightDoorsClosing = true; RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Right ? DoorInterlockStates.Locked : DoorInterlockStates.Left; @@ -295,23 +161,6 @@ namespace OpenbveFcmbTrainPlugin RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Right ? DoorInterlockStates.Locked : DoorInterlockStates.Left; } } - if (RequireDoorClosingSound) - { - if ((oldState == DoorStates.None || oldState == DoorStates.Left) && (newState == DoorStates.Both || newState == DoorStates.Right)) - { - RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Left ? DoorInterlockStates.Left : DoorInterlockStates.Locked; - } - if ((oldState == DoorStates.None || oldState == DoorStates.Right) && (newState == DoorStates.Both || newState == DoorStates.Left)) - { - RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Right ? DoorInterlockStates.Right : DoorInterlockStates.Locked; - } - } - - // Set arrival time when doors open - if (oldState == DoorStates.None && newState != DoorStates.None) - { - DoorOpenTime = CurrentTime; - } } /// Is called when the device should perform the AI. @@ -383,13 +232,6 @@ namespace OpenbveFcmbTrainPlugin break; } } - if (AiTriggerDoorClosingSound) - { - KeyChange(VirtualKeys.F, true, train); - data.Response = AIResponse.Short; - KeyChange(VirtualKeys.F, false, train); - AiTriggerDoorClosingSound = false; - } } } } diff --git a/src/Devices/DoorTractionCut.cs b/src/Devices/DoorTractionCut.cs new file mode 100644 index 0000000..6265821 --- /dev/null +++ b/src/Devices/DoorTractionCut.cs @@ -0,0 +1,21 @@ +using System; +using OpenBveApi.Runtime; + +namespace OpenbveFcmbTrainPlugin +{ + /// A device cutting power if doors are open. + internal class DoorTractionCut : Device + { + /// 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) + { + // Cut power when the doors are open + RequestedPowerNotch = (train.DoorState != DoorStates.None) ? 0 : -1; + } + } +} + diff --git a/src/Managers/ConfigManager.cs b/src/Managers/ConfigManager.cs index bb6dd8d..7ecd3e1 100644 --- a/src/Managers/ConfigManager.cs +++ b/src/Managers/ConfigManager.cs @@ -8,16 +8,18 @@ namespace OpenbveFcmbTrainPlugin /// Represents a collection of plugin settings. internal class SettingsCollection { - internal bool DoorsDeviceEnabled; + internal bool DoorSelectionDeviceEnabled; + internal bool DoorClosingSoundDeviceEnabled; + internal bool DoorTractionCutDeviceEnabled; internal bool DeadmanDeviceEnabled; internal bool TrainStopDeviceEnabled; internal bool AtcBombardierDeviceEnabled; internal bool AtcDimetronicDeviceEnabled; - internal bool DoorsRequireClosingSound; - internal int DoorsClosingSoundDuration = 5; - internal int DoorsClosingSoundTimeout = 15; - internal int DoorsClosingSoundIndex = 10; + internal bool DoorClosingSoundRequired; + internal int DoorClosingSoundDuration = 5; + internal int DoorClosingSoundTimeout = 15; + internal int DoorClosingSoundIndex = 10; internal int DeadmanBrakeDelay = 5; internal bool DeadmanRequireFullStop; @@ -68,41 +70,57 @@ namespace OpenbveFcmbTrainPlugin } switch (Section) { - case "doors": + case "doorselection": switch (Key) { case "enabled": - PluginSettings.DoorsDeviceEnabled = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; + PluginSettings.DoorSelectionDeviceEnabled = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; - case "requireclosingsound": - PluginSettings.DoorsRequireClosingSound = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; + } + break; + case "doorclosingsound": + switch (Key) + { + case "enabled": + PluginSettings.DoorClosingSoundDeviceEnabled = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; - case "closingsoundduration": + case "required": + PluginSettings.DoorClosingSoundRequired = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; + break; + case "duration": { if (int.TryParse(Value, out int a)) { - PluginSettings.DoorsClosingSoundDuration = a; + PluginSettings.DoorClosingSoundDuration = a; } } break; - case "closingsoundtimeout": + case "timeout": { if (int.TryParse(Value, out int a)) { - PluginSettings.DoorsClosingSoundTimeout = a; + PluginSettings.DoorClosingSoundTimeout = a; } } break; - case "closingsoundindex": + case "index": { if (int.TryParse(Value, out int a)) { - PluginSettings.DoorsClosingSoundIndex = a; + PluginSettings.DoorClosingSoundIndex = a; } } break; } break; + case "doortractioncut": + switch (Key) + { + case "enabled": + PluginSettings.DoorTractionCutDeviceEnabled = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; + break; + } + break; case "deadman": switch (Key) { diff --git a/src/OpenbveFcmbTrainPlugin.csproj b/src/OpenbveFcmbTrainPlugin.csproj index 164a12d..22a5da0 100644 --- a/src/OpenbveFcmbTrainPlugin.csproj +++ b/src/OpenbveFcmbTrainPlugin.csproj @@ -37,7 +37,7 @@ - + @@ -47,6 +47,8 @@ + + diff --git a/src/Train/Train.cs b/src/Train/Train.cs index f928b1a..1131cd8 100644 --- a/src/Train/Train.cs +++ b/src/Train/Train.cs @@ -27,6 +27,9 @@ namespace OpenbveFcmbTrainPlugin /// The current state of the train doors. internal DoorStates DoorState { get; private set; } + /// The current interlock state of the train doors. + internal DoorInterlockStates DoorInterlockState { get; private set; } + /// The previous state of the train doors. internal DoorStates PreviousDoorState { get; private set; } @@ -69,9 +72,17 @@ namespace OpenbveFcmbTrainPlugin // Create list of devices and populate it according to settings Devices = new List(); ConfigManager.SettingsCollection settings = ConfigManager.PluginSettings; - if (settings.DoorsDeviceEnabled) + if (settings.DoorSelectionDeviceEnabled) { - Devices.Add(new Doors(settings.DoorsRequireClosingSound, settings.DoorsClosingSoundDuration, settings.DoorsClosingSoundTimeout, settings.DoorsClosingSoundIndex)); + Devices.Add(new DoorSelection()); + } + if (settings.DoorClosingSoundDeviceEnabled) + { + Devices.Add(new DoorClosingSound(settings.DoorClosingSoundRequired, settings.DoorClosingSoundDuration, settings.DoorClosingSoundTimeout, settings.DoorClosingSoundIndex)); + } + if (settings.DoorTractionCutDeviceEnabled) + { + Devices.Add(new DoorTractionCut()); } if (settings.DeadmanDeviceEnabled) { @@ -114,6 +125,7 @@ namespace OpenbveFcmbTrainPlugin internal void Elapse(ElapseData data, Route route) { State = data.Vehicle; + DoorInterlockState = data.DoorInterlockState; // Reset data to be passed to the simulator data.DoorInterlockState = DoorInterlockStates.Unlocked;