public void SkipStep(bool setLights = true)
        {
            if (!isMasterNode())
            {
                return;
            }

            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            var newCurrentStep = (CurrentStep + 1) % NumSteps();

            foreach (ushort slaveNodeId in NodeGroup)
            {
                TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                if (slaveSim == null || !slaveSim.IsTimedLight())
                {
                    continue;
                }

                slaveSim.TimedLight.Steps[CurrentStep].SetStepDone();
                slaveSim.TimedLight.CurrentStep = newCurrentStep;
                slaveSim.TimedLight.Steps[newCurrentStep].Start();
                if (setLights)
                {
                    slaveSim.TimedLight.Steps[newCurrentStep].SetLights();
                }
            }
        }
Ejemplo n.º 2
0
 internal void Housekeeping()
 {
     if (TrafficManagerTool.GetToolMode() != ToolMode.AddPrioritySigns && TrafficLightSimulationManager.Instance().GetNodeSimulation(NodeId) == null && Type == PriorityType.None)
     {
         TrafficPriorityManager.Instance().RemovePrioritySegments(NodeId);
     }
 }
 internal void RemoveNodeFromGroup(ushort otherNodeId)
 {
     NodeGroup.Remove(otherNodeId);
     if (NodeGroup.Count <= 0)
     {
         TrafficLightSimulationManager.Instance().RemoveNodeFromSimulation(NodeId, true, false);
         return;
     }
     masterNodeId = NodeGroup[0];
 }
        internal TimedTrafficLights MasterLights()
        {
            TrafficLightSimulation masterSim = TrafficLightSimulationManager.Instance().GetNodeSimulation(masterNodeId);

            if (masterSim == null || !masterSim.IsTimedLight())
            {
                return(null);
            }
            return(masterSim.TimedLight);
        }
Ejemplo n.º 5
0
        public void OnUpdate(NodeGeometry nodeGeometry)
        {
#if DEBUG
            Log._Debug($"TrafficLightSimulation: OnUpdate @ node {NodeId} ({nodeGeometry.NodeId})");
#endif

            if (!Flags.mayHaveTrafficLight(NodeId))
            {
                Log.Warning($"Housekeeping: Node {NodeId} has traffic light simulation but must not have a traffic light!");
                TrafficLightSimulationManager.Instance().RemoveNodeFromSimulation(NodeId, false, true);
            }

            if (!IsManualLight() && !IsTimedLight())
            {
                return;
            }

            if (!nodeGeometry.IsValid())
            {
                // node has become invalid. Remove manual/timed traffic light and destroy custom lights
                TrafficLightSimulationManager.Instance().RemoveNodeFromSimulation(NodeId, false, false);
                return;
            }

            CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance();

            for (var s = 0; s < 8; s++)
            {
                var segmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[NodeId].GetSegment(s);

                if (segmentId == 0)
                {
                    continue;
                }

#if DEBUG
                Log._Debug($"TrafficLightSimulation: OnUpdate @ node {NodeId}: Adding live traffic lights to segment {segmentId}");
#endif

                // add custom lights
                if (!customTrafficLightsManager.IsSegmentLight(NodeId, segmentId))
                {
                    customTrafficLightsManager.AddSegmentLights(NodeId, segmentId);
                }

                // housekeep timed light
                customTrafficLightsManager.GetSegmentLights(NodeId, segmentId).housekeeping(true);
            }

            // ensure there is a physical traffic light
            Flags.setNodeTrafficLight(NodeId, true);

            TimedLight?.handleNewSegments();
            TimedLight?.housekeeping();
        }
Ejemplo n.º 6
0
        public bool CustomCheckTrafficLights(ushort node, ushort segment)
        {
            var nodeSimulation = Options.timedLightsEnabled ? TrafficLightSimulationManager.Instance().GetNodeSimulation(node) : null;

            var instance          = Singleton <NetManager> .instance;
            var currentFrameIndex = Singleton <SimulationManager> .instance.m_currentFrameIndex;

            var num  = (uint)((node << 8) / 32768);
            var num2 = currentFrameIndex - num & 255u;

            // NON-STOCK CODE START //
            RoadBaseAI.TrafficLightState pedestrianLightState;
            CustomSegmentLights          lights = CustomTrafficLightsManager.Instance().GetSegmentLights(node, segment);

            if (lights == null || nodeSimulation == null || !nodeSimulation.IsSimulationActive())
            {
                RoadBaseAI.TrafficLightState vehicleLightState;
                bool vehicles;
                bool pedestrians;

                RoadBaseAI.GetTrafficLightState(node, ref instance.m_segments.m_buffer[segment], currentFrameIndex - num, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians);
                if ((pedestrianLightState == RoadBaseAI.TrafficLightState.GreenToRed || pedestrianLightState == RoadBaseAI.TrafficLightState.Red) && !pedestrians && num2 >= 196u)
                {
                    RoadBaseAI.SetTrafficLightState(node, ref instance.m_segments.m_buffer[segment], currentFrameIndex - num, vehicleLightState, pedestrianLightState, vehicles, true);
                    return(true);
                }
            }
            else
            {
                if (lights.InvalidPedestrianLight)
                {
                    pedestrianLightState = RoadBaseAI.TrafficLightState.Green;
                }
                else
                {
                    pedestrianLightState = (RoadBaseAI.TrafficLightState)lights.PedestrianLightState;
                }
            }
            // NON-STOCK CODE END //

            switch (pedestrianLightState)
            {
            case RoadBaseAI.TrafficLightState.RedToGreen:
                if (num2 < 60u)
                {
                    return(false);
                }
                break;

            case RoadBaseAI.TrafficLightState.Red:
            case RoadBaseAI.TrafficLightState.GreenToRed:
                return(false);
            }
            return(true);
        }
Ejemplo n.º 7
0
        public override void OnPrimaryClickOverlay()
        {
            if (IsCursorInPanel())
            {
                return;
            }

            if (SelectedNodeId != 0)
            {
                return;
            }

            TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance;
            TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance;

            if (!tlsMan.TrafficLightSimulations[HoveredNodeId].IsTimedLight())
            {
                if ((Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_flags &
                     NetNode.Flags.TrafficLights) == NetNode.Flags.None)
                {
                    prioMan.RemovePrioritySignsFromNode(HoveredNodeId);
                    TrafficLightManager.Instance.AddTrafficLight(
                        HoveredNodeId,
                        ref Singleton <NetManager> .instance.m_nodes.m_buffer[
                            HoveredNodeId]);
                }

                if (tlsMan.SetUpManualTrafficLight(HoveredNodeId))
                {
                    SelectedNodeId = HoveredNodeId;
                }

                // for (var s = 0; s < 8; s++) {
                //        var segment = Singleton<NetManager>
                //                      .instance.m_nodes.m_buffer[SelectedNodeId].GetSegment(s);
                //        if (segment != 0 &&
                //            !TrafficPriority.IsPrioritySegment(SelectedNodeId, segment)) {
                //            TrafficPriority.AddPrioritySegment(
                //                SelectedNodeId,
                //                segment,
                //                SegmentEnd.PriorityType.None);
                //        }
                //    }
            }
            else
            {
                MainTool.ShowError(
                    Translation.TrafficLights.Get("Dialog.Text:Node has timed TL script"));
            }
        }
