/// <summary> /// returns the number of all target lanes from input segment toward the secified direction. /// </summary> private static int CountTargetLanesTowardDirection(ushort segmentId, ushort nodeId, ArrowDirection dir) { int count = 0; bool startNode = segmentId.ToSegment().m_startNode == nodeId; IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)]; LaneArrowManager.Instance.Services.NetService.IterateNodeSegments( nodeId, (ushort otherSegmentId, ref NetSegment otherSeg) => { ArrowDirection dir2 = segEndMan.GetDirection(ref segEnd, otherSegmentId); if (dir == dir2) { int forward = 0, backward = 0; otherSeg.CountLanes( otherSegmentId, LaneArrowManager.LANE_TYPES, LaneArrowManager.VEHICLE_TYPES, ref forward, ref backward); bool startNode2 = otherSeg.m_startNode == nodeId; //xor because inverting 2 times is redundant. if (startNode2) { count += forward; } else { count += backward; } //Log._Debug( // $"dir={dir} startNode={startNode} segmentId={segmentId}\n" + // $"startNode2={startNode2} forward={forward} backward={backward} count={count}"); } return true; }); return count; }
internal static void FixRulesRAbout(ushort segmentId) { foreach (bool startNode in Constants.ALL_BOOL) { if (OptionsMassEditTab.RoundAboutQuickFix_PrioritySigns) { TrafficPriorityManager.Instance.SetPrioritySign( segmentId, startNode, PriorityType.Main); } ushort nodeId = netService.GetSegmentNodeId( segmentId, startNode); ExtSegmentEnd curEnd = GetSegEnd(segmentId, startNode); if (OptionsMassEditTab.RoundAboutQuickFix_NoCrossMainR) { JunctionRestrictionsManager.Instance.SetPedestrianCrossingAllowed( segmentId, startNode, false); } JunctionRestrictionsManager.Instance.SetEnteringBlockedJunctionAllowed( segmentId, startNode, true); } }
/// <summary> /// Determines whether or not the given segment end has lane arrows. /// </summary> private bool HasSegmentEndLaneArrows(ushort segmentId, ushort nodeId) { if (nodeId == 0 || segmentId == 0) { return(false); } #if DEBUG if (!Constants.ServiceFactory.NetService.IsNodeValid(nodeId) || !Constants.ServiceFactory.NetService.IsSegmentValid(segmentId)) { Debug.LogError("Invalid node or segment ID"); } #endif ExtSegmentEndManager segEndMan = ExtSegmentEndManager.Instance; int segmentEndId = segEndMan.GetIndex(segmentId, nodeId); if (segmentEndId < 0) { Log._Debug($"Node {nodeId} is not connected to segment {segmentId}"); return(false); } ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, nodeId)]; NetNode[] nodesBuffer = Singleton <NetManager> .instance.m_nodes.m_buffer; bool bJunction = (nodesBuffer[nodeId].m_flags & NetNode.Flags.Junction) != 0; // Outgoing lanes toward the node is incomming lanes to the segment end. return(bJunction && segEnd.incoming); }
/// <summary> /// returns the number of all target lanes from input segment toward the secified direction. /// </summary> private static int CountTargetLanesTowardDirection(ushort segmentId, ushort nodeId, ArrowDirection dir) { int count = 0; bool startNode = segmentId.ToSegment().m_startNode == nodeId; IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)]; ref NetNode node = ref nodeId.ToNode();
private ExtSegmentEndManager() { ExtSegmentEnds = new ExtSegmentEnd[NetManager.MAX_SEGMENT_COUNT * 2]; for (uint i = 0; i < NetManager.MAX_SEGMENT_COUNT; ++i) { ExtSegmentEnds[GetIndex((ushort)i, true)] = new ExtSegmentEnd((ushort)i, true); ExtSegmentEnds[GetIndex((ushort)i, false)] = new ExtSegmentEnd((ushort)i, false); } }
private void ToggleMode(ref ExtSegmentEnd segEnd, ref NetNode node) { IExtSegmentManager extSegMan = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager; bool startNode = lights.StartNode; bool hasLeftSegment; bool hasForwardSegment; bool hasRightSegment; extSegEndMan.CalculateOutgoingLeftStraightRightSegments(ref segEnd, ref node, out hasLeftSegment, out hasForwardSegment, out hasRightSegment); #if DEBUG Log._Debug($"ChangeMode. segment {SegmentId} @ node {NodeId}, hasLeftSegment={hasLeftSegment}, hasForwardSegment={hasForwardSegment}, hasRightSegment={hasRightSegment}"); #endif LightMode newMode = LightMode.Simple; if (CurrentMode == LightMode.Simple) { if (!hasLeftSegment) { newMode = LightMode.SingleRight; } else { newMode = LightMode.SingleLeft; } } else if (CurrentMode == LightMode.SingleLeft) { if (!hasForwardSegment || !hasRightSegment) { newMode = LightMode.Simple; } else { newMode = LightMode.SingleRight; } } else if (CurrentMode == LightMode.SingleRight) { if (!hasLeftSegment) { newMode = LightMode.Simple; } else { newMode = LightMode.All; } } else { newMode = LightMode.Simple; } CurrentMode = newMode; }
/// <summary> /// Configures traffic light for and for all lane types at input segmentId, nodeId, and step. /// </summary> /// <param name="step"></param> /// <param name="nodeId"></param> /// <param name="segmentId"></param> /// <param name="m">Determines which directions are green</param> private static void SetupHelper(ITimedTrafficLightsStep step, ushort nodeId, ushort segmentId, GreenDir m) { bool startNode = (bool)netService.IsStartNode(segmentId, nodeId); //get step data for side seg ICustomSegmentLights liveSegmentLights = customTrafficLightsManager.GetSegmentLights(segmentId, startNode); //for each lane type foreach (ExtVehicleType vehicleType in liveSegmentLights.VehicleTypes) { //set light mode ICustomSegmentLight liveSegmentLight = liveSegmentLights.GetCustomLight(vehicleType); liveSegmentLight.CurrentMode = LightMode.All; TimedLight(nodeId).ChangeLightMode( segmentId, vehicleType, liveSegmentLight.CurrentMode); // set light states var green = RoadBaseAI.TrafficLightState.Green; var red = RoadBaseAI.TrafficLightState.Red; switch (m) { case GreenDir.AllRed: liveSegmentLight.SetStates(red, red, red); break; case GreenDir.AllGreen: liveSegmentLight.SetStates(green, green, green); break; case GreenDir.ShortOnly: { // calculate directions ref ExtSegmentEnd segEnd = ref segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, nodeId)]; ref NetNode node = ref Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId]; segEndMan.CalculateOutgoingLeftStraightRightSegments(ref segEnd, ref node, out bool bLeft, out bool bForward, out bool bRight); bool bShort = RHT ? bRight : bLeft; bool bLong = RHT ? bLeft : bRight; if (bShort) { SetStates(liveSegmentLight, red, red, green); } else if (bLong) { // go forward instead of short SetStates(liveSegmentLight, green, red, red); } else { Debug.LogAssertion("Unreachable code."); liveSegmentLight.SetStates(green, green, green); } break; }
protected void UpdateSegmentEnd(ref ExtSegment seg, ref ExtSegmentEnd end) { #if DEBUG bool logTurnOnRed = DebugSwitch.TurnOnRed.Get(); #else const bool logTurnOnRed = false; #endif if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}) called."); } IExtSegmentManager segmentManager = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager segmentEndManager = Constants.ManagerFactory.ExtSegmentEndManager; ushort segmentId = seg.segmentId; ushort nodeId = end.nodeId; bool hasOutgoingSegment = false; Services.NetService.IterateNodeSegments( end.nodeId, (ushort otherSegId, ref NetSegment otherSeg) => { int index0 = segmentEndManager.GetIndex(otherSegId, otherSeg.m_startNode == nodeId); if (otherSegId != segmentId && segmentEndManager.ExtSegmentEnds[index0].outgoing) { hasOutgoingSegment = true; return(false); } return(true); }); // check if traffic can flow to the node and that there is at least one left segment if (!end.incoming || !hasOutgoingSegment) { if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + "outgoing one-way or insufficient number of outgoing segments."); } return; } bool lht = Services.SimulationService.TrafficDrivesOnLeft; // check node // note that we must not check for the `TrafficLights` flag here because the flag might not be loaded yet ref NetNode node = ref nodeId.ToNode();
protected void UpdateSegmentEnd(ref ExtSegment seg, ref ExtSegmentEnd end) { #if DEBUG bool logTurnOnRed = DebugSwitch.TurnOnRed.Get(); #else const bool logTurnOnRed = false; #endif if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}) called."); } IExtSegmentManager segmentManager = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager segmentEndManager = Constants.ManagerFactory.ExtSegmentEndManager; ushort segmentId = seg.segmentId; ushort nodeId = end.nodeId; bool hasOutgoingSegment = false; ref NetNode endNode = ref end.nodeId.ToNode();
/// <summary> /// Links the given vehicle to the given segment end. /// </summary> /// <param name="extVehicle">vehicle</param> /// <param name="end">ext. segment end</param> /// <param name="laneIndex">lane index</param> private void Link(ref ExtVehicle extVehicle, ref ExtSegmentEnd end, byte laneIndex) { #if DEBUG if (DebugSwitch.VehicleLinkingToSegmentEnd.Get()) { Log._Debug( $"ExtVehicleManager.Link({extVehicle.vehicleId}) called: Linking vehicle to " + $"segment end {end}\nstate:{extVehicle}"); } #endif extVehicle.currentSegmentId = end.segmentId; extVehicle.currentStartNode = end.startNode; extVehicle.currentLaneIndex = laneIndex; ushort oldFirstRegVehicleId = end.firstVehicleId; if (oldFirstRegVehicleId != 0) { ExtVehicles[oldFirstRegVehicleId].previousVehicleIdOnSegment = extVehicle.vehicleId; extVehicle.nextVehicleIdOnSegment = oldFirstRegVehicleId; } end.firstVehicleId = extVehicle.vehicleId; #if DEBUG if (DebugSwitch.PedestrianPathfinding.Get()) { string vehicleChainDebugInfo = ExtSegmentEndManager.Instance.GenerateVehicleChainDebugInfo( extVehicle.currentSegmentId, extVehicle.currentStartNode); Log._Debug( $"ExtVehicleManager.Link({extVehicle.vehicleId}) finished: Linked vehicle " + $"to segment end: {end}\nstate:{extVehicle}\nsegment end vehicle chain: " + vehicleChainDebugInfo); } #endif }
public override void OnToolGUI(Event e) { IExtSegmentManager segMan = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; var hoveredSegment = false; if (SelectedNodeId != 0) { CustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance; TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance; JunctionRestrictionsManager junctionRestrictionsManager = JunctionRestrictionsManager.Instance; if (!tlsMan.HasManualSimulation(SelectedNodeId)) { return; } tlsMan.TrafficLightSimulations[SelectedNodeId].Housekeeping(); // TODO check // if (Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode].CountSegments() == 2) { // _guiManualTrafficLightsCrosswalk( // ref Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode]); // return; // } NetNode[] nodesBuffer = Singleton <NetManager> .instance.m_nodes.m_buffer; NetSegment[] segmentsBuffer = Singleton <NetManager> .instance.m_segments.m_buffer; for (int i = 0; i < 8; ++i) { ushort segmentId = nodesBuffer[SelectedNodeId].GetSegment(i); if (segmentId == 0) { continue; } bool startNode = (bool)Constants.ServiceFactory.NetService.IsStartNode( segmentId, SelectedNodeId); Vector3 position = CalculateNodePositionForSegment( nodesBuffer[SelectedNodeId], ref segmentsBuffer[segmentId]); ICustomSegmentLights segmentLights = customTrafficLightsManager.GetSegmentLights(segmentId, startNode, false); if (segmentLights == null) { continue; } bool showPedLight = segmentLights.PedestrianLightState != null && junctionRestrictionsManager.IsPedestrianCrossingAllowed( segmentLights.SegmentId, segmentLights.StartNode); bool visible = MainTool.WorldToScreenPoint(position, out Vector3 screenPos); if (!visible) { continue; } Vector3 diff = position - Camera.main.transform.position; float zoom = 1.0f / diff.magnitude * 100f; // original / 2.5 float lightWidth = 41f * zoom; float lightHeight = 97f * zoom; float pedestrianWidth = 36f * zoom; float pedestrianHeight = 61f * zoom; // SWITCH MODE BUTTON float modeWidth = 41f * zoom; float modeHeight = 38f * zoom; Color guiColor = GUI.color; if (showPedLight) { // pedestrian light // SWITCH MANUAL PEDESTRIAN LIGHT BUTTON hoveredSegment = RenderManualPedestrianLightSwitch( zoom, segmentId, screenPos, lightWidth, segmentLights, hoveredSegment); // SWITCH PEDESTRIAN LIGHT guiColor.a = TrafficManagerTool.GetHandleAlpha( hoveredButton[0] == segmentId && hoveredButton[1] == 2 && segmentLights.ManualPedestrianMode); GUI.color = guiColor; var myRect3 = new Rect( screenPos.x - pedestrianWidth / 2 - lightWidth + 5f * zoom, screenPos.y - pedestrianHeight / 2 + 22f * zoom, pedestrianWidth, pedestrianHeight); switch (segmentLights.PedestrianLightState) { case RoadBaseAI.TrafficLightState.Green: { GUI.DrawTexture( myRect3, TrafficLightTextures.PedestrianGreenLight); break; } // also: case RoadBaseAI.TrafficLightState.Red: default: { GUI.DrawTexture( myRect3, TrafficLightTextures.PedestrianRedLight); break; } } hoveredSegment = IsPedestrianLightHovered( myRect3, segmentId, hoveredSegment, segmentLights); } int lightOffset = -1; foreach (ExtVehicleType vehicleType in segmentLights.VehicleTypes) { ++lightOffset; ICustomSegmentLight segmentLight = segmentLights.GetCustomLight(vehicleType); Vector3 offsetScreenPos = screenPos; offsetScreenPos.y -= (lightHeight + 10f * zoom) * lightOffset; SetAlpha(segmentId, -1); var myRect1 = new Rect( offsetScreenPos.x - modeWidth / 2, offsetScreenPos.y - modeHeight / 2 + modeHeight - 7f * zoom, modeWidth, modeHeight); GUI.DrawTexture(myRect1, TrafficLightTextures.LightMode); hoveredSegment = GetHoveredSegment( myRect1, segmentId, hoveredSegment, segmentLight); // COUNTER hoveredSegment = RenderCounter( segmentId, offsetScreenPos, modeWidth, modeHeight, zoom, segmentLights, hoveredSegment); if (vehicleType != ExtVehicleType.None) { // Info sign float infoWidth = 56.125f * zoom; float infoHeight = 51.375f * zoom; int numInfos = 0; for (int k = 0; k < TrafficManagerTool.InfoSignsToDisplay.Length; ++k) { if ((TrafficManagerTool.InfoSignsToDisplay[k] & vehicleType) == ExtVehicleType.None) { continue; } var infoRect = new Rect( offsetScreenPos.x + modeWidth / 2f + (7f * zoom * (float)(numInfos + 1)) + (infoWidth * (float)numInfos), offsetScreenPos.y - (infoHeight / 2f), infoWidth, infoHeight); guiColor.a = TrafficManagerTool.GetHandleAlpha(false); GUI.DrawTexture( infoRect, RoadUITextures.VehicleInfoSignTextures[TrafficManagerTool.InfoSignsToDisplay[k]]); ++numInfos; } } ExtSegment seg = segMan.ExtSegments[segmentId]; ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)]; if (seg.oneWay && segEnd.outgoing) { continue; } segEndMan.CalculateOutgoingLeftStraightRightSegments( ref segEnd, ref nodesBuffer[segmentId], out bool hasLeftSegment, out bool hasForwardSegment, out bool hasRightSegment); switch (segmentLight.CurrentMode) { case LightMode.Simple: { hoveredSegment = SimpleManualSegmentLightMode( segmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment); break; } case LightMode.SingleLeft: { hoveredSegment = LeftForwardRManualSegmentLightMode( hasLeftSegment, segmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment, hasForwardSegment, hasRightSegment); break; } case LightMode.SingleRight: { hoveredSegment = RightForwardLSegmentLightMode( segmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, hasForwardSegment, hasLeftSegment, segmentLight, hasRightSegment, hoveredSegment); break; } default: { // left arrow light if (hasLeftSegment) { hoveredSegment = LeftArrowLightMode( segmentId, lightWidth, hasRightSegment, hasForwardSegment, offsetScreenPos, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment); } // forward arrow light if (hasForwardSegment) { hoveredSegment = ForwardArrowLightMode( segmentId, lightWidth, hasRightSegment, offsetScreenPos, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment); } // right arrow light if (hasRightSegment) { hoveredSegment = RightArrowLightMode( segmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment); } break; } } // end switch } // end foreach all vehicle type } // end for all 8 segments } // end if a node is selected if (hoveredSegment) { return; } hoveredButton[0] = 0; hoveredButton[1] = 0; }
public override void OnPrimaryClickOverlay() { bool ctrlDown = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl); bool shiftDown = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); if (ctrlDown || shiftDown) { if (HoveredSegmentId == 0) { return; } SelectedNodeId = 0; } // TODO provide revert/clear mode issue #568 if (ctrlDown && shiftDown) { bool isRAbout = RoundaboutMassEdit.Instance.FixRabout(HoveredSegmentId); if (!isRAbout) { PriorityRoad.FixRoad(HoveredSegmentId); } RefreshMassEditOverlay(); return; } else if (ctrlDown) { PriorityRoad.FixJunction(HoveredNodeId); RefreshMassEditOverlay(); return; } if (shiftDown) { var primaryPrioType = PriorityType.None; var secondaryPrioType = PriorityType.None; switch (massEditMode) { case PrioritySignsMassEditMode.MainYield: { primaryPrioType = PriorityType.Main; secondaryPrioType = PriorityType.Yield; break; } case PrioritySignsMassEditMode.MainStop: { primaryPrioType = PriorityType.Main; secondaryPrioType = PriorityType.Stop; break; } case PrioritySignsMassEditMode.YieldMain: { primaryPrioType = PriorityType.Yield; secondaryPrioType = PriorityType.Main; break; } case PrioritySignsMassEditMode.StopMain: { primaryPrioType = PriorityType.Stop; secondaryPrioType = PriorityType.Main; break; } } IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; bool VisitorFun(SegmentVisitData data) { foreach (bool startNode in Constants.ALL_BOOL) { TrafficPriorityManager.Instance.SetPrioritySign( data.CurSeg.segmentId, startNode, primaryPrioType); ushort nodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId( data.CurSeg.segmentId, startNode); ExtSegmentEnd curEnd = segEndMan.ExtSegmentEnds[ segEndMan.GetIndex(data.CurSeg.segmentId, startNode)]; for (int i = 0; i < 8; ++i) { ushort otherSegmentId = Singleton <NetManager> .instance.m_nodes .m_buffer[nodeId] .GetSegment(i); if (otherSegmentId == 0 || otherSegmentId == data.CurSeg.segmentId) { continue; } ArrowDirection dir = segEndMan.GetDirection( ref curEnd, otherSegmentId); if (dir != ArrowDirection.Forward) { TrafficPriorityManager.Instance.SetPrioritySign( otherSegmentId, (bool)Constants.ServiceFactory.NetService.IsStartNode( otherSegmentId, nodeId), secondaryPrioType); } } } return(true); } bool isRAbout = RoundaboutMassEdit.Instance.TraverseLoop(HoveredSegmentId, out var segmentList); if (isRAbout) { SegmentTraverser.Traverse(segmentList, VisitorFun); } else { SegmentTraverser.Traverse( HoveredSegmentId, TraverseDirection.AnyDirection, TraverseSide.Straight, SegmentStopCriterion.None, VisitorFun); } // cycle mass edit mode massEditMode = (PrioritySignsMassEditMode)(((int)massEditMode + 1) % Enum.GetValues(typeof(PrioritySignsMassEditMode)) .GetLength(0)); } else { if (TrafficPriorityManager.Instance.HasNodePrioritySign(HoveredNodeId)) { return; } if (!MayNodeHavePrioritySigns(HoveredNodeId)) { return; } SelectedNodeId = HoveredNodeId; Log._Debug($"PrioritySignsTool.OnPrimaryClickOverlay: SelectedNodeId={SelectedNodeId}"); } // update priority node cache RefreshCurrentPriorityNodeIds(); }
public void UpdatePosition(ref ExtVehicle extVehicle, ref Vehicle vehicleData, ref ExtSegmentEnd segEnd, ref PathUnit.Position curPos, ref PathUnit.Position nextPos) { #if DEBUG bool logVehicleLinking = DebugSwitch.VehicleLinkingToSegmentEnd.Get(); #else const bool logVehicleLinking = false; #endif if (logVehicleLinking) { Log._Debug($"ExtVehicleManager.UpdatePosition({extVehicle.vehicleId}) called: {extVehicle}"); } if ((extVehicle.flags & ExtVehicleFlags.Spawned) == ExtVehicleFlags.None) { if (logVehicleLinking) { Log._Debug( $"ExtVehicleManager.UpdatePosition({extVehicle.vehicleId}): Vehicle is not yet spawned."); } OnSpawn(ref extVehicle, ref vehicleData); } if (extVehicle.nextSegmentId != nextPos.m_segment || extVehicle.nextLaneIndex != nextPos.m_lane) { extVehicle.nextSegmentId = nextPos.m_segment; extVehicle.nextLaneIndex = nextPos.m_lane; } if (extVehicle.currentSegmentId != segEnd.segmentId || extVehicle.currentStartNode != segEnd.startNode || extVehicle.currentLaneIndex != curPos.m_lane) { if (logVehicleLinking) { Log._Debug( $"ExtVehicleManager.UpdatePosition({extVehicle.vehicleId}): " + $"Current segment end changed. seg. {extVehicle.currentSegmentId}, " + $"start {extVehicle.currentStartNode}, lane {extVehicle.currentLaneIndex} -> " + $"seg. {segEnd.segmentId}, start {segEnd.startNode}, lane {curPos.m_lane}"); } if (extVehicle.currentSegmentId != 0) { if (logVehicleLinking) { Log._Debug( $"ExtVehicleManager.UpdatePosition({extVehicle.vehicleId}): " + "Unlinking from current segment end"); } Unlink(ref extVehicle); } extVehicle.lastPathId = vehicleData.m_path; extVehicle.lastPathPositionIndex = vehicleData.m_pathPositionIndex; extVehicle.waitTime = 0; #if DEBUGVSTATE if (logVehicleLinking) { Log._DebugFormat( "ExtVehicleManager.UpdatePosition({0}): Linking vehicle to segment end {1} " + "@ {2} ({3}). Current position: Seg. {4}, lane {5}, offset {6} / " + "Next position: Seg. {7}, lane {8}, offset {9}", extVehicle.vehicleId, segEnd.segmentId, segEnd.startNode, segEnd.nodeId, curPos.m_segment, curPos.m_lane, curPos.m_offset, nextPos.m_segment, nextPos.m_lane, nextPos.m_offset); } #endif if (segEnd.segmentId != 0) { Link(ref extVehicle, ref segEnd, curPos.m_lane); } SetJunctionTransitState(ref extVehicle, VehicleJunctionTransitState.Approach); } if (logVehicleLinking) { Log._Debug($"ExtVehicleManager.UpdatePosition({extVehicle.vehicleId}) finshed: {extVehicle}"); } }
public override void OnPrimaryClickOverlay() { if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { if (HoveredSegmentId != 0) { SelectedNodeId = 0; PriorityType primaryPrioType = PriorityType.None; PriorityType secondaryPrioType = PriorityType.None; switch (massEditMode) { case PrioritySignsMassEditMode.MainYield: primaryPrioType = PriorityType.Main; secondaryPrioType = PriorityType.Yield; break; case PrioritySignsMassEditMode.MainStop: primaryPrioType = PriorityType.Main; secondaryPrioType = PriorityType.Stop; break; case PrioritySignsMassEditMode.YieldMain: primaryPrioType = PriorityType.Yield; secondaryPrioType = PriorityType.Main; break; case PrioritySignsMassEditMode.StopMain: primaryPrioType = PriorityType.Stop; secondaryPrioType = PriorityType.Main; break; case PrioritySignsMassEditMode.Delete: default: break; } IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; SegmentTraverser.Traverse(HoveredSegmentId, TraverseDirection.AnyDirection, TraverseSide.Straight, SegmentStopCriterion.None, delegate(SegmentVisitData data) { foreach (bool startNode in Constants.ALL_BOOL) { TrafficPriorityManager.Instance.SetPrioritySign(data.curSeg.segmentId, startNode, primaryPrioType); ushort nodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId(data.curSeg.segmentId, startNode); ExtSegmentEnd curEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(data.curSeg.segmentId, startNode)]; for (int i = 0; i < 8; ++i) { ushort otherSegmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].GetSegment(i); if (otherSegmentId == 0 || otherSegmentId == data.curSeg.segmentId) { continue; } ArrowDirection dir = segEndMan.GetDirection(ref curEnd, otherSegmentId); if (dir != ArrowDirection.Forward) { TrafficPriorityManager.Instance.SetPrioritySign(otherSegmentId, (bool)Constants.ServiceFactory.NetService.IsStartNode(otherSegmentId, nodeId), secondaryPrioType); } } } return(true); }); // cycle mass edit mode massEditMode = (PrioritySignsMassEditMode)(((int)massEditMode + 1) % Enum.GetValues(typeof(PrioritySignsMassEditMode)).GetLength(0)); // update priority node cache RefreshCurrentPriorityNodeIds(); } return; } if (TrafficPriorityManager.Instance.HasNodePrioritySign(HoveredNodeId)) { return; } if (!MayNodeHavePrioritySigns(HoveredNodeId)) { return; } SelectedNodeId = HoveredNodeId; Log._Debug($"PrioritySignsTool.OnPrimaryClickOverlay: SelectedNodeId={SelectedNodeId}"); // update priority node cache RefreshCurrentPriorityNodeIds(); }
private static void TraverseRec(ref ExtSegment prevSeg, ref ExtSegmentEnd prevSegEnd, ref NetNode node, bool viaInitialStartNode, TraverseDirection direction, TraverseSide side, SegmentStopCriterion stopCrit, SegmentVisitor visitorFun, HashSet <ushort> visitedSegmentIds) { // Log._Debug($"SegmentTraverser: Traversing segment {prevSegEnd.segmentId}"); // collect next segment ids to traverse if (direction == TraverseDirection.None) { throw new ArgumentException($"Invalid direction {direction} given."); } if (side == TraverseSide.None) { throw new ArgumentException($"Invalid side {side} given."); } IExtSegmentManager extSegMan = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager; HashSet <ushort> nextSegmentIds = new HashSet <ushort>(); for (int i = 0; i < 8; ++i) { ushort nextSegmentId = node.GetSegment(i); if (nextSegmentId == 0 || nextSegmentId == prevSegEnd.segmentId) { continue; } bool nextIsStartNode = (bool)Constants.ServiceFactory.NetService.IsStartNode( nextSegmentId, prevSegEnd.nodeId); ExtSegmentEnd nextSegEnd = extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(nextSegmentId, nextIsStartNode)]; if (direction == TraverseDirection.AnyDirection || (direction == TraverseDirection.Incoming && nextSegEnd.incoming) || (direction == TraverseDirection.Outgoing && nextSegEnd.outgoing)) { if (side == TraverseSide.AnySide) { nextSegmentIds.Add(nextSegmentId); } else { ArrowDirection dir = extSegEndMan.GetDirection( ref prevSegEnd, nextSegmentId); if (((side & TraverseSide.Left) != TraverseSide.None && dir == ArrowDirection.Left) || ((side & TraverseSide.Straight) != TraverseSide.None && dir == ArrowDirection.Forward) || ((side & TraverseSide.Right) != TraverseSide.None && dir == ArrowDirection.Right)) { nextSegmentIds.Add(nextSegmentId); } } } } nextSegmentIds.Remove(0); // Log._Debug($"SegmentTraverser: Fetched next segments to traverse: // {nextSegmentIds.CollectionToString()}"); if (nextSegmentIds.Count >= 2 && (stopCrit & SegmentStopCriterion.Junction) != SegmentStopCriterion.None) { // Log._Debug($"SegmentTraverser: Stop criterion reached @ {prevSegEnd.segmentId}: // {nextSegmentIds.Count} connected segments"); return; } // explore next segments foreach (ushort nextSegmentId in nextSegmentIds) { if (visitedSegmentIds.Contains(nextSegmentId)) { continue; } visitedSegmentIds.Add(nextSegmentId); // Log._Debug($"SegmentTraverser: Traversing segment {nextSegmentId}"); ushort nextStartNodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId(nextSegmentId, true); if (!visitorFun( new SegmentVisitData( ref prevSeg, ref extSegMan.ExtSegments[nextSegmentId], viaInitialStartNode, prevSegEnd.nodeId == nextStartNodeId, false))) { continue; } bool nextNodeIsStartNode = nextStartNodeId != prevSegEnd.nodeId; ExtSegmentEnd nextSegEnd = extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(nextSegmentId, nextNodeIsStartNode)]; Constants.ServiceFactory.NetService.ProcessNode( nextSegEnd.nodeId, (ushort nId, ref NetNode nextNode) => { TraverseRec( ref extSegMan.ExtSegments[nextSegmentId], ref nextSegEnd, ref nextNode, viaInitialStartNode, direction, side, stopCrit, visitorFun, visitedSegmentIds); return(true); }); } // end foreach }
protected void UpdateSegmentEnd(ref ExtSegment seg, ref ExtSegmentEnd end) { #if DEBUG bool logTurnOnRed = DebugSwitch.TurnOnRed.Get(); #else const bool logTurnOnRed = false; #endif if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}) called."); } IExtSegmentManager segmentManager = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager segmentEndManager = Constants.ManagerFactory.ExtSegmentEndManager; ushort segmentId = seg.segmentId; ushort nodeId = end.nodeId; bool hasOutgoingSegment = false; Services.NetService.IterateNodeSegments( end.nodeId, (ushort otherSegId, ref NetSegment otherSeg) => { int index0 = segmentEndManager.GetIndex(otherSegId, otherSeg.m_startNode == nodeId); if (otherSegId != segmentId && segmentEndManager.ExtSegmentEnds[index0].outgoing) { hasOutgoingSegment = true; return(false); } return(true); }); // check if traffic can flow to the node and that there is at least one left segment if (!end.incoming || !hasOutgoingSegment) { if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + "outgoing one-way or insufficient number of outgoing segments."); } return; } bool lhd = Services.SimulationService.LeftHandDrive; // check node // note that we must not check for the `TrafficLights` flag here because the flag might not be loaded yet bool nodeValid = false; Services.NetService.ProcessNode( nodeId, (ushort _, ref NetNode node) => { nodeValid = (node.m_flags & NetNode.Flags.LevelCrossing) == NetNode.Flags.None && node.Info?.m_class?.m_service != ItemClass.Service.Beautification; return(true); }); if (!nodeValid) { if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): node invalid"); } return; } // get left/right segments ushort leftSegmentId = 0; ushort rightSegmentId = 0; Services.NetService.ProcessSegment( end.segmentId, (ushort _, ref NetSegment segment) => { segment.GetLeftAndRightSegments( nodeId, out leftSegmentId, out rightSegmentId); return(true); }); if (logTurnOnRed) { Log._Debug( $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + $"got left/right segments: {leftSegmentId}/{rightSegmentId}"); } // validate left/right segments according to geometric properties if (leftSegmentId != 0 && segmentEndManager.GetDirection(ref end, leftSegmentId) != ArrowDirection.Left) { if (logTurnOnRed) { Log._Debug( $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + "left segment is not geometrically left"); } leftSegmentId = 0; } if (rightSegmentId != 0 && segmentEndManager.GetDirection(ref end, rightSegmentId) != ArrowDirection.Right) { if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + "right segment is not geometrically right"); } rightSegmentId = 0; } // check for incoming one-ways if (leftSegmentId != 0 && !segmentEndManager.ExtSegmentEnds[segmentEndManager.GetIndex(leftSegmentId, nodeId)].outgoing) { if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + "left segment is incoming one-way"); } leftSegmentId = 0; } if (rightSegmentId != 0 && !segmentEndManager.ExtSegmentEnds[segmentEndManager.GetIndex(rightSegmentId, nodeId)].outgoing) { if (logTurnOnRed) { Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + "right segment is incoming one-way"); } rightSegmentId = 0; } if (seg.oneWay) { if ((lhd && rightSegmentId != 0) || (!lhd && leftSegmentId != 0)) { // special case: one-way to one-way in non-preferred direction if (logTurnOnRed) { Log._Debug( $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + "source is incoming one-way. checking for one-way in non-preferred direction"); } ushort targetSegmentId = lhd ? rightSegmentId : leftSegmentId; if (!segmentManager.ExtSegments[targetSegmentId].oneWay) { // disallow turn in non-preferred direction if (logTurnOnRed) { Log._Debug( $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + $"turn in non-preferred direction {(lhd ? "right" : "left")} disallowed"); } if (lhd) { rightSegmentId = 0; } else { leftSegmentId = 0; } } } } else if (lhd) { // default case (LHD): turn in preferred direction rightSegmentId = 0; } else { // default case (RHD): turn in preferred direction leftSegmentId = 0; } int index = GetIndex(end.segmentId, end.startNode); TurnOnRedSegments[index].leftSegmentId = leftSegmentId; TurnOnRedSegments[index].rightSegmentId = rightSegmentId; if (logTurnOnRed) { Log._Debug( $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " + $"Finished calculation. leftSegmentId={leftSegmentId}, rightSegmentId={rightSegmentId}"); } }
public void ShowGUI(bool viewOnly) { try { IExtSegmentManager segMan = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance; TrafficPriorityManager prioMan = TrafficPriorityManager.Instance; TrafficLightManager tlm = TrafficLightManager.Instance; Vector3 camPos = Constants.ServiceFactory.SimulationService.CameraPosition; bool clicked = !viewOnly?MainTool.CheckClicked() : false; ushort removedNodeId = 0; bool showRemoveButton = false; foreach (ushort nodeId in currentPriorityNodeIds) { if (!Constants.ServiceFactory.NetService.IsNodeValid(nodeId)) { continue; } if (!MainTool.IsNodeWithinViewDistance(nodeId)) { continue; } Vector3 nodePos = default(Vector3); Constants.ServiceFactory.NetService.ProcessNode(nodeId, delegate(ushort nId, ref NetNode node) { nodePos = node.m_position; return(true); }); for (int i = 0; i < 8; ++i) { ushort segmentId = 0; Constants.ServiceFactory.NetService.ProcessNode(nodeId, delegate(ushort nId, ref NetNode node) { segmentId = node.GetSegment(i); return(true); }); if (segmentId == 0) { continue; } bool startNode = (bool)Constants.ServiceFactory.NetService.IsStartNode(segmentId, nodeId); ExtSegment seg = segMan.ExtSegments[segmentId]; ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)]; if (seg.oneWay && segEnd.outgoing) { continue; } // calculate sign position Vector3 signPos = nodePos; Constants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate(ushort sId, ref NetSegment segment) { signPos += 10f * (startNode ? segment.m_startDirection : segment.m_endDirection); return(true); }); Vector3 signScreenPos; if (!MainTool.WorldToScreenPoint(signPos, out signScreenPos)) { continue; } // draw sign and handle input PriorityType sign = prioMan.GetPrioritySign(segmentId, startNode); if (viewOnly && sign == PriorityType.None) { continue; } if (!viewOnly && sign != PriorityType.None) { showRemoveButton = true; } if (MainTool.DrawGenericSquareOverlayTexture(TextureResources.PrioritySignTextures[sign], camPos, signPos, 90f, !viewOnly) && clicked) { PriorityType?newSign = null; switch (sign) { case PriorityType.Main: newSign = PriorityType.Yield; break; case PriorityType.Yield: newSign = PriorityType.Stop; break; case PriorityType.Stop: newSign = PriorityType.Main; break; case PriorityType.None: default: newSign = prioMan.CountPrioritySignsAtNode(nodeId, PriorityType.Main) >= 2 ? PriorityType.Yield : PriorityType.Main; break; } if (newSign != null) { SetPrioritySign(segmentId, startNode, (PriorityType)newSign); } } // draw sign } // foreach segment end if (viewOnly) { continue; } // draw remove button and handle click if (showRemoveButton && MainTool.DrawHoverableSquareOverlayTexture(TextureResources.SignRemoveTexture2D, camPos, nodePos, 90f) && clicked) { prioMan.RemovePrioritySignsFromNode(nodeId); Log._Debug($"PrioritySignsTool.ShowGUI: Removed priority signs from node {nodeId}"); removedNodeId = nodeId; } } // foreach node if (removedNodeId != 0) { currentPriorityNodeIds.Remove(removedNodeId); SelectedNodeId = 0; } } catch (Exception e) { Log.Error(e.ToString()); } }
/// <summary> /// Handles a segment replacement /// </summary> /// <param name="replacement">segment replacement</param> /// <param name="newEndGeo">new segment end geometry</param> protected virtual void HandleSegmentEndReplacement(SegmentEndReplacement replacement, ref ExtSegmentEnd segEnd) { }
/// <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); }
public void CalculateAutoPedestrianLightState(ref NetNode node, bool propagate = true) { #if DEBUGTTL bool debug = DebugSwitch.TimedTrafficLights.Get() && DebugSettings.NodeId == NodeId; #endif #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Calculating pedestrian light state of seg. {SegmentId} @ node {NodeId}"); } #endif IExtSegmentManager segMan = Constants.ManagerFactory.ExtSegmentManager; IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager; ExtSegment seg = segMan.ExtSegments[SegmentId]; ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(SegmentId, StartNode)]; ushort nodeId = segEnd.nodeId; if (nodeId != NodeId) { Log.Warning($"CustomSegmentLights.CalculateAutoPedestrianLightState: Node id mismatch! segment end node is {nodeId} but we are node {NodeId}. segEnd={segEnd} this={this}"); return; } if (propagate) { for (int i = 0; i < 8; ++i) { ushort otherSegmentId = node.GetSegment(i); if (otherSegmentId == 0 || otherSegmentId == SegmentId) { continue; } ICustomSegmentLights otherLights = LightsManager.GetSegmentLights(nodeId, otherSegmentId); if (otherLights == null) { #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Expected other (propagate) CustomSegmentLights at segment {otherSegmentId} @ {NodeId} but there was none. Original segment id: {SegmentId}"); } #endif continue; } otherLights.CalculateAutoPedestrianLightState(ref node, false); } } if (IsAnyGreen()) { #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Any green at seg. {SegmentId} @ {NodeId}"); } #endif AutoPedestrianLightState = RoadBaseAI.TrafficLightState.Red; return; } #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Querying incoming segments at seg. {SegmentId} @ {NodeId}"); } #endif ItemClass prevConnectionClass = null; Constants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate(ushort prevSegId, ref NetSegment segment) { prevConnectionClass = segment.Info.GetConnectionClass(); return(true); }); RoadBaseAI.TrafficLightState autoPedestrianLightState = RoadBaseAI.TrafficLightState.Green; bool lhd = Constants.ServiceFactory.SimulationService.LeftHandDrive; if (!(segEnd.incoming && seg.oneWay)) { for (int i = 0; i < 8; ++i) { ushort otherSegmentId = node.GetSegment(i); if (otherSegmentId == 0 || otherSegmentId == SegmentId) { continue; } //ExtSegment otherSeg = segMan.ExtSegments[otherSegmentId]; if (!segEndMan.ExtSegmentEnds[segEndMan.GetIndex(otherSegmentId, (bool)Constants.ServiceFactory.NetService.IsStartNode(otherSegmentId, NodeId))].incoming) { continue; } #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Checking incoming straight segment {otherSegmentId} at seg. {SegmentId} @ {NodeId}"); } #endif ICustomSegmentLights otherLights = LightsManager.GetSegmentLights(nodeId, otherSegmentId); if (otherLights == null) { #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Expected other (straight) CustomSegmentLights at segment {otherSegmentId} @ {NodeId} but there was none. Original segment id: {SegmentId}"); } #endif continue; } ItemClass nextConnectionClass = null; Constants.ServiceFactory.NetService.ProcessSegment(otherSegmentId, delegate(ushort otherSegId, ref NetSegment segment) { nextConnectionClass = segment.Info.GetConnectionClass(); return(true); }); if (nextConnectionClass.m_service != prevConnectionClass.m_service) { #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Other (straight) segment {otherSegmentId} @ {NodeId} has different connection service than segment {SegmentId} ({nextConnectionClass.m_service} vs. {prevConnectionClass.m_service}). Ignoring traffic light state."); } #endif continue; } ArrowDirection dir = segEndMan.GetDirection(ref segEnd, otherSegmentId); if (dir == ArrowDirection.Forward) { if (!otherLights.IsAllMainRed()) { #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Not all main red at {otherSegmentId} at seg. {SegmentId} @ {NodeId}"); } #endif autoPedestrianLightState = RoadBaseAI.TrafficLightState.Red; break; } } else if ((dir == ArrowDirection.Left && lhd) || (dir == ArrowDirection.Right && !lhd)) { if ((lhd && !otherLights.IsAllRightRed()) || (!lhd && !otherLights.IsAllLeftRed())) { #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Not all left red at {otherSegmentId} at seg. {SegmentId} @ {NodeId}"); } #endif autoPedestrianLightState = RoadBaseAI.TrafficLightState.Red; break; } } } } AutoPedestrianLightState = autoPedestrianLightState; #if DEBUGTTL if (debug) { Log._Debug($"CustomSegmentLights.CalculateAutoPedestrianLightState: Calculated AutoPedestrianLightState for segment {SegmentId} @ {NodeId}: {AutoPedestrianLightState}"); } #endif }