private void DrawSpeedLimitHandles(ushort segmentId,
                                           ref NetSegment segment,
                                           bool viewOnly,
                                           ref Vector3 camPos)
        {
            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return;
            }

            Vector3    center          = segment.m_bounds.center;
            NetManager netManager      = Singleton <NetManager> .instance;
            SpeedValue speedLimitToSet = viewOnly
                                             ? new SpeedValue(-1f)
                                             : currentPaletteSpeedLimit;
            bool showPerLane = showLimitsPerLane;

            if (!viewOnly)
            {
                showPerLane = showLimitsPerLane ^
                              (Input.GetKey(KeyCode.LeftControl) ||
                               Input.GetKey(KeyCode.RightControl));
            }

            // US signs are rectangular, all other are round
            float speedLimitSignVerticalScale = GetVerticalTextureScale();

            if (showPerLane)
            {
                // show individual speed limit handle per lane
                int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(
                    segmentId,
                    null,
                    out int numDirections,
                    SpeedLimitManager.VEHICLE_TYPES);

                NetInfo segmentInfo = segment.Info;
                Vector3 yu          = (segment.m_endDirection - segment.m_startDirection).normalized;
                Vector3 xu          = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;

                // if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) {
                //        xu = -xu; }
                float   f    = viewOnly ? 4f : 7f; // reserved sign size in game coordinates
                Vector3 zero = center - (0.5f * (((numLanes - 1) + numDirections) - 1) * f * xu);

                uint            x           = 0;
                IList <LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(
                    segmentId,
                    ref segment,
                    null,
                    SpeedLimitManager.LANE_TYPES,
                    SpeedLimitManager.VEHICLE_TYPES);
                bool onlyMonorailLanes = sortedLanes.Count > 0;

                if (!viewOnly)
                {
                    foreach (LanePos laneData in sortedLanes)
                    {
                        byte         laneIndex = laneData.laneIndex;
                        NetInfo.Lane laneInfo  = segmentInfo.m_lanes[laneIndex];

                        if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) ==
                            VehicleInfo.VehicleType.None)
                        {
                            onlyMonorailLanes = false;
                            break;
                        }
                    }
                }

                var directions      = new HashSet <NetInfo.Direction>();
                int sortedLaneIndex = -1;

                foreach (LanePos laneData in sortedLanes)
                {
                    ++sortedLaneIndex;
                    uint laneId    = laneData.laneId;
                    byte laneIndex = laneData.laneIndex;

                    NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];
                    if (!directions.Contains(laneInfo.m_finalDirection))
                    {
                        if (directions.Count > 0)
                        {
                            ++x; // space between different directions
                        }

                        directions.Add(laneInfo.m_finalDirection);
                    }

                    SpeedValue laneSpeedLimit = new SpeedValue(
                        SpeedLimitManager.Instance.GetCustomSpeedLimit(laneId));

                    bool hoveredHandle = MainTool.DrawGenericOverlayGridTexture(
                        SpeedLimitTextures.GetSpeedLimitTexture(laneSpeedLimit),
                        camPos,
                        zero,
                        f,
                        f,
                        xu,
                        yu,
                        x,
                        0,
                        speedLimitSignSize,
                        speedLimitSignSize * speedLimitSignVerticalScale,
                        !viewOnly);

                    if (!viewOnly &&
                        !onlyMonorailLanes &&
                        ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) !=
                         VehicleInfo.VehicleType.None))
                    {
                        Texture2D tex1 = RoadUITextures.VehicleInfoSignTextures[
                            LegacyExtVehicleType.ToNew(ExtVehicleType.PassengerTrain)];
                        MainTool.DrawStaticSquareOverlayGridTexture(
                            tex1,
                            camPos,
                            zero,
                            f,
                            xu,
                            yu,
                            x,
                            1,
                            speedLimitSignSize);
                    }

                    if (hoveredHandle)
                    {
                    }

                    if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                    {
                        SpeedLimitManager.Instance.SetSpeedLimit(
                            segmentId,
                            laneIndex,
                            laneInfo,
                            laneId,
                            speedLimitToSet.GameUnits);

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            int slIndexCopy = sortedLaneIndex;

                            SegmentLaneTraverser.Traverse(
                                segmentId,
                                SegmentTraverser.TraverseDirection.AnyDirection,
                                SegmentTraverser.TraverseSide.AnySide,
                                SegmentLaneTraverser.LaneStopCriterion.LaneCount,
                                SegmentTraverser.SegmentStopCriterion.Junction,
                                SpeedLimitManager.LANE_TYPES,
                                SpeedLimitManager.VEHICLE_TYPES,
                                data => {
                                if (data.SegVisitData.Initial)
                                {
                                    return(true);
                                }

                                if (slIndexCopy != data.SortedLaneIndex)
                                {
                                    return(true);
                                }

                                Constants.ServiceFactory.NetService.ProcessSegment(
                                    data.SegVisitData.CurSeg.segmentId,
                                    (ushort curSegmentId, ref NetSegment curSegment) =>
                                {
                                    NetInfo.Lane curLaneInfo = curSegment.Info.m_lanes[
                                        data.CurLanePos.laneIndex];

                                    SpeedLimitManager.Instance.SetSpeedLimit(
                                        curSegmentId,
                                        data.CurLanePos.laneIndex,
                                        curLaneInfo,
                                        data.CurLanePos.laneId,
                                        speedLimitToSet.GameUnits);
                                    return(true);
                                });

                                return(true);
                            });
                        }
                    }

                    ++x;
                }
            }
            else
            {
                // draw speedlimits over mean middle points of lane beziers

                if (!segmentCenterByDir.TryGetValue(
                        segmentId,
                        out Dictionary <NetInfo.Direction, Vector3> segCenter))
                {
                    segCenter = new Dictionary <NetInfo.Direction, Vector3>();
                    segmentCenterByDir.Add(segmentId, segCenter);
                    TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segCenter);
                }

                foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segCenter)
                {
                    bool visible = MainTool.WorldToScreenPoint(e.Value, out Vector3 screenPos);

                    if (!visible)
                    {
                        continue;
                    }

                    float zoom        = (1.0f / (e.Value - camPos).magnitude) * 100f * MainTool.GetBaseZoom();
                    float size        = (viewOnly ? 0.8f : 1f) * speedLimitSignSize * zoom;
                    Color guiColor    = GUI.color;
                    var   boundingBox = new Rect(screenPos.x - (size / 2),
                                                 screenPos.y - (size / 2),
                                                 size,
                                                 size * speedLimitSignVerticalScale);
                    bool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                    guiColor.a = TrafficManagerTool.GetHandleAlpha(hoveredHandle);

                    if (hoveredHandle)
                    {
                        // mouse hovering over sign
                    }

                    // Draw something right here, the road sign texture
                    GUI.color = guiColor;
                    SpeedValue displayLimit = new SpeedValue(
                        SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key));
                    Texture2D tex = SpeedLimitTextures.GetSpeedLimitTexture(displayLimit);

                    GUI.DrawTexture(boundingBox, tex);

                    if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                    {
                        // change the speed limit to the selected one
                        // Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()}
                        //     to {speedLimitToSet}");
                        SpeedLimitManager.Instance.SetSpeedLimit(segmentId,
                                                                 e.Key,
                                                                 currentPaletteSpeedLimit.GameUnits);

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            NetInfo.Direction normDir = e.Key;
                            if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None)
                            {
                                normDir = NetInfo.InvertDirection(normDir);
                            }

                            SegmentLaneTraverser.Traverse(
                                segmentId,
                                SegmentTraverser.TraverseDirection.AnyDirection,
                                SegmentTraverser.TraverseSide.AnySide,
                                SegmentLaneTraverser.LaneStopCriterion.LaneCount,
                                SegmentTraverser.SegmentStopCriterion.Junction,
                                SpeedLimitManager.LANE_TYPES,
                                SpeedLimitManager.VEHICLE_TYPES,
                                data =>
                            {
                                if (data.SegVisitData.Initial)
                                {
                                    return(true);
                                }

                                bool reverse =
                                    data.SegVisitData.ViaStartNode
                                    == data.SegVisitData.ViaInitialStartNode;

                                ushort otherSegmentId    = data.SegVisitData.CurSeg.segmentId;
                                NetInfo otherSegmentInfo =
                                    netManager.m_segments.m_buffer[otherSegmentId].Info;
                                byte laneIndex        = data.CurLanePos.laneIndex;
                                NetInfo.Lane laneInfo = otherSegmentInfo.m_lanes[laneIndex];

                                NetInfo.Direction otherNormDir = laneInfo.m_finalDirection;

                                if (((netManager.m_segments.m_buffer[otherSegmentId].m_flags
                                      & NetSegment.Flags.Invert)
                                     != NetSegment.Flags.None) ^ reverse)
                                {
                                    otherNormDir = NetInfo.InvertDirection(otherNormDir);
                                }

                                if (otherNormDir == normDir)
                                {
                                    SpeedLimitManager.Instance.SetSpeedLimit(
                                        otherSegmentId,
                                        laneInfo.m_finalDirection,
                                        speedLimitToSet.GameUnits);
                                }

                                return(true);
                            });
                        }
                    }

                    guiColor.a = 1f;
                    GUI.color  = guiColor;
                }
            }
        }
