Skip to content

Commit

Permalink
Merge branch 'master' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
leezer3 committed Apr 10, 2024
2 parents 3ddaf2d + 46d1393 commit 2991f91
Show file tree
Hide file tree
Showing 18 changed files with 149 additions and 30 deletions.
3 changes: 2 additions & 1 deletion assets/Controls/Default.controls
Expand Up @@ -110,4 +110,5 @@ ACCESSIBILITY_CURRENT_SPEED, keyboard, S, 3
ACCESSIBILITY_NEXT_SIGNAL, keyboard, A, 3
ACCESSIBILITY_NEXT_STATION, keyboard, T, 3
UNCOUPLE_REAR, keyboard, Semicolon, 2
UNCOUPLE_FRONT, keyboard, Semicolon, 3
UNCOUPLE_FRONT, keyboard, Semicolon, 3
SWITCH_MENU, Keyboard, F9, 2, 0
3 changes: 2 additions & 1 deletion source/OpenBVE/Game/SwitchChange/SwitchChangeDialog.cs
Expand Up @@ -37,6 +37,7 @@
using OpenBveApi.Textures;
using RouteManager2;
using TrainManager;
using TrainManager.SafetySystems;
using MouseCursor = OpenTK.MouseCursor;
using Vector2 = OpenBveApi.Math.Vector2;

Expand Down Expand Up @@ -115,7 +116,7 @@ internal void ProcessMouseMove(int x, int y)
break;
}
}
if (selectedSwitch == Guid.Empty)
if (selectedSwitch == Guid.Empty || Program.CurrentRoute.Switches[selectedSwitch].FixedRoute)
{
// Not found an appropriate switch, so set back to default
Program.currentGameWindow.Cursor = MouseCursor.Default;
Expand Down
4 changes: 2 additions & 2 deletions source/OpenBVE/Game/TrainManager.cs
Expand Up @@ -30,7 +30,7 @@ internal void UpdateTrains(double TimeElapsed)
}

// ReSharper disable once PossibleInvalidCastExceptionInForeachLoop
foreach (TrackFollowingObject Train in TFOs) //Must not use var, as otherwise the wrong inferred type
foreach (ScriptedTrain Train in TFOs) //Must not use var, as otherwise the wrong inferred type
{
Train.Update(TimeElapsed);
}
Expand Down Expand Up @@ -379,7 +379,7 @@ internal void UpdateTrains(double TimeElapsed)
{
if (TFOs[i].State != TrainState.Disposed & TFOs[i].State != TrainState.Bogus)
{
TrackFollowingObject t = (TrackFollowingObject) TFOs[i];
ScriptedTrain t = (ScriptedTrain) TFOs[i];
foreach (var Car in t.Cars)
{
Car.FrontAxle.Follower.UpdateWorldCoordinates(true);
Expand Down
17 changes: 8 additions & 9 deletions source/OpenBVE/Parsers/Script/TrackFollowingObjectParser.cs
Expand Up @@ -12,7 +12,6 @@
using OpenBveApi.Trains;
using TrainManager.Trains;
using Path = OpenBveApi.Path;
using TrackFollowingObject = TrainManager.Trains.TrackFollowingObject;

namespace OpenBve
{
Expand All @@ -23,24 +22,24 @@ internal static class TrackFollowingObjectParser
/// <summary>Parses a track following object</summary>
/// <param name="ObjectPath">Absolute path to the object folder of route data</param>
/// <param name="FileName">The XML file to parse</param>
internal static TrackFollowingObject ParseTrackFollowingObject(string ObjectPath, string FileName)
internal static ScriptedTrain ParseTrackFollowingObject(string ObjectPath, string FileName)
{
// The current XML file to load
XDocument CurrentXML = XDocument.Load(FileName, LoadOptions.SetLineInfo);
List<XElement> TfoElements = CurrentXML.XPathSelectElements("/openBVE/TrackFollowingObject").ToList();
List<XElement> ScriptedTrainElements = CurrentXML.XPathSelectElements("/openBVE/TrackFollowingObject").ToList();

// Check this file actually contains OpenBVE other train definition elements
if (!TfoElements.Any())
if (!ScriptedTrainElements.Any())
{
// We couldn't find any valid XML, so return false
throw new InvalidDataException();
}

TrackFollowingObject Train = new TrackFollowingObject(TrainState.Pending);
ScriptedTrain Train = new ScriptedTrain(TrainState.Pending);

foreach (XElement Element in TfoElements)
foreach (XElement Element in ScriptedTrainElements)
{
ParseTrackFollowingObjectNode(ObjectPath, FileName, Element, Train);
ParseScriptedTrainNode(ObjectPath, FileName, Element, Train);
}

return Train;
Expand All @@ -51,7 +50,7 @@ internal static TrackFollowingObject ParseTrackFollowingObject(string ObjectPath
/// <param name="FileName">The filename of the containing XML file</param>
/// <param name="SectionElement">The XElement to parse</param>
/// <param name="Train">The track following object to parse this node into</param>
private static void ParseTrackFollowingObjectNode(string ObjectPath, string FileName, XElement SectionElement, TrackFollowingObject Train)
private static void ParseScriptedTrainNode(string ObjectPath, string FileName, XElement SectionElement, ScriptedTrain Train)
{
string Section = SectionElement.Name.LocalName;

Expand Down Expand Up @@ -155,7 +154,7 @@ private static void ParseTrackFollowingObjectNode(string ObjectPath, string File
/// <param name="FileName">The filename of the containing XML file</param>
/// <param name="SectionElement">The XElement to parse</param>
/// <param name="Train">The track following object to parse this node into</param>
private static void ParseDefinitionNode(string FileName, XElement SectionElement, TrackFollowingObject Train)
private static void ParseDefinitionNode(string FileName, XElement SectionElement, ScriptedTrain Train)
{
string Section = SectionElement.Name.LocalName;

Expand Down
2 changes: 1 addition & 1 deletion source/OpenBVE/System/GameWindow.cs
Expand Up @@ -738,7 +738,7 @@ private void SetupSimulation()
}

// ReSharper disable once PossibleInvalidCastExceptionInForeachLoop
foreach (TrackFollowingObject Train in Program.TrainManager.TFOs) //Must not use var, as otherwise the wrong inferred type
foreach (ScriptedTrain Train in Program.TrainManager.TFOs) //Must not use var, as otherwise the wrong inferred type
{
Train.Initialize();

Expand Down
37 changes: 37 additions & 0 deletions source/OpenBVE/System/Host.cs
Expand Up @@ -615,6 +615,17 @@ public override AbstractTrain ClosestTrain(AbstractTrain Train)
{
AbstractTrain closestTrain = null;
double bestLocation = double.MaxValue;

/*
* Eventually this should take into account the player path.
* This would mean itinerating backwards / forwards through the elements collection and any resulting switches, so that we ignore trains on the other tracks etc.
*
* However, this is likely to be a performance issue in this case (a 100km route could have ~400,000 elements)
* Possibly we could update the cached train when a switch is changed (??)
*
* Sort of thing that will probably want a spinning thread to deal with it possibly
*/

if(Train is TrainBase baseTrain)
{
for (int i = 0; i < Program.TrainManager.Trains.Length; i++)
Expand Down Expand Up @@ -663,6 +674,32 @@ public override AbstractTrain ClosestTrain(double TrackPosition)
}
}
}

for (int j = 0; j < Program.TrainManager.TFOs.Length; j++)
{
ScriptedTrain scriptedTrain = Program.TrainManager.TFOs[j] as ScriptedTrain;
if (scriptedTrain.State == TrainState.Available)
{
double distance;
if (scriptedTrain.Cars[0].FrontAxle.Follower.TrackPosition < TrackPosition)
{
distance = TrackPosition - scriptedTrain.Cars[0].TrackPosition;
}
else if (scriptedTrain.Cars[scriptedTrain.Cars.Length - 1].RearAxle.Follower.TrackPosition > TrackPosition)
{
distance = scriptedTrain.Cars[scriptedTrain.Cars.Length - 1].RearAxle.Follower.TrackPosition - TrackPosition;
}
else
{
distance = 0;
}
if (distance < trainDistance)
{
closestTrain = scriptedTrain;
trainDistance = distance;
}
}
}
return closestTrain;
}

Expand Down
2 changes: 2 additions & 0 deletions source/OpenBveApi/System/Hosts/HostInterface.cs
Expand Up @@ -642,6 +642,7 @@ public virtual void AddScore(int Score, string Message, MessageColor Color, doub
/// <returns>The closest train, or null if no other trains</returns>
public virtual AbstractTrain ClosestTrain(AbstractTrain Train)
{
// NOTE: This copy of the method is used in determining plugin data only
return null;
}

Expand All @@ -650,6 +651,7 @@ public virtual AbstractTrain ClosestTrain(AbstractTrain Train)
/// <returns>The closest train, or null if no other trains</returns>
public virtual AbstractTrain ClosestTrain(double TrackPosition)
{
// NOTE: This copy of the method is used by animated objects
return null;
}

Expand Down
4 changes: 2 additions & 2 deletions source/Plugins/Route.CsvRw/CsvRwRouteParser.ApplyRouteData.cs
Expand Up @@ -599,7 +599,7 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
{
type = SwitchType.RightHanded;
}
CurrentRoute.Switches.Add(newSwitch, new RouteManager2.Tracks.Switch(new[] { j, Data.Blocks[i].Switches[j].SecondTrack }, Data.Blocks[i].Switches[j].TrackNames, j, Data.Blocks[i].Switches[j].InitialSetting, CurrentRoute.Tracks[0].Elements[n].StartingTrackPosition, type, Data.Blocks[i].Switches[j].Name, TrackDirection.Forwards));
CurrentRoute.Switches.Add(newSwitch, new RouteManager2.Tracks.Switch(new[] { j, Data.Blocks[i].Switches[j].SecondTrack }, Data.Blocks[i].Switches[j].TrackNames, j, Data.Blocks[i].Switches[j].InitialSetting, CurrentRoute.Tracks[0].Elements[n].StartingTrackPosition, type, Data.Blocks[i].Switches[j].Name, Data.Blocks[i].Switches[j].FixedRoute, TrackDirection.Forwards));
//Assign facing switch event
CurrentRoute.Tracks[j].Elements[n].Events.Add(new SwitchEvent(newSwitch, CurrentRoute.Tracks[j].Elements[n].StartingTrackPosition, CurrentRoute));
CurrentRoute.Tracks[j].Elements[n].Events.Add(new PointSoundEvent());
Expand All @@ -614,7 +614,7 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
{
type = SwitchType.RightHanded;
}
CurrentRoute.Switches.Add(newSwitch, new RouteManager2.Tracks.Switch(new[] { Data.Blocks[i].Switches[j].SecondTrack, j }, Data.Blocks[i].Switches[j].TrackNames, j, Data.Blocks[i].Switches[j].InitialSetting, CurrentRoute.Tracks[0].Elements[n].StartingTrackPosition, type, Data.Blocks[i].Switches[j].Name, TrackDirection.Reverse));
CurrentRoute.Switches.Add(newSwitch, new RouteManager2.Tracks.Switch(new[] { Data.Blocks[i].Switches[j].SecondTrack, j }, Data.Blocks[i].Switches[j].TrackNames, j, Data.Blocks[i].Switches[j].InitialSetting, CurrentRoute.Tracks[0].Elements[n].StartingTrackPosition, type, Data.Blocks[i].Switches[j].Name, Data.Blocks[i].Switches[j].FixedRoute, TrackDirection.Reverse));
//Assign facing switch event
CurrentRoute.Tracks[j].Elements[n].Events.Add(new SwitchEvent(newSwitch, CurrentRoute.Tracks[j].Elements[n].StartingTrackPosition, CurrentRoute));
CurrentRoute.Tracks[j].Elements[n].Events.Add(new PointSoundEvent());
Expand Down
2 changes: 2 additions & 0 deletions source/Plugins/Route.CsvRw/Namespaces/Track/Track.Commands.cs
Expand Up @@ -136,6 +136,8 @@ internal enum TrackCommand
RailLimit,
/// <summary>Adds a buffer stop</summary>
RailBuffer,
/// <summary>Changes the player path</summary>
PlayerPath,

/*
* HMMSIM
Expand Down
67 changes: 67 additions & 0 deletions source/Plugins/Route.CsvRw/Namespaces/Track/Track.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using OpenBveApi;
using OpenBveApi.Colors;
using OpenBveApi.Interface;
Expand Down Expand Up @@ -3498,6 +3499,72 @@ private void ParseTrackCommand(TrackCommand Command, string[] Arguments, string
}
}
break;
case TrackCommand.PlayerPath:
{
int idx = 0;
if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out idx))
{
Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RailIndex is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File);
break;
}

if (idx < 0)
{
Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RailIndex is expected to be positive in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File);
break;
}

int idx1 = 0;
if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[1], out idx1))
{
Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RailIndex2 is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File);
break;
}

if (idx1 < 0)
{
Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RailIndex2 is expected to be positive in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File);
break;
}

if (idx != 0)
{
int block = BlockIndex;
while (Data.Blocks[block].Rails.ContainsKey(idx))
{
Data.Blocks[block].Rails[idx].IsDriveable = true;
if (Data.Blocks[block].Rails[idx].RailEnded)
{
break;
}
block--;
}
}
if (idx1 != 0)
{
int block = BlockIndex;
while (Data.Blocks[block].Rails.ContainsKey(idx1))
{
Data.Blocks[block].Rails[idx1].IsDriveable = true;
if (Data.Blocks[block].Rails[idx1].RailEnded)
{
break;
}
block--;
}
}
if (Data.Blocks[BlockIndex].Switches.Length <= idx)
{
Array.Resize(ref Data.Blocks[BlockIndex].Switches, idx + 1);
}
Data.Blocks[BlockIndex].Switches[idx] = new Switch
{
SecondTrack = idx1,
FixedRoute = true,
InitialSetting = idx1
};
}
break;
case TrackCommand.RailLimit:
{
int railIndex = -1;
Expand Down
2 changes: 2 additions & 0 deletions source/Plugins/Route.CsvRw/Structures/Tracks/Switch.cs
Expand Up @@ -14,5 +14,7 @@ internal class Switch
internal string Name;
/// <summary>The track names</summary>
internal string[] TrackNames;
/// <summary>Whether the switch has a fixed route</summary>
internal bool FixedRoute;
}
}
4 changes: 2 additions & 2 deletions source/Plugins/Train.OpenBve/Panel/PanelAnimatedXmlParser.cs
Expand Up @@ -175,7 +175,7 @@ private void ParsePanelAnimatedNode(XElement Element, string FileName, string Tr
break;
}

if (Enum.TryParse(Value, true, out Translations.Command command))
if (Enum.TryParse(Value.Replace("_", string.Empty), true, out Translations.Command command))
{
CommandEntry.Command = command;
}
Expand Down Expand Up @@ -340,7 +340,7 @@ private static void ParseTouchCommandEntryNode(string fileName, XElement parent,
break;
}

if (Enum.TryParse(value, true, out Translations.Command command))
if (Enum.TryParse(value.Replace("_", string.Empty), true, out Translations.Command command))
{
entry.Command = command;
}
Expand Down
4 changes: 2 additions & 2 deletions source/Plugins/Train.OpenBve/Panel/PanelXmlParser.cs
Expand Up @@ -423,7 +423,7 @@ private void ParsePanelNode(XElement Element, string FileName, TrainBase Train,
break;
}

if (Enum.TryParse(Value, true, out Translations.Command command))
if (Enum.TryParse(Value.Replace("_", string.Empty), true, out Translations.Command command))
{
CommandEntry.Command = command;
}
Expand Down Expand Up @@ -1735,7 +1735,7 @@ private void ParseTouchCommandEntryNode(string fileName, XElement parent, IColle
break;
}

if (Enum.TryParse(value, true, out Translations.Command command))
if (Enum.TryParse(value.Replace("_", string.Empty), true, out Translations.Command command))
{
entry.Command = command;
}
Expand Down
10 changes: 9 additions & 1 deletion source/RouteManager2/Tracks/Switch.cs
Expand Up @@ -71,11 +71,14 @@ public string CurrentSetting
/// <summary>The nominal direction of the switch</summary>
public readonly TrackDirection Direction;

/// <summary>Whether the switch has a fixed route</summary>
public readonly bool FixedRoute;

/// <summary>Whether a train has run through the switch in the wrong direction</summary>
/// <remarks>Toggle switch to reset</remarks>
public bool RunThrough;

public Switch(int[] tracks, string[] trackNames, int toeRail, int initialTrack, double trackPosition, SwitchType type, string name, TrackDirection direction)
public Switch(int[] tracks, string[] trackNames, int toeRail, int initialTrack, double trackPosition, SwitchType type, string name, bool fixedRoute, TrackDirection direction)
{
Type = type;
TrackNames = trackNames;
Expand All @@ -92,13 +95,18 @@ public Switch(int[] tracks, string[] trackNames, int toeRail, int initialTrack,
}

Name = name;
FixedRoute = fixedRoute;
Direction = direction;
RunThrough = false;
}

/// <summary>Toggles the switch to the next track</summary>
public void Toggle()
{
if (FixedRoute)
{
return;
}
setTrack++;
if (setTrack > availableTracks.Length - 1)
{
Expand Down
Expand Up @@ -9,7 +9,7 @@ namespace TrainManager.Trains
/// <summary>The AI class used to operate a TFO</summary>
public class TrackFollowingObjectAI : GeneralAI
{
private readonly TrackFollowingObject Train;
private readonly ScriptedTrain Train;

// Time to operate the door
private readonly double OpeningDoorsTime;
Expand All @@ -24,7 +24,7 @@ public class TrackFollowingObjectAI : GeneralAI
private double TimeLastProcessed;
private double CurrentPosition;

public TrackFollowingObjectAI(TrackFollowingObject train, TravelData[] data)
public TrackFollowingObjectAI(ScriptedTrain train, TravelData[] data)
{
Train = train;
Data = data;
Expand Down

0 comments on commit 2991f91

Please sign in to comment.