/// <summary> /// Recalculates lane arrows based on present lane connections. /// </summary> /// <param name="laneId">Affected lane</param> /// <param name="nodeId">Affected node</param> private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) { #if DEBUG bool logLaneConnections = DebugSwitch.LaneConnections.Get(); #else const bool logLaneConnections = false; #endif if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}) called"); } if (!Options.laneConnectorEnabled) { return; } if (!Flags.CanHaveLaneArrows(laneId, startNode)) { if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"lane {laneId}, startNode? {startNode} must not have lane arrows"); } return; } if (!HasConnections(laneId, startNode)) { if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"lane {laneId} does not have outgoing connections"); } return; } if (nodeId == 0) { if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + "invalid node"); } return; } var arrows = LaneArrows.None; NetManager netManager = Singleton <NetManager> .instance; ushort segmentId = netManager.m_lanes.m_buffer[laneId].m_segment; if (segmentId == 0) { if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + "invalid segment"); } return; } if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"startNode? {startNode}"); } if (!Services.NetService.IsNodeValid(nodeId)) { if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + "Node is invalid"); } return; } IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)]; Services.NetService.IterateNodeSegments( nodeId, (ushort otherSegmentId, ref NetSegment otherSeg) => { ArrowDirection dir = segEndMan.GetDirection(ref segEnd, otherSegmentId); if (logLaneConnections) { Log._Debug( $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"processing connected segment {otherSegmentId}. dir={dir}"); } // check if arrow has already been set for this direction switch (dir) { case ArrowDirection.Turn: { if (Constants.ServiceFactory.SimulationService.TrafficDrivesOnLeft) { if ((arrows & LaneArrows.Right) != LaneArrows.None) { return(true); } } else { if ((arrows & LaneArrows.Left) != LaneArrows.None) { return(true); } } break; } case ArrowDirection.Forward: { if ((arrows & LaneArrows.Forward) != LaneArrows.None) { return(true); } break; } case ArrowDirection.Left: { if ((arrows & LaneArrows.Left) != LaneArrows.None) { return(true); } break; } case ArrowDirection.Right: { if ((arrows & LaneArrows.Right) != LaneArrows.None) { return(true); } break; } default: { return(true); } } if (logLaneConnections) { Log._Debug( $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"processing connected segment {otherSegmentId}: need to determine arrows"); } bool addArrow = false; uint curLaneId = netManager.m_segments.m_buffer[otherSegmentId].m_lanes; while (curLaneId != 0) { if (logLaneConnections) { Log._Debug( $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"processing connected segment {otherSegmentId}: checking lane {curLaneId}"); } if (AreLanesConnected(laneId, curLaneId, startNode)) { if (logLaneConnections) { Log._Debug( $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"processing connected segment {otherSegmentId}: checking lane " + $"{curLaneId}: lanes are connected"); } addArrow = true; break; } curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane; } if (logLaneConnections) { Log._Debug( $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"processing connected segment {otherSegmentId}: finished processing " + $"lanes. addArrow={addArrow} arrows (before)={arrows}"); } if (!addArrow) { return(true); } switch (dir) { case ArrowDirection.Turn: { if (Constants.ServiceFactory.SimulationService.TrafficDrivesOnLeft) { arrows |= LaneArrows.Right; } else { arrows |= LaneArrows.Left; } break; } case ArrowDirection.Forward: { arrows |= LaneArrows.Forward; break; } case ArrowDirection.Left: { arrows |= LaneArrows.Left; break; } case ArrowDirection.Right: { arrows |= LaneArrows.Right; break; } default: { return(true); } } if (logLaneConnections) { Log._Debug( $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"processing connected segment {otherSegmentId}: arrows={arrows}"); } return(true); }); if (logLaneConnections) { Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " + $"setting lane arrows to {arrows}"); } LaneArrowManager.Instance.SetLaneArrows(laneId, arrows, true); }