diff --git a/TLM/TLM/Manager/Impl/ExtNodeManager.cs b/TLM/TLM/Manager/Impl/ExtNodeManager.cs
index 4a935c1db..7a75b416c 100644
--- a/TLM/TLM/Manager/Impl/ExtNodeManager.cs
+++ b/TLM/TLM/Manager/Impl/ExtNodeManager.cs
@@ -38,7 +38,7 @@ private ExtNodeManager() {
/// NodeId of the node to test.
///
public static bool JunctionHasHighwayRules(ushort nodeId) {
- return JunctionHasOnlyHighwayRoads(nodeId) && !LaneConnection.LaneConnectionManager.Instance.Sub.HasNodeConnections(nodeId);
+ return JunctionHasOnlyHighwayRoads(nodeId) && !LaneConnection.LaneConnectionManager.Instance.Road.HasNodeConnections(nodeId);
}
public GetNodeSegmentIdsEnumerable GetNodeSegmentIds(ushort nodeId, ClockDirection clockDirection) {
diff --git a/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionManager.cs b/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionManager.cs
index 52806d2b7..bf0a3ffe1 100644
--- a/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionManager.cs
+++ b/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionManager.cs
@@ -4,6 +4,7 @@ namespace TrafficManager.Manager.Impl.LaneConnection {
using TrafficManager.API.Manager;
using TrafficManager.API.Traffic.Data;
using TrafficManager.API.Traffic.Enums;
+ using TrafficManager.Util;
using TrafficManager.Util.Extensions;
using UnityEngine;
#if DEBUG
@@ -23,8 +24,8 @@ public class LaneConnectionManager
| VehicleInfo.VehicleType.Monorail
| VehicleInfo.VehicleType.Trolleybus;
- public LaneConnectionSubManager Sub = // TODO #354 divide into Road/Track
- new LaneConnectionSubManager(LaneEndTransitionGroup.Vehicle);
+ public LaneConnectionSubManager Road = new LaneConnectionSubManager(LaneEndTransitionGroup.Road);
+ public LaneConnectionSubManager Track = new LaneConnectionSubManager(LaneEndTransitionGroup.Track);
public NetInfo.LaneType LaneTypes => LANE_TYPES;
@@ -38,16 +39,19 @@ static LaneConnectionManager() {
public override void OnBeforeLoadData() {
base.OnBeforeLoadData();
- Sub.OnBeforeLoadData();
+ Road.OnBeforeLoadData();
+ Track.OnBeforeLoadData();
}
public override void OnLevelUnloading() {
base.OnLevelUnloading();
- Sub.OnLevelUnloading();
+ Road.OnLevelUnloading();
+ Track.OnLevelUnloading();
}
protected override void InternalPrintDebugInfo() {
base.InternalPrintDebugInfo();
- Sub.PrintDebugInfo();
+ Road.PrintDebugInfo();
+ Track.PrintDebugInfo();
}
///
@@ -55,7 +59,60 @@ protected override void InternalPrintDebugInfo() {
///
/// check at start node of source lane?
public bool AreLanesConnected(uint sourceLaneId, uint targetLaneId, bool sourceStartNode) {
- return Sub.AreLanesConnected(sourceLaneId, targetLaneId, sourceStartNode);
+ return Road.AreLanesConnected(sourceLaneId, targetLaneId, sourceStartNode) ||
+ Track.AreLanesConnected(sourceLaneId, targetLaneId, sourceStartNode);
+ }
+
+ public bool AreLanesConnected(uint sourceLaneId, uint targetLaneId, bool sourceStartNode, LaneEndTransitionGroup group) {
+ bool ret = Road.Supports(group) &&
+ Road.AreLanesConnected(sourceLaneId, targetLaneId, sourceStartNode);
+ if (!ret) {
+ ret = Track.Supports(group) &&
+ Track.AreLanesConnected(sourceLaneId, targetLaneId, sourceStartNode);
+ }
+ return ret;
+ }
+
+
+ ///
+ /// Adds a lane connection between two lanes.
+ /// pass in LaneEndTransitionGroup.All to add lane connections in every sub manager that supports both lanes.
+ ///
+ /// From lane id
+ /// To lane id
+ /// The affected node
+ /// lane or track
+ /// true if any connection was added, falsse otherwise
+ public bool AddLaneConnection(uint sourceLaneId, uint targetLaneId, bool sourceStartNode, LaneEndTransitionGroup group) {
+ bool success = true;
+ if (Road.Supports(group)) {
+ success = Road.AddLaneConnection(sourceLaneId, targetLaneId, sourceStartNode);
+ }
+ if (Track.Supports(group)) {
+ success &= Track.AddLaneConnection(sourceLaneId, targetLaneId, sourceStartNode);
+ }
+ return success;
+ }
+
+ public bool RemoveLaneConnection(uint sourceLaneId, uint targetLaneId, bool sourceStartNode, LaneEndTransitionGroup group) {
+ bool success = true;
+ var sourceLaneInfo = ExtLaneManager.Instance.GetLaneInfo(sourceLaneId);
+ var targetLaneInfo = ExtLaneManager.Instance.GetLaneInfo(targetLaneId);
+ if (Road.Supports(group)) {
+ success = Road.RemoveLaneConnection(sourceLaneId, targetLaneId, sourceStartNode);
+ } else {
+ bool canConnect = Road.Supports(sourceLaneInfo) && Road.Supports(targetLaneInfo);
+ if (!canConnect)
+ Road.RemoveLaneConnection(sourceLaneId, targetLaneId, sourceStartNode);
+ }
+ if (Track.Supports(group)) {
+ success |= Track.RemoveLaneConnection(sourceLaneId, targetLaneId, sourceStartNode);
+ } else {
+ bool canConnect = Track.Supports(sourceLaneInfo) && Track.Supports(targetLaneInfo);
+ if (!canConnect)
+ Track.RemoveLaneConnection(sourceLaneId, targetLaneId, sourceStartNode);
+ }
+ return success;
}
///
@@ -63,23 +120,60 @@ public bool AreLanesConnected(uint sourceLaneId, uint targetLaneId, bool sourceS
/// Performance note: This act as HasOutgoingConnections for uni-directional lanes but faster
///
public bool HasConnections(uint laneId, bool startNode) =>
- Sub.HasConnections(laneId, startNode);
+ Road.HasConnections(laneId, startNode) || Track.HasConnections(laneId, startNode);
///
/// Determines if there exist custom lane connections at the specified node
///
- public bool HasNodeConnections(ushort nodeId) => Sub.HasNodeConnections(nodeId);
+ public bool HasNodeConnections(ushort nodeId) =>
+ Road.HasNodeConnections(nodeId) || Track.HasNodeConnections(nodeId);
// Note: Not performance critical
public bool HasUturnConnections(ushort segmentId, bool startNode) =>
- Sub.HasUturnConnections(segmentId, startNode);
+ Road.HasUturnConnections(segmentId, startNode);
///
/// Removes all lane connections at the specified node
///
/// Affected node
internal void RemoveLaneConnectionsFromNode(ushort nodeId) {
- Sub.RemoveLaneConnectionsFromNode(nodeId);
+ Road.RemoveLaneConnectionsFromNode(nodeId);
+ Track.RemoveLaneConnectionsFromNode(nodeId);
+ }
+
+
+ ///
+ /// Checks if the turning angle between two segments at the given node is within bounds.
+ ///
+ public static bool CheckSegmentsTurningAngle(ushort sourceSegmentId,
+ bool sourceStartNode,
+ ushort targetSegmentId,
+ bool targetStartNode) {
+ if (sourceSegmentId == targetSegmentId) {
+ return false;
+ }
+
+ ref NetSegment sourceSegment = ref sourceSegmentId.ToSegment();
+ ref NetSegment targetSegment = ref targetSegmentId.ToSegment();
+ float turningAngle = 0.01f - Mathf.Min(
+ sourceSegment.Info.m_maxTurnAngleCos,
+ targetSegment.Info.m_maxTurnAngleCos);
+
+ if (turningAngle < 1f) {
+ Vector3 sourceDirection = sourceStartNode
+ ? sourceSegment.m_startDirection
+ : sourceSegment.m_endDirection;
+
+ Vector3 targetDirection = targetStartNode
+ ? targetSegment.m_startDirection
+ : targetSegment.m_endDirection;
+
+ float dirDotProd = (sourceDirection.x * targetDirection.x) +
+ (sourceDirection.z * targetDirection.z);
+ return dirDotProd < turningAngle;
+ }
+
+ return true;
}
internal bool GetLaneEndPoint(ushort segmentId,
@@ -170,14 +264,17 @@ internal bool GetLaneEndPoint(ushort segmentId,
}
public bool LoadData(List data) {
- bool success = true;
+ bool success;
Log.Info($"Loading {data.Count} lane connections");
- success = Sub.LoadData(data);
+ success = Road.LoadData(data);
+ success &= Track.LoadData(data);
return success;
}
public List SaveData(ref bool success) {
- return Sub.SaveData(ref success);
+ var ret = Road.SaveData(ref success);
+ ret.AddRange(Track.SaveData(ref success));
+ return ret;
}
}
}
\ No newline at end of file
diff --git a/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionSubManager.cs b/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionSubManager.cs
index fb68c7144..bf34aa2fe 100644
--- a/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionSubManager.cs
+++ b/TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionSubManager.cs
@@ -9,11 +9,8 @@ namespace TrafficManager.Manager.Impl.LaneConnection {
using TrafficManager.Lifecycle;
using TrafficManager.State;
using TrafficManager.Util.Extensions;
- using UnityEngine;
using static TrafficManager.Util.Shortcuts;
using TrafficManager.Util;
- using TrafficManager.Util.Extensions;
- using TrafficManager.Lifecycle;
using TrafficManager.Patch;
#if DEBUG
using TrafficManager.State.ConfigData;
@@ -24,13 +21,36 @@ public class LaneConnectionSubManager :
ICustomDataManager>, ILaneConnectionManager {
private ConnectionDataBase connectionDataBase_;
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "RAS0002:Readonly field for a non-readonly struct", Justification = "False alarm")]
+#pragma warning disable RAS0002 // Readonly field for a non-readonly struct
public readonly LaneEndTransitionGroup Group;
+ public readonly NetInfo.LaneType laneTypes_;
+ public readonly VehicleInfo.VehicleType vehicleTypes_;
+#pragma warning restore RAS0002 // Readonly field for a non-readonly struct
+
+ internal LaneConnectionSubManager(LaneEndTransitionGroup group) {
+ Group = group;
+ laneTypes_ = default;
+ vehicleTypes_ = default;
+ if (Group == LaneEndTransitionGroup.Road) {
+ laneTypes_ |= TrackUtils.ROAD_LANE_TYPES;
+ vehicleTypes_ |= TrackUtils.ROAD_VEHICLE_TYPES;
+ }
+ if (Group == LaneEndTransitionGroup.Track) {
+ laneTypes_ |= TrackUtils.TRACK_LANE_TYPES;
+ vehicleTypes_ |= TrackUtils.TRACK_VEHICLE_TYPES;
+ }
+ }
- internal LaneConnectionSubManager(LaneEndTransitionGroup group) => Group = group;
+ public NetInfo.LaneType LaneTypes => laneTypes_;
+
+ public VehicleInfo.VehicleType VehicleTypes => vehicleTypes_;
+
+ ///
+ /// tests if the input group is supported by this sub-manager.
+ ///
+ public bool Supports(LaneEndTransitionGroup group) => (group & Group) != 0;
- public NetInfo.LaneType LaneTypes => LaneConnectionManager.LANE_TYPES;
- public VehicleInfo.VehicleType VehicleTypes => LaneConnectionManager.VEHICLE_TYPES;
+ public bool Supports(NetInfo.Lane laneInfo) => laneInfo.Matches(laneTypes_, vehicleTypes_);
private LaneConnectionSubManager() {
NetManagerEvents.Instance.ReleasingSegment += ReleasingSegment;
@@ -242,7 +262,7 @@ internal bool RemoveLaneConnection(uint sourceLaneId, uint targetLaneId, bool so
return false;
}
- if ((Group & LaneEndTransitionGroup.Road) != 0) {
+ if (Supports(LaneEndTransitionGroup.Road)) {
RecalculateLaneArrows(sourceLaneId, nodeId, sourceStartNode);
}
@@ -340,9 +360,48 @@ internal bool AddLaneConnection(uint sourceLaneId, uint targetLaneId, bool sourc
return false;
}
+ var sourceLaneInfo = ExtLaneManager.Instance.GetLaneInfo(sourceLaneId);
+ var targetLaneInfo = ExtLaneManager.Instance.GetLaneInfo(targetLaneId);
+ ref NetLane sourceNetLane = ref sourceLaneId.ToLane();
+ ref NetLane targetNetLane = ref targetLaneId.ToLane();
+ bool canConnect = Supports(sourceLaneInfo) && Supports(targetLaneInfo);
+ if (!canConnect) {
+ return false;
+ }
+
ushort sourceSegmentId = sourceLaneId.ToLane().m_segment;
ushort targetSegmentId = targetLaneId.ToLane().m_segment;
ushort nodeId = sourceSegmentId.ToSegment().GetNodeId(sourceStartNode);
+ ref NetNode netNode = ref nodeId.ToNode();
+
+ // check if source lane goes toward the node
+ // and target lane goes away from the node.
+ static bool IsDirectionValid(ref NetLane lane, NetInfo.Lane laneInfo, ushort nodeId, bool source) {
+ bool invert = lane.m_segment.ToSegment().m_flags.IsFlagSet(NetSegment.Flags.Invert);
+ bool startNode = lane.IsStartNode(nodeId);
+ var dir = laneInfo.m_finalDirection;
+ if (source ^ startNode ^ invert) {
+ return dir.IsFlagSet(NetInfo.Direction.Forward);
+ } else {
+ return dir.IsFlagSet(NetInfo.Direction.Backward);
+ }
+ }
+ canConnect =
+ IsDirectionValid(ref sourceNetLane, sourceLaneInfo, nodeId, true) &&
+ IsDirectionValid(ref targetNetLane, targetLaneInfo, nodeId, false);
+ if (!canConnect) {
+ return false;
+ }
+
+ if (Group == LaneEndTransitionGroup.Track) {
+ bool targetStartnode = targetSegmentId.ToSegment().IsStartNode(nodeId);
+ canConnect = LaneConnectionManager.CheckSegmentsTurningAngle(
+ sourceSegmentId, sourceStartNode, targetSegmentId, targetStartnode);
+ if (!canConnect) {
+ return false;
+ }
+ }
+
connectionDataBase_.ConnectTo(sourceLaneId, targetLaneId, nodeId);
Assert(AreLanesConnected(sourceLaneId, targetLaneId, sourceStartNode), $"AreLanesConnected({sourceLaneId}, {targetLaneId}, {sourceStartNode})");
@@ -357,9 +416,9 @@ internal bool AddLaneConnection(uint sourceLaneId, uint targetLaneId, bool sourc
$"{targetLaneId}, {sourceStartNode})");
}
-
-
- RecalculateLaneArrows(sourceLaneId, nodeId, sourceStartNode);
+ if (Supports(LaneEndTransitionGroup.Road)) {
+ RecalculateLaneArrows(sourceLaneId, nodeId, sourceStartNode);
+ }
if (sourceSegmentId == targetSegmentId) {
JunctionRestrictionsManager.Instance.SetUturnAllowed(
@@ -368,8 +427,7 @@ internal bool AddLaneConnection(uint sourceLaneId, uint targetLaneId, bool sourc
true);
}
- RoutingManager.Instance.RequestRecalculation(sourceSegmentId, false);
- RoutingManager.Instance.RequestRecalculation(targetSegmentId, false);
+ RoutingManager.Instance.RequestNodeRecalculation(ref netNode);
if (TMPELifecycle.Instance.MayPublishSegmentChanges()) {
ExtSegmentManager extSegmentManager = ExtSegmentManager.Instance;
@@ -388,7 +446,7 @@ private void ReleasingSegment(ushort segmentId, ref NetSegment segment) {
const bool logLaneConnections = false;
#endif
if (logLaneConnections) {
- Log._Debug($"LaneConnectionManager.ReleasingSegment({segmentId}, isValid={segment.IsValid()}): " +
+ Log._Debug($"LaneConnectionSubManager({Group}).ReleasingSegment({segmentId}, isValid={segment.IsValid()}): " +
"Segment is about to become invalid. Removing lane connections.");
}
@@ -402,76 +460,6 @@ private void ReleasingSegment(ushort segmentId, ref NetSegment segment) {
}
}
- internal bool GetLaneEndPoint(ushort segmentId,
- bool startNode,
- byte laneIndex,
- uint? laneId,
- NetInfo.Lane laneInfo,
- out bool outgoing,
- out bool incoming,
- out Vector3? pos) {
- ref NetSegment netSegment = ref segmentId.ToSegment();
-
- pos = null;
- outgoing = false;
- incoming = false;
-
- if ((netSegment.m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created) {
- return false;
- }
-
- if (laneId == null) {
- laneId = ExtSegmentManager.Instance.GetLaneId(segmentId, laneIndex);
- if (laneId == 0) {
- return false;
- }
- }
-
- ref NetLane netLane = ref ((uint)laneId).ToLane();
-
- if ((netLane.m_flags &
- ((ushort)NetLane.Flags.Created | (ushort)NetLane.Flags.Deleted)) !=
- (ushort)NetLane.Flags.Created) {
- return false;
- }
-
- if (laneInfo == null) {
- if (laneIndex < netSegment.Info.m_lanes.Length) {
- laneInfo = netSegment.Info.m_lanes[laneIndex];
- } else {
- return false;
- }
- }
-
- NetInfo.Direction laneDir = ((netSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None)
- ? laneInfo.m_finalDirection
- : NetInfo.InvertDirection(laneInfo.m_finalDirection);
-
- if (startNode) {
- if ((laneDir & NetInfo.Direction.Backward) != NetInfo.Direction.None) {
- outgoing = true;
- }
-
- if ((laneDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
- incoming = true;
- }
-
- pos = netLane.m_bezier.a;
- } else {
- if ((laneDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
- outgoing = true;
- }
-
- if ((laneDir & NetInfo.Direction.Backward) != NetInfo.Direction.None) {
- incoming = true;
- }
-
- pos = netLane.m_bezier.d;
- }
-
- return true;
- }
-
///
/// Recalculates lane arrows based on present lane connections.
///
@@ -701,6 +689,10 @@ public bool LoadData(List data) {
foreach (Configuration.LaneConnection conn in data) {
try {
+ if(!Supports(conn.group)) {
+ continue;
+ }
+
ref NetLane lowerLane = ref conn.sourceLaneId.ToLane();
if (!lowerLane.IsValidWithSegment()) {
continue;
@@ -719,7 +711,7 @@ public bool LoadData(List data) {
Log._Debug($"Loading lane connection: lane {conn.sourceLaneId} -> {conn.targetLaneId}");
#endif
AddLaneConnection(conn.sourceLaneId, conn.targetLaneId, conn.sourceStartNode);
- if (conn.Legacy) {
+ if (conn.LegacyBidirectional) {
ushort segmentId = conn.sourceLaneId.ToLane().m_segment;
ushort nodeId = segmentId.ToSegment().GetNodeId(conn.sourceStartNode);
bool targetStartNode = conn.targetLaneId.ToLane().IsStartNode(nodeId);
@@ -753,7 +745,8 @@ public bool LoadData(List data) {
new Configuration.LaneConnection(
source.LaneId,
target.LaneId,
- source.StartNode));
+ source.StartNode,
+ Group));
}
} catch (Exception e) {
Log.Error($"Exception occurred while saving lane data @ {source.LaneId}: {e.ToString()}");
diff --git a/TLM/TLM/Manager/Impl/RoutingManager.cs b/TLM/TLM/Manager/Impl/RoutingManager.cs
index b52f2775c..c8f280003 100644
--- a/TLM/TLM/Manager/Impl/RoutingManager.cs
+++ b/TLM/TLM/Manager/Impl/RoutingManager.cs
@@ -676,11 +676,11 @@ void _ExtendedLogImpl(params object[] lines) => DetailLogger.LogDebug(
// routing tracked vehicles (trains, trams, metros, monorails)
// lane may be mixed car+tram
bool nextHasConnections =
- LaneConnectionManager.Instance.Sub.HasConnections(
+ LaneConnectionManager.Instance.Track.HasConnections(
nextLaneId,
isNodeStartNodeOfNextSegment);
if (nextHasConnections) {
- bool connected = LaneConnectionManager.Instance.Sub.AreLanesConnected(
+ bool connected = LaneConnectionManager.Instance.Track.AreLanesConnected(
nextLaneId,
prevLaneId,
isNodeStartNodeOfNextSegment);
@@ -763,11 +763,11 @@ void _ExtendedLogImpl(params object[] lines) => DetailLogger.LogDebug(
bool connected = true;
bool nextHasConnections =
- LaneConnectionManager.Instance.Sub.HasConnections(
+ LaneConnectionManager.Instance.Road.HasConnections(
nextLaneId,
isNodeStartNodeOfNextSegment);
if (nextHasConnections) {
- connected = LaneConnectionManager.Instance.Sub.AreLanesConnected(
+ connected = LaneConnectionManager.Instance.Road.AreLanesConnected(
nextLaneId,
prevLaneId,
isNodeStartNodeOfNextSegment);
@@ -1082,7 +1082,7 @@ bool laneChangesAllowed
// skip lanes having lane connections
// in highway-rules HasConnections() gives the same result as HasOutgoingConnections but faster.
- if (LaneConnectionManager.Instance.Sub.HasConnections(
+ if (LaneConnectionManager.Instance.Road.HasConnections(
nextCompatibleTransitionDatas[nextTransitionIndex].laneId,
isNodeStartNodeOfNextSegment)) {
int laneConnectionTransIndex =
@@ -1404,7 +1404,8 @@ bool laneChangesAllowed
}
// skip lanes having lane connections
- if (LaneConnectionManager.Instance.Sub.HasOutgoingConnections(
+ // nextCompatibleTransitionDatas is only for roads.
+ if (LaneConnectionManager.Instance.Road.HasOutgoingConnections(
nextCompatibleTransitionDatas[nextTransitionIndex].laneId,
isNodeStartNodeOfNextSegment)) {
int laneConnectionTransIndex =
diff --git a/TLM/TLM/Resources/LaneConnectionManager/direction_arrow.png b/TLM/TLM/Resources/LaneConnectionManager/direction_arrow.png
index 4b18f7b53..0d21786a3 100644
Binary files a/TLM/TLM/Resources/LaneConnectionManager/direction_arrow.png and b/TLM/TLM/Resources/LaneConnectionManager/direction_arrow.png differ
diff --git a/TLM/TLM/State/Configuration.cs b/TLM/TLM/State/Configuration.cs
index 9a8d13876..126517e22 100644
--- a/TLM/TLM/State/Configuration.cs
+++ b/TLM/TLM/State/Configuration.cs
@@ -9,6 +9,7 @@ namespace TrafficManager {
using System.Runtime.Serialization;
using TrafficManager.Lifecycle;
using Util;
+ using LaneEndTransitionGroup = TrafficManager.API.Traffic.Enums.LaneEndTransitionGroup;
[Serializable]
public class Configuration {
@@ -186,12 +187,15 @@ public class LaneConnection : ISerializable {
public bool sourceStartNode;
- public bool Legacy => SerializableDataExtension.Version < 2;
+ public LaneEndTransitionGroup group = LaneEndTransitionGroup.Vehicle;
- public LaneConnection(uint sourceLaneId, uint targetLaneId, bool sourceStartNode) {
+ public bool LegacyBidirectional => SerializableDataExtension.Version < 2;
+
+ public LaneConnection(uint sourceLaneId, uint targetLaneId, bool sourceStartNode, LaneEndTransitionGroup group) {
this.sourceLaneId = sourceLaneId;
this.targetLaneId = targetLaneId;
this.sourceStartNode = sourceStartNode;
+ this.group = group;
}
//serialization
@@ -215,6 +219,9 @@ public LaneConnection(SerializationInfo info, StreamingContext context) {
case nameof(sourceStartNode):
sourceStartNode = (bool)item.Value;
break;
+ case nameof(group):
+ group = (LaneEndTransitionGroup)item.Value;
+ break;
}
}
}
diff --git a/TLM/TLM/State/Flags.cs b/TLM/TLM/State/Flags.cs
index f291727df..78b8a0049 100644
--- a/TLM/TLM/State/Flags.cs
+++ b/TLM/TLM/State/Flags.cs
@@ -315,7 +315,7 @@ public static bool ResetLaneArrowFlags(uint laneId) {
#if DEBUGFLAGS
Log._Debug($"Flags.resetLaneArrowFlags: Resetting lane arrows of lane {laneId}.");
#endif
- if (LaneConnectionManager.Instance.Sub.HasOutgoingConnections(laneId)) {
+ if (LaneConnectionManager.Instance.Road.HasOutgoingConnections(laneId)) {
return false;
}
@@ -392,7 +392,7 @@ public static bool ToggleLaneArrowFlags(uint laneId,
return false; // disallow custom lane arrows in highway rule mode
}
- if (LaneConnectionManager.Instance.Sub.HasOutgoingConnections(laneId, startNode)) {
+ if (LaneConnectionManager.Instance.Road.HasOutgoingConnections(laneId, startNode)) {
// TODO refactor
res = SetLaneArrow_Result.LaneConnection;
return false; // custom lane connection present
diff --git a/TLM/TLM/UI/Helpers/Highlight.cs b/TLM/TLM/UI/Helpers/Highlight.cs
index 3b3d947ea..14b8e6768 100644
--- a/TLM/TLM/UI/Helpers/Highlight.cs
+++ b/TLM/TLM/UI/Helpers/Highlight.cs
@@ -11,6 +11,12 @@ namespace TrafficManager.UI.Helpers {
/// Must be called from GUI callbacks only, will not work from other code.
///
public static class Highlight {
+ public enum Shape {
+ Circle,
+ Square,
+ Diamond,
+ }
+
///
/// Create this to describe a grid for rendering multiple icons.
/// Icons are positioned in the XZ plane in the world around the GridOrigin, but rendered
@@ -117,6 +123,9 @@ public bool DrawGenericOverlayGridTexture(Texture2D texture,
}
}
+ public static Texture2D SquareTexture;
+ public static Texture2D TriangleTexture;
+
public static void DrawNodeCircle(RenderManager.CameraInfo cameraInfo,
ushort nodeId,
bool warning = false,
@@ -153,7 +162,7 @@ public static bool IsNodeVisible(ushort node) {
}
///
- /// draw triangular arrow head at the given of the
+ /// draw triangular (sides = 2, 2.24, 2.24) arrow head at the given of the
///
public static void DrawArrowHead(
RenderManager.CameraInfo cameraInfo,
@@ -190,7 +199,7 @@ public static void DrawArrowHead(
}
///
- /// draw triangular arrow head at the given of the
+ /// draw triangular (sides = 2, 2.24, 2.24) arrow head at the given of the
///
public static void DrawArrowHead(
RenderManager.CameraInfo cameraInfo,
@@ -224,39 +233,57 @@ public static void DrawArrowHead(
alphaBlend: alphaBlend);
}
- ///
- /// draw '>' shaped arrow head at the given of the
- ///
- public static void DrawArrowHead2(
+
+
+ public static void DrawShape(
RenderManager.CameraInfo cameraInfo,
- ref Bezier3 bezier,
- float t,
+ Shape shape,
Color color,
+ Vector3 center,
+ Vector3 tangent,
float size,
- float length,
float minY,
float maxY,
bool renderLimits,
- bool alphaBlend = false) {
- Vector3 center = bezier.Position(t);
- Vector3 dir = bezier.Tangent(t).normalized * length;
- Vector3 dir90 = dir.RotateXZ90CW();
-
- Segment3 line1 = new(center + dir, center + dir90);
- Segment3 line2 = new(center + dir, center - dir90);
-
+ bool alphaBlend) {
Singleton.instance.m_drawCallData.m_overlayCalls++;
- RenderManager.instance.OverlayEffect.DrawSegment(
- cameraInfo,
- color,
- segment1: line1,
- segment2: line2,
- size: size,
- dashLen: 0,
- minY: minY,
- maxY: maxY,
- renderLimits: renderLimits,
- alphaBlend: alphaBlend);
+ if (shape is Shape.Circle) {
+ RenderManager.instance.OverlayEffect.DrawCircle(
+ cameraInfo: cameraInfo,
+ color: color,
+ center: center,
+ size: size,
+ minY: minY,
+ maxY: maxY,
+ renderLimits: renderLimits,
+ alphaBlend: alphaBlend);
+ } else {
+ size *= 0.5f;
+ Vector3 dir = tangent * size;
+ Vector3 dir90 = dir.RotateXZ90CW();
+ Quad3 quad = shape switch {
+ Shape.Square => new Quad3 {
+ a = center - dir + dir90,
+ b = center + dir + dir90,
+ c = center + dir - dir90,
+ d = center - dir - dir90,
+ },
+ Shape.Diamond => new Quad3 {
+ a = center - dir,
+ b = center + dir90,
+ c = center + dir,
+ d = center - dir90,
+ },
+ };
+ RenderManager.instance.OverlayEffect.DrawQuad(
+ cameraInfo,
+ color,
+ quad,
+ minY,
+ maxY,
+ renderLimits: renderLimits,
+ alphaBlend: alphaBlend);
+ }
}
public static void DrawNodeCircle(RenderManager.CameraInfo cameraInfo,
diff --git a/TLM/TLM/UI/Helpers/NodeLaneMarker.cs b/TLM/TLM/UI/Helpers/NodeLaneMarker.cs
index 17f5dbc4b..510a9cd79 100644
--- a/TLM/TLM/UI/Helpers/NodeLaneMarker.cs
+++ b/TLM/TLM/UI/Helpers/NodeLaneMarker.cs
@@ -1,13 +1,14 @@
-using ColossalFramework.Math;
-using UnityEngine;
-
namespace TrafficManager.UI.Helpers {
- using Util;
+ using System;
+ using TrafficManager.Util;
+ using UnityEngine;
internal class NodeLaneMarker {
+ internal const float RADIUS = 1f;
+
internal Vector3 TerrainPosition; // projected on terrain
internal Vector3 Position; // original height.
- static internal float Radius = 1f;
+ internal Vector3 Direction; // pointing toward the lane.
///
/// Intersects mouse ray with marker bounds.
@@ -15,31 +16,42 @@ internal class NodeLaneMarker {
/// trueif mouse ray intersects with marker false otherwise
internal bool IntersectRay() {
Ray mouseRay = InGameUtil.Instance.CachedMainCamera.ScreenPointToRay(Input.mousePosition);
- float hitH = TrafficManagerTool.GetAccurateHitHeight();
-
- Bounds bounds = new Bounds(Vector3.zero, Vector3.one * Radius) {
- center = Position
+ Bounds bounds = new(Vector3.zero, Vector3.one * RADIUS) {
+ center = Position,
};
return bounds.IntersectRay(mouseRay);
}
- internal void RenderOverlay(RenderManager.CameraInfo cameraInfo, Color color, bool enlarge = false, bool renderLimits = false) {
- float magnification = enlarge ? 2f : 1f;
+ internal void RenderOverlay(
+ RenderManager.CameraInfo cameraInfo,
+ Color color,
+ Highlight.Shape shape = Highlight.Shape.Circle,
+ bool enlarge = false,
+ bool renderLimits = false) {
float overdrawHeight = renderLimits ? 0f : 5f;
- RenderManager.instance.OverlayEffect.DrawCircle(
+ float magnification = enlarge ? 2f : 1f;
+ float size = RADIUS * magnification;
+ float outlineScale = shape is Highlight.Shape.Circle ? 0.75f : 0.9f;
+ float outlineSize = RADIUS * outlineScale * magnification;
+
+ Highlight.DrawShape(
cameraInfo,
+ shape,
color,
Position,
- Radius * magnification,
+ Direction,
+ size,
Position.y - overdrawHeight,
Position.y + overdrawHeight,
renderLimits,
true);
- RenderManager.instance.OverlayEffect.DrawCircle(
+ Highlight.DrawShape(
cameraInfo,
+ shape,
Color.black,
Position,
- Radius * 0.75f * magnification, // inner black
+ Direction,
+ outlineSize, // black outline
Position.y - overdrawHeight,
Position.y + overdrawHeight,
renderLimits,
diff --git a/TLM/TLM/UI/Helpers/SegmentLaneMarker.cs b/TLM/TLM/UI/Helpers/SegmentLaneMarker.cs
index 7ae16acc8..969009a42 100644
--- a/TLM/TLM/UI/Helpers/SegmentLaneMarker.cs
+++ b/TLM/TLM/UI/Helpers/SegmentLaneMarker.cs
@@ -66,9 +66,17 @@ private void CalculateBounds() {
}
/// Renders lane overlay.
- internal void RenderOverlay(RenderManager.CameraInfo cameraInfo, Color color, bool enlarge = false, bool renderLimits = false) {
+ internal void RenderOverlay(
+ RenderManager.CameraInfo cameraInfo,
+ Color color,
+ bool enlarge = false,
+ bool renderLimits = false,
+ bool alphaBlend = false,
+ bool cutStart = false,
+ bool cutEnd = false) {
float minH = Mathf.Min(Bezier.a.y, Bezier.d.y);
float maxH = Mathf.Max(Bezier.a.y, Bezier.d.y);
+ float size = enlarge ? Size * 1.41f : Size;
float overdrawHeight = IsUnderground || renderLimits ? 0f : 5f;
ColossalFramework.Singleton.instance.m_drawCallData.m_overlayCalls++;
@@ -76,13 +84,13 @@ internal void RenderOverlay(RenderManager.CameraInfo cameraInfo, Color color, bo
cameraInfo: cameraInfo,
color: color,
bezier: Bezier,
- size: enlarge ? Size * 1.41f : Size,
- cutStart: 0,
- cutEnd: 0,
+ size: size,
+ cutStart: cutStart ? size * 0.50f : 0,
+ cutEnd: cutEnd ? size * 0.50f : 0,
minY: minH - overdrawHeight,
maxY: maxH + overdrawHeight,
renderLimits: IsUnderground || renderLimits,
- alphaBlend: false);
+ alphaBlend: alphaBlend);
}
}
}
diff --git a/TLM/TLM/UI/SubTools/LaneArrows/LaneArrowTool.cs b/TLM/TLM/UI/SubTools/LaneArrows/LaneArrowTool.cs
index d7233241d..e723eaccd 100644
--- a/TLM/TLM/UI/SubTools/LaneArrows/LaneArrowTool.cs
+++ b/TLM/TLM/UI/SubTools/LaneArrows/LaneArrowTool.cs
@@ -269,7 +269,7 @@ private static bool CanReset(ushort segmentId, bool startNode) {
sort: false);
foreach (var lane in lanes) {
- if (!LaneConnectionManager.Instance.Sub.HasOutgoingConnections(lane.laneId))
+ if (!LaneConnectionManager.Instance.Road.HasOutgoingConnections(lane.laneId))
return true;
}
diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs
index 9ea279db5..434cdd371 100644
--- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs
+++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs
@@ -20,11 +20,11 @@ namespace TrafficManager.UI.SubTools {
using TrafficManager.Util.Extensions;
using TrafficManager.U;
using TrafficManager.UI.Textures;
+ using TrafficManager.API.Traffic.Enums;
public class LaneConnectorTool
: LegacySubTool,
- UI.MainMenu.IOnscreenDisplayProvider
- {
+ UI.MainMenu.IOnscreenDisplayProvider {
public LaneConnectorTool(TrafficManagerTool mainTool)
: base(mainTool) {
// Log._Debug($"LaneConnectorTool: Constructor called");
@@ -36,6 +36,7 @@ public LaneConnectorTool(TrafficManagerTool mainTool)
addCursor_ = CursorUtil.LoadCursorFromResource("LaneConnectionManager.add_cursor.png");
removeCursor_ = CursorUtil.LoadCursorFromResource("LaneConnectionManager.remove_cursor.png");
directionArrow_ = TextureResources.LoadDllResource("LaneConnectionManager.direction_arrow.png", new IntVector2(256, 256));
+ Highlight.TriangleTexture = directionArrow_;
}
/// State of the tool UI.
@@ -86,6 +87,16 @@ public enum StayInLaneMode {
private Texture2D directionArrow_;
+ private Texture2D square_;
+
+ private LaneEndTransitionGroup selectedNodeTransitionGroups_;
+
+ private LaneEndTransitionGroup selectedLaneTransitionGroup_;
+
+ private LaneEndTransitionGroup group_;
+
+ private static LaneEndTransitionGroup[] ALL_GROUPS = new[] {LaneEndTransitionGroup.Road, LaneEndTransitionGroup.Track };
+
///
/// Stores potentially visible ids for nodes while the camera did not move
///
@@ -96,30 +107,50 @@ public enum StayInLaneMode {
///
private CameraTransformValue LastCachedCamera { get; set; }
+
///
- /// create all lane connections (bidirectional, cars+tram).
+ /// all lane connections (bidirectional, cars+tram).
///
private bool MultiMode => ShiftIsPressed;
+ private void UpdateGroup() {
+ if (selectedLaneTransitionGroup_ != LaneEndTransitionGroup.None) {
+ group_ = selectedLaneTransitionGroup_;
+ } else if (selectedNodeTransitionGroups_ == LaneEndTransitionGroup.Vehicle) {
+ // No node is selected or selected node has road+track
+ if (MultiMode) {
+ group_ = LaneEndTransitionGroup.Vehicle;
+ } else if (AltIsPressed) {
+ group_ = LaneEndTransitionGroup.Track;
+ } else {
+ group_ = LaneEndTransitionGroup.Road;
+ }
+ } else {
+ group_ = selectedNodeTransitionGroups_;
+ }
+ }
+
private class LaneEnd {
- internal readonly List ConnectedLaneEnds = new List();
- internal Color Color;
- internal int InnerSimilarLaneIndex; // used for stay in lane.
- internal bool IsBidirectional; // can be source AND/OR target of a lane connection.
+ internal ushort SegmentId;
+ internal ushort NodeId;
+ internal bool StartNode;
+ internal uint LaneId;
internal bool IsSource;
internal bool IsTarget;
- internal uint LaneId;
-
- internal NetInfo.LaneType LaneType;
- internal ushort NodeId;
- internal NodeLaneMarker NodeMarker;
internal int OuterSimilarLaneIndex;
- internal ushort SegmentId;
+ internal int InnerSimilarLaneIndex; // used for stay in lane.
internal int SegmentIndex; // index accesable by NetNode.GetSegment(SegmentIndex);
+ internal bool IsBidirectional; // can be source AND/OR target of a lane connection.
+ internal readonly HashSet ConnectedCarLaneEnds = new ();
+ internal readonly HashSet ConnectedTrackLaneEnds = new ();
+ internal HashSet ConnectedLaneEnds(bool track) => track ? ConnectedTrackLaneEnds : ConnectedCarLaneEnds;
+ internal Color Color;
internal SegmentLaneMarker SegmentMarker;
- internal bool StartNode;
- internal VehicleInfo.VehicleType VehicleType;
+ internal NodeLaneMarker NodeMarker;
+
+ internal NetInfo.Lane LaneInfo;
+ internal LaneEndTransitionGroup TransitionGroup;
///
/// Intersects mouse ray with marker bounds.
@@ -131,11 +162,33 @@ private class LaneEnd {
/// renders lane overlay. If highlighted, renders englarged sheath(lane+circle) overlay. Otherwise
/// renders circle at lane end.
///
- internal void RenderOverlay(RenderManager.CameraInfo cameraInfo, Color color, bool highlight = false, bool renderLimits = false) {
+ internal void RenderOverlay(
+ RenderManager.CameraInfo cameraInfo,
+ Color color,
+ bool highlight = false,
+ bool renderLimits = false,
+ LaneEndTransitionGroup groupFilter = LaneEndTransitionGroup.Vehicle) {
+
+ var groups = TransitionGroup & groupFilter;
+
+ Highlight.Shape shape;
+ bool cutEnd;
+ if (this.IsBidirectional) {
+ shape = Highlight.Shape.Diamond;
+ cutEnd = true;
+ } else if((groups & LaneEndTransitionGroup.Track) != 0) {
+ shape = Highlight.Shape.Square;
+ cutEnd = true;
+ } else {
+ shape = Highlight.Shape.Circle;
+ cutEnd = false;
+ }
+
if (highlight) {
- SegmentMarker.RenderOverlay(cameraInfo, color, enlarge: true, renderLimits);
+ SegmentMarker.RenderOverlay(cameraInfo, color, cutEnd: cutEnd, enlarge: true, renderLimits: renderLimits);
}
- NodeMarker.RenderOverlay(cameraInfo, color, enlarge: highlight, renderLimits);
+
+ NodeMarker.RenderOverlay(cameraInfo, color, shape: shape, enlarge: highlight, renderLimits: renderLimits);
}
}
@@ -175,6 +228,7 @@ private void ShowOverlay(bool viewOnly, RenderManager.CameraInfo cameraInfo) {
MassEditOverlay.IsActive)) {
return;
}
+ UpdateGroup();
NetManager netManager = Singleton.instance;
@@ -248,6 +302,15 @@ private void ShowOverlay(bool viewOnly, RenderManager.CameraInfo cameraInfo) {
float intersectionY = Singleton.instance.SampleDetailHeightSmooth(nodeId.ToNode().m_position);
+ LaneEndTransitionGroup groupAtNode = group_;
+ if (nodeId != SelectedNodeId) {
+ if (AltIsPressed) {
+ groupAtNode = LaneEndTransitionGroup.Track;
+ } else {
+ groupAtNode = LaneEndTransitionGroup.Vehicle;
+ }
+ }
+
foreach (LaneEnd laneEnd in laneEnds) {
ref NetLane sourceLane = ref laneEnd.LaneId.ToLane();
if (!sourceLane.IsValidWithSegment()) {
@@ -255,28 +318,34 @@ private void ShowOverlay(bool viewOnly, RenderManager.CameraInfo cameraInfo) {
}
if (laneEnd != selectedLaneEnd) {
- foreach (LaneEnd targetLaneEnd in laneEnd.ConnectedLaneEnds) {
- ref NetLane targetLane = ref targetLaneEnd.LaneId.ToLane();
- if (!targetLane.IsValidWithSegment()) {
+ foreach (var group in ALL_GROUPS) {
+ if((group & groupAtNode) == 0) {
continue;
}
+ bool track = group == LaneEndTransitionGroup.Track;
+ foreach (LaneEnd targetLaneEnd in laneEnd.ConnectedLaneEnds(track)) {
+ ref NetLane targetLane = ref targetLaneEnd.LaneId.ToLane();
+ if (!targetLane.IsValidWithSegment()) {
+ continue;
+ }
- // render lane connection from laneEnd to targetLaneEnd
- Bezier3 bezier = CalculateBezierConnection(laneEnd, targetLaneEnd);
- Vector3 height = bezier.Max();
- bool underground = (height.y + 1f) < intersectionY || laneEnd.NodeId == SelectedNodeId;
-
- Color fillColor = laneEnd.Color.WithAlpha(TransparentAlpha);
- Color outlineColor = Color.black.WithAlpha(TransparentAlpha);
- bool showArrow = ShouldShowDirectionOfConnection(laneEnd, targetLaneEnd);
- DrawLaneCurve(
- cameraInfo: cameraInfo,
- bezier: ref bezier,
- color: fillColor,
- outlineColor: outlineColor,
- arrowColor: showArrow ? fillColor : default,
- arrowOutlineColor: showArrow ? outlineColor : default,
- underground: underground);
+ // render lane connection from laneEnd to targetLaneEnd
+ Bezier3 bezier = CalculateBezierConnection(laneEnd, targetLaneEnd);
+ Vector3 height = bezier.Max();
+ bool underground = (height.y + 1f) < intersectionY || laneEnd.NodeId == SelectedNodeId;
+
+ Color fillColor = laneEnd.Color.WithAlpha(TransparentAlpha);
+ Color outlineColor = Color.black.WithAlpha(TransparentAlpha);
+ bool showArrow = track && ShouldShowDirectionOfConnection(laneEnd, targetLaneEnd);
+ DrawLaneCurve(
+ cameraInfo: cameraInfo,
+ bezier: ref bezier,
+ color: fillColor,
+ outlineColor: outlineColor,
+ arrowColor: showArrow ? fillColor : default,
+ arrowOutlineColor: showArrow ? outlineColor : default,
+ underground: underground);
+ }
}
}
@@ -285,22 +354,29 @@ private void ShowOverlay(bool viewOnly, RenderManager.CameraInfo cameraInfo) {
}
bool drawMarker = false;
+ bool acute = true;
bool sourceMode = GetSelectionMode() == SelectionMode.SelectSource;
bool targetMode = GetSelectionMode() == SelectionMode.SelectTarget;
if ( sourceMode & laneEnd.IsSource) {
// draw source marker in source selection mode,
// make exception for markers that have no target:
foreach(var targetLaneEnd in laneEnds) {
- if (CanConnect(laneEnd, targetLaneEnd)) {
+ if (CanConnect(laneEnd, targetLaneEnd, group_, out bool acute2)) {
drawMarker = true;
- break;
+ if (!acute2) {
+ acute = false;
+ break;
+ }
}
}
} else if (targetMode) {
// selected source marker in target selection mode
- drawMarker =
- selectedLaneEnd == laneEnd ||
- CanConnect(selectedLaneEnd, laneEnd);
+ if(selectedLaneEnd == laneEnd) {
+ drawMarker = true;
+ acute = false;
+ } else {
+ drawMarker = CanConnect(selectedLaneEnd, laneEnd, group_, out acute);
+ }
}
// highlight hovered marker and selected marker
@@ -316,10 +392,16 @@ private void ShowOverlay(bool viewOnly, RenderManager.CameraInfo cameraInfo) {
}
}
- bool isTarget = selectedLaneEnd != null && laneEnd != selectedLaneEnd;
- var color = isTarget ? Color.white : laneEnd.Color;
- bool highlightMarker = laneEnd == selectedLaneEnd || markerIsHovered;
- laneEnd.RenderOverlay(cameraInfo, color, highlightMarker, true);
+ var group = laneEnd.TransitionGroup & group_;
+ if (acute) {
+ group &= ~LaneEndTransitionGroup.Track;
+ }
+ if (group != 0) {
+ bool isTarget = selectedLaneEnd != null && laneEnd != selectedLaneEnd;
+ var color = isTarget ? Color.white : laneEnd.Color;
+ bool highlightMarker = laneEnd == selectedLaneEnd || markerIsHovered;
+ laneEnd.RenderOverlay(cameraInfo, color, highlightMarker, true, group);
+ }
} // if drawMarker
} // end foreach lanemarker in node markers
} // end for node in all nodes
@@ -327,24 +409,30 @@ private void ShowOverlay(bool viewOnly, RenderManager.CameraInfo cameraInfo) {
if (this.selectedLaneEnd != null) {
// lane curves for selectedMarker will be drawn last to
// be on the top of other lane markers.
- foreach (LaneEnd targetLaneEnd in this.selectedLaneEnd.ConnectedLaneEnds) {
- ref NetLane targetLane = ref targetLaneEnd.LaneId.ToLane();
- if (!targetLane.IsValidWithSegment()) {
+ foreach (var group in ALL_GROUPS) {
+ if ((group & group_) == 0) {
continue;
}
+ bool track = group == LaneEndTransitionGroup.Track;
+ foreach (LaneEnd targetLaneEnd in this.selectedLaneEnd.ConnectedLaneEnds(track)) {
+ ref NetLane targetLane = ref targetLaneEnd.LaneId.ToLane();
+ if (!targetLane.IsValidWithSegment()) {
+ continue;
+ }
- Bezier3 bezier = CalculateBezierConnection(selectedLaneEnd, targetLaneEnd);
- bool showArrow = ShouldShowDirectionOfConnection(selectedLaneEnd, targetLaneEnd);
- DrawLaneCurve(
- cameraInfo: cameraInfo,
- bezier: ref bezier,
- color: this.selectedLaneEnd.Color,
- outlineColor: Color.black,
- arrowColor: showArrow ? this.selectedLaneEnd.Color : default,
- arrowOutlineColor: showArrow ? Color.black : default,
- size: 0.18f, // Embolden
- underground: true);
- } // end foreach selectedMarker.ConnectedMarkers
+ Bezier3 bezier = CalculateBezierConnection(selectedLaneEnd, targetLaneEnd);
+ bool showArrow = track & ShouldShowDirectionOfConnection(selectedLaneEnd, targetLaneEnd);
+ DrawLaneCurve(
+ cameraInfo: cameraInfo,
+ bezier: ref bezier,
+ color: this.selectedLaneEnd.Color,
+ outlineColor: Color.black,
+ arrowColor: showArrow ? this.selectedLaneEnd.Color : default,
+ arrowOutlineColor: showArrow ? Color.black : default,
+ size: 0.18f, // Embolden
+ underground: true);
+ } // end foreach selectedMarker.ConnectedMarkers
+ }
} // end if selectedMarker != null
}
@@ -388,38 +476,40 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {
} else {
// snap to hovered, render accurate connection bezier
Bezier3 bezier = CalculateBezierConnection(selectedLaneEnd, hoveredLaneEnd);
- bool connected = LaneConnectionManager.Instance.Sub.AreLanesConnected(
- selectedLaneEnd.LaneId, hoveredLaneEnd.LaneId, selectedLaneEnd.StartNode);
+ bool connected = LaneConnectionManager.Instance.AreLanesConnected(
+ selectedLaneEnd.LaneId, hoveredLaneEnd.LaneId, selectedLaneEnd.StartNode, group_);
Color fillColor = connected ?
Color.Lerp(a: selectedLaneEnd.Color, b: Color.white, t: 0.33f) : // show underneath color if there is connection.
default; // hollow if there isn't connection
- bool showArrow = ShouldShowDirectionOfConnection(selectedLaneEnd, hoveredLaneEnd);
+ bool track = (group_ & LaneEndTransitionGroup.Track) != 0;
+ bool showArrow = !connected && track && ShouldShowDirectionOfConnection(selectedLaneEnd, hoveredLaneEnd);
DrawLaneCurve(
cameraInfo: cameraInfo,
bezier: ref bezier,
color: fillColor,
outlineColor: Color.white,
- arrowColor: default,
- arrowOutlineColor: connected ? default : Color.white,
+ arrowColor: default,
+ arrowOutlineColor: showArrow ? Color.white : default,
size: 0.18f, // Embolden
underground: true);
if(!connected && MultiMode && selectedLaneEnd.IsBidirectional && hoveredLaneEnd.IsBidirectional) {
Bezier3 bezier2 = CalculateBezierConnection(hoveredLaneEnd, selectedLaneEnd);
// draw backward arrow only:
- bool connected2 = LaneConnectionManager.Instance.Sub.AreLanesConnected(
- hoveredLaneEnd.LaneId, selectedLaneEnd.LaneId, selectedLaneEnd.StartNode);
+ bool connected2 = LaneConnectionManager.Instance.AreLanesConnected(
+ hoveredLaneEnd.LaneId, selectedLaneEnd.LaneId, selectedLaneEnd.StartNode, group_);
DrawLaneCurve(
cameraInfo: cameraInfo,
bezier: ref bezier2,
color: default,
- outlineColor: default,
+ outlineColor: Color.white,
arrowColor: default,
arrowOutlineColor: connected2 ? default : Color.white,
+ size: 0.18f, // Embolden
underground: true);
}
- OverrideCursor = connected ? removeCursor_ : addCursor_;
+ OverrideCursor = connected ? removeCursor_ : addCursor_;
}
}
@@ -438,6 +528,7 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {
frameStayInLanePressed = 0; // not pressed anymore (consumed)
frameClearPressed = 0; // consumed
selectedLaneEnd = null;
+ selectedLaneTransitionGroup_ = 0;
ref NetNode node = ref SelectedNodeId.ToNode();
bool stayInLane = GetSortedSegments(SelectedNodeId, out List segList);
@@ -807,7 +898,7 @@ bool ConnectToMinor2(int sourceIdx, int targetIdx) {
targetIdx >= LowerBound(totalSource - laneCountMinor2Source);
}
- List laneEnds = GetLaneEnds(nodeId, ref node);
+ List laneEnds = GetLaneEnds(nodeId, ref node, out _);
foreach (LaneEnd sourceLaneEnd in laneEnds) {
if (!sourceLaneEnd.IsSource ||
sourceLaneEnd.SegmentId == mainSegmentTargetId) {
@@ -817,8 +908,7 @@ bool ConnectToMinor2(int sourceIdx, int targetIdx) {
if (!targetLaneEnd.IsTarget ||
targetLaneEnd.SegmentId == sourceLaneEnd.SegmentId ||
targetLaneEnd.SegmentId == mainSegmentSourceId ||
- !CanConnect(sourceLaneEnd, targetLaneEnd)
- ) {
+ !CanConnect(sourceLaneEnd, targetLaneEnd, LaneEndTransitionGroup.Vehicle, out _)) {
continue;
}
bool connect = false;
@@ -858,10 +948,11 @@ bool ConnectToMinor2(int sourceIdx, int targetIdx) {
}
if (connect) {
- LaneConnectionManager.Instance.Sub.AddLaneConnection(
+ LaneConnectionManager.Instance.AddLaneConnection(
sourceLaneEnd.LaneId,
targetLaneEnd.LaneId,
- sourceLaneEnd.StartNode);
+ sourceLaneEnd.StartNode,
+ LaneEndTransitionGroup.Vehicle);
}
} // foreach
} // foreach
@@ -921,6 +1012,8 @@ public override void OnPrimaryClickOverlay() {
SelectedNodeId = 0;
selectedLaneEnd = null;
+ selectedNodeTransitionGroups_ = 0;
+ selectedLaneTransitionGroup_ = 0;
stayInLaneMode = StayInLaneMode.None;
MainTool.RequestOnscreenDisplayUpdate();
return;
@@ -932,11 +1025,12 @@ public override void OnPrimaryClickOverlay() {
() => $"Node {HoveredNodeId} has been selected. Creating markers.");
// selected node has changed. create markers
- List laneEnds = GetLaneEnds(HoveredNodeId, ref hoveredNode);
+ List laneEnds = GetLaneEnds(HoveredNodeId, ref hoveredNode, out selectedNodeTransitionGroups_);
if (laneEnds != null) {
SelectedNodeId = HoveredNodeId;
selectedLaneEnd = null;
+ selectedLaneTransitionGroup_ = 0;
stayInLaneMode = StayInLaneMode.None;
currentLaneEnds[SelectedNodeId] = laneEnds;
@@ -953,6 +1047,7 @@ public override void OnPrimaryClickOverlay() {
// click on free spot. deselect node
SelectedNodeId = 0;
selectedLaneEnd = null;
+ selectedLaneTransitionGroup_ = 0;
stayInLaneMode = StayInLaneMode.None;
MainTool.RequestOnscreenDisplayUpdate();
return;
@@ -976,6 +1071,7 @@ public override void OnPrimaryClickOverlay() {
if (GetSelectionMode() == SelectionMode.SelectSource) {
// select source marker
selectedLaneEnd = hoveredLaneEnd;
+ selectedLaneTransitionGroup_ = group_ & selectedLaneEnd.TransitionGroup;
Log._DebugIf(
logLaneConn,
() => "LaneConnectorTool: set selected marker");
@@ -983,53 +1079,57 @@ public override void OnPrimaryClickOverlay() {
} else if (GetSelectionMode() == SelectionMode.SelectTarget) {
// toggle lane connection
bool canBeBidirectional = selectedLaneEnd.IsBidirectional && hoveredLaneEnd.IsBidirectional;
- if (LaneConnectionManager.Instance.Sub.AreLanesConnected(
- selectedLaneEnd.LaneId,
- hoveredLaneEnd.LaneId,
- selectedLaneEnd.StartNode)) {
- RemoveLaneConnection(selectedLaneEnd, hoveredLaneEnd);
+ if (LaneConnectionManager.Instance.AreLanesConnected(
+ selectedLaneEnd.LaneId, hoveredLaneEnd.LaneId, selectedLaneEnd.StartNode, group_)) {
+ RemoveLaneConnection(selectedLaneEnd, hoveredLaneEnd, group_);
if (canBeBidirectional && ShiftIsPressed) {
- RemoveLaneConnection(hoveredLaneEnd, selectedLaneEnd);
+ RemoveLaneConnection(hoveredLaneEnd, selectedLaneEnd, group_);
}
} else {
- AddLaneConnection(selectedLaneEnd, hoveredLaneEnd);
+ AddLaneConnection(selectedLaneEnd, hoveredLaneEnd, group_);
if (canBeBidirectional && ShiftIsPressed) {
- AddLaneConnection(hoveredLaneEnd, selectedLaneEnd);
+ AddLaneConnection(hoveredLaneEnd, selectedLaneEnd, group_);
}
}
+ UpdateConnectionTwoway(selectedLaneEnd, hoveredLaneEnd);
+
MainTool.RequestOnscreenDisplayUpdate();
}
}
+ private static void UpdateConnectionTwoway(LaneEnd laneEnd1, LaneEnd laneEnd2) {
+ UpdateConnection(laneEnd1, laneEnd2);
+ UpdateConnection(laneEnd2, laneEnd1);
+ }
- private void RemoveLaneConnection(LaneEnd source, LaneEnd target) {
- if (LaneConnectionManager.Instance.Sub.RemoveLaneConnection(
- source.LaneId,
- target.LaneId,
- source.StartNode)) {
-
- // try to remove connection
- source.ConnectedLaneEnds.Remove(target);
- Log._DebugIf(
- verbose_,
- () => $"LaneConnectorTool: removed lane connection: {source.LaneId}, " +
- $"{target.LaneId}");
+ private static void UpdateConnection(LaneEnd source, LaneEnd target) {
+ Log._Debug($"LaneConnectorTool.UpdateConnection({source.LaneId}, {target.LaneId}) called at node{source.NodeId})");
+ if (LaneConnectionManager.Instance.Road.AreLanesConnected(
+ source.LaneId, target.LaneId, source.StartNode)) {
+ source.ConnectedCarLaneEnds.Add(target);
+ Log._Debug("there is car connection");
+ } else {
+ source.ConnectedCarLaneEnds.Remove(target);
+ Log._Debug("there is no car connection");
+ }
+ if (LaneConnectionManager.Instance.Track.AreLanesConnected(
+ source.LaneId, target.LaneId, source.StartNode)) {
+ source.ConnectedTrackLaneEnds.Add(target);
+ Log._Debug("there is track connection");
+ } else {
+ source.ConnectedTrackLaneEnds.Remove(target);
+ Log._Debug("there is no track connection");
}
}
- private void AddLaneConnection(LaneEnd source, LaneEnd target) {
- if (LaneConnectionManager.Instance.Sub.AddLaneConnection(
- source.LaneId,
- target.LaneId,
- source.StartNode)) {
- // try to add connection
- source.ConnectedLaneEnds.Add(target);
- Log._DebugIf(
- verbose_,
- () => $"LaneConnectorTool: added lane connection: {source.LaneId}, " +
- $"{target.LaneId}");
- }
+ private static void RemoveLaneConnection(LaneEnd source, LaneEnd target, LaneEndTransitionGroup group) {
+ LaneConnectionManager.Instance.RemoveLaneConnection(
+ source.LaneId, target.LaneId, source.StartNode, group);
+ }
+ private static void AddLaneConnection(LaneEnd source, LaneEnd target, LaneEndTransitionGroup group) {
+ LaneConnectionManager.Instance.AddLaneConnection(
+ source.LaneId, target.LaneId, source.StartNode, group);
}
public override void OnSecondaryClickOverlay() {
@@ -1062,6 +1162,7 @@ public override void OnSecondaryClickOverlay() {
logLaneConn,
() => "LaneConnectorTool: OnSecondaryClickOverlay: selected node id = 0");
SelectedNodeId = 0;
+ selectedNodeTransitionGroups_= 0;
MainTool.RequestOnscreenDisplayUpdate();
break;
}
@@ -1072,6 +1173,7 @@ public override void OnSecondaryClickOverlay() {
logLaneConn,
() => "LaneConnectorTool: OnSecondaryClickOverlay: switch to selected source mode");
selectedLaneEnd = null;
+ selectedLaneTransitionGroup_ = 0;
MainTool.RequestOnscreenDisplayUpdate();
break;
}
@@ -1092,6 +1194,8 @@ public override void OnActivate() {
#endif
SelectedNodeId = 0;
selectedLaneEnd = null;
+ selectedNodeTransitionGroups_ = 0;
+ selectedLaneTransitionGroup_ = 0;
hoveredLaneEnd = null;
stayInLaneMode = StayInLaneMode.None;
RefreshCurrentNodeMarkers();
@@ -1115,11 +1219,11 @@ private void RefreshCurrentNodeMarkers(ushort forceNodeId = 0) {
}
if (nodeId != SelectedNodeId &&
- !LaneConnectionManager.Instance.Sub.HasNodeConnections(nodeId)) {
+ !LaneConnectionManager.Instance.HasNodeConnections(nodeId)) {
continue;
}
- List laneEnds = GetLaneEnds(nodeId, ref netNode);
+ List laneEnds = GetLaneEnds(nodeId, ref netNode, out _);
if (laneEnds == null) {
continue;
@@ -1156,7 +1260,8 @@ public override void Initialize() {
/// Node id.
/// Ref to the node struct.
/// List of lane end structs.
- private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
+ private static List GetLaneEnds(ushort nodeId, ref NetNode node, out LaneEndTransitionGroup groups) {
+ groups = 0;
if (nodeId == 0) {
return null;
}
@@ -1189,6 +1294,7 @@ private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
float offsetT = FloatUtil.IsZero(netSegment.m_averageLength) ? 0.1f : offset / netSegment.m_averageLength;
for (byte laneIndex = 0; (laneIndex < lanes.Length) && (laneId != 0); laneIndex++) {
+ ref NetLane netLane = ref laneId.ToLane();
NetInfo.Lane laneInfo = lanes[laneIndex];
if (((laneInfo.m_laneType & LaneConnectionManager.LANE_TYPES) != NetInfo.LaneType.None)
@@ -1204,15 +1310,16 @@ private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
incoming: out bool isTarget,
pos: out _))
{
- Vector3 pos;
- Bezier3 bezier = laneId.ToLane().m_bezier;
+ groups |= laneInfo.GetLaneEndTransitionGroup();
+ Bezier3 bezier = netLane.m_bezier;
if (startNode) {
- bezier = bezier.Cut(offsetT, 1f);
- pos = bezier.a;
- } else {
- bezier = bezier.Cut(0, 1f - offsetT);
- pos = bezier.d;
+ // reverse bezier.
+ bezier = new Bezier3(bezier.d, bezier.c, bezier.b, bezier.a);
}
+ bezier = bezier.Cut(0, 1f - offsetT);
+ Vector3 pos = bezier.d;
+ Vector3 dir = VectorUtils.NormalizeXZ(bezier.c - bezier.d);
+ dir.y = 0;
float terrainY = Singleton.instance.SampleDetailHeightSmooth(pos);
var terrainPos = new Vector3(pos.x, terrainY, pos.z);
@@ -1224,7 +1331,8 @@ private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
}
NodeLaneMarker nodeMarker = new NodeLaneMarker {
TerrainPosition = terrainPos,
- Position = (Vector3)pos,
+ Position = pos,
+ Direction = dir,
};
Color32 nodeMarkerColor = isSource
@@ -1250,8 +1358,8 @@ private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
Color = nodeMarkerColor,
IsSource = isSource,
IsTarget = isTarget,
- LaneType = laneInfo.m_laneType,
- VehicleType = laneInfo.m_vehicleType,
+ LaneInfo = laneInfo,
+ TransitionGroup = laneInfo.GetLaneEndTransitionGroup(),
InnerSimilarLaneIndex = innerSimilarLaneIndex,
OuterSimilarLaneIndex = outerSimilarLaneIndex,
SegmentIndex = segmentIndex,
@@ -1266,7 +1374,7 @@ private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
}
}
- laneId = laneId.ToLane().m_nextLane;
+ laneId = netLane.m_nextLane;
}
}
@@ -1274,27 +1382,28 @@ private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
return null;
}
- foreach (LaneEnd laneEnd1 in laneEnds) {
- if (!laneEnd1.IsSource) {
- continue;
- }
-
- uint[] connections =
- LaneConnectionManager.Instance.Sub.GetLaneConnections(
- laneEnd1.LaneId,
- laneEnd1.StartNode);
-
- if ((connections == null) || (connections.Length == 0)) {
+ foreach (LaneEnd sourceLaneEnd in laneEnds) {
+ if (!sourceLaneEnd.IsSource) {
continue;
}
- foreach (LaneEnd laneEnd2 in laneEnds) {
- if (!laneEnd2.IsTarget) {
- continue;
+ uint[] carConnections = LaneConnectionManager.Instance.Road.GetLaneConnections(
+ sourceLaneEnd.LaneId, sourceLaneEnd.StartNode);
+ if (!carConnections.IsNullOrEmpty()) {
+ foreach (LaneEnd targetLaneEnd in laneEnds) {
+ if (targetLaneEnd.IsTarget && carConnections.Contains(targetLaneEnd.LaneId)) {
+ sourceLaneEnd.ConnectedCarLaneEnds.Add(targetLaneEnd);
+ }
}
+ }
- if (connections.Contains(laneEnd2.LaneId)) {
- laneEnd1.ConnectedLaneEnds.Add(laneEnd2);
+ uint[] trackConnections = LaneConnectionManager.Instance.Track.GetLaneConnections(
+ sourceLaneEnd.LaneId, sourceLaneEnd.StartNode);
+ if (!trackConnections.IsNullOrEmpty()) {
+ foreach (LaneEnd targetLaneEnd in laneEnds) {
+ if (targetLaneEnd.IsTarget && trackConnections.Contains(targetLaneEnd.LaneId)) {
+ sourceLaneEnd.ConnectedTrackLaneEnds.Add(targetLaneEnd);
+ }
}
}
}
@@ -1302,66 +1411,30 @@ private static List GetLaneEnds(ushort nodeId, ref NetNode node) {
return laneEnds;
}
- private static bool CanConnect(LaneEnd source, LaneEnd target) {
- bool ret = source != target && source.IsSource && target.IsTarget;
- ret &= (target.VehicleType & source.VehicleType) != 0;
-
- bool IsRoad(LaneEnd laneEnd) =>
- (laneEnd.LaneType & LaneArrowManager.LANE_TYPES) != 0 &&
- (laneEnd.VehicleType & LaneArrowManager.VEHICLE_TYPES) != 0;
+ private static bool CanConnect(LaneEnd source, LaneEnd target, LaneEndTransitionGroup groups, out bool acute) {
+ acute = true;
+ bool canConnect = (source.LaneInfo.m_vehicleType & target.LaneInfo.m_vehicleType) != 0;
+ if (!canConnect) {
+ return false;
+ }
+ canConnect = source != target && source.IsSource && target.IsTarget;
+ if (!canConnect) {
+ return false;
+ }
// turning angle does not apply to roads.
- bool isRoad = IsRoad(source) && IsRoad(target);
+ bool road = groups != LaneEndTransitionGroup.Track &&
+ source.LaneInfo.MatchesRoad() &&
+ target.LaneInfo.MatchesRoad();
// check track turning angles are within bounds
- ret &= isRoad || CheckSegmentsTurningAngle(
+ acute = !LaneConnectionManager.CheckSegmentsTurningAngle(
sourceSegmentId: source.SegmentId,
sourceStartNode: source.StartNode,
targetSegmentId: target.SegmentId,
targetStartNode: target.StartNode);
- return ret;
- }
-
- ///
- /// Checks if the turning angle between two segments at the given node is within bounds.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private static bool CheckSegmentsTurningAngle(ushort sourceSegmentId,
- bool sourceStartNode,
- ushort targetSegmentId,
- bool targetStartNode) {
- if(sourceSegmentId == targetSegmentId) {
- return false;
- }
-
- ref NetSegment sourceSegment = ref sourceSegmentId.ToSegment();
- ref NetSegment targetSegment = ref targetSegmentId.ToSegment();
- float turningAngle = 0.01f - Mathf.Min(
- sourceSegment.Info.m_maxTurnAngleCos,
- targetSegment.Info.m_maxTurnAngleCos);
-
- if (turningAngle < 1f) {
- Vector3 sourceDirection = sourceStartNode
- ? sourceSegment.m_startDirection
- : sourceSegment.m_endDirection;
-
- Vector3 targetDirection = targetStartNode
- ? targetSegment.m_startDirection
- : targetSegment.m_endDirection;
-
- float dirDotProd = (sourceDirection.x * targetDirection.x) +
- (sourceDirection.z * targetDirection.z);
- return dirDotProd < turningAngle;
- }
-
- return true;
+ return road || !acute;
}
///
@@ -1446,20 +1519,19 @@ private void DrawLaneCurve(RenderManager.CameraInfo cameraInfo,
Bounds bounds = bezier.GetBounds();
float minY = bounds.min.y - 0.5f;
float maxY = bounds.max.y + 0.5f;
+ if (arrowOutlineColor.a != 0) {
+ Highlight.DrawArrowHead(
+ cameraInfo: cameraInfo,
+ bezier: ref bezier,
+ t: 2f / 3f,
+ color: arrowOutlineColor,
+ size: size + 0.5f,
+ minY: minY,
+ maxY: maxY,
+ alphaBlend: arrowColor.a == 0f, // avoid strange shape.
+ renderLimits: underground);
+ }
if (outlineColor.a != 0) {
- if (arrowOutlineColor.a != 0) {
- Highlight.DrawArrowHead(
- cameraInfo: cameraInfo,
- bezier: ref bezier,
- t: 2f / 3f,
- color: arrowOutlineColor,
- size: size + 0.5f,
- minY: minY,
- maxY: maxY,
- alphaBlend: arrowColor.a == 0f, // avoid strange shape.
- renderLimits: underground);
- }
-
RenderManager.instance.OverlayEffect.DrawBezier(
cameraInfo: cameraInfo,
color: outlineColor,
@@ -1618,9 +1690,11 @@ public void UpdateOnscreenDisplayPanel() {
if(selectedLaneEnd != null) {
bool bidirectional = selectedLaneEnd.IsBidirectional;
if (bidirectional) {
- string key = "UI.Key:Shift bidirectional";
- items.Add(new HoldModifier(shift: true, localizedText: T(key)));
+ items.Add(new HoldModifier(shift: true, localizedText: T("UI.Key:Shift bidirectional")));
}
+ } else if(selectedNodeTransitionGroups_ == LaneEndTransitionGroup.Vehicle) {
+ items.Add(new HoldModifier(alt: true, localizedText: T("UI.Key:alt track mode")));
+ items.Add(new HoldModifier(shift: true, localizedText: T("UI.Key:Shift car+track mode")));
}
OnscreenDisplay.Display(items);
diff --git a/TLM/TLM/UI/SubTools/RoutingDetector/LaneEnd.cs b/TLM/TLM/UI/SubTools/RoutingDetector/LaneEnd.cs
index bafb5c794..151d32378 100644
--- a/TLM/TLM/UI/SubTools/RoutingDetector/LaneEnd.cs
+++ b/TLM/TLM/UI/SubTools/RoutingDetector/LaneEnd.cs
@@ -25,9 +25,9 @@ internal class LaneEnd {
///
internal void RenderOverlay(RenderManager.CameraInfo cameraInfo, Color color, bool highlight = false, bool renderLimits = false) {
if (highlight) {
- SegmentMarker.RenderOverlay(cameraInfo, color, enlarge: true, renderLimits);
+ SegmentMarker.RenderOverlay(cameraInfo, color, enlarge: true, renderLimits: renderLimits);
}
- NodeMarker.RenderOverlay(cameraInfo, color, enlarge: highlight, renderLimits);
+ NodeMarker.RenderOverlay(cameraInfo, color, enlarge: highlight, renderLimits: renderLimits);
}
}
}
diff --git a/TLM/TLM/Util/Record/LaneConnectionRecord.cs b/TLM/TLM/Util/Record/LaneConnectionRecord.cs
index 8d1b259f4..c993b1dfb 100644
--- a/TLM/TLM/Util/Record/LaneConnectionRecord.cs
+++ b/TLM/TLM/Util/Record/LaneConnectionRecord.cs
@@ -15,42 +15,54 @@ public class LaneConnectionRecord : IRecordable {
public byte LaneIndex;
public bool StartNode;
- private uint[] connections_;
+ private uint[] connections_; // legacy
+ private uint[] roadConnections_;
+ private uint[] trackConnections_;
private static LaneConnectionManager connMan => LaneConnectionManager.Instance;
- private uint[] GetCurrentConnections() => connMan.Sub.GetLaneConnections(LaneId, StartNode);
-
public void Record() {
- connections_ = GetCurrentConnections();
- //Log._Debug($"LaneConnectionRecord.Record: connections_=" + connections_.ToSTR());
-
- if (connections_ != null)
- connections_ = (uint[])connections_.Clone();
+ connections_ = null;
+ roadConnections_ = connMan.Road.GetLaneConnections(LaneId, StartNode)?.Clone() as uint[];
+ trackConnections_ = connMan.Track.GetLaneConnections(LaneId, StartNode)?.Clone() as uint[];
}
- public void Restore() {
- if (connections_ == null) {
- connMan.Sub.RemoveLaneConnections(LaneId, StartNode);
+ private void RestoreImpl(LaneConnectionSubManager man, uint[] connections) {
+ if (connections == null) {
+ man.RemoveLaneConnections(LaneId, StartNode);
return;
}
- var currentConnections = GetCurrentConnections();
+ var currentConnections = man.GetLaneConnections(LaneId, StartNode) ?? new uint[0];
//Log._Debug($"currentConnections=" + currentConnections.ToSTR());
- //Log._Debug($"connections_=" + connections_.ToSTR());
+ //Log._Debug($"connections=" + connections.ToSTR());
- foreach (uint targetLaneId in connections_) {
+ foreach (uint targetLaneId in connections) {
if (currentConnections == null || !currentConnections.Contains(targetLaneId)) {
- connMan.Sub.AddLaneConnection(LaneId, targetLaneId, StartNode);
+ man.AddLaneConnection(LaneId, targetLaneId, StartNode);
}
}
- foreach (uint targetLaneId in currentConnections ?? Enumerable.Empty()) {
- if (!connections_.Contains(targetLaneId)) {
- connMan.Sub.RemoveLaneConnection(LaneId, targetLaneId, StartNode);
+ foreach (uint targetLaneId in currentConnections) {
+ if (!connections.Contains(targetLaneId)) {
+ man.RemoveLaneConnection(LaneId, targetLaneId, StartNode);
}
}
}
- public void Transfer(Dictionary map) {
+ public void Restore() {
+ if (connections_ != null) {
+ // legacy
+ RestoreImpl(connMan.Road, connections_);
+ RestoreImpl(connMan.Track, connections_);
+ } else {
+ RestoreImpl(connMan.Road, roadConnections_);
+ RestoreImpl(connMan.Track, trackConnections_);
+ }
+ }
+
+ private void TransferImpl(
+ Dictionary map,
+ LaneConnectionSubManager man,
+ uint[] connections) {
uint MappedLaneId(uint originalLaneID) {
var originalLaneInstanceID = new InstanceID { NetLane = originalLaneID };
if (map.TryGetValue(originalLaneInstanceID, out var ret))
@@ -60,21 +72,32 @@ uint MappedLaneId(uint originalLaneID) {
}
var mappedLaneId = MappedLaneId(LaneId);
- if (connections_ == null) {
- connMan.Sub.RemoveLaneConnections(mappedLaneId, StartNode);
+ if (connections == null) {
+ man.RemoveLaneConnections(mappedLaneId, StartNode);
return;
}
if (mappedLaneId == 0)
return;
- //Log._Debug($"connections_=" + connections_.ToSTR());
- foreach (uint targetLaneId in connections_) {
+ //Log._Debug($"connections=" + connections.ToSTR());
+ foreach (uint targetLaneId in connections) {
var mappedTargetLaneId = MappedLaneId(targetLaneId);
if (mappedTargetLaneId == 0)
continue;
//Log._Debug($"connecting lanes: {mappedLaneId}->{mappedTargetLaneId}");
- connMan.Sub.AddLaneConnection(mappedLaneId, mappedTargetLaneId, StartNode);
+ man.AddLaneConnection(mappedLaneId, mappedTargetLaneId, StartNode);
+ }
+ }
+
+ public void Transfer(Dictionary map) {
+ if (connections_ != null) {
+ // legacy
+ TransferImpl(map, connMan.Road, connections_);
+ TransferImpl(map, connMan.Track, connections_);
+ } else {
+ TransferImpl(map, connMan.Road, roadConnections_);
+ TransferImpl(map, connMan.Track, trackConnections_);
}
}
@@ -93,7 +116,7 @@ public static List GetLanes(ushort nodeId) {
continue;
}
- foreach (LaneIdAndIndex laneIdAndIndex in extSegmentManager.GetSegmentLaneIdsAndLaneIndexes(segmentId)) {
+ foreach (LaneIdAndIndex laneIdAndIndex in netSegment.GetSegmentLaneIdsAndLaneIndexes()) {
NetInfo.Lane laneInfo = netInfo.m_lanes[laneIdAndIndex.laneIndex];
bool match = (laneInfo.m_laneType & LaneConnectionManager.LANE_TYPES) != 0 &&
(laneInfo.m_vehicleType & LaneConnectionManager.VEHICLE_TYPES) != 0;
diff --git a/TLM/TLM/Util/SeparateTurningLanesUtil.cs b/TLM/TLM/Util/SeparateTurningLanesUtil.cs
index f3ff09bbb..7f69b9ad6 100644
--- a/TLM/TLM/Util/SeparateTurningLanesUtil.cs
+++ b/TLM/TLM/Util/SeparateTurningLanesUtil.cs
@@ -80,7 +80,7 @@ public static void SeparateNode(ushort nodeId, out SetLaneArrow_Result res, bool
return;
}
- if (LaneConnectionManager.Instance.Sub.HasNodeConnections(nodeId)) {
+ if (LaneConnectionManager.Instance.Road.HasNodeConnections(nodeId)) {
res = SetLaneArrow_Result.LaneConnection;
return;
}
@@ -491,7 +491,7 @@ public static SetLaneArrow_Result CanChangeLanes(ushort segmentId, ushort nodeId
int srcLaneCount = laneList.Count();
for (int i = 0; i < srcLaneCount; ++i) {
- if (LaneConnectionManager.Instance.Sub.HasOutgoingConnections(laneList[i].laneId, startNode)) {
+ if (LaneConnectionManager.Instance.Road.HasOutgoingConnections(laneList[i].laneId, startNode)) {
return SetLaneArrow_Result.LaneConnection;
}
}