From b32c1aafebc4804419201f151d7a8be6ec9f12e7 Mon Sep 17 00:00:00 2001 From: Marc Riera Date: Sun, 13 Oct 2024 00:31:40 +0200 Subject: [PATCH] Doors --- src/Devices/Deadman.cs | 6 +- src/Devices/Device.cs | 6 +- src/Devices/Doors.cs | 99 ++++++++++++++++++++------ src/OpenbveFcmbTrainPlugin.cs | 10 ++- src/OpenbveFcmbTrainPlugin.csproj | 2 + src/Route/Route.cs | 112 ++++++++++++++++++++++++++++++ src/Train/Train.cs | 11 +-- 7 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 src/Route/Route.cs diff --git a/src/Devices/Deadman.cs b/src/Devices/Deadman.cs index a541693..1d4955c 100644 --- a/src/Devices/Deadman.cs +++ b/src/Devices/Deadman.cs @@ -31,9 +31,10 @@ namespace OpenbveFcmbTrainPlugin /// 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, bool init, Time elapsedTime) + internal override void Update(Train train, Route route, bool init, Time elapsedTime) { if (train.VigilanceOverride) { @@ -100,7 +101,8 @@ namespace OpenbveFcmbTrainPlugin /// Is called when the plugin should perform the AI. /// The AI data. /// The current train. - internal override void PerformAI(AIData data, Train train) + /// The current route. + internal override void PerformAI(AIData data, Train train, Route route) { if (DeviceState != DeviceStates.Inactive) { diff --git a/src/Devices/Device.cs b/src/Devices/Device.cs index 3bd0843..c0e0461 100644 --- a/src/Devices/Device.cs +++ b/src/Devices/Device.cs @@ -17,9 +17,10 @@ namespace OpenbveFcmbTrainPlugin /// 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 virtual void Update(Train train, bool init, Time elapsedTime) { } + internal virtual void Update(Train train, Route route, bool init, Time elapsedTime) { } /// Is called when the driver changes the reverser. /// The new reverser position. @@ -59,6 +60,7 @@ namespace OpenbveFcmbTrainPlugin /// Is called when the plugin should perform the AI. /// The AI data. /// The current train. - internal virtual void PerformAI(AIData data, Train train) { } + /// The current route. + internal virtual void PerformAI(AIData data, Train train, Route route) { } } } diff --git a/src/Devices/Doors.cs b/src/Devices/Doors.cs index 6d231dd..5140d80 100644 --- a/src/Devices/Doors.cs +++ b/src/Devices/Doors.cs @@ -14,9 +14,10 @@ namespace OpenbveFcmbTrainPlugin /// 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, bool init, Time elapsedTime) + internal override void Update(Train train, Route route, bool init, Time elapsedTime) { if (init) { @@ -136,31 +137,89 @@ namespace OpenbveFcmbTrainPlugin /// The new state of the doors. internal override void DoorChange(DoorStates oldState, DoorStates newState) { - LeftDoorsClosing = false; - RightDoorsClosing = false; + if ((oldState == DoorStates.Both || oldState == DoorStates.Left) && (newState == DoorStates.None || newState == DoorStates.Right)) + { + LeftDoorsClosing = false; + // Unselect doors automatically when closed, just in case + RequestedDoorInterlock = newState == DoorStates.None ? DoorInterlockStates.Locked : DoorInterlockStates.Right; + } + if ((oldState == DoorStates.Both || oldState == DoorStates.Right) && (newState == DoorStates.None || newState == DoorStates.Left)) + { + RightDoorsClosing = false; + // Unselect doors automatically when closed, just in case + RequestedDoorInterlock = newState == DoorStates.None ? DoorInterlockStates.Locked : DoorInterlockStates.Left; + } } /// Is called when the device should perform the AI. /// The AI data. /// The current train. - internal override void PerformAI(AIData data, Train train) + /// The current route. + internal override void PerformAI(AIData data, Train train, Route route) { - //// Select the correct doors for the current station - //if (Stations.State != Stations.StationStates.Completed) - //{ - // if (((Stations.Current.OpenLeftDoors && !LeftDoorsSelected) || (!Stations.Current.OpenLeftDoors && LeftDoorsSelected)) && !LeftDoorsClosing) - // { - // // Select or unselect the doors on the left. - // KeyDown(VirtualKeys.G); - // data.Response = AIResponse.Short; - // } - // if (((Stations.Current.OpenRightDoors && !RightDoorsSelected) || (!Stations.Current.OpenRightDoors && RightDoorsSelected)) && !RightDoorsClosing) - // { - // // Select or unselect the doors on the right. - // KeyDown(VirtualKeys.H); - // data.Response = AIResponse.Short; - // } - //} + // Select doors to be opened on the current stop, at least 200 meters before the stop point. + if (route.CurrentStation.PlayerStops() && train.State.Location >= route.CurrentStation.StopPosition - 200 && route.StationState == Route.StationStates.Pending) + { + switch (RequestedDoorInterlock) + { + case DoorInterlockStates.Unlocked: + if (!route.CurrentStation.OpenLeftDoors) + { + KeyChange(VirtualKeys.G, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.G, false, train); + } + if (!route.CurrentStation.OpenRightDoors) + { + KeyChange(VirtualKeys.H, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.H, false, train); + } + break; + case DoorInterlockStates.Left: + if (!route.CurrentStation.OpenLeftDoors) + { + KeyChange(VirtualKeys.G, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.G, false, train); + } + if (route.CurrentStation.OpenRightDoors) + { + KeyChange(VirtualKeys.H, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.H, false, train); + } + break; + case DoorInterlockStates.Right: + if (route.CurrentStation.OpenLeftDoors) + { + KeyChange(VirtualKeys.G, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.G, false, train); + } + if (!route.CurrentStation.OpenRightDoors) + { + KeyChange(VirtualKeys.H, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.H, false, train); + } + break; + case DoorInterlockStates.Locked: + if (route.CurrentStation.OpenLeftDoors) + { + KeyChange(VirtualKeys.G, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.G, false, train); + } + if (route.CurrentStation.OpenRightDoors) + { + KeyChange(VirtualKeys.H, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.H, false, train); + } + break; + } + } } } } diff --git a/src/OpenbveFcmbTrainPlugin.cs b/src/OpenbveFcmbTrainPlugin.cs index 203e429..dcc400e 100644 --- a/src/OpenbveFcmbTrainPlugin.cs +++ b/src/OpenbveFcmbTrainPlugin.cs @@ -9,6 +9,9 @@ namespace OpenbveFcmbTrainPlugin /// The train that is simulated by this plugin. private Train Train; + /// The route where the train is running. + private Route Route; + /// Whether the train is initializing or reinitializing. internal static bool Initializing = true; @@ -23,6 +26,7 @@ namespace OpenbveFcmbTrainPlugin properties.AISupport = AISupport.Basic; properties.Panel = new int[256]; Train = new Train(properties.Panel); + Route = new Route(); return true; } @@ -51,7 +55,8 @@ namespace OpenbveFcmbTrainPlugin /// The data passed to the plugin. public void Elapse(ElapseData data) { - Train.Elapse(data); + Route.Elapse(data); + Train.Elapse(data, Route); Initializing = false; } @@ -100,6 +105,7 @@ namespace OpenbveFcmbTrainPlugin /// The new state of the doors. public void DoorChange(DoorStates oldState, DoorStates newState) { + Route.DoorChange(oldState, newState); Train.DoorChange(oldState, newState); } @@ -119,7 +125,7 @@ namespace OpenbveFcmbTrainPlugin /// The AI data. public void PerformAI(AIData data) { - Train.PerformAI(data); + Train.PerformAI(data, Route); } } diff --git a/src/OpenbveFcmbTrainPlugin.csproj b/src/OpenbveFcmbTrainPlugin.csproj index 54c6b9b..e6fc0c3 100644 --- a/src/OpenbveFcmbTrainPlugin.csproj +++ b/src/OpenbveFcmbTrainPlugin.csproj @@ -40,10 +40,12 @@ + + diff --git a/src/Route/Route.cs b/src/Route/Route.cs new file mode 100644 index 0000000..0602f7c --- /dev/null +++ b/src/Route/Route.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using OpenBveApi.Runtime; + +namespace OpenbveFcmbTrainPlugin +{ + internal class Route + { + /// Represents the state of the current station. + internal enum StationStates + { + /// The train has not arrived at the station yet. + Pending = 0, + /// The train has arrived at the station. + Arrived = 1, + /// The train is ready to depart. + Completed = 2, + } + + /// The state of the current station. + internal StationStates StationState { get; private set; } = StationStates.Arrived; + + /// The list of stations in the route. + internal List Stations; + + /// The current station in the route. + internal Station CurrentStation; + + /// The next station in the route. + internal Station NextStation; + + /// The current state of the train doors. + private DoorStates DoorState; + + /// The previous state of the train doors. + private DoorStates PreviousDoorState; + + /// Is called every frame. + /// The data passed to the plugin. + internal void Elapse(ElapseData data) + { + if (OpenbveFcmbTrainPlugin.Initializing) + { + // Get the list of stations and set the current station + Stations = data.Stations; + UpdateStationData(data.Vehicle.Location); + } + + if (StationState == StationStates.Completed && data.Vehicle.Location > CurrentStation.StopPosition + CurrentStation.ForwardTolerance) + { + // The train is heading for the next station + StationState = StationStates.Pending; + UpdateStationData(data.Vehicle.Location); + } + + if (StationState == StationStates.Pending && data.Vehicle.Location > CurrentStation.StopPosition + CurrentStation.ForwardTolerance + 100) + { + // The train has passed the station without stopping + UpdateStationData(data.Vehicle.Location); + } + + // Update station status after closing the doors + if (StationState == StationStates.Arrived && DoorState == DoorStates.None) + { + // The train is ready to leave the station + StationState = StationStates.Completed; + } + + // Check if the train is at a station where the doors should open + bool ArrivedDoorStop = (PreviousDoorState == DoorStates.None && DoorState != DoorStates.None && (CurrentStation.OpenLeftDoors || CurrentStation.OpenRightDoors)); + // Check if the train is at a station where the doors should not open + bool ArrivedNoDoorStop = (DoorState == DoorStates.None && !CurrentStation.OpenLeftDoors && !CurrentStation.OpenRightDoors); + if (StationState == StationStates.Pending && (ArrivedDoorStop || ArrivedNoDoorStop) && data.Vehicle.Speed.KilometersPerHour < 0.05 && data.Vehicle.Location >= CurrentStation.DefaultTrackPosition) + { + // The train has arrived at the station + StationState = StationStates.Arrived; + } + } + + /// Is called when the state of the doors changes. + /// The old state of the doors. + /// The new state of the doors. + internal void DoorChange(DoorStates oldState, DoorStates newState) + { + PreviousDoorState = oldState; + DoorState = newState; + } + + /// Updates the data for the current and next station. + /// The current location of the train. + internal void UpdateStationData(double location) + { + // Calculate the index of the current station + double position = location; + int currentIndex = 0; + if (Stations.Count > 1) + { + while (currentIndex > 0 && (Stations[currentIndex].StopPosition + Stations[currentIndex].ForwardTolerance > position || !Stations[currentIndex].PlayerStops())) + { + // Detects the station index when driving or jumping backwards + currentIndex--; + } + while (currentIndex < Stations.Count - 1 && (Stations[currentIndex].StopPosition + Stations[currentIndex].ForwardTolerance <= position || !Stations[currentIndex].PlayerStops())) + { + // Detects the station index when driving or jumping forwards + currentIndex++; + } + } + CurrentStation = Stations[currentIndex]; + NextStation = currentIndex < Stations.Count - 1 ? Stations[currentIndex + 1] : null; + } + } +} diff --git a/src/Train/Train.cs b/src/Train/Train.cs index 8c5726f..daf2898 100644 --- a/src/Train/Train.cs +++ b/src/Train/Train.cs @@ -62,7 +62,8 @@ namespace OpenbveFcmbTrainPlugin /// Is called every frame. /// The data. - internal void Elapse(ElapseData data) + /// The route data. + internal void Elapse(ElapseData data, Route route) { State = data.Vehicle; @@ -74,12 +75,11 @@ namespace OpenbveFcmbTrainPlugin // Retrieve data from all devices foreach (Device dev in Devices) { - dev.Update(this, OpenbveFcmbTrainPlugin.Initializing, data.ElapsedTime); + dev.Update(this, route, OpenbveFcmbTrainPlugin.Initializing, data.ElapsedTime); data.DoorInterlockState |= dev.RequestedDoorInterlock; if (dev.RequestedPowerNotch != -1) data.Handles.PowerNotch = dev.RequestedPowerNotch; if (dev.RequestedBrakeNotch != -1) data.Handles.BrakeNotch = dev.RequestedBrakeNotch; } - data.DebugMessage = DoorState.ToString() + " " + data.DoorInterlockState.ToString(); } /// Is called when the driver changes the reverser. @@ -141,11 +141,12 @@ namespace OpenbveFcmbTrainPlugin /// Is called when the plugin should perform the AI. /// The AI data. - internal void PerformAI(AIData data) + /// The route data. + internal void PerformAI(AIData data, Route route) { foreach (Device dev in Devices) { - dev.PerformAI(data, this); + dev.PerformAI(data, this, route); } } }