Ejemplo n.º 8
0
        public override void Cleanup()
        {
            if (SelectedNodeId == 0)
            {
                return;
            }
            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;

            if (!tlsMan.HasManualSimulation(SelectedNodeId))
            {
                return;
            }

            tlsMan.RemoveNodeFromSimulation(SelectedNodeId, true, false);
        }
        public override void OnPrimaryClickOverlay()
        {
            if (IsCursorInPanel())
            {
                return;
            }

            if (SelectedNodeId != 0)
            {
                return;
            }

            TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance;
            TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance;
            ref NetNode hoveredNetNode            = ref HoveredNodeId.ToNode();
        public void SetLights()
        {
            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            // set lights
            foreach (ushort slaveNodeId in NodeGroup)
            {
                TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                if (slaveSim == null || !slaveSim.IsTimedLight())
                {
                    //TrafficLightSimulation.RemoveNodeFromSimulation(slaveNodeId, false); // we iterate over NodeGroup!!
                    continue;
                }
                slaveSim.TimedLight.Steps[CurrentStep].SetLights();
            }
        }
Ejemplo n.º 11
0
        public override void Cleanup()
        {
            if (SelectedNodeId == 0)
            {
                return;
            }
            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;

            var nodeSimulation = tlsMan.GetNodeSimulation(SelectedNodeId);

            if (nodeSimulation == null || !nodeSimulation.IsManualLight())
            {
                return;
            }

            tlsMan.RemoveNodeFromSimulation(SelectedNodeId, true, false);
        }
Ejemplo n.º 12
0
        public override void OnPrimaryClickOverlay()
        {
            if (IsCursorInPanel())
            {
                return;
            }
            if (SelectedNodeId != 0)
            {
                return;
            }

            TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance;
            TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance;

            TrafficLightSimulation sim = tlsMan.GetNodeSimulation(HoveredNodeId);

            if (sim == null || !sim.IsTimedLight())
            {
                if ((Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None)
                {
                    prioMan.RemovePrioritySegments(HoveredNodeId);
                    TrafficLightManager.Instance.AddTrafficLight(HoveredNodeId);
                }

                SelectedNodeId = HoveredNodeId;

                sim = tlsMan.AddNodeToSimulation(SelectedNodeId);
                sim.SetupManualTrafficLight();

                /*for (var s = 0; s < 8; s++) {
                 *      var segment = Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNodeId].GetSegment(s);
                 *      if (segment != 0 && !TrafficPriority.IsPrioritySegment(SelectedNodeId, segment)) {
                 *              TrafficPriority.AddPrioritySegment(SelectedNodeId, segment, SegmentEnd.PriorityType.None);
                 *      }
                 * }*/
            }
            else
            {
                MainTool.ShowTooltip(Translation.GetString("NODE_IS_TIMED_LIGHT"), Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_position);
            }
        }
Ejemplo n.º 13
0
        private void RenderManualNodeOverlays(RenderManager.CameraInfo cameraInfo)
        {
            var nodeSimulation = TrafficLightSimulationManager.Instance().GetNodeSimulation(SelectedNodeId);
            CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance();

            for (var i = 0; i < 8; i++)
            {
                var    colorGray = new Color(0.25f, 0.25f, 0.25f, 0.25f);
                ushort segmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[SelectedNodeId].GetSegment(i);

                if (segmentId == 0 ||
                    (nodeSimulation != null && customTrafficLightsManager.IsSegmentLight(SelectedNodeId, segmentId)))
                {
                    continue;
                }

                var position = CalculateNodePositionForSegment(Singleton <NetManager> .instance.m_nodes.m_buffer[SelectedNodeId], segmentId);

                var width = _hoveredButton[0] == segmentId ? 11.25f : 10f;
                MainTool.DrawOverlayCircle(cameraInfo, colorGray, position, width, segmentId != _hoveredButton[0]);
            }
        }
Ejemplo n.º 14
0
        public override void OnPrimaryClickOverlay()
        {
            if (IsCursorInPanel())
            {
                return;
            }

            if (SelectedNodeId != 0)
            {
                return;
            }

            TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance;
            TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance;

            if (!tlsMan.TrafficLightSimulations[HoveredNodeId].IsTimedLight())
            {
                if ((Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_flags &
                     NetNode.Flags.TrafficLights) == NetNode.Flags.None)
                {
                    prioMan.RemovePrioritySignsFromNode(HoveredNodeId);
                    TrafficLightManager.Instance.AddTrafficLight(
                        HoveredNodeId,
                        ref Singleton <NetManager> .instance.m_nodes.m_buffer[
                            HoveredNodeId]);
                }

                if (tlsMan.SetUpManualTrafficLight(HoveredNodeId))
                {
                    SelectedNodeId = HoveredNodeId;
                    MainTool.RequestOnscreenDisplayUpdate();
                }
            }
            else
            {
                MainTool.WarningPrompt(Translation.TrafficLights.Get("Dialog.Text:Node has timed TL script"));
            }
        }
        public override void OnPrimaryClickOverlay()
        {
            if (HoveredNodeId == 0)
            {
                return;
            }

            if ((Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.Junction) == NetNode.Flags.None)
            {
                return;
            }

            TrafficLightSimulation sim = TrafficLightSimulationManager.Instance().GetNodeSimulation(HoveredNodeId);

            if (sim != null && sim.IsTimedLight())
            {
                MainTool.ShowTooltip(Translation.GetString("NODE_IS_TIMED_LIGHT"), Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_position);
                return;
            }

            TrafficPriorityManager.Instance().RemovePrioritySegments(HoveredNodeId);
            Flags.setNodeTrafficLight(HoveredNodeId, (Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None);
        }
Ejemplo n.º 16
0
        public void ShowGUI(bool viewOnly)
        {
            try {
                TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance;
                TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance;
                TrafficLightManager           tlm     = TrafficLightManager.Instance;

                bool clicked = !viewOnly?MainTool.CheckClicked() : false;

                var hoveredSegment = false;
                //Log.Message("_guiPrioritySigns called. num of prio segments: " + TrafficPriority.PrioritySegments.Count);

                HashSet <ushort> nodeIdsWithSigns = new HashSet <ushort>();
                foreach (ushort segmentId in currentPrioritySegmentIds)
                {
                    var trafficSegment = prioMan.TrafficSegments[segmentId];
                    if (trafficSegment == null)
                    {
                        continue;
                    }
                    SegmentGeometry geometry = SegmentGeometry.Get(segmentId);

                    prioritySegments[0] = null;
                    prioritySegments[1] = null;

                    if (tlsMan.GetNodeSimulation(trafficSegment.Node1) == null)
                    {
                        SegmentEnd tmpSeg1   = prioMan.GetPrioritySegment(trafficSegment.Node1, segmentId);
                        bool       startNode = geometry.StartNodeId() == trafficSegment.Node1;
                        if (tmpSeg1 != null && !geometry.IsOutgoingOneWay(startNode))
                        {
                            prioritySegments[0] = tmpSeg1;
                            nodeIdsWithSigns.Add(trafficSegment.Node1);
                            prioMan.AddPriorityNode(trafficSegment.Node1);
                        }
                    }
                    if (tlsMan.GetNodeSimulation(trafficSegment.Node2) == null)
                    {
                        SegmentEnd tmpSeg2   = prioMan.GetPrioritySegment(trafficSegment.Node2, segmentId);
                        bool       startNode = geometry.StartNodeId() == trafficSegment.Node2;
                        if (tmpSeg2 != null && !geometry.IsOutgoingOneWay(startNode))
                        {
                            prioritySegments[1] = tmpSeg2;
                            nodeIdsWithSigns.Add(trafficSegment.Node2);
                            prioMan.AddPriorityNode(trafficSegment.Node2);
                        }
                    }

                    //Log.Message("init ok");

                    foreach (var prioritySegment in prioritySegments)
                    {
                        if (prioritySegment == null)
                        {
                            continue;
                        }

                        var nodeId = prioritySegment.NodeId;
                        //Log.Message("_guiPrioritySigns: nodeId=" + nodeId);

                        var nodePositionVector3 = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].m_position;
                        var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
                        var diff   = nodePositionVector3 - camPos;
                        if (diff.magnitude > TrafficManagerTool.PriorityCloseLod)
                        {
                            continue;                             // do not draw if too distant
                        }
                        if (Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_startNode == (ushort)nodeId)
                        {
                            nodePositionVector3.x += Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_startDirection.x * 10f;
                            nodePositionVector3.y += Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_startDirection.y * 10f;
                            nodePositionVector3.z += Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_startDirection.z * 10f;
                        }
                        else
                        {
                            nodePositionVector3.x += Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_endDirection.x * 10f;
                            nodePositionVector3.y += Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_endDirection.y * 10f;
                            nodePositionVector3.z += Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_endDirection.z * 10f;
                        }

                        var nodeScreenPosition = Camera.main.WorldToScreenPoint(nodePositionVector3);
                        nodeScreenPosition.y = Screen.height - nodeScreenPosition.y;
                        if (nodeScreenPosition.z < 0)
                        {
                            continue;
                        }
                        var zoom            = 1.0f / diff.magnitude * 100f * MainTool.GetBaseZoom();
                        var size            = 110f * zoom;
                        var guiColor        = GUI.color;
                        var nodeBoundingBox = new Rect(nodeScreenPosition.x - size / 2, nodeScreenPosition.y - size / 2, size, size);
                        hoveredSegment = !viewOnly && TrafficManagerTool.IsMouseOver(nodeBoundingBox);

                        if (hoveredSegment)
                        {
                            // mouse hovering over sign
                            guiColor.a = 0.8f;
                        }
                        else
                        {
                            guiColor.a = 0.5f;
                            size       = 90f * zoom;
                        }
                        var nodeDrawingBox = new Rect(nodeScreenPosition.x - size / 2, nodeScreenPosition.y - size / 2, size, size);

                        GUI.color = guiColor;

                        bool setUndefinedSignsToMainRoad = false;
                        switch (prioritySegment.Type)
                        {
                        case SegmentEnd.PriorityType.Main:
                            GUI.DrawTexture(nodeDrawingBox, TrafficLightToolTextureResources.SignPriorityTexture2D);
                            if (clicked && hoveredSegment)
                            {
                                //Log._Debug("Click on node " + nodeId + ", segment " + segmentId + " to change prio type (1)");
                                //Log.Message("PrioritySegment.Type = Yield");
                                prioritySegment.Type        = SegmentEnd.PriorityType.Yield;
                                setUndefinedSignsToMainRoad = true;
                                clicked = false;
                            }
                            break;

                        case SegmentEnd.PriorityType.Yield:
                            GUI.DrawTexture(nodeDrawingBox, TrafficLightToolTextureResources.SignYieldTexture2D);
                            if (clicked && hoveredSegment)
                            {
                                //Log._Debug("Click on node " + nodeId + ", segment " + segmentId + " to change prio type (2)");
                                prioritySegment.Type        = SegmentEnd.PriorityType.Stop;
                                setUndefinedSignsToMainRoad = true;
                                clicked = false;
                            }

                            break;

                        case SegmentEnd.PriorityType.Stop:
                            GUI.DrawTexture(nodeDrawingBox, TrafficLightToolTextureResources.SignStopTexture2D);
                            if (clicked && hoveredSegment)
                            {
                                //Log._Debug("Click on node " + nodeId + ", segment " + segmentId + " to change prio type (3)");
                                prioritySegment.Type = SegmentEnd.PriorityType.Main;
                                clicked = false;
                            }
                            break;

                        case SegmentEnd.PriorityType.None:
                            if (viewOnly)
                            {
                                break;
                            }
                            GUI.DrawTexture(nodeDrawingBox, TrafficLightToolTextureResources.SignNoneTexture2D);

                            if (clicked && hoveredSegment)
                            {
                                //Log._Debug("Click on node " + nodeId + ", segment " + segmentId + " to change prio type (4)");
                                //Log.Message("PrioritySegment.Type = None");
                                prioritySegment.Type = GetNumberOfMainRoads(nodeId, ref Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId]) >= 2
                                                                                ? SegmentEnd.PriorityType.Yield
                                                                                : SegmentEnd.PriorityType.Main;
                                if (prioritySegment.Type == SegmentEnd.PriorityType.Yield)
                                {
                                    setUndefinedSignsToMainRoad = true;
                                }
                                clicked = false;
                            }
                            break;
                        }

                        if (setUndefinedSignsToMainRoad)
                        {
                            foreach (var otherPrioritySegment in prioMan.GetPrioritySegments(nodeId))
                            {
                                if (otherPrioritySegment.SegmentId == prioritySegment.SegmentId)
                                {
                                    continue;
                                }
                                if (otherPrioritySegment.Type == SegmentEnd.PriorityType.None)
                                {
                                    otherPrioritySegment.Type = SegmentEnd.PriorityType.Main;
                                }
                            }
                        }
                    }
                }

                if (viewOnly)
                {
                    return;
                }

                ushort hoveredExistingNodeId = 0;
                foreach (ushort nodeId in nodeIdsWithSigns)
                {
                    var nodePositionVector3 = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].m_position;
                    var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
                    var diff   = nodePositionVector3 - camPos;
                    if (diff.magnitude > TrafficManagerTool.PriorityCloseLod)
                    {
                        continue;
                    }

                    // draw deletion button
                    var nodeScreenPosition = Camera.main.WorldToScreenPoint(nodePositionVector3);
                    nodeScreenPosition.y = Screen.height - nodeScreenPosition.y;
                    if (nodeScreenPosition.z < 0)
                    {
                        continue;
                    }
                    var zoom            = 1.0f / diff.magnitude * 100f * MainTool.GetBaseZoom();
                    var size            = 90f * zoom;
                    var nodeBoundingBox = new Rect(nodeScreenPosition.x - size / 2, nodeScreenPosition.y - size / 2, size, size);

                    var guiColor          = GUI.color;
                    var nodeCenterHovered = TrafficManagerTool.IsMouseOver(nodeBoundingBox);
                    if (nodeCenterHovered)
                    {
                        hoveredExistingNodeId = nodeId;
                        guiColor.a            = 0.8f;
                    }
                    else
                    {
                        guiColor.a = 0.5f;
                    }
                    GUI.color = guiColor;

                    GUI.DrawTexture(nodeBoundingBox, TrafficLightToolTextureResources.SignRemoveTexture2D);
                }

                // add a new or delete a priority segment node
                if (HoveredNodeId != 0 || hoveredExistingNodeId != 0)
                {
                    bool delete = false;
                    if (hoveredExistingNodeId != 0)
                    {
                        delete = true;
                    }

                    // determine if we may add new priority signs to this node
                    bool ok = false;
                    TrafficLightSimulation nodeSim = tlsMan.GetNodeSimulation(HoveredNodeId);
                    if ((Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None)
                    {
                        // no traffic light set
                        ok = true;
                    }
                    else if (nodeSim == null || !nodeSim.IsTimedLight())
                    {
                        ok = true;
                    }

                    if (!Flags.mayHaveTrafficLight(HoveredNodeId))
                    {
                        ok = false;
                    }

                    if (clicked)
                    {
                        Log._Debug("_guiPrioritySigns: hovered+clicked @ nodeId=" + HoveredNodeId + "/" + hoveredExistingNodeId + " ok=" + ok);

                        if (delete)
                        {
                            prioMan.RemovePrioritySegments(hoveredExistingNodeId);
                            RefreshCurrentPrioritySegmentIds();
                        }
                        else if (ok)
                        {
                            //if (!prioMan.IsPriorityNode(HoveredNodeId)) {
                            Log._Debug("_guiPrioritySigns: adding prio segments @ nodeId=" + HoveredNodeId);
                            tlsMan.RemoveNodeFromSimulation(HoveredNodeId, false, true);
                            tlm.RemoveTrafficLight(HoveredNodeId);
                            prioMan.AddPriorityNode(HoveredNodeId);
                            RefreshCurrentPrioritySegmentIds();
                            //}
                        }
                        else if (nodeSim != null && nodeSim.IsTimedLight())
                        {
                            MainTool.ShowTooltip(Translation.GetString("NODE_IS_TIMED_LIGHT"), Singleton <NetManager> .instance.m_nodes.m_buffer[HoveredNodeId].m_position);
                        }
                    }
                }
            } catch (Exception e) {
                Log.Error(e.ToString());
            }
        }
Ejemplo n.º 17
0
        public override void OnToolGUI(Event e)
        {
            var hoveredSegment = false;

            if (SelectedNodeId != 0)
            {
                CustomSegmentLightsManager    customTrafficLightsManager = CustomSegmentLightsManager.Instance;
                TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;

                var nodeSimulation = tlsMan.GetNodeSimulation(SelectedNodeId);
                if (nodeSimulation == null || !nodeSimulation.IsManualLight())
                {
                    return;
                }
                nodeSimulation.housekeeping();

                /*if (Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode].CountSegments() == 2) {
                 *      _guiManualTrafficLightsCrosswalk(ref Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode]);
                 *      return;
                 * }*/// TODO check

                NodeGeometry nodeGeometry = NodeGeometry.Get(SelectedNodeId);
                foreach (SegmentEndGeometry end in nodeGeometry.SegmentEndGeometries)
                {
                    if (end == null)
                    {
                        continue;
                    }

                    var position      = CalculateNodePositionForSegment(Singleton <NetManager> .instance.m_nodes.m_buffer[SelectedNodeId], ref Singleton <NetManager> .instance.m_segments.m_buffer[end.SegmentId]);
                    var segmentLights = customTrafficLightsManager.GetSegmentLights(end.SegmentId, end.StartNode, false);
                    if (segmentLights == null)
                    {
                        continue;
                    }

                    var screenPos = Camera.main.WorldToScreenPoint(position);
                    screenPos.y = Screen.height - screenPos.y;

                    if (screenPos.z < 0)
                    {
                        continue;
                    }

                    var diff = position - Camera.main.transform.position;
                    var zoom = 1.0f / diff.magnitude * 100f;

                    // original / 2.5
                    var lightWidth  = 41f * zoom;
                    var lightHeight = 97f * zoom;

                    var pedestrianWidth  = 36f * zoom;
                    var pedestrianHeight = 61f * zoom;

                    // SWITCH MODE BUTTON
                    var modeWidth  = 41f * zoom;
                    var modeHeight = 38f * zoom;

                    var guiColor = GUI.color;

                    if (segmentLights.PedestrianLightState != null)
                    {
                        // pedestrian light

                        // SWITCH MANUAL PEDESTRIAN LIGHT BUTTON
                        hoveredSegment = RenderManualPedestrianLightSwitch(zoom, end.SegmentId, screenPos, lightWidth, segmentLights, hoveredSegment);

                        // SWITCH PEDESTRIAN LIGHT
                        guiColor.a = _hoveredButton[0] == end.SegmentId && _hoveredButton[1] == 2 && segmentLights.ManualPedestrianMode ? 0.92f : 0.6f;
                        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, TrafficLightToolTextureResources.PedestrianGreenLightTexture2D);
                            break;

                        case RoadBaseAI.TrafficLightState.Red:
                        default:
                            GUI.DrawTexture(myRect3, TrafficLightToolTextureResources.PedestrianRedLightTexture2D);
                            break;
                        }

                        hoveredSegment = IsPedestrianLightHovered(myRect3, end.SegmentId, hoveredSegment, segmentLights);
                    }

                    int lightOffset = -1;
                    foreach (ExtVehicleType vehicleType in segmentLights.VehicleTypes)
                    {
                        ++lightOffset;
                        CustomSegmentLight segmentLight = segmentLights.GetCustomLight(vehicleType);

                        Vector3 offsetScreenPos = screenPos;
                        offsetScreenPos.y -= (lightHeight + 10f * zoom) * lightOffset;

                        SetAlpha(end.SegmentId, -1);

                        var myRect1 = new Rect(offsetScreenPos.x - modeWidth / 2, offsetScreenPos.y - modeHeight / 2 + modeHeight - 7f * zoom, modeWidth, modeHeight);

                        GUI.DrawTexture(myRect1, TrafficLightToolTextureResources.LightModeTexture2D);

                        hoveredSegment = GetHoveredSegment(myRect1, end.SegmentId, hoveredSegment, segmentLight);

                        // COUNTER
                        hoveredSegment = RenderCounter(end.SegmentId, offsetScreenPos, modeWidth, modeHeight, zoom, segmentLights, hoveredSegment);

                        if (lightOffset > 0)
                        {
                            // Info sign
                            var infoWidth  = 56.125f * zoom;
                            var 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 = 0.6f;
                                GUI.DrawTexture(infoRect, TrafficLightToolTextureResources.VehicleInfoSignTextures[TrafficManagerTool.InfoSignsToDisplay[k]]);
                                ++numInfos;
                            }
                        }

                        if (end.OutgoingOneWay)
                        {
                            continue;
                        }

                        var hasLeftSegment    = end.NumLeftSegments > 0;
                        var hasForwardSegment = end.NumStraightSegments > 0;
                        var hasRightSegment   = end.NumRightSegments > 0;

                        switch (segmentLight.CurrentMode)
                        {
                        case CustomSegmentLight.Mode.Simple:
                            hoveredSegment = SimpleManualSegmentLightMode(end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);
                            break;

                        case CustomSegmentLight.Mode.SingleLeft:
                            hoveredSegment = LeftForwardRManualSegmentLightMode(hasLeftSegment, end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment, hasForwardSegment, hasRightSegment);
                            break;

                        case CustomSegmentLight.Mode.SingleRight:
                            hoveredSegment = RightForwardLSegmentLightMode(end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, hasForwardSegment, hasLeftSegment, segmentLight, hasRightSegment, hoveredSegment);
                            break;

                        default:
                            // left arrow light
                            if (hasLeftSegment)
                            {
                                hoveredSegment = LeftArrowLightMode(end.SegmentId, lightWidth, hasRightSegment, hasForwardSegment, offsetScreenPos, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);
                            }

                            // forward arrow light
                            if (hasForwardSegment)
                            {
                                hoveredSegment = ForwardArrowLightMode(end.SegmentId, lightWidth, hasRightSegment, offsetScreenPos, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);
                            }

                            // right arrow light
                            if (hasRightSegment)
                            {
                                hoveredSegment = RightArrowLightMode(end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);
                            }
                            break;
                        }
                    }
                }
            }

            if (hoveredSegment)
            {
                return;
            }
            _hoveredButton[0] = 0;
            _hoveredButton[1] = 0;
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Calculates the current metrics for flowing and waiting vehicles
        /// </summary>
        /// <param name="wait"></param>
        /// <param name="flow"></param>
        /// <returns>true if the values could be calculated, false otherwise</returns>
        public bool calcWaitFlow(out float wait, out float flow)
        {
#if TRACE
            Singleton <CodeProfiler> .instance.Start("TimedTrafficLightsStep.calcWaitFlow");
#endif

#if DEBUGMETRIC
            bool debug = timedNode.NodeId == 3201;
#else
            bool debug = false;
#endif

#if DEBUGMETRIC
            if (debug)
            {
                Log.Warning($"TimedTrafficLightsStep.calcWaitFlow: ***START*** @ node {timedNode.NodeId}");
            }
#endif

            uint numFlows    = 0;
            uint numWaits    = 0;
            uint curMeanFlow = 0;
            uint curMeanWait = 0;

            TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance();
            TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance();

            // we are the master node. calculate traffic data
            foreach (ushort timedNodeId in timedNode.NodeGroup)
            {
                TrafficLightSimulation sim = tlsMan.GetNodeSimulation(timedNodeId);
                if (sim == null || !sim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights     slaveTimedNode = sim.TimedLight;
                TimedTrafficLightsStep slaveStep      = slaveTimedNode.Steps[timedNode.CurrentStep];

                //List<int> segmentIdsToDelete = new List<int>();

                // minimum time reached. check traffic!
                foreach (KeyValuePair <ushort, CustomSegmentLights> e in slaveStep.segmentLights)
                {
                    var fromSegmentId = e.Key;
                    var segLights     = e.Value;

                    // one of the traffic lights at this segment is green: count minimum traffic flowing through
                    SegmentEnd fromSeg = prioMan.GetPrioritySegment(timedNodeId, fromSegmentId);
                    if (fromSeg == null)
                    {
#if DEBUGMETRIC
                        if (debug)
                        {
                            Log.Warning($"TimedTrafficLightsStep.calcWaitFlow: No priority segment @ seg. {fromSegmentId} found!");
                        }
#endif
                        //Log.Warning("stepDone(): prioSeg is null");
                        //segmentIdsToDelete.Add(fromSegmentId);
                        continue;                         // skip invalid segment
                    }

                    //bool startPhase = getCurrentFrame() <= startFrame + minTime + 2; // during start phase all vehicles on "green" segments are counted as flowing
                    ExtVehicleType validVehicleTypes = VehicleRestrictionsManager.Instance().GetAllowedVehicleTypes(fromSegmentId, timedNode.NodeId);

                    foreach (KeyValuePair <byte, ExtVehicleType> e2 in segLights.VehicleTypeByLaneIndex)
                    {
                        byte           laneIndex   = e2.Key;
                        ExtVehicleType vehicleType = e2.Value;
                        if (vehicleType != ExtVehicleType.None && (validVehicleTypes & vehicleType) == ExtVehicleType.None)
                        {
                            continue;
                        }
                        CustomSegmentLight segLight = segLights.GetCustomLight(laneIndex);
                        if (segLight == null)
                        {
                            Log.Warning($"Timed traffic light step: Failed to get custom light for vehicleType {vehicleType} @ seg. {fromSegmentId}, node {timedNode.NodeId}!");
                            continue;
                        }

#if DEBUGMETRIC
                        if (debug)
                        {
                            Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Checking lane {laneIndex} @ seg. {fromSegmentId}. Vehicle types: {vehicleType}");
                        }
#endif

                        Dictionary <ushort, uint> carsFlowingToSegmentMetric = null;
                        Dictionary <ushort, uint> allCarsToSegmentMetric     = null;
                        try {
                            carsFlowingToSegmentMetric = fromSeg.GetVehicleMetricGoingToSegment(false, laneIndex, debug);
                        } catch (Exception ex) {
                            Log.Warning("calcWaitFlow (1): " + ex.ToString());
                        }

                        try {
                            allCarsToSegmentMetric = fromSeg.GetVehicleMetricGoingToSegment(true, laneIndex, debug);
                        } catch (Exception ex) {
                            Log.Warning("calcWaitFlow (2): " + ex.ToString());
                        }

                        if (carsFlowingToSegmentMetric == null)
                        {
                            continue;
                        }

                        // build directions from toSegment to fromSegment
                        Dictionary <ushort, ArrowDirection> directions = new Dictionary <ushort, ArrowDirection>();
                        foreach (KeyValuePair <ushort, uint> f in allCarsToSegmentMetric)
                        {
                            var             toSegmentId = f.Key;
                            SegmentGeometry geometry    = SegmentGeometry.Get(fromSegmentId);
                            ArrowDirection  dir         = geometry.GetDirection(toSegmentId, timedNodeId == geometry.StartNodeId());
                            directions[toSegmentId] = dir;
#if DEBUGMETRIC
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Calculated direction for seg. {fromSegmentId} -> seg. {toSegmentId}: {dir}");
                            }
#endif
                        }

                        // calculate waiting/flowing traffic
                        foreach (KeyValuePair <ushort, uint> f in allCarsToSegmentMetric)
                        {
                            ushort toSegmentId               = f.Key;
                            uint   totalNormCarLength        = f.Value;
                            uint   totalFlowingNormCarLength = carsFlowingToSegmentMetric[f.Key];

#if DEBUGMETRIC
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Total norm. car length of vehicles on lane {laneIndex} going to seg. {toSegmentId}: {totalNormCarLength}");
                            }
#endif

                            bool addToFlow = false;
                            switch (directions[toSegmentId])
                            {
                            case ArrowDirection.Turn:
                                addToFlow = TrafficPriorityManager.IsLeftHandDrive() ? segLight.isRightGreen() : segLight.isLeftGreen();
                                break;

                            case ArrowDirection.Left:
                                addToFlow = segLight.isLeftGreen();
                                break;

                            case ArrowDirection.Right:
                                addToFlow = segLight.isRightGreen();
                                break;

                            case ArrowDirection.Forward:
                            default:
                                addToFlow = segLight.isForwardGreen();
                                break;
                            }

                            if (addToFlow)
                            {
                                ++numFlows;
                                curMeanFlow += totalFlowingNormCarLength;
                            }
                            else
                            {
                                ++numWaits;
                                curMeanWait += totalNormCarLength;
                            }

#if DEBUGMETRIC
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Vehicles on lane {laneIndex} on seg. {fromSegmentId} going to seg. {toSegmentId} flowing? {addToFlow} curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}");
                            }
#endif
                        }
                    }
                }

                // delete invalid segments from step

                /*foreach (int segmentId in segmentIdsToDelete) {
                 *      slaveStep.segmentLightStates.Remove(segmentId);
                 * }*/
            }