예제 #2
0
        private bool DrawParkingRestrictionHandles(ushort segmentId,
                                                   bool clicked,
                                                   ref NetSegment segment,
                                                   bool viewOnly,
                                                   ref Vector3 camPos)
        {
            if (viewOnly && !Options.parkingRestrictionsOverlay)
            {
                return(false);
            }

            NetManager netManager = Singleton <NetManager> .instance;
            ParkingRestrictionsManager parkingManager = ParkingRestrictionsManager.Instance;
            bool hovered = false;

            // draw parking restriction signs over mean middle points of lane beziers
            if (!segmentCenterByDir.TryGetValue(
                    segmentId,
                    out Dictionary <NetInfo.Direction, Vector3> segCenter))
            {
                segCenter = new Dictionary <NetInfo.Direction, Vector3>();
                segmentCenterByDir.Add(segmentId, segCenter);
                TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segCenter);
            }

            foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segCenter)
            {
                bool allowed = parkingManager.IsParkingAllowed(segmentId, e.Key);
                if (allowed && viewOnly)
                {
                    continue;
                }

                bool visible = MainTool.WorldToScreenPoint(e.Value, out Vector3 screenPos);

                if (!visible)
                {
                    continue;
                }

                float zoom        = (1.0f / (e.Value - camPos).magnitude) * 100f * MainTool.GetBaseZoom();
                float size        = (viewOnly ? 0.8f : 1f) * SIGN_SIZE * zoom;
                Color guiColor    = GUI.color;
                Rect  boundingBox = new Rect(
                    screenPos.x - (size / 2),
                    screenPos.y - (size / 2),
                    size,
                    size);

                if (Options.speedLimitsOverlay)
                {
                    boundingBox.y -= size + 10f;
                }

                bool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                guiColor.a = TrafficManagerTool.GetHandleAlpha(hoveredHandle);

                if (hoveredHandle)
                {
                    // mouse hovering over sign
                    hovered = true;
                }

                GUI.color = guiColor;
                GUI.DrawTexture(boundingBox, RoadUITextures.ParkingRestrictionTextures[allowed]);

                if (hoveredHandle && clicked && !IsCursorInPanel() &&
                    parkingManager.ToggleParkingAllowed(segmentId, e.Key))
                {
                    allowed = !allowed;

                    if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                    {
                        NetInfo.Direction normDir = e.Key;

                        if ((netManager.m_segments.m_buffer[segmentId].m_flags &
                             NetSegment.Flags.Invert) != NetSegment.Flags.None)
                        {
                            normDir = NetInfo.InvertDirection(normDir);
                        }

                        bool LaneVisitor(SegmentLaneVisitData data)
                        {
                            if (data.SegVisitData.Initial)
                            {
                                return(true);
                            }

                            bool reverse = data.SegVisitData.ViaStartNode ==
                                           data.SegVisitData.ViaInitialStartNode;

                            ushort  otherSegmentId   = data.SegVisitData.CurSeg.segmentId;
                            NetInfo otherSegmentInfo =
                                netManager.m_segments.m_buffer[otherSegmentId].Info;
                            byte laneIndex = data.CurLanePos.laneIndex;

                            NetInfo.Lane laneInfo = otherSegmentInfo.m_lanes[laneIndex];

                            NetInfo.Direction otherNormDir = laneInfo.m_finalDirection;
                            if (((netManager.m_segments.m_buffer[otherSegmentId].m_flags &
                                  NetSegment.Flags.Invert) != NetSegment.Flags.None) ^ reverse)
                            {
                                otherNormDir = NetInfo.InvertDirection(otherNormDir);
                            }

                            if (otherNormDir == normDir)
                            {
                                parkingManager.SetParkingAllowed(
                                    otherSegmentId,
                                    laneInfo.m_finalDirection,
                                    allowed);
                            }

                            return(true);
                        }

                        SegmentLaneTraverser.Traverse(
                            segmentId,
                            SegmentTraverser.TraverseDirection.AnyDirection,
                            SegmentTraverser.TraverseSide.AnySide,
                            SegmentLaneTraverser.LaneStopCriterion.LaneCount,
                            SegmentTraverser.SegmentStopCriterion.Junction,
                            ParkingRestrictionsManager.LANE_TYPES,
                            ParkingRestrictionsManager.VEHICLE_TYPES,
                            LaneVisitor);
                    }
                }

                guiColor.a = 1f;
                GUI.color  = guiColor;
            }

            return(hovered);
        }
        private bool drawSpeedLimitHandles(ushort segmentId, bool viewOnly, ref Vector3 camPos)
        {
            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return(false);
            }

            // draw speedlimits over mean middle points of lane beziers
            if (!segmentCenterByDir.ContainsKey(segmentId))
            {
                segmentCenterByDir.Add(segmentId, new Dictionary <NetInfo.Direction, Vector3>());
                TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segmentCenterByDir[segmentId]);
            }

            bool hovered = false;

            foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segmentCenterByDir[segmentId])
            {
                var screenPos = Camera.main.WorldToScreenPoint(e.Value);
                screenPos.y = Screen.height - screenPos.y;

                float zoom          = 1.0f / (e.Value - camPos).magnitude * 100f * MainTool.GetBaseZoom();
                float size          = speedLimitSignSize * zoom;
                Color guiColor      = GUI.color;
                Rect  boundingBox   = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size);
                bool  hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                if (hoveredHandle)
                {
                    // mouse hovering over sign
                    hovered    = true;
                    guiColor.a = 0.8f;
                }
                else
                {
                    guiColor.a = 0.5f;
                }

                GUI.color = guiColor;

                try {
                    GUI.DrawTexture(boundingBox, TrafficLightToolTextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key)]);
                } catch (Exception ex) {
                    Log.Error("segment " + segmentId + " limit: " + SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key) + ", ex: " + ex.ToString());
                }

                if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                {
                    // change the speed limit to the selected one
                    ushort speedLimitToSet = SpeedLimitManager.Instance.AvailableSpeedLimits[curSpeedLimitIndex];
                    //Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()} to {speedLimitToSet}");
                    SpeedLimitManager.Instance.SetSpeedLimit(segmentId, e.Key, speedLimitToSet);

                    // TODO use SegmentTraverser
                    if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                    {
                        // apply new speed limit to connected segments
                        NetInfo         selectedSegmentInfo = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].Info;
                        List <object[]> selectedSortedLanes = TrafficManagerTool.GetSortedVehicleLanes(segmentId, selectedSegmentInfo, null, VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Train);                        // TODO refactor vehicle mask

                        LinkedList <ushort> nodesToProcess    = new LinkedList <ushort>();
                        HashSet <ushort>    processedNodes    = new HashSet <ushort>();
                        HashSet <ushort>    processedSegments = new HashSet <ushort>();
                        processedSegments.Add(SelectedSegmentId);

                        ushort selectedStartNodeId = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_startNode;
                        ushort selectedEndNodeId   = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_endNode;

                        if (selectedStartNodeId != 0)
                        {
                            nodesToProcess.AddFirst(selectedStartNodeId);
                        }
                        if (selectedEndNodeId != 0)
                        {
                            nodesToProcess.AddFirst(selectedEndNodeId);
                        }

                        while (nodesToProcess.First != null)
                        {
                            ushort nodeId = nodesToProcess.First.Value;
                            nodesToProcess.RemoveFirst();
                            processedNodes.Add(nodeId);

                            if (Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].CountSegments() > 2)
                            {
                                continue;                                 // junction. stop.
                            }
                            // explore segments at node
                            for (var s = 0; s < 8; s++)
                            {
                                var otherSegmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].GetSegment(s);

                                if (otherSegmentId <= 0 || processedSegments.Contains(otherSegmentId))
                                {
                                    continue;
                                }
                                processedSegments.Add(otherSegmentId);

                                NetInfo         segmentInfo = Singleton <NetManager> .instance.m_segments.m_buffer[otherSegmentId].Info;
                                List <object[]> sortedLanes = TrafficManagerTool.GetSortedVehicleLanes(otherSegmentId, segmentInfo, null, VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Train);                                // TODO refactor vehicle mask

                                if (sortedLanes.Count == selectedSortedLanes.Count)
                                {
                                    // number of lanes matches selected segment
                                    for (int i = 0; i < sortedLanes.Count; ++i)
                                    {
                                        object[] selectedLaneData = selectedSortedLanes[i];
                                        object[] laneData         = sortedLanes[i];

                                        uint         selectedLaneId    = (uint)selectedLaneData[0];
                                        uint         selectedLaneIndex = (uint)selectedLaneData[2];
                                        NetInfo.Lane selectedLaneInfo  = segmentInfo.m_lanes[selectedLaneIndex];

                                        uint         laneId    = (uint)laneData[0];
                                        uint         laneIndex = (uint)laneData[2];
                                        NetInfo.Lane laneInfo  = segmentInfo.m_lanes[laneIndex];

                                        if (laneInfo.m_finalDirection == e.Key)
                                        {
                                            SpeedLimitManager.Instance.SetSpeedLimit(otherSegmentId, laneInfo.m_finalDirection, speedLimitToSet);
                                        }

                                        // apply restrictions of selected segment & lane
                                        //VehicleRestrictionsManager.SetAllowedVehicleTypes(otherSegmentId, segmentInfo, laneIndex, laneInfo, laneId, VehicleRestrictionsManager.GetAllowedVehicleTypes(SelectedSegmentId, selectedSegmentInfo, selectedLaneIndex, selectedLaneInfo));
                                    }

                                    // add nodes to explore
                                    ushort startNodeId = Singleton <NetManager> .instance.m_segments.m_buffer[otherSegmentId].m_startNode;
                                    ushort endNodeId   = Singleton <NetManager> .instance.m_segments.m_buffer[otherSegmentId].m_endNode;

                                    if (startNodeId != 0 && !processedNodes.Contains(startNodeId))
                                    {
                                        nodesToProcess.AddFirst(startNodeId);
                                    }
                                    if (endNodeId != 0 && !processedNodes.Contains(endNodeId))
                                    {
                                        nodesToProcess.AddFirst(endNodeId);
                                    }
                                }
                            }
                        }
                    }
                    //mouseClickProcessed = true;
                }

                guiColor.a = 1f;
                GUI.color  = guiColor;
            }
            return(hovered);
        }
        private bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, bool viewOnly, ref Vector3 camPos)
        {
            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return(false);
            }

            Vector3    center     = segment.m_bounds.center;
            NetManager netManager = Singleton <NetManager> .instance;

            bool   hovered         = false;
            ushort speedLimitToSet = viewOnly ? (ushort)0 : SpeedLimitManager.Instance.AvailableSpeedLimits[curSpeedLimitIndex];

            bool showPerLane = showLimitsPerLane;

            if (!viewOnly)
            {
                showPerLane = showLimitsPerLane ^ (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl));
            }
            if (showPerLane)
            {
                // show individual speed limit handle per lane
                int numDirections;
                int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(segmentId, null, out numDirections, SpeedLimitManager.VEHICLE_TYPES);

                NetInfo segmentInfo = segment.Info;
                Vector3 yu          = (segment.m_endDirection - segment.m_startDirection).normalized;
                Vector3 xu          = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;

                /*if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) {
                 *      xu = -xu;
                 * }*/
                float   f    = viewOnly ? 4f : 7f;            // reserved sign size in game coordinates
                Vector3 zero = center - 0.5f * (float)(numLanes - 1 + numDirections - 1) * f * xu;

                uint            x                 = 0;
                var             guiColor          = GUI.color;
                IList <LanePos> sortedLanes       = Constants.ServiceFactory.NetService.GetSortedLanes(segmentId, ref segment, null, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES);
                bool            onlyMonorailLanes = sortedLanes.Count > 0;
                if (!viewOnly)
                {
                    foreach (LanePos laneData in sortedLanes)
                    {
                        byte         laneIndex = laneData.laneIndex;
                        NetInfo.Lane laneInfo  = segmentInfo.m_lanes[laneIndex];

                        if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None)
                        {
                            onlyMonorailLanes = false;
                            break;
                        }
                    }
                }

                HashSet <NetInfo.Direction> directions = new HashSet <NetInfo.Direction>();
                int sortedLaneIndex = -1;
                foreach (LanePos laneData in sortedLanes)
                {
                    ++sortedLaneIndex;
                    uint laneId    = laneData.laneId;
                    byte laneIndex = laneData.laneIndex;

                    NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];
                    if (!directions.Contains(laneInfo.m_finalDirection))
                    {
                        if (directions.Count > 0)
                        {
                            ++x;                             // space between different directions
                        }
                        directions.Add(laneInfo.m_finalDirection);
                    }

                    bool hoveredHandle = MainTool.DrawGenericSquareOverlayGridTexture(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(laneId)], camPos, zero, f, xu, yu, x, 0, speedLimitSignSize, !viewOnly, 0.5f, 0.8f);
                    if (!viewOnly && !onlyMonorailLanes && (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) != VehicleInfo.VehicleType.None)
                    {
                        MainTool.DrawStaticSquareOverlayGridTexture(TextureResources.VehicleInfoSignTextures[ExtVehicleType.PassengerTrain], camPos, zero, f, xu, yu, x, 1, speedLimitSignSize, 0.5f);
                    }
                    if (hoveredHandle)
                    {
                        hovered = true;
                    }

                    if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                    {
                        SpeedLimitManager.Instance.SetSpeedLimit(segmentId, laneIndex, laneInfo, laneId, speedLimitToSet);

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            SegmentLaneTraverser.Traverse(segmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, delegate(SegmentLaneVisitData data) {
                                if (data.segVisitData.initial)
                                {
                                    return(true);
                                }

                                if (sortedLaneIndex != data.sortedLaneIndex)
                                {
                                    return(true);
                                }

                                Constants.ServiceFactory.NetService.ProcessSegment(data.segVisitData.curGeo.SegmentId, delegate(ushort curSegmentId, ref NetSegment curSegment) {
                                    NetInfo.Lane curLaneInfo = curSegment.Info.m_lanes[data.curLanePos.laneIndex];
                                    SpeedLimitManager.Instance.SetSpeedLimit(curSegmentId, data.curLanePos.laneIndex, curLaneInfo, data.curLanePos.laneId, speedLimitToSet);
                                    return(true);
                                });

                                return(true);
                            });
                        }
                    }

                    ++x;
                }
            }
            else
            {
                // draw speedlimits over mean middle points of lane beziers
                Dictionary <NetInfo.Direction, Vector3> segCenter;
                if (!segmentCenterByDir.TryGetValue(segmentId, out segCenter))
                {
                    segCenter = new Dictionary <NetInfo.Direction, Vector3>();
                    segmentCenterByDir.Add(segmentId, segCenter);
                    TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segCenter);
                }

                foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segCenter)
                {
                    Vector3 screenPos;
                    bool    visible = MainTool.WorldToScreenPoint(e.Value, out screenPos);

                    if (!visible)
                    {
                        continue;
                    }

                    float zoom          = 1.0f / (e.Value - camPos).magnitude * 100f * MainTool.GetBaseZoom();
                    float size          = (viewOnly ? 0.8f : 1f) * speedLimitSignSize * zoom;
                    Color guiColor      = GUI.color;
                    Rect  boundingBox   = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size);
                    bool  hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                    if (hoveredHandle)
                    {
                        // mouse hovering over sign
                        hovered    = true;
                        guiColor.a = 0.8f;
                    }
                    else
                    {
                        guiColor.a = 0.5f;
                    }

                    GUI.color = guiColor;
                    GUI.DrawTexture(boundingBox, TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key)]);

                    if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                    {
                        // change the speed limit to the selected one
                        //Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()} to {speedLimitToSet}");
                        SpeedLimitManager.Instance.SetSpeedLimit(segmentId, e.Key, speedLimitToSet);

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            NetInfo.Direction normDir = e.Key;
                            if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None)
                            {
                                normDir = NetInfo.InvertDirection(normDir);
                            }

                            SegmentLaneTraverser.Traverse(segmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, delegate(SegmentLaneVisitData data) {
                                if (data.segVisitData.initial)
                                {
                                    return(true);
                                }
                                bool reverse = data.segVisitData.viaStartNode == data.segVisitData.viaInitialStartNode;

                                ushort otherSegmentId    = data.segVisitData.curGeo.SegmentId;
                                NetInfo otherSegmentInfo = netManager.m_segments.m_buffer[otherSegmentId].Info;
                                uint laneId           = data.curLanePos.laneId;
                                byte laneIndex        = data.curLanePos.laneIndex;
                                NetInfo.Lane laneInfo = otherSegmentInfo.m_lanes[laneIndex];

                                NetInfo.Direction otherNormDir = laneInfo.m_finalDirection;
                                if ((netManager.m_segments.m_buffer[otherSegmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None ^
                                    reverse)
                                {
                                    otherNormDir = NetInfo.InvertDirection(otherNormDir);
                                }

                                if (otherNormDir == normDir)
                                {
                                    SpeedLimitManager.Instance.SetSpeedLimit(otherSegmentId, laneInfo.m_finalDirection, speedLimitToSet);
                                }

                                return(true);
                            });
                        }
                    }

                    guiColor.a = 1f;
                    GUI.color  = guiColor;
                }
            }
            return(hovered);
        }
        private bool drawSpeedLimitHandles(ushort segmentId, bool viewOnly)
        {
            if (!LoadingExtension.IsPathManagerCompatible)
            {
                return(false);
            }

            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return(false);
            }

            // draw speedlimits over mean middle points of lane beziers
            if (!segmentCenterByDir.ContainsKey(segmentId))
            {
                segmentCenterByDir.Add(segmentId, new Dictionary <NetInfo.Direction, Vector3>());
                TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segmentCenterByDir[segmentId]);
            }

            bool hovered = false;

            foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segmentCenterByDir[segmentId])
            {
                Vector3 signPos   = e.Value;
                var     screenPos = Camera.main.WorldToScreenPoint(signPos);
                screenPos.y = Screen.height - screenPos.y;
                if (screenPos.z < 0)
                {
                    return(false);
                }
                var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
                var diff   = signPos - camPos;

                if (diff.magnitude > TrafficManagerTool.PriorityCloseLod)
                {
                    return(false);                    // do not draw if too distant
                }
                ItemClass connectionClass = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].Info.GetConnectionClass();
                if (!(connectionClass.m_service == ItemClass.Service.Road ||
                      (connectionClass.m_service == ItemClass.Service.PublicTransport && connectionClass.m_subService == ItemClass.SubService.PublicTransportTrain)))
                {
                    return(false);
                }

                var  zoom          = 1.0f / diff.magnitude * 100f * MainTool.GetBaseZoom();
                var  size          = speedLimitSignSize * zoom;
                var  guiColor      = GUI.color;
                var  boundingBox   = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size);
                bool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                if (hoveredHandle)
                {
                    // mouse hovering over sign
                    hovered    = true;
                    guiColor.a = 0.8f;
                }
                else
                {
                    guiColor.a = 0.5f;
                }

                GUI.color = guiColor;

                try {
                    GUI.DrawTexture(boundingBox, TrafficLightToolTextureResources.SpeedLimitTextures[SpeedLimitManager.GetCustomSpeedLimit(segmentId, e.Key)]);
                } catch (Exception ex) {
                    Log.Error("segment " + segmentId + " limit: " + SpeedLimitManager.GetCustomSpeedLimit(segmentId, e.Key) + ", ex: " + ex.ToString());
                }

                if (hoveredHandle && Input.GetMouseButton(0))
                {
                    // change the speed limit to the selected one
                    ushort speedLimitToSet = SpeedLimitManager.AvailableSpeedLimits[curSpeedLimitIndex];
                    //Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()} to {speedLimitToSet}");
                    SpeedLimitManager.SetSpeedLimit(segmentId, e.Key, speedLimitToSet);
                    //mouseClickProcessed = true;
                }

                guiColor.a = 1f;
                GUI.color  = guiColor;
            }
            return(hovered);
        }