Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
662 changes: 658 additions & 4 deletions Source/Orts.Formats.Msts/TrackDatabaseFile.cs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Source/Orts.Simulation/Simulation/AIs/AIPath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ public class AIPathNode
public WorldLocation Location; // coordinates for this path node
public int JunctionIndex = -1; // index of junction node, -1 if none
public bool IsFacingPoint; // true if this node entered from the facing point end
public bool IsIntermediateNode; // true if the path node is an intermediate node within a vector node
//public bool IsLastSwitchUse; //true if this node is last to touch a switch
public bool IsVisited; // true if the train has visited this node

Expand Down Expand Up @@ -300,6 +301,7 @@ public AIPathNode(AIPathNode otherNode)
Location = otherNode.Location;
JunctionIndex = otherNode.JunctionIndex;
IsFacingPoint = otherNode.IsFacingPoint;
IsIntermediateNode = otherNode.IsIntermediateNode;
IsVisited = otherNode.IsVisited;
}

Expand All @@ -318,6 +320,8 @@ public AIPathNode(AIPathNode otherNode)
// in principle it would be more logical to have it in PATfile.cs. But this leads to too much code duplication
private void InterpretPathNodeFlags(TrPathNode tpn, TrackPDP pdp, bool isTimetableMode)
{
if ((tpn.pathFlags & 04) != 0) IsIntermediateNode = true;

if ((tpn.pathFlags & 03) == 0) return;
// Bit 0 and/or bit 1 is set.

Expand Down Expand Up @@ -386,6 +390,7 @@ public AIPathNode(BinaryReader inf)
NextSidingTVNIndex = inf.ReadInt32();
JunctionIndex = inf.ReadInt32();
IsFacingPoint = inf.ReadBoolean();
IsIntermediateNode = inf.ReadBoolean();
Location = new WorldLocation();
Location.TileX = inf.ReadInt32();
Location.TileZ = inf.ReadInt32();
Expand All @@ -407,6 +412,7 @@ public void Save(BinaryWriter outf)
outf.Write(NextSidingTVNIndex);
outf.Write(JunctionIndex);
outf.Write(IsFacingPoint);
outf.Write(IsIntermediateNode);
outf.Write(Location.TileX);
outf.Write(Location.TileZ);
outf.Write(Location.Location.X);
Expand Down
5 changes: 3 additions & 2 deletions Source/Orts.Simulation/Simulation/Activity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,9 +528,10 @@ public void AddRestrictZones(Tr_RouteFile routeFile, TrackSectionsFile tsectionD
zones.ActivityRestrictedSpeedZoneList[idxZone].EndPosition, false, worldPosition2, false);

// Add the speedposts to the track database. This will set the TrItemId's of all speedposts
trackDB.AddTrItems(newSpeedPostItems);
trackDB.AddTrItems(newSpeedPostItems);
// TODO: shoulde the item be added to the TrVectorNode's TrItemRefs?

// And now update the various (vector) tracknodes (this needs the TrItemIds.
// And now update the various (vector) tracknodes (this needs the TrItemIds.
var endOffset = AddItemIdToTrackNode(ref zones.ActivityRestrictedSpeedZoneList[idxZone].EndPosition,
tsectionDat, trackDB, newSpeedPostItems[1], out traveller);
var startOffset = AddItemIdToTrackNode(ref zones.ActivityRestrictedSpeedZoneList[idxZone].StartPosition,
Expand Down
100 changes: 100 additions & 0 deletions Source/Orts.Simulation/Simulation/Physics/Train.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
using ORTS.Common;
using ORTS.Scripting.Api;
using ORTS.Settings;
using static System.Collections.Specialized.BitVector32;
using Event = Orts.Common.Event;

namespace Orts.Simulation.Physics
Expand Down Expand Up @@ -14678,6 +14679,7 @@ public String[] AddRestartTime(String[] stateString)
public List<TrainObjectItem>[] PlayerTrainSpeedposts; // 0 forward, 1 backward
public List<TrainObjectItem>[,] PlayerTrainDivergingSwitches; // 0 forward, 1 backward; second index 0 facing, 1 trailing
public List<TrainObjectItem>[] PlayerTrainMileposts; // 0 forward, 1 backward
public List<TrainObjectItem>[] PlayerTrainGradeposts; // 0 forward, 1 backward
public List<TrainObjectItem>[] PlayerTrainTunnels; // 0 forward, 1 backward

/// <summary>
Expand All @@ -14691,6 +14693,7 @@ public void InitializePlayerTrainData()
PlayerTrainSpeedposts = new List<TrainObjectItem>[2];
PlayerTrainDivergingSwitches = new List<TrainObjectItem>[2, 2];
PlayerTrainMileposts = new List<TrainObjectItem>[2];
PlayerTrainGradeposts = new List<TrainObjectItem>[2];
PlayerTrainTunnels = new List<TrainObjectItem>[2];
for (int dir = 0; dir < 2; dir++)
{
Expand All @@ -14702,6 +14705,7 @@ public void InitializePlayerTrainData()
for (int i = 0; i < 2; i++)
PlayerTrainDivergingSwitches[dir, i] = new List<TrainObjectItem>();
PlayerTrainMileposts[dir] = new List<TrainObjectItem>();
PlayerTrainGradeposts[dir] = new List<TrainObjectItem>();
PlayerTrainTunnels[dir] = new List<TrainObjectItem>();
}
}
Expand All @@ -14717,6 +14721,8 @@ public void InitializePlayerTrainData()
playerTrainDivergingSwitchList?.Clear();
foreach (var playerTrainMilepostList in PlayerTrainMileposts)
playerTrainMilepostList?.Clear();
foreach (var playerTrainGradepostList in PlayerTrainGradeposts)
playerTrainGradepostList?.Clear();
foreach (var playerTrainTunnelList in PlayerTrainTunnels)
playerTrainTunnelList?.Clear();
}
Expand All @@ -14731,6 +14737,11 @@ public void UpdatePlayerTrainData()
//TODO add generation of other train data
}