#if DEBUGMETRIC
            if (debug)
            {
                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: ### Calculation completed. numFlows={numFlows}, numWaits={numWaits}, curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}");
            }

            wait = curMeanWait;
            flow = curMeanFlow;
#else
            if (numFlows > 0)
            {
                curMeanFlow /= numFlows;
            }
            if (numWaits > 0)
            {
                curMeanWait /= numWaits;
            }

            float fCurMeanFlow = curMeanFlow;
            fCurMeanFlow /= waitFlowBalance;             // a value smaller than 1 rewards steady traffic currents

            wait = (float)curMeanWait;
            flow = fCurMeanFlow;
#endif
#if TRACE
            Singleton <CodeProfiler> .instance.Stop("TimedTrafficLightsStep.calcWaitFlow");
#endif
            return(true);
        }
        public void SimulationStep()
        {
            // TODO [version 1.9] this method is currently called on each node, but should be called on the master node only

            if (!IsMasterNode() || !IsStarted())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} isMasterNode={isMasterNode()} IsStarted={IsStarted()}");
#endif
                return;
            }
            // we are the master node

            /*if (!housekeeping()) {
             #if DEBUGTTL
             *      Log.Warning($"TTL SimStep: *STOP* NodeId={NodeId} Housekeeping detected that this timed traffic light has become invalid: {NodeId}.");
             #endif
             *      Stop();
             *      return;
             * }*/

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (1)");
#endif
            SetLights();

            if (!Steps[CurrentStep].StepDone(true))
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}) is not done.");
#endif
                return;
            }
            // step is done

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (2)");
#endif

#if DEBUG
            bool debug = GlobalConfig.Instance.DebugSwitches[7] && GlobalConfig.Instance.TTLDebugNodeId == NodeId;
#endif

            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;
            if (Steps[CurrentStep].NextStepRefIndex < 0)
            {
#if DEBUG
                if (debug)
                {
                    Log._Debug($"Step {CurrentStep} is done at timed light {NodeId}. Determining next step.");
                }
#endif
                // next step has not yet identified yet. check for minTime=0 steps
                int nextStepIndex = (CurrentStep + 1) % NumSteps();
                if (Steps[nextStepIndex].minTime == 0)
                {
                    // next step has minTime=0. calculate flow/wait ratios and compare.
                    int prevStepIndex = CurrentStep;

                    float maxWaitFlowDiff = Steps[CurrentStep].minFlow - Steps[CurrentStep].maxWait;
                    if (float.IsNaN(maxWaitFlowDiff))
                    {
                        maxWaitFlowDiff = float.MinValue;
                    }
                    int bestNextStepIndex = prevStepIndex;

#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"Next step {nextStepIndex} has minTime = 0 at timed light {NodeId}. Old step {CurrentStep} has waitFlowDiff={maxWaitFlowDiff} (flow={Steps[CurrentStep].minFlow}, wait={Steps[CurrentStep].maxWait}).");
                    }
#endif

                    while (nextStepIndex != prevStepIndex)
                    {
                        float wait;
                        float flow;
                        Steps[nextStepIndex].calcWaitFlow(false, nextStepIndex, out wait, out flow);

                        float flowWaitDiff = flow - wait;
                        if (flowWaitDiff > maxWaitFlowDiff)
                        {
                            maxWaitFlowDiff   = flowWaitDiff;
                            bestNextStepIndex = nextStepIndex;
                        }

#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Checking upcoming step {nextStepIndex} @ node {NodeId}: flow={flow} wait={wait} minTime={Steps[nextStepIndex].minTime}. bestWaitFlowDiff={bestNextStepIndex}, bestNextStepIndex={bestNextStepIndex}");
                        }
#endif

                        if (Steps[nextStepIndex].minTime != 0)
                        {
                            break;
                        }

                        nextStepIndex = (nextStepIndex + 1) % NumSteps();
                    }

                    if (bestNextStepIndex == CurrentStep)
                    {
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) equals CurrentStep @ node {NodeId}.");
                        }
#endif

                        // restart the current step
                        foreach (ushort slaveNodeId in NodeGroup)
                        {
                            TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                            if (slaveSim == null || !slaveSim.IsTimedLight())
                            {
                                continue;
                            }

                            slaveSim.TimedLight.Steps[CurrentStep].Start(CurrentStep);
                            slaveSim.TimedLight.Steps[CurrentStep].UpdateLiveLights();
                        }
                        return;
                    }
                    else
                    {
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) does not equal CurrentStep @ node {NodeId}.");
                        }
#endif

                        // set next step reference index for assuring a correct end transition
                        foreach (ushort slaveNodeId in NodeGroup)
                        {
                            TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                            if (slaveSim == null || !slaveSim.IsTimedLight())
                            {
                                continue;
                            }
                            TimedTrafficLights timedLights = slaveSim.TimedLight;
                            timedLights.Steps[CurrentStep].NextStepRefIndex = bestNextStepIndex;
                        }
                    }
                }
                else
                {
                    Steps[CurrentStep].NextStepRefIndex = nextStepIndex;
                }
            }

            SetLights();             // check if this is needed

            if (!Steps[CurrentStep].IsEndTransitionDone())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}): end transition is not done.");
#endif
                return;
            }
            // ending transition (yellow) finished

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} ending transition done. NodeGroup={string.Join(", ", NodeGroup.Select(x => x.ToString()).ToArray())}, nodeId={NodeId}, NumSteps={NumSteps()}");
#endif

            // change step
            int newStepIndex = Steps[CurrentStep].NextStepRefIndex;
            int oldStepIndex = CurrentStep;

            foreach (ushort slaveNodeId in NodeGroup)
            {
                TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                if (slaveSim == null || !slaveSim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights timedLights = slaveSim.TimedLight;
                timedLights.CurrentStep = newStepIndex;

#if DEBUGTTL
                Log._Debug($"TTL SimStep: NodeId={slaveNodeId} setting lgihts of next step: {CurrentStep}");
#endif

                timedLights.Steps[oldStepIndex].NextStepRefIndex = -1;
                timedLights.Steps[newStepIndex].Start(oldStepIndex);
                timedLights.Steps[newStepIndex].UpdateLiveLights();
            }
        }
Ejemplo n.º 20
0
        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;
        }
        private static void LoadDataState(out bool error)
        {
            error = false;

            Log.Info("Loading State from Config");
            if (_configuration == null)
            {
                Log.Warning("Configuration NULL, Couldn't load save data. Possibly a new game?");
                return;
            }

            TrafficPriorityManager prioMan = TrafficPriorityManager.Instance();

            // load priority segments
            if (_configuration.PrioritySegments != null)
            {
                Log.Info($"Loading {_configuration.PrioritySegments.Count()} priority segments");
                foreach (var segment in _configuration.PrioritySegments)
                {
                    try {
                        if (segment.Length < 3)
                        {
                            continue;
                        }
#if DEBUG
                        bool debug = segment[0] == 13630;
#endif

                        if ((SegmentEnd.PriorityType)segment[2] == SegmentEnd.PriorityType.None)
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: Not adding 'None' priority segment: {segment[1]} @ node {segment[0]}");
                            }
#endif
                            continue;
                        }

                        if (!NetUtil.IsNodeValid((ushort)segment[0]))
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: node {segment[0]} is invalid");
                            }
#endif
                            continue;
                        }
                        if (!NetUtil.IsSegmentValid((ushort)segment[1]))
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: segment {segment[1]} @ node {segment[0]} is invalid");
                            }
#endif
                            continue;
                        }
                        if (prioMan.IsPrioritySegment((ushort)segment[0], (ushort)segment[1]))
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: segment {segment[1]} @ node {segment[0]} is already a priority segment");
                            }
#endif
                            prioMan.GetPrioritySegment((ushort)segment[0], (ushort)segment[1]).Type = (SegmentEnd.PriorityType)segment[2];
                            continue;
                        }
#if DEBUG
                        Log._Debug($"Adding Priority Segment of type: {segment[2].ToString()} to segment {segment[1]} @ node {segment[0]}");
#endif
                        prioMan.AddPrioritySegment((ushort)segment[0], (ushort)segment[1], (SegmentEnd.PriorityType)segment[2]);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading data from Priority segments: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Priority segments data structure undefined!");
            }

            // load vehicle restrictions (warning: has to be done before loading timed lights!)
            if (_configuration.LaneAllowedVehicleTypes != null)
            {
                Log.Info($"Loading lane vehicle restriction data. {_configuration.LaneAllowedVehicleTypes.Count} elements");
                foreach (Configuration.LaneVehicleTypes laneVehicleTypes in _configuration.LaneAllowedVehicleTypes)
                {
                    try {
                        ExtVehicleType baseMask   = VehicleRestrictionsManager.Instance().GetBaseMask(laneVehicleTypes.laneId);
                        ExtVehicleType maskedType = laneVehicleTypes.vehicleTypes & baseMask;
                        Log._Debug($"Loading lane vehicle restriction: lane {laneVehicleTypes.laneId} = {laneVehicleTypes.vehicleTypes}, masked = {maskedType}");
                        if (maskedType != baseMask)
                        {
                            Flags.setLaneAllowedVehicleTypes(laneVehicleTypes.laneId, maskedType);
                        }
                        else
                        {
                            Log._Debug($"Masked type does not differ from base type. Ignoring.");
                        }
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading data from vehicle restrictions: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Vehicle restrctions structure undefined!");
            }

            NetManager netManager = Singleton <NetManager> .instance;
            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            if (_configuration.TimedLights != null)
            {
                Log.Info($"Loading {_configuration.TimedLights.Count()} timed traffic lights (new method)");

                foreach (Configuration.TimedTrafficLights cnfTimedLights in _configuration.TimedLights)
                {
                    try {
                        if (!NetUtil.IsNodeValid(cnfTimedLights.nodeId))
                        {
                            continue;
                        }
                        Flags.setNodeTrafficLight(cnfTimedLights.nodeId, true);

                        Log._Debug($"Adding Timed Node at node {cnfTimedLights.nodeId}");

                        TrafficLightSimulation sim = tlsMan.AddNodeToSimulation(cnfTimedLights.nodeId);
                        sim.SetupTimedTrafficLight(cnfTimedLights.nodeGroup);
                        var timedNode = sim.TimedLight;

                        int j = 0;
                        foreach (Configuration.TimedTrafficLightsStep cnfTimedStep in cnfTimedLights.timedSteps)
                        {
                            Log._Debug($"Loading timed step {j} at node {cnfTimedLights.nodeId}");
                            TimedTrafficLightsStep step = timedNode.AddStep(cnfTimedStep.minTime, cnfTimedStep.maxTime, cnfTimedStep.waitFlowBalance);

                            foreach (KeyValuePair <ushort, Configuration.CustomSegmentLights> e in cnfTimedStep.segmentLights)
                            {
                                Log._Debug($"Loading timed step {j}, segment {e.Key} at node {cnfTimedLights.nodeId}");
                                CustomSegmentLights lights = null;
                                if (!step.segmentLights.TryGetValue(e.Key, out lights))
                                {
                                    Log._Debug($"No segment lights found at timed step {j} for segment {e.Key}, node {cnfTimedLights.nodeId}");
                                    continue;
                                }
                                Configuration.CustomSegmentLights cnfLights = e.Value;

                                Log._Debug($"Loading pedestrian light @ seg. {e.Key}, step {j}: {cnfLights.pedestrianLightState} {cnfLights.manualPedestrianMode}");

                                lights.ManualPedestrianMode = cnfLights.manualPedestrianMode;
                                lights.PedestrianLightState = cnfLights.pedestrianLightState;

                                foreach (KeyValuePair <ExtVehicleType, Configuration.CustomSegmentLight> e2 in cnfLights.customLights)
                                {
                                    Log._Debug($"Loading timed step {j}, segment {e.Key}, vehicleType {e2.Key} at node {cnfTimedLights.nodeId}");
                                    CustomSegmentLight light = null;
                                    if (!lights.CustomLights.TryGetValue(e2.Key, out light))
                                    {
                                        Log._Debug($"No segment light found for timed step {j}, segment {e.Key}, vehicleType {e2.Key} at node {cnfTimedLights.nodeId}");
                                        continue;
                                    }
                                    Configuration.CustomSegmentLight cnfLight = e2.Value;

                                    light.CurrentMode = (CustomSegmentLight.Mode)cnfLight.currentMode;
                                    light.LightLeft   = cnfLight.leftLight;
                                    light.LightMain   = cnfLight.mainLight;
                                    light.LightRight  = cnfLight.rightLight;
                                }
                            }
                            ++j;
                        }

                        if (cnfTimedLights.started)
                        {
                            timedNode.Start();
                        }
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading data from TimedNode (new method): " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Timed traffic lights data structure undefined!");
            }

            if (_configuration.NodeTrafficLights != null)
            {
                var trafficLightDefs = _configuration.NodeTrafficLights.Split(',');

                Log.Info($"Loading junction traffic light data");

                // new method
                foreach (var split in trafficLightDefs.Select(def => def.Split(':')).Where(split => split.Length > 1))
                {
                    try {
                        Log._Debug($"Traffic light split data: {split[0]} , {split[1]}");
                        var  nodeId = Convert.ToUInt16(split[0]);
                        uint flag   = Convert.ToUInt16(split[1]);

                        Flags.setNodeTrafficLight(nodeId, flag > 0);
                    } catch (Exception e) {
                        // ignore as it's probably bad save data.
                        Log.Error($"Error setting the NodeTrafficLights: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Junction traffic lights data structure undefined!");
            }

            if (_configuration.LaneFlags != null)
            {
                Log.Info($"Loading lane arrow data");
#if DEBUG
                Log._Debug($"LaneFlags: {_configuration.LaneFlags}");
#endif
                var lanes = _configuration.LaneFlags.Split(',');

                if (lanes.Length > 1)
                {
                    foreach (var split in lanes.Select(lane => lane.Split(':')).Where(split => split.Length > 1))
                    {
                        try {
                            Log._Debug($"Split Data: {split[0]} , {split[1]}");
                            var  laneId = Convert.ToUInt32(split[0]);
                            uint flags  = Convert.ToUInt32(split[1]);

                            //make sure we don't cause any overflows because of bad save data.
                            if (Singleton <NetManager> .instance.m_lanes.m_buffer.Length <= laneId)
                            {
                                continue;
                            }

                            if (flags > ushort.MaxValue)
                            {
                                continue;
                            }

                            if (!NetUtil.IsLaneValid(laneId))
                            {
                                continue;
                            }

                            //Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags = fixLaneFlags(Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags);

                            uint laneArrowFlags = flags & Flags.lfr;
                            uint origFlags      = (Singleton <NetManager> .instance.m_lanes.m_buffer[laneId].m_flags & Flags.lfr);
#if DEBUG
                            Log._Debug("Setting flags for lane " + laneId + " to " + flags + " (" + ((Flags.LaneArrows)(laneArrowFlags)).ToString() + ")");
                            if ((origFlags | laneArrowFlags) == origFlags)                               // only load if setting differs from default
                            {
                                Log._Debug("Flags for lane " + laneId + " are original (" + ((NetLane.Flags)(origFlags)).ToString() + ")");
                            }
#endif
                            Flags.setLaneArrowFlags(laneId, (Flags.LaneArrows)(laneArrowFlags));
                        } catch (Exception e) {
                            Log.Error($"Error loading Lane Split data. Length: {split.Length} value: {split}\nError: {e.ToString()}");
                            error = true;
                        }
                    }
                }
            }
            else
            {
                Log.Warning("Lane arrow data structure undefined!");
            }

            // load lane connections
            if (_configuration.LaneConnections != null)
            {
                Log.Info($"Loading {_configuration.LaneConnections.Count()} lane connections");
                foreach (Configuration.LaneConnection conn in _configuration.LaneConnections)
                {
                    try {
                        Log._Debug($"Loading lane connection: lane {conn.lowerLaneId} -> {conn.higherLaneId}");
                        LaneConnectionManager.Instance().AddLaneConnection(conn.lowerLaneId, conn.higherLaneId, conn.lowerStartNode);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Error("Error loading data from lane connection: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Lane connection data structure undefined!");
            }

            // load speed limits
            if (_configuration.LaneSpeedLimits != null)
            {
                Log.Info($"Loading lane speed limit data. {_configuration.LaneSpeedLimits.Count} elements");
                foreach (Configuration.LaneSpeedLimit laneSpeedLimit in _configuration.LaneSpeedLimits)
                {
                    try {
                        Log._Debug($"Loading lane speed limit: lane {laneSpeedLimit.laneId} = {laneSpeedLimit.speedLimit}");
                        Flags.setLaneSpeedLimit(laneSpeedLimit.laneId, laneSpeedLimit.speedLimit);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading speed limits: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Lane speed limit structure undefined!");
            }

            // Load segment-at-node flags
            if (_configuration.SegmentNodeConfs != null)
            {
                Log.Info($"Loading segment-at-node data. {_configuration.SegmentNodeConfs.Count} elements");
                foreach (Configuration.SegmentNodeConf segNodeConf in _configuration.SegmentNodeConfs)
                {
                    try {
                        if (!NetUtil.IsSegmentValid(segNodeConf.segmentId))
                        {
                            continue;
                        }
                        Flags.setSegmentNodeFlags(segNodeConf.segmentId, true, segNodeConf.startNodeFlags);
                        Flags.setSegmentNodeFlags(segNodeConf.segmentId, false, segNodeConf.endNodeFlags);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading segment-at-node config: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Segment-at-node structure undefined!");
            }
        }
        public override void OnSaveData()
        {
            bool error = false;

            /*try {
             *      Log.Info("Recalculating segment geometries");
             *      SegmentGeometry.OnBeforeSaveData();
             * } catch (Exception e) {
             *      Log.Error($"OnSaveData: Exception occurred while calling SegmentGeometry.OnBeforeSaveData: {e.ToString()}");
             *      error = true;
             * }*/

            try {
                Log.Info("Applying all flags");
                Flags.applyAllFlags();
            } catch (Exception e) {
                Log.Error($"OnSaveData: Exception occurred while applying all flags: {e.ToString()}");
                error = true;
            }

            try {
                Log.Info("Saving Mod Data.");
                var configuration = new Configuration();

                TrafficPriorityManager prioMan = TrafficPriorityManager.Instance();

                if (prioMan.TrafficSegments != null)
                {
                    for (ushort i = 0; i < Singleton <NetManager> .instance.m_segments.m_size; i++)
                    {
                        try {
                            SavePrioritySegment(i, configuration);
                        } catch (Exception e) {
                            Log.Error($"Exception occurred while saving priority segment @ {i}: {e.ToString()}");
                            error = true;
                        }

                        try {
                            SaveSegmentNodeFlags(i, configuration);
                        } catch (Exception e) {
                            Log.Error($"Exception occurred while saving segment node flags @ {i}: {e.ToString()}");
                            error = true;
                        }
                    }
                }

                TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

                for (ushort i = 0; i < Singleton <NetManager> .instance.m_nodes.m_size; i++)
                {
                    /*if (TrafficLightSimulation.LightSimulationByNodeId != null) {
                     *      SaveTrafficLightSimulation(i, configuration);
                     * }*/

                    /*if (TrafficLightsManual.ManualSegments != null) {
                     *      SaveManualTrafficLight(i, configuration);
                     * }*/

                    TrafficLightSimulation sim = tlsMan.GetNodeSimulation(i);
                    if (sim != null && sim.IsTimedLight())
                    {
                        try {
                            SaveTimedTrafficLight(i, configuration);
                        } catch (Exception e) {
                            Log.Error($"Exception occurred while saving timed traffic light @ {i}: {e.ToString()}");
                            error = true;
                        }
                        // TODO save new traffic lights
                    }

                    try {
                        SaveNodeLights(i, configuration);
                    } catch (Exception e) {
                        Log.Error($"Exception occurred while saving node traffic light @ {i}: {e.ToString()}");
                        error = true;
                    }
                }

#if !TAM
                if (LoadingExtension.IsPathManagerCompatible)
                {
#endif
                for (uint i = 0; i < Singleton <NetManager> .instance.m_lanes.m_buffer.Length; i++)
                {
                    try {
                        SaveLaneData(i, configuration);
                    } catch (Exception e) {
                        Log.Error($"Exception occurred while saving lane data @ {i}: {e.ToString()}");
                        error = true;
                    }
                }
#if !TAM
            }
#endif

                foreach (KeyValuePair <uint, ushort> e in Flags.getAllLaneSpeedLimits())
                {
                    try {
                        SaveLaneSpeedLimit(new Configuration.LaneSpeedLimit(e.Key, e.Value), configuration);
                    } catch (Exception ex) {
                        Log.Error($"Exception occurred while saving lane speed limit @ {e.Key}: {ex.ToString()}");
                        error = true;
                    }
                }

                foreach (KeyValuePair <uint, ExtVehicleType> e in Flags.getAllLaneAllowedVehicleTypes())
                {
                    try {
                        SaveLaneAllowedVehicleTypes(new Configuration.LaneVehicleTypes(e.Key, e.Value), configuration);
                    } catch (Exception ex) {
                        Log.Error($"Exception occurred while saving lane vehicle restrictions @ {e.Key}: {ex.ToString()}");
                        error = true;
                    }
                }

                var binaryFormatter = new BinaryFormatter();
                var memoryStream    = new MemoryStream();

                try {
                    binaryFormatter.Serialize(memoryStream, configuration);
                    memoryStream.Position = 0;
                    Log.Info($"Save data byte length {memoryStream.Length}");
                    _serializableData.SaveData(DataId, memoryStream.ToArray());
                } catch (Exception ex) {
                    Log.Error("Unexpected error while saving data: " + ex.ToString());
                    error = true;
                } finally {
                    memoryStream.Close();
                }

                try {
                    // save options
                    _serializableData.SaveData("TMPE_Options", new byte[] {
                        (byte)Options.simAccuracy,
                        (byte)0,                        //Options.laneChangingRandomization,
                        (byte)Options.recklessDrivers,
                        (byte)(Options.relaxedBusses ? 1 : 0),
                        (byte)(Options.nodesOverlay ? 1 : 0),
                        (byte)(Options.allowEnterBlockedJunctions ? 1 : 0),
                        (byte)(Options.advancedAI ? 1 : 0),
                        (byte)(Options.highwayRules ? 1 : 0),
                        (byte)(Options.prioritySignsOverlay ? 1 : 0),
                        (byte)(Options.timedLightsOverlay ? 1 : 0),
                        (byte)(Options.speedLimitsOverlay ? 1 : 0),
                        (byte)(Options.vehicleRestrictionsOverlay ? 1 : 0),
                        (byte)(Options.strongerRoadConditionEffects ? 1 : 0),
                        (byte)(Options.allowUTurns ? 1 : 0),
                        (byte)(Options.allowLaneChangesWhileGoingStraight ? 1 : 0),
                        (byte)(Options.enableDespawning ? 1 : 0),
                        (byte)(Options.IsDynamicPathRecalculationActive() ? 1 : 0),
                        (byte)(Options.connectedLanesOverlay ? 1 : 0),
                        (byte)(Options.prioritySignsEnabled ? 1 : 0),
                        (byte)(Options.timedLightsEnabled ? 1 : 0),
                        (byte)(Options.customSpeedLimitsEnabled ? 1 : 0),
                        (byte)(Options.vehicleRestrictionsEnabled ? 1 : 0),
                        (byte)(Options.laneConnectorEnabled ? 1 : 0),
                        (byte)(Options.junctionRestrictionsOverlay ? 1 : 0),
                        (byte)(Options.junctionRestrictionsEnabled ? 1 : 0)
                    });
                } catch (Exception ex) {
                    Log.Error("Unexpected error while saving options: " + ex.Message);
                    error = true;
                }
            } catch (Exception e) {
                error = true;
                Log.Error($"Error occurred while saving data: {e.ToString()}");
                //UIView.library.ShowModal<ExceptionPanel>("ExceptionPanel").SetMessage("An error occurred while saving", "Traffic Manager: President Edition detected an error while saving. To help preventing future errors, please navigate to http://steamcommunity.com/sharedfiles/filedetails/?id=583429740 and follow the steps under 'In case problems arise'.", true);
            }
        }
        private static void SaveTimedTrafficLight(ushort i, Configuration configuration)
        {
            try {
                TrafficLightSimulation sim = TrafficLightSimulationManager.Instance().GetNodeSimulation(i);
                if (sim == null || !sim.IsTimedLight())
                {
                    return;
                }

                Log._Debug($"Going to save timed light at node {i}.");

                var timedNode = sim.TimedLight;
                timedNode.handleNewSegments();

                Configuration.TimedTrafficLights cnfTimedLights = new Configuration.TimedTrafficLights();
                configuration.TimedLights.Add(cnfTimedLights);

                cnfTimedLights.nodeId     = timedNode.NodeId;
                cnfTimedLights.nodeGroup  = timedNode.NodeGroup;
                cnfTimedLights.started    = timedNode.IsStarted();
                cnfTimedLights.timedSteps = new List <Configuration.TimedTrafficLightsStep>();

                for (var j = 0; j < timedNode.NumSteps(); j++)
                {
                    Log._Debug($"Saving timed light step {j} at node {i}.");
                    TimedTrafficLightsStep timedStep = timedNode.Steps[j];
                    Configuration.TimedTrafficLightsStep cnfTimedStep = new Configuration.TimedTrafficLightsStep();
                    cnfTimedLights.timedSteps.Add(cnfTimedStep);

                    cnfTimedStep.minTime         = timedStep.minTime;
                    cnfTimedStep.maxTime         = timedStep.maxTime;
                    cnfTimedStep.waitFlowBalance = timedStep.waitFlowBalance;
                    cnfTimedStep.segmentLights   = new Dictionary <ushort, Configuration.CustomSegmentLights>();
                    foreach (KeyValuePair <ushort, CustomSegmentLights> e in timedStep.segmentLights)
                    {
                        Log._Debug($"Saving timed light step {j}, segment {e.Key} at node {i}.");

                        CustomSegmentLights segLights = e.Value;
                        Configuration.CustomSegmentLights cnfSegLights = new Configuration.CustomSegmentLights();
                        cnfTimedStep.segmentLights.Add(e.Key, cnfSegLights);

                        cnfSegLights.nodeId               = segLights.NodeId;
                        cnfSegLights.segmentId            = segLights.SegmentId;
                        cnfSegLights.customLights         = new Dictionary <ExtVehicleType, Configuration.CustomSegmentLight>();
                        cnfSegLights.pedestrianLightState = segLights.PedestrianLightState;
                        cnfSegLights.manualPedestrianMode = segLights.ManualPedestrianMode;

                        Log._Debug($"Saving pedestrian light @ seg. {e.Key}, step {j}: {cnfSegLights.pedestrianLightState} {cnfSegLights.manualPedestrianMode}");

                        foreach (KeyValuePair <ExtVehicleType, CustomSegmentLight> e2 in segLights.CustomLights)
                        {
                            Log._Debug($"Saving timed light step {j}, segment {e.Key}, vehicleType {e2.Key} at node {i}.");

                            CustomSegmentLight segLight = e2.Value;
                            Configuration.CustomSegmentLight cnfSegLight = new Configuration.CustomSegmentLight();
                            cnfSegLights.customLights.Add(e2.Key, cnfSegLight);

                            cnfSegLight.nodeId      = segLight.NodeId;
                            cnfSegLight.segmentId   = segLight.SegmentId;
                            cnfSegLight.currentMode = (int)segLight.CurrentMode;
                            cnfSegLight.leftLight   = segLight.LightLeft;
                            cnfSegLight.mainLight   = segLight.LightMain;
                            cnfSegLight.rightLight  = segLight.LightRight;
                        }
                    }
                }
            } catch (Exception e) {
                Log.Error($"Error adding TimedTrafficLights to save {e.Message}");
            }
        }
        internal void Join(TimedTrafficLights otherTimedLight)
        {
            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            if (NumSteps() < otherTimedLight.NumSteps())
            {
                // increase the number of steps at our timed lights
                for (int i = NumSteps(); i < otherTimedLight.NumSteps(); ++i)
                {
                    TimedTrafficLightsStep otherStep = otherTimedLight.GetStep(i);
                    foreach (ushort slaveNodeId in NodeGroup)
                    {
                        TrafficLightSimulation ourSim = tlsMan.GetNodeSimulation(slaveNodeId);
                        if (ourSim == null || !ourSim.IsTimedLight())
                        {
                            continue;
                        }
                        TimedTrafficLights ourTimedLight = ourSim.TimedLight;
                        ourTimedLight.AddStep(otherStep.minTime, otherStep.maxTime, otherStep.waitFlowBalance, true);
                    }
                }
            }
            else
            {
                // increase the number of steps at their timed lights
                for (int i = otherTimedLight.NumSteps(); i < NumSteps(); ++i)
                {
                    TimedTrafficLightsStep ourStep = GetStep(i);
                    foreach (ushort slaveNodeId in otherTimedLight.NodeGroup)
                    {
                        TrafficLightSimulation theirSim = tlsMan.GetNodeSimulation(slaveNodeId);
                        if (theirSim == null || !theirSim.IsTimedLight())
                        {
                            continue;
                        }
                        TimedTrafficLights theirTimedLight = theirSim.TimedLight;
                        theirTimedLight.AddStep(ourStep.minTime, ourStep.maxTime, ourStep.waitFlowBalance, true);
                    }
                }
            }

            // join groups and re-defined master node, determine mean min/max times & mean wait-flow-balances
            HashSet <ushort> newNodeGroupSet = new HashSet <ushort>();

            newNodeGroupSet.UnionWith(NodeGroup);
            newNodeGroupSet.UnionWith(otherTimedLight.NodeGroup);
            List <ushort> newNodeGroup    = new List <ushort>(newNodeGroupSet);
            ushort        newMasterNodeId = newNodeGroup[0];

            int[]   minTimes         = new int[NumSteps()];
            int[]   maxTimes         = new int[NumSteps()];
            float[] waitFlowBalances = new float[NumSteps()];

            foreach (ushort timedNodeId in newNodeGroup)
            {
                TrafficLightSimulation timedSim = tlsMan.GetNodeSimulation(timedNodeId);
                if (timedSim == null || !timedSim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights timedLight = timedSim.TimedLight;
                for (int i = 0; i < NumSteps(); ++i)
                {
                    minTimes[i]         += timedLight.GetStep(i).minTime;
                    maxTimes[i]         += timedLight.GetStep(i).maxTime;
                    waitFlowBalances[i] += timedLight.GetStep(i).waitFlowBalance;
                }

                timedLight.NodeGroup    = newNodeGroup;
                timedLight.masterNodeId = newMasterNodeId;
            }

            // build means
            if (NumSteps() > 0)
            {
                for (int i = 0; i < NumSteps(); ++i)
                {
                    minTimes[i]         = Math.Max(1, minTimes[i] / newNodeGroup.Count);
                    maxTimes[i]         = Math.Max(1, maxTimes[i] / newNodeGroup.Count);
                    waitFlowBalances[i] = Math.Max(0.001f, waitFlowBalances[i] / (float)newNodeGroup.Count);
                }
            }

            // apply means & reset
            foreach (ushort timedNodeId in newNodeGroup)
            {
                TrafficLightSimulation timedSim = tlsMan.GetNodeSimulation(timedNodeId);
                if (timedSim == null || !timedSim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights timedLight = timedSim.TimedLight;
                timedLight.Stop();
                timedLight.testMode           = false;
                timedLight.lastSimulationStep = 0;
                for (int i = 0; i < NumSteps(); ++i)
                {
                    timedLight.GetStep(i).minTime         = minTimes[i];
                    timedLight.GetStep(i).maxTime         = maxTimes[i];
                    timedLight.GetStep(i).waitFlowBalance = waitFlowBalances[i];
                }
            }
        }
Ejemplo n.º 25
0
        /// <summary>
        /// Calculates the current metrics for flowing and waiting vehicles
        /// </summary>
        /// <param name="wait"></param>
        /// <param name="flow"></param>
        /// <returns>true if the values could be calculated, false otherwise</returns>
        public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out float flow)
        {
            uint numFlows    = 0;
            uint numWaits    = 0;
            uint curMeanFlow = 0;
            uint curMeanWait = 0;

            // TODO checking agains getCurrentFrame() is only valid if this is the current step
            if (onlyMoving && getCurrentFrame() <= startFrame + minTime + 1)               // during start phase all vehicles on "green" segments are counted as flowing
            {
                onlyMoving = false;
            }

            TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance;
            TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance;

            foreach (ushort timedNodeId in timedNode.NodeGroup)
            {
                TrafficLightSimulation sim = tlsMan.GetNodeSimulation(timedNodeId);
                if (sim == null || !sim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights     slaveTimedNode = sim.TimedLight;
                TimedTrafficLightsStep slaveStep      = slaveTimedNode.Steps[stepRefIndex];

                //List<int> segmentIdsToDelete = new List<int>();

                // minimum time reached. check traffic!
                foreach (KeyValuePair <ushort, CustomSegmentLights> e in slaveStep.segmentLights)
                {
                    var sourceSegmentId = e.Key;
                    var segLights       = e.Value;

#if DEBUGMETRIC
                    bool debug = sourceSegmentId == 20857 && GlobalConfig.Instance.DebugSwitches[1];
#elif DEBUG
                    bool debug = GlobalConfig.Instance.DebugSwitches[7] && GlobalConfig.Instance.TTLDebugNodeId == timedNodeId;
#else
                    bool debug = false;
#endif

                    Dictionary <ushort, ArrowDirection> directions = null;
                    if (slaveStep.timedNode.Directions.ContainsKey(sourceSegmentId))
                    {
                        directions = slaveStep.timedNode.Directions[sourceSegmentId];
                    }
                    else
                    {
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"calcWaitFlow: No arrow directions defined for segment {sourceSegmentId} @ {timedNodeId}");
                        }
#endif
                        continue;
                    }

                    // one of the traffic lights at this segment is green: count minimum traffic flowing through
                    SegmentEnd sourceSegmentEnd = prioMan.GetPrioritySegment(timedNodeId, sourceSegmentId);
                    if (sourceSegmentEnd == null)
                    {
                        Log.Warning($"TimedTrafficLightsStep.calcWaitFlow: No priority segment @ seg. {sourceSegmentId} found!");
                        continue;                         // skip invalid segment
                    }

                    ExtVehicleType validVehicleTypes = VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(sourceSegmentId, timedNode.NodeId);

                    foreach (KeyValuePair <byte, ExtVehicleType> e2 in segLights.VehicleTypeByLaneIndex)
                    {
                        byte           laneIndex   = e2.Key;
                        ExtVehicleType vehicleType = e2.Value;
                        if (vehicleType != ExtVehicleType.None && (validVehicleTypes & vehicleType) == ExtVehicleType.None)
                        {
                            continue;
                        }
                        CustomSegmentLight segLight = segLights.GetCustomLight(laneIndex);
                        if (segLight == null)
                        {
                            Log.Warning($"Timed traffic light step: Failed to get custom light for vehicleType {vehicleType} @ seg. {sourceSegmentId}, node {timedNode.NodeId}!");
                            continue;
                        }

#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Checking lane {laneIndex} @ seg. {sourceSegmentId}. Vehicle types: {vehicleType}");
                        }
#endif

                        Dictionary <ushort, uint> carsFlowingToSegmentMetric = null;
                        Dictionary <ushort, uint> allCarsToSegmentMetric     = null;
                        bool evalFlowingVehicles = segLight.IsAnyGreen();                         // flowing vehicle need only to be evaluated if a light is green
                        if (evalFlowingVehicles && onlyMoving)
                        {
                            carsFlowingToSegmentMetric = sourceSegmentEnd.GetVehicleMetricGoingToSegment(false, laneIndex, debug);
                        }
                        allCarsToSegmentMetric = sourceSegmentEnd.GetVehicleMetricGoingToSegment(true, laneIndex, debug);

                        // calculate waiting/flowing traffic
                        foreach (KeyValuePair <ushort, uint> f in allCarsToSegmentMetric)
                        {
                            ushort targetSegmentId = f.Key;
                            uint   totalNumCars    = f.Value;

                            if (!directions.ContainsKey(targetSegmentId))
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Direction undefined for target segment {targetSegmentId} @ {timedNodeId}");
                                continue;
                            }

                            if (evalFlowingVehicles)
                            {
                                uint totalNumFlowingCars = onlyMoving ? carsFlowingToSegmentMetric[f.Key] : totalNumCars;

#if DEBUG
                                if (debug)
                                {
                                    Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Total num of flowing cars on seg. {sourceSegmentId}, lane {laneIndex} going to seg. {targetSegmentId}: {totalNumFlowingCars}");
                                }
#endif

                                bool addToFlow = false;
                                switch (directions[targetSegmentId])
                                {
                                case ArrowDirection.Turn:
                                    addToFlow = TrafficPriorityManager.IsLeftHandDrive() ? segLight.IsRightGreen() : segLight.IsLeftGreen();
                                    break;

                                case ArrowDirection.Left:
                                    addToFlow = segLight.IsLeftGreen();
                                    break;

                                case ArrowDirection.Right:
                                    addToFlow = segLight.IsRightGreen();
                                    break;

                                case ArrowDirection.Forward:
                                default:
                                    addToFlow = segLight.IsMainGreen();
                                    break;
                                }

                                if (addToFlow)
                                {
                                    curMeanFlow += totalNumFlowingCars;
                                    ++numFlows;
                                }
                                else
                                {
                                    curMeanWait += totalNumCars;
                                    ++numWaits;
                                }
                            }
                            else
                            {
                                curMeanWait += totalNumCars;
                                ++numWaits;
                            }

#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Vehicles on lane {laneIndex} on seg. {sourceSegmentId} going to seg. {targetSegmentId} curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}, numFlows={numFlows}, numWaits={numWaits}");
                            }
#endif
                        }
                    }
                }
            }

            /*if (numFlows > 0)
             *      curMeanFlow /= numFlows;
             * if (numWaits > 0)
             *      curMeanWait /= numWaits;*/

            float fCurMeanFlow = numFlows > 0 ? (float)curMeanFlow / (float)numFlows : 0;
            float fCurMeanWait = numWaits > 0 ? (float)curMeanWait / (float)numWaits : 0;
            fCurMeanFlow /= waitFlowBalance;             // a value smaller than 1 rewards steady traffic currents

            wait = (float)fCurMeanWait;
            flow = fCurMeanFlow;

            return(true);
        }
        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());
            }
        }
        internal void Recalculate()
        {
#if DEBUGGEO
            Log._Debug($"NodeGeometry: Recalculate @ {NodeId}");
#endif

            Cleanup();

            Flags.applyNodeTrafficLightFlag(NodeId);

            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            // check if node is valid
            if (!IsValid())
            {
                for (int i = 0; i < 8; ++i)
                {
                    SegmentEndGeometries[i] = null;
                }
                tlsMan.RemoveNodeFromSimulation(NodeId, false, true);
                Flags.setNodeTrafficLight(NodeId, false);
            }
            else
            {
                NetManager             netManager = Singleton <NetManager> .instance;
                TrafficPriorityManager prioMan    = TrafficPriorityManager.Instance();

                bool hasTrafficLight = (netManager.m_nodes.m_buffer[NodeId].m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None;
                var  nodeSim         = tlsMan.GetNodeSimulation(NodeId);
                if (nodeSim == null)
                {
                    byte numSegmentsWithSigns = 0;
                    for (var s = 0; s < 8; s++)
                    {
                        var segmentId = netManager.m_nodes.m_buffer[NodeId].GetSegment(s);
                        if (segmentId <= 0)
                        {
                            continue;
                        }

#if DEBUGx
                        Log._Debug($"NodeGeometry.Recalculate: Housekeeping segment {segmentId}");
#endif

                        SegmentEnd prioritySegment = prioMan.GetPrioritySegment(NodeId, segmentId);
                        if (prioritySegment == null)
                        {
                            continue;
                        }

                        // if node is a traffic light, it must not have priority signs
                        if (hasTrafficLight && prioritySegment.Type != SegmentEnd.PriorityType.None)
                        {
                            Log.Warning($"Housekeeping: Node {NodeId}, Segment {segmentId} is a priority sign but node has a traffic light!");
                            prioritySegment.Type = SegmentEnd.PriorityType.None;
                        }

                        // if a priority sign is set, everything is ok
                        if (prioritySegment.Type != SegmentEnd.PriorityType.None)
                        {
                            ++numSegmentsWithSigns;
                        }
                    }

                    if (numSegmentsWithSigns > 0)
                    {
                        // add priority segments for newly created segments
                        numSegmentsWithSigns += prioMan.AddPriorityNode(NodeId);
                    }
                }

                // calculate node properties
                byte incomingSegments = 0;
                byte outgoingSegments = 0;
                for (int i = 0; i < 8; ++i)
                {
                    if (SegmentEndGeometries[i] == null)
                    {
                        continue;
                    }
#if DEBUGGEO
                    Log._Debug($"NodeGeometry.Recalculate: Iterating over segment end {SegmentEndGeometries[i].SegmentId} @ node {NodeId}");
#endif

                    bool startNode = SegmentEndGeometries[i].StartNode;
                    if (SegmentEndGeometries[i].GetSegmentGeometry().IsIncoming(startNode))
                    {
                        ++incomingSegments;
                    }
                    if (SegmentEndGeometries[i].GetSegmentGeometry().IsOutgoing(startNode))
                    {
                        ++outgoingSegments;
                    }
                }

                IsSimpleJunction = incomingSegments == 1 || outgoingSegments == 1;
#if DEBUGGEO
                Log._Debug($"NodeGeometry.Recalculate: Node {NodeId} has {incomingSegments} incoming and {outgoingSegments} outgoing segments.");
#endif
            }

            NotifyObservers();
        }