#if DEBUG
public int NextDumpTime = 0; // DEBUG
public int CallCount = 2;
#endif

/// <summary>
/// Updates the Player train data;
/// For every section it adds the TrainObjectItems to the various lists;
Expand Down Expand Up @@ -14816,6 +14827,9 @@ public void UpdatePlayerTrainData(float maxDistanceM)
var routePath = ValidRoute[dir];
var prevMilepostValue = -1f;
var prevMilepostDistance = -1f;
var prevGradepostValue = -1f;
var prevGradepostDistance = -1f;

while (index < routePath.Count && totalLength - lengthOffset < maxDistanceNORMALM)
{
var sectionDistanceToTrainM = totalLength - lengthOffset;
Expand Down Expand Up @@ -14983,6 +14997,34 @@ public void UpdatePlayerTrainData(float maxDistanceM)
}
}

// search for grade posts
// no change if using sectionDirection, match as 0 or match as 1
int relativeDirection = sectionDirection == dir ? 0 : 1;
if (thisSection.CircuitItems.TrackCircuitGradeposts[relativeDirection] != null)
{
foreach (TrackCircuitGradepost thisGradepostItem in thisSection.CircuitItems.TrackCircuitGradeposts[sectionDirection])
{
Gradepost thisGradepost = thisGradepostItem.GradepostRef;
var distanceToTrainM = thisGradepostItem.GradepostLocation + sectionDistanceToTrainM;
if (distanceToTrainM < maxDistanceM)
{
if (!(distanceToTrainM - prevGradepostDistance < 50 && thisGradepost.GradePct == prevGradepostValue) && distanceToTrainM > 0)
{
thisItem = new TrainObjectItem(thisGradepost.GradePct, distanceToTrainM);
prevGradepostDistance = distanceToTrainM;
prevGradepostValue = thisGradepost.GradePct;
PlayerTrainGradeposts[dir].Add(thisItem);
#if DEBUG
if (System.DateTime.Now.Second > NextDumpTime || CallCount > 0)
Debug.WriteLine(String.Format("Train-Adding: dir {0}, TNIdx {1}, distance {2:F1}, grade {3:F2}, circuitIDX {4}, TrItemId {5}",
dir, thisGradepost.TrackNodeIdx, thisItem.DistanceToTrainM, thisItem.GradePct, thisSection.Index, thisGradepost.TrItemId));
#endif
}
}
else break;
}
}

// search for tunnels
if (thisSection.TunnelInfo != null)
{
Expand Down Expand Up @@ -15019,6 +15061,30 @@ public void UpdatePlayerTrainData(float maxDistanceM)
continue;
}
}
#if DEBUG
if (System.DateTime.Now.Second > NextDumpTime || CallCount > 0)
{
int cnt = 0;
foreach (var gradepost in PlayerTrainGradeposts[0])
{
Debug.WriteLine(String.Format("TrainGradepost-Fwd: {0}, distance {1:F1}, grade = {2:F2}",
cnt, gradepost.DistanceToTrainM, gradepost.GradePct));
cnt++;

}
int cnt1 = 0;
foreach (var gradepost in PlayerTrainGradeposts[1])
{
Debug.WriteLine(String.Format("TrainGradepost-Rev: {0}, distance {1:F1}, grade = {2:F2}",
cnt1, gradepost.DistanceToTrainM, gradepost.GradePct));
cnt1++;

}
Debug.WriteLine(String.Format("TrainGradepost-count: fwd {0}, rev {1}", cnt, cnt1));
NextDumpTime = System.DateTime.Now.Second + 60;
CallCount--;
}
#endif
}

/// <summary>
Expand Down Expand Up @@ -15136,6 +15202,13 @@ public void GetTrainInfoAuto(ref TrainInfo thisInfo)
else break;
}

// Add all grade posts within maximum distance
foreach (TrainObjectItem thisTrainItem in PlayerTrainGradeposts[0])
{
if (thisTrainItem.DistanceToTrainM <= maxDistanceM) thisInfo.ObjectInfoForward.Add(thisTrainItem);
else break;
}

// Add all diverging switches within maximum distance
foreach (TrainObjectItem thisTrainItem in PlayerTrainDivergingSwitches[0, 0])
{
Expand Down Expand Up @@ -15311,6 +15384,13 @@ public void GetTrainInfoManual(ref TrainInfo thisInfo)
else break;
}

// Add all grade posts within maximum distance
foreach (TrainObjectItem thisTrainItem in PlayerTrainGradeposts[0])
{
if (thisTrainItem.DistanceToTrainM <= maxDistanceM) thisInfo.ObjectInfoForward.Add(thisTrainItem);
else break;
}

// Add all diverging switches within maximum distance
foreach (TrainObjectItem thisTrainItem in PlayerTrainDivergingSwitches[0, 0])
{
Expand Down Expand Up @@ -15358,6 +15438,13 @@ public void GetTrainInfoManual(ref TrainInfo thisInfo)
else break;
}

// Add all grade posts within maximum distance
foreach (TrainObjectItem thisTrainItem in PlayerTrainGradeposts[1])
{
if (thisTrainItem.DistanceToTrainM <= maxDistanceM) thisInfo.ObjectInfoBackward.Add(thisTrainItem);
else break;
}

// Add all diverging switches within maximum distance
foreach (TrainObjectItem thisTrainItem in PlayerTrainDivergingSwitches[1, 0])
{
Expand Down Expand Up @@ -21431,6 +21518,7 @@ public enum TRAINOBJECTTYPE
TRAILING_SWITCH,
GENERIC_SIGNAL,
TUNNEL,
GRADEPOST,
}

public enum SpeedItemType
Expand All @@ -21452,6 +21540,7 @@ public enum SpeedItemType
public SpeedItemType SpeedObjectType;
public bool Valid;
public string ThisMile;
public float GradePct;
public bool IsRightSwitch;
public SignalObject SignalObject;

Expand Down Expand Up @@ -21595,6 +21684,17 @@ public TrainObjectItem(string thisMile, float thisDistanceM)
ThisMile = thisMile;
}

// Constructor for Gradepost
public TrainObjectItem(float gradePct, float thisDistanceM)
{
ItemType = TRAINOBJECTTYPE.GRADEPOST;
AuthorityType = END_AUTHORITY.NO_PATH_RESERVED;
SignalState = TrackMonitorSignalAspect.Clear_2;
AllowedSpeedMpS = -1;
DistanceToTrainM = thisDistanceM;
GradePct = gradePct;
}

// Constructor for facing or trailing Switch
public TrainObjectItem(bool isRightSwitch, float thisDistanceM, TRAINOBJECTTYPE type)
{
Expand Down
62 changes: 62 additions & 0 deletions Source/Orts.Simulation/Simulation/Signalling/Gradepost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// COPYRIGHT 2025 by the Open Rails project.
//
// This file is part of Open Rails.
//
// Open Rails is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Open Rails is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.

// This is part of the effort to respresent grade information in the track monitor.
// The initial version determinates significant grade changes from the track database.
//
// Milempost are taken as a model for presenting the grade information in the simulation.
// This will allow, in the future, to add grade-posts to the track, as is the case at some
// railways (eg. in Switzerland).

namespace Orts.Simulation.Signalling
{
/// <summary>
/// Represents either a world-object grade post, or a calculated grade post.
/// Only the latter is currently supported.
/// </summary>
public class Gradepost
{
/// <summary>Reference to TrItem; index into TrackDB.TrItemTable.</summary>
public uint TrItemId;
/// <summary>Reference to TrackCircuitSection; index into Signals.TrackCircuitList.</summary>
public int TCReference = -1; // undefined
/// <summary>Position within TrackCircuit. Distance im meters?</summary>
public float TCOffset;
/// <summary>Grade in percent.</summary>
public float GradePct;
/// <summary>Distance in meters for which the grade applies.</summary>
public float ForDistanceM;
/// <summary>The direction in which the grade applies. 0 is in track circuit direction, 1 is in reverse direction.</summary>
public int Direction;
/// <summary>Reference to TrackNode; index into TrackDB.TrackNodes.</summary>
public int TrackNodeIdx;

/// <summary>Constructor with base attributes.</summary>
public Gradepost(uint trItemId, float gradePct, float distanceM, int dir)
{
TrItemId = trItemId;
GradePct = gradePct;
ForDistanceM = distanceM;
Direction = dir;
}

/// <summary>Dummy constructor</summary>
public Gradepost()
{
}
}
}
Loading