void SetSegments(ushort segmentId)
        {
            NetSegment segment = NetManager.instance.m_segments.m_buffer[segmentId];
            Segment    seg     = new Segment()
            {
                m_segmentId  = segmentId,
                m_targetNode = segment.m_endNode
            };

            m_segments[segmentId] = seg;

            ushort  infoIndex = segment.m_infoIndex;
            NetNode node      = NetManager.instance.m_nodes.m_buffer[segment.m_startNode];

            if (node.CountSegments() == 2)
            {
                SetSegments(node.m_segment0 == segmentId ? node.m_segment1 : node.m_segment0, infoIndex, ref seg);
            }

            node = NetManager.instance.m_nodes.m_buffer[segment.m_endNode];
            if (node.CountSegments() == 2)
            {
                SetSegments(node.m_segment0 == segmentId ? node.m_segment1 : node.m_segment0, infoIndex, ref seg);
            }
        }
        public IEnumerator SearchForGhostNodes()
        {
            Debug.Log("[BND] Searching for ghost nodes");
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Ghost/broken nodes list: ");
            LastGhostNodesCount = 0;
            for (var i = 0; i < NetManager.instance.m_nodes.m_buffer.Length; i++)
            {
                NetNode node = NetManager.instance.m_nodes.m_buffer[i];
                if (node.m_flags != 0 &&
                    (node.m_flags & NetNode.Flags.Untouchable) == 0 &&
                    node.CountSegments() == 0 &&
                    (node.m_flags & NetNode.Flags.Created) != 0)
                {
                    LastGhostNodesCount++;
                    sb.Append("[").Append(i).Append("] - ").Append(node.m_flags.ToString()).Append(" info: ").Append(node.Info.ToString()).AppendLine("]");
                    NetManager.instance.ReleaseNode((ushort)i);
                }
            }

            sb.AppendLine("=================================================");
            Debug.Log("[BND] Searching finished. Found and released " + LastGhostNodesCount + " ghost nodes");
            Debug.Log(sb);
            yield return(null);
        }
        public bool IsLaneChangingAllowedWhenGoingStraightConfigurable(ushort segmentId, bool startNode, ref NetNode node)
        {
#if DEBUG
            bool debug = GlobalConfig.Instance.Debug.Switches[11];
#endif

            SegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(startNode);

            if (endGeo == null)
            {
                Log.Warning($"JunctionRestrictionsManager.IsLaneChangingAllowedWhenGoingStraightConfigurable({segmentId}, {startNode}): Could not get segment end geometry");
                return(false);
            }

            bool ret =
                (node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None &&
                node.Info?.m_class?.m_service != ItemClass.Service.Beautification &&
                !endGeo.OutgoingOneWay &&
                node.CountSegments() > 2
            ;
#if DEBUG
            if (debug)
            {
                Log._Debug($"JunctionRestrictionsManager.IsLaneChangingAllowedWhenGoingStraightConfigurable({segmentId}, {startNode}): ret={ret}, flags={node.m_flags}, service={node.Info?.m_class?.m_service}, incomingOneWay={endGeo.IncomingOneWay}, outgoingOneWay={endGeo.OutgoingOneWay}, node.CountSegments()={node.CountSegments()}");
            }
#endif
            return(ret);
        }
        public override void UpdateNode(ushort nodeID, ref NetNode data)
        {
            if ((data.m_flags & NetNode.Flags.Untouchable) != NetNode.Flags.None)
            {
                ushort index = NetNode.FindOwnerBuilding(nodeID, 32f);
                if (index != 0)
                {
                    BuildingManager      buildingManager = Singleton <BuildingManager> .instance;
                    Notification.Problem oldProblems     = buildingManager.m_buildings.m_buffer[index].m_problems;
                    Notification.Problem newProblems;
                    if (data.CountSegments() != 0)
                    {
                        newProblems = Notification.RemoveProblems(oldProblems, Notification.Problem.WaterNotConnected);
                    }
                    else
                    {
                        newProblems = Notification.AddProblems(oldProblems, Notification.Problem.WaterNotConnected);
                    }
                    if (newProblems != oldProblems)
                    {
                        buildingManager.m_buildings.m_buffer[index].m_problems = newProblems;
                        buildingManager.UpdateNotifications(index, oldProblems, newProblems);
                    }
                }
                data.m_problems = Notification.RemoveProblems(data.m_problems, Notification.Problem.WaterNotConnected);
            }
            float minX = data.m_position.x - 100f;
            float maxX = data.m_position.x + 100f;
            float minZ = data.m_position.z - 100f;
            float maxZ = data.m_position.z + 100f;

            Singleton <WaterManager> .instance.UpdateGrid(minX, minZ, maxX, maxZ);
        }
        static void Postfix(ref NetNode data)
        {
            if (data.CountSegments() != 2)
            {
                return;
            }

            ushort   nodeID   = NetUtil.GetID(data);
            NodeData nodeData = NodeManager.Instance.buffer[nodeID];

            if (nodeData == null)
            {
                return;
            }

            if (nodeData.FirstTimeTrafficLight && TrafficLightManager.Instance.CanEnableTrafficLight(nodeID, ref data, out var res))
            {
                TrafficLightManager.Instance.SetTrafficLight(nodeID, true, ref data);
                nodeData.FirstTimeTrafficLight = false;
            }
            else if (nodeData.CanHaveTrafficLights(out _) == TernaryBool.False)
            {
                data.m_flags &= ~NetNode.Flags.TrafficLights;
            }
        }
Пример #6
0
        public void AfterRenderInstanceImpl(RenderManager.CameraInfo cameraInfo, ushort nodeID, ref NetNode data)
        {
            if (Data.DescriptorRulesOrder == null)
            {
                Data.DescriptorRulesOrder = new BoardInstanceRoadNodeXml[0];
            }
            if (Data.DescriptorRulesOrder.Length == 0 || m_lastFrameUpdate[nodeID] == m_getCurrentFrame(RenderManager.instance))
            {
                return;
            }

            m_lastFrameUpdate[nodeID] = m_getCurrentFrame(RenderManager.instance);
            if (data.CountSegments() < 2)
            {
                return;
            }
            if (m_updatedStreetPositions[nodeID] == null)
            {
                LogUtils.DoLog($"m_updatedStreetPositions[{nodeID}] == null!");
                m_updatedStreetPositions[nodeID] = false;
                WriteTheSignsMod.Controller.StartCoroutine(CalculateSigns(nodeID));
            }

            RenderNodeSigns(cameraInfo, nodeID);
        }
Пример #7
0
        /* If node distance is too short, we travel one segment up from the border node and set the new node as the one to connect to */
        private static void RepairShortSegment(ref Vector3 direction, ref ushort node)
        {
            //Debug.Log("Repairing short segment...");

            NetNode netNode = NetAccess.GetNode(node);

            // If there is more than one segment we cannot safely delete it (we don't even know from which segment we should pick)
            if (netNode.CountSegments() != 1)
            {
                return;
            }

            ushort     segmentId  = NetAccess.GetFirstSegment(netNode);
            NetSegment netSegment = NetAccess.GetSegment(segmentId);

            if (node == netSegment.m_startNode)
            {
                direction = netSegment.m_endDirection;
                node      = netSegment.m_endNode;
            }
            else
            {
                direction = netSegment.m_startDirection;
                node      = netSegment.m_startNode;
            }

            NetAccess.ReleaseSegment(segmentId, true);
        }
Пример #8
0
        public static Actions.AutoAction PopulateRoadConnection()
        {
            List <NetNode> openNodes = new List <NetNode>();

            for (ushort i = 1; i < NetManager.instance.m_nodes.m_size; i++)
            {
                NetNode val       = NetManager.instance.m_nodes.m_buffer[i];
                int     areaIndex = GameAreaManager.instance.GetAreaIndex(val.m_position);
                GameAreaManager.instance.GetTileXZ(areaIndex, out int areaX, out int areaZ);
                if (GameAreaManager.instance.IsUnlocked(areaX, areaZ))
                {
                    if (val.CountSegments() == 1)
                    {
                        openNodes.Add(val);
                        if (openNodes.Count == 2)
                        {
                            break;
                        }
                    }
                }
            }
            var action = new Actions.ConnectTwoNodes(openNodes[0], openNodes[1]);

            Debug.Log(openNodes[0]);
            Debug.Log(openNodes[1]);
            return(action);
        }
 public override void UpdateNode(ushort nodeID, ref NetNode data)
 {
     if ((data.m_flags & NetNode.Flags.Untouchable) != NetNode.Flags.None)
     {
         ushort index = NetNode.FindOwnerBuilding(nodeID, 32f);
         if (index != 0)
         {
             BuildingManager buildingManager = Singleton<BuildingManager>.instance;
             Notification.Problem oldProblems = buildingManager.m_buildings.m_buffer[index].m_problems;
             Notification.Problem newProblems;
             if (data.CountSegments() != 0)
             {
                 newProblems = Notification.RemoveProblems(oldProblems, Notification.Problem.WaterNotConnected);
             }
             else
             {
                 newProblems = Notification.AddProblems(oldProblems, Notification.Problem.WaterNotConnected);
             }
             if (newProblems != oldProblems)
             {
                 buildingManager.m_buildings.m_buffer[index].m_problems = newProblems;
                 buildingManager.UpdateNotifications(index, oldProblems, newProblems);
             }
         }
         data.m_problems = Notification.RemoveProblems(data.m_problems, Notification.Problem.WaterNotConnected);
     }
     float minX = data.m_position.x - 100f;
     float maxX = data.m_position.x + 100f;
     float minZ = data.m_position.z - 100f;
     float maxZ = data.m_position.z + 100f;
     Singleton<WaterManager>.instance.UpdateGrid(minX, minZ, maxX, maxZ);
 }
        public static List<NetSegment> GetNodeSegments(NetNode node)
        {
            var list = new List<NetSegment>(node.CountSegments());

            GetNodeSegments(node, list);

            return list;
        }
Пример #11
0
        public static List <NetSegment> GetNodeSegments(NetNode node)
        {
            var list = new List <NetSegment>(node.CountSegments());

            GetNodeSegments(node, list);

            return(list);
        }
        /* Sometimes it happens that we split the road too close to another segment. If that occur, the roads do glitch. In that case
         * we remove one more segment up the road. This method is still glitchy, would need improvement. */
        /* intersection - node outside the ellipse */
        private bool nextSegmentInfo(ushort intersection, ushort closeSegmentId, out ushort outerNodeId, out Vector3 directions)
        {
            NetSegment closeSegment = NetAccess.Segment(closeSegmentId);

            outerNodeId = 0;
            directions  = new Vector3(0, 0, 0);
            NetNode node         = NetAccess.Node(intersection);
            int     segmentcount = node.CountSegments();

            /* If there is an intersection right behind the ellipse, we can't go on as we can merge only segments which are in fact
             * only one road without an intersection. */
            if (segmentcount != 2)
            {
                //Debug.Log("Ambiguous node.");
                return(false);
            }

            /*string debugString = "Close segment id: " + closeSegmentId + "; ";
             * for(int i = 0; i < 8; i++)
             * {
             *  debugString += node.GetSegment(i) + ", ";
             * }
             * Debug.Log(debugString);*/
            ushort nextSegmentId = node.GetSegment(0);

            /* We need the segment that goes away from the ellipse, not the one we already have. */
            if (closeSegmentId == nextSegmentId)
            {
                //Debug.Log("Taking the other of the two segments. " + node.GetSegment(1));
                nextSegmentId = node.GetSegment(1);
                if (nextSegmentId == 0)
                {
                    return(false);
                }
            }
            NetSegment nextSegment = NetAccess.Segment(nextSegmentId);

            nextSegment = NetAccess.Segment(nextSegmentId);
            outerNodeId = nextSegment.m_startNode;
            directions  = nextSegment.m_startDirection;
            /* We need the node further away */
            if (outerNodeId == intersection)
            {
                //Debug.Log("Taking the other of the nodes.");
                outerNodeId = nextSegment.m_endNode;
                directions  = nextSegment.m_endDirection;
                if (outerNodeId == 0)
                {
                    return(false);
                }
            }

            /* After merging the roads, we release the segment and intersection inbetween. When I was debugging this method, I tried to release them after
             * everything is done. It might not be necessary.*/
            ToBeReleasedSegments.Add(nextSegmentId);
            ToBeReleasedNodes.Add(intersection);
            return(true);
        }
Пример #13
0
        static void Postfix(ref NetNode data)
        {
            if (data.CountSegments() != 2)
            {
                return;
            }

            ushort nodeID = NetUtil.GetID(data);

            ref NetNodeExt netNodeExt = ref NetworkExtensionManager.Instance.NodeBuffer[nodeID];
Пример #14
0
 private bool CheckForSegmentConnectionThroughNode(ushort seg, NetNode node)
 {
     for (int i = 0; i < node.CountSegments(); i++)
     {
         if (node.GetSegment(i) == seg)
         {
             return(true);
         }
     }
     return(false);
 }
Пример #15
0
        void AddNodeConnections(NetNode node)
        {
            LaneChangerPathManager pathManager = (LaneChangerPathManager)PathManager.instance;

            for (int i = 0; i < node.CountSegments(); i++)
            {
                ushort seg = node.GetSegment(i);
                if (seg != this.segmentId)
                {
                    pathManager.laneChangerSegments[segmentId].AddPermittedConnection(seg);
                }
            }
        }
        bool IsSuitableJunction()
        {
            if (HoveredNodeId == 0)
            {
                return(false);
            }
            NetNode node = HoveredNodeId.ToNode();

            if (node.CountSegments() < 2)
            {
                return(false);
            }

            return(true);
        }
Пример #17
0
        bool IsSuitableJunction()
        {
            if (HoveredNodeId == 0)
            {
                return(false);
            }
            NetNode node = HoveredNodeId.ToNode();

            if (node.CountSegments() < 3)
            {
                return(false);
            }

            if (!node.Info.CanConnectPath())
            {
                return(false);
            }

            return(true);
        }
 public static void Postfix(ref NetNode __instance)
 {
     if (OptionUI.noJunction)
     {
         NetInfo asset = __instance.Info;
         if (asset != null)
         {
             if (asset.m_netAI is RoadAI)
             {
                 if (CSURUtil.IsCSURNoJunction(asset))
                 {
                     if (__instance.CountSegments() == 2)
                     {
                         __instance.m_flags &= ~NetNode.Flags.Junction;
                     }
                 }
             }
         }
     }
 }
Пример #19
0
        void RenderOverlayForSegments(RenderManager.CameraInfo cameraInfo, NetNode node)
        {
            LaneChangerPathManager pathManager = (LaneChangerPathManager)PathManager.instance;

            for (int i = 0; i < node.CountSegments(); i++)
            {
                ushort segId = node.GetSegment(i);
                if (segId != currentSegment)
                {
                    if (pathManager.laneChangerSegments[currentSegment].PermittedConnectionTo(segId))
                    {
                        NetTool.RenderOverlay(RenderManager.instance.CurrentCameraInfo, ref NetManager.instance.m_segments.m_buffer[segId], new Color(0.0f, 1f, 0.0f, 0.6f), new Color(0f, 1f, 0f, 0.9f));
                    }
                    else
                    {
                        NetTool.RenderOverlay(RenderManager.instance.CurrentCameraInfo, ref NetManager.instance.m_segments.m_buffer[segId], new Color(1f, 0.0f, 0.0f, 0.6f), new Color(1f, 0f, 0f, 0.9f));
                    }
                }
            }
        }
Пример #20
0
        void SetSegments(ushort segmentId, ushort infoIndex, ref Segment previousSeg)
        {
            NetSegment segment = NetManager.instance.m_segments.m_buffer[segmentId];

            if (segment.m_infoIndex != infoIndex || m_segments.ContainsKey(segmentId))
            {
                return;
            }

            Segment seg = default(Segment);

            seg.m_segmentId = segmentId;

            NetSegment previousSegment = NetManager.instance.m_segments.m_buffer[previousSeg.m_segmentId];
            ushort     nextNode;

            if ((segment.m_startNode == previousSegment.m_endNode) ||
                (segment.m_startNode == previousSegment.m_startNode))
            {
                nextNode         = segment.m_endNode;
                seg.m_targetNode = segment.m_startNode == previousSeg.m_targetNode
                    ? segment.m_endNode
                    : segment.m_startNode;
            }
            else
            {
                nextNode         = segment.m_startNode;
                seg.m_targetNode = segment.m_endNode == previousSeg.m_targetNode
                    ? segment.m_startNode
                    : segment.m_endNode;
            }

            m_segments[segmentId] = seg;

            NetNode node = NetManager.instance.m_nodes.m_buffer[nextNode];

            if (node.CountSegments() == 2)
            {
                SetSegments(node.m_segment0 == segmentId ? node.m_segment1 : node.m_segment0, infoIndex, ref seg);
            }
        }
Пример #21
0
        /* If node distance is too short, we travel one segment up from the border node and set the new node as the one to connect to */
        private void RepairShortSegment(ref Vector3 direction, ref ushort node)
        {
            //Debug.Log("Repairing short segment...");

            NetNode netNode = NetUtil.Node(node);

            // If there is more than one segment we cannot safely delete it (we don't even know from which segment we should pick)
            if (netNode.CountSegments() != 1)
            {
                return;
            }

            ushort     segmentId  = NetUtil.GetFirstSegment(netNode);
            NetSegment netSegment = NetUtil.Segment(segmentId);

            WrappedNode nodeW = _networkDictionary.RegisterNode(node);

            if (node == netSegment.m_startNode)
            {
                direction = netSegment.m_endDirection;
                node      = netSegment.m_endNode;
            }
            else
            {
                direction = netSegment.m_startDirection;
                node      = netSegment.m_startNode;
            }

            WrappedSegment segmentW = _networkDictionary.RegisterSegment(segmentId);

            _actionGroup.Actions.Add(segmentW);
            _actionGroup.Actions.Add(nodeW);
            segmentW.Release();
            nodeW.Release();
            segmentW.IsBuildAction = false;
            nodeW.IsBuildAction    = false;

            //NetUtil.ReleaseSegment(segmentId, true);
        }
Пример #22
0
        public static bool RayCastNodeMasked(ref NetNode node, Segment3 ray, float snapElevation, bool bothSides, out float t, out float priority)
        {
            bool lht = false;
            //if (SimulationManager.instance.m_metaData.m_invertTraffic == SimulationMetaData.MetaBool.True) lht = true;
            NetInfo info = node.Info;
            float   num  = (float)node.m_elevation + info.m_netAI.GetSnapElevation();
            float   t2;

            if (info.m_netAI.IsUnderground())
            {
                t2 = Mathf.Clamp01(Mathf.Abs(snapElevation + num) / 12f);
            }
            else
            {
                t2 = Mathf.Clamp01(Mathf.Abs(snapElevation - num) / 12f);
            }
            float collisionHalfWidth = Mathf.Max(3f, info.m_halfWidth);
            float maskHalfWidth      = Mathf.Min(collisionHalfWidth - 1.5f, info.m_pavementWidth);
            float num2      = Mathf.Lerp(info.GetMinNodeDistance(), collisionHalfWidth, t2);
            float num2m     = Mathf.Lerp(info.GetMinNodeDistance(), maskHalfWidth, t2);
            float num2delta = Mathf.Lerp(info.GetMinNodeDistance(), collisionHalfWidth - maskHalfWidth, t2);

            if (node.CountSegments() != 0)
            {
                NetManager instance  = Singleton <NetManager> .instance;
                NetSegment mysegment = CSURUtil.GetSameInfoSegment(node);
                Vector3    direction = CSURUtil.CheckNodeEq(mysegment.m_startNode, node) ? mysegment.m_startDirection : -mysegment.m_endDirection;
                //Debug.Log(direction);
                if ((mysegment.m_flags & NetSegment.Flags.Invert) != 0)
                {
                    lht = true;
                }
                // normal to the right hand side
                Vector3 normal         = new Vector3(direction.z, 0, -direction.x).normalized;
                Vector3 trueNodeCenter = node.m_position + (lht ? -collisionHalfWidth : collisionHalfWidth) * normal;
                //Debug.Log($"num2: {num2}, num2m: {num2m}");
                //Debug.Log($"node: {node.m_position}, center: {trueNodeCenter}");
                if (Segment1.Intersect(ray.a.y, ray.b.y, node.m_position.y, out t))
                {
                    float num3 = Vector3.Distance(ray.Position(t), trueNodeCenter);
                    if (num3 < num2delta)
                    {
                        priority = Mathf.Max(0f, num3 - collisionHalfWidth);
                        return(true);
                    }
                }
            }
            else
            {
                if (Segment1.Intersect(ray.a.y, ray.b.y, node.m_position.y, out t))
                {
                    float num3 = Vector3.Distance(ray.Position(t), node.m_position);
                    if (num3 < num2)
                    {
                        priority = Mathf.Max(0f, num3 - collisionHalfWidth);
                        return(true);
                    }
                }
            }
            t        = 0f;
            priority = 0f;
            return(false);
        }
        private static List <NodeLaneMarker> GetNodeMarkers(ushort nodeId, ref NetNode node)
        {
            if (nodeId == 0)
            {
                return(null);
            }

            if ((node.m_flags & NetNode.Flags.Created) == NetNode.Flags.None)
            {
                return(null);
            }

            List <NodeLaneMarker> nodeMarkers = new List <NodeLaneMarker>();
            int nodeMarkerColorIndex          = 0;
            LaneConnectionManager connManager = LaneConnectionManager.Instance;

            int offsetMultiplier = node.CountSegments() <= 2 ? 3 : 1;

            for (int i = 0; i < 8; i++)
            {
                ushort segmentId = node.GetSegment(i);

                if (segmentId == 0)
                {
                    continue;
                }

                NetSegment[] segmentsBuffer = NetManager.instance.m_segments.m_buffer;
                bool         startNode      = segmentsBuffer[segmentId].m_startNode == nodeId;
                Vector3      offset         = segmentsBuffer[segmentId]
                                              .FindDirection(segmentId, nodeId) * offsetMultiplier;
                NetInfo.Lane[] lanes  = segmentsBuffer[segmentId].Info.m_lanes;
                uint           laneId = segmentsBuffer[segmentId].m_lanes;

                for (byte laneIndex = 0; (laneIndex < lanes.Length) && (laneId != 0); laneIndex++)
                {
                    NetInfo.Lane laneInfo = lanes[laneIndex];

                    if (((laneInfo.m_laneType & LaneConnectionManager.LANE_TYPES) != NetInfo.LaneType.None) &&
                        ((laneInfo.m_vehicleType & LaneConnectionManager.VEHICLE_TYPES)
                         != VehicleInfo.VehicleType.None))
                    {
                        if (connManager.GetLaneEndPoint(
                                segmentId,
                                startNode,
                                laneIndex,
                                laneId,
                                laneInfo,
                                out bool isSource,
                                out bool isTarget,
                                out Vector3? pos))
                        {
                            pos = pos.Value + offset;

                            float terrainY =
                                Singleton <TerrainManager> .instance.SampleDetailHeightSmooth(pos.Value);

                            var finalPos = new Vector3(pos.Value.x, terrainY, pos.Value.z);

                            Color32 nodeMarkerColor
                                = isSource
                                      ? COLOR_CHOICES[nodeMarkerColorIndex % COLOR_CHOICES.Length]
                                      : default; // or black (not used while rendering)

                            NetLane lane   = NetManager.instance.m_lanes.m_buffer[laneId];
                            Bezier3 bezier = lane.m_bezier;
                            if (startNode)
                            {
                                bezier.a = (Vector3)pos;
                            }
                            else
                            {
                                bezier.d = (Vector3)pos;
                            }
                            SegmentLaneMarker segmentLaneMarker = new SegmentLaneMarker {
                                renderBezier  = bezier,
                                raycastBezier = bezier,
                                laneID        = laneId,
                                laneIndex     = laneIndex,
                            };

                            nodeMarkers.Add(
                                new NodeLaneMarker {
                                SegmentId             = segmentId,
                                LaneId                = laneId,
                                NodeId                = nodeId,
                                StartNode             = startNode,
                                Position              = finalPos,
                                SecondaryPosition     = (Vector3)pos,
                                Color                 = nodeMarkerColor,
                                IsSource              = isSource,
                                IsTarget              = isTarget,
                                LaneType              = laneInfo.m_laneType,
                                VehicleType           = laneInfo.m_vehicleType,
                                InnerSimilarLaneIndex =
                                    ((byte)(laneInfo.m_direction & NetInfo.Direction.Forward) != 0)
                                            ? laneInfo.m_similarLaneIndex
                                            : laneInfo.m_similarLaneCount -
                                    laneInfo.m_similarLaneIndex - 1,
                                SegmentIndex      = i,
                                segmentLaneMarker = segmentLaneMarker,
                            });

                            if (isSource)
                            {
                                nodeMarkerColorIndex++;
                            }
                        }
                    }

                    laneId = NetManager.instance.m_lanes.m_buffer[laneId].m_nextLane;
                }
            }

            if (nodeMarkers.Count == 0)
            {
                return(null);
            }

            foreach (NodeLaneMarker laneMarker1 in nodeMarkers)
            {
                if (!laneMarker1.IsSource)
                {
                    continue;
                }

                uint[] connections =
                    LaneConnectionManager.Instance.GetLaneConnections(
                        laneMarker1.LaneId,
                        laneMarker1.StartNode);

                if ((connections == null) || (connections.Length == 0))
                {
                    continue;
                }

                foreach (NodeLaneMarker laneMarker2 in nodeMarkers)
                {
                    if (!laneMarker2.IsTarget)
                    {
                        continue;
                    }

                    if (connections.Contains(laneMarker2.LaneId))
                    {
                        laneMarker1.ConnectedMarkers.Add(laneMarker2);
                    }
                }
            }

            return(nodeMarkers);
        }
        private List <NodeLaneMarker> GetNodeMarkers(ushort nodeId, ref NetNode node)
        {
            if (nodeId == 0)
            {
                return(null);
            }
            if ((node.m_flags & NetNode.Flags.Created) == NetNode.Flags.None)
            {
                return(null);
            }

            List <NodeLaneMarker> nodeMarkers = new List <NodeLaneMarker>();
            LaneConnectionManager connManager = LaneConnectionManager.Instance;

            int offsetMultiplier = node.CountSegments() <= 2 ? 3 : 1;

            for (int i = 0; i < 8; i++)
            {
                ushort segmentId = node.GetSegment(i);
                if (segmentId == 0)
                {
                    continue;
                }

                bool           isEndNode = NetManager.instance.m_segments.m_buffer[segmentId].m_endNode == nodeId;
                Vector3        offset    = NetManager.instance.m_segments.m_buffer[segmentId].FindDirection(segmentId, nodeId) * offsetMultiplier;
                NetInfo.Lane[] lanes     = NetManager.instance.m_segments.m_buffer[segmentId].Info.m_lanes;
                uint           laneId    = NetManager.instance.m_segments.m_buffer[segmentId].m_lanes;
                for (byte laneIndex = 0; laneIndex < lanes.Length && laneId != 0; laneIndex++)
                {
                    NetInfo.Lane laneInfo = lanes[laneIndex];
                    if ((laneInfo.m_laneType & LaneConnectionManager.LANE_TYPES) != NetInfo.LaneType.None &&
                        (laneInfo.m_vehicleType & LaneConnectionManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None)
                    {
                        Vector3?pos      = null;
                        bool    isSource = false;
                        bool    isTarget = false;
                        if (connManager.GetLaneEndPoint(segmentId, !isEndNode, laneIndex, laneId, laneInfo, out isSource, out isTarget, out pos))
                        {
                            pos = (Vector3)pos + offset;
                            float terrainY = Singleton <TerrainManager> .instance.SampleDetailHeightSmooth(((Vector3)pos));

                            Vector3 finalPos = new Vector3(((Vector3)pos).x, terrainY, ((Vector3)pos).z);

                            nodeMarkers.Add(new NodeLaneMarker()
                            {
                                segmentId             = segmentId,
                                laneId                = laneId,
                                nodeId                = nodeId,
                                startNode             = !isEndNode,
                                position              = finalPos,
                                secondaryPosition     = (Vector3)pos,
                                color                 = colors[nodeMarkers.Count % colors.Length],
                                isSource              = isSource,
                                isTarget              = isTarget,
                                laneType              = laneInfo.m_laneType,
                                vehicleType           = laneInfo.m_vehicleType,
                                innerSimilarLaneIndex = ((byte)(laneInfo.m_direction & NetInfo.Direction.Forward) != 0) ? laneInfo.m_similarLaneIndex : laneInfo.m_similarLaneCount - laneInfo.m_similarLaneIndex - 1,
                                segmentIndex          = i
                            });
                        }
                    }

                    laneId = NetManager.instance.m_lanes.m_buffer[laneId].m_nextLane;
                }
            }

            if (nodeMarkers.Count == 0)
            {
                return(null);
            }

            foreach (NodeLaneMarker laneMarker1 in nodeMarkers)
            {
                if (!laneMarker1.isSource)
                {
                    continue;
                }

                uint[] connections = LaneConnectionManager.Instance.GetLaneConnections(laneMarker1.laneId, laneMarker1.startNode);
                if (connections == null || connections.Length == 0)
                {
                    continue;
                }

                foreach (NodeLaneMarker laneMarker2 in nodeMarkers)
                {
                    if (!laneMarker2.isTarget)
                    {
                        continue;
                    }

                    if (connections.Contains(laneMarker2.laneId))
                    {
                        laneMarker1.connectedMarkers.Add(laneMarker2);
                    }
                }
            }

            return(nodeMarkers);
        }
        public bool GetDefaultEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode, ref NetNode node)
        {
#if DEBUG
            bool debug = GlobalConfig.Instance.Debug.Switches[11];
#endif

            SegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(startNode);

            if (endGeo == null)
            {
                Log.Warning($"JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed({segmentId}, {startNode}): Could not get segment end geometry");
                return(false);
            }

            if (!IsEnteringBlockedJunctionAllowedConfigurable(segmentId, startNode, ref node))
            {
                bool res = (node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) != NetNode.Flags.Junction || node.CountSegments() == 2;
#if DEBUG
                if (debug)
                {
                    Log._Debug($"JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed({segmentId}, {startNode}): Setting is not configurable. res={res}, flags={node.m_flags}, node.CountSegments()={node.CountSegments()}");
                }
#endif
                return(res);
            }

            bool ret;
            if (Options.allowEnterBlockedJunctions)
            {
                ret = true;
            }
            else
            {
                int numOutgoing = 0;
                int numIncoming = 0;
                node.CountLanes(endGeo.NodeId(), 0, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, true, ref numOutgoing, ref numIncoming);
                ret = numOutgoing == 1 || numIncoming == 1;
            }

#if DEBUG
            if (debug)
            {
                Log._Debug($"JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed({segmentId}, {startNode}): Setting is configurable. ret={ret}");
            }
#endif

            return(ret);
        }
Пример #26
0
        // Token: 0x06000025 RID: 37 RVA: 0x00002E08 File Offset: 0x00001008
        public bool DissolveNode(ushort nodeID)
        {
            NetNode node = this.GetNode(nodeID);
            bool    result;

            if (node.CountSegments() != 2)
            {
                result = this.ThrowError("To dissolve a node, you must have exactly two connecting segments.");
            }
            else
            {
                ushort num  = 0;
                ushort num2 = 0;
                for (int i = 0; i < 8; i++)
                {
                    if (node.GetSegment(i) > 0)
                    {
                        if (num != 0)
                        {
                            num2 = node.GetSegment(i);
                            break;
                        }
                        num = node.GetSegment(i);
                    }
                }
                if (num != 0 && num2 != 0)
                {
                    NetSegment segment;
                    NetSegment segment2;
                    try
                    {
                        segment  = this.GetSegment(num);
                        segment2 = this.GetSegment(num2);
                    }
                    catch (Exception)
                    {
                        return(true);
                    }
                    ushort  num3    = (segment.m_startNode == nodeID) ? segment.m_endNode : segment.m_startNode;
                    Vector3 vector  = (segment.m_startNode == nodeID) ? segment.m_endDirection : segment.m_startDirection;
                    ushort  num4    = (segment2.m_startNode == nodeID) ? segment2.m_endNode : segment2.m_startNode;
                    Vector3 vector2 = (segment2.m_startNode == nodeID) ? segment2.m_endDirection : segment2.m_startDirection;

                    //Here is the fix:
                    bool   flag  = num3 == segment.m_startNode;
                    bool   flag2 = (segment.m_flags & NetSegment.Flags.Invert) > NetSegment.Flags.None;
                    ushort num5;
                    if (flag ^ flag2)
                    {
                        this.Manager.CreateSegment(out num5, ref Singleton <SimulationManager> .instance.m_randomizer, segment.Info, num3, num4, vector, vector2, Singleton <SimulationManager> .instance.m_currentBuildIndex, Singleton <SimulationManager> .instance.m_currentBuildIndex, false);
                    }
                    else
                    {
                        this.Manager.CreateSegment(out num5, ref Singleton <SimulationManager> .instance.m_randomizer, segment.Info, num4, num3, vector2, vector, Singleton <SimulationManager> .instance.m_currentBuildIndex, Singleton <SimulationManager> .instance.m_currentBuildIndex, false);
                    }
                    //end of fix

                    Singleton <SimulationManager> .instance.m_currentBuildIndex += 1u;
                    this.NetworkSkinsFixNewPrefab(node.GetSegment(0), num5, segment.Info);
                    this.Manager.ReleaseNode(nodeID);
                    return(num5 > 0);
                }
                result = this.ThrowError("Invalid segment detected.");
            }
            return(result);
        }
        /* Sometimes it happens that we split the road too close to another segment. If that occur, the roads do glitch. In that case
         * we remove one more segment up the road. This method is still glitchy, would need improvement. */
        /* intersection - node outside the ellipse */
        private bool nextSegmentInfo(WrappedSegment closeSegmentW, ref WrappedNode outerNodeW, ref Vector3 endDirection)
        {
            NetSegment closeSegment = closeSegmentW.Get;
            //outerNodeId = 0;
            //directions = new Vector3(0,0,0);
            NetNode node         = outerNodeW.Get;
            int     segmentcount = node.CountSegments();

            /* If there is an intersection right behind the ellipse, we can't go on as we can merge only segments which are in fact
             * only one road without an intersection. */
            if (segmentcount != 2)
            {
                //Debug.Log("Ambiguous node.");
                return(false);
            }

            /*string debugString = "Close segment id: " + closeSegmentId + "; ";
             * for(int i = 0; i < 8; i++)
             * {
             *  debugString += node.GetSegment(i) + ", ";
             * }
             * Debug.Log(debugString);*/
            ushort nextSegmentId = NetUtil.GetNonzeroSegment(node, 0);

            /* We need the segment that goes away from the ellipse, not the one we already have. */
            if (closeSegmentW.Id == nextSegmentId)
            {
                //Debug.Log("Taking the other of the two segments. " + node.GetSegment(1));
                nextSegmentId = NetUtil.GetNonzeroSegment(node, 1);
                if (nextSegmentId == 0)
                {
                    return(false);
                }
            }
            NetSegment nextSegment = NetUtil.Segment(nextSegmentId);

            ushort  outerNodeId = nextSegment.m_startNode;
            Vector3 directions  = nextSegment.m_startDirection;

            /* We need the node further away */
            if (outerNodeId == outerNodeW.Id)
            {
                //Debug.Log("Taking the other of the nodes.");
                outerNodeId = nextSegment.m_endNode;
                directions  = nextSegment.m_endDirection;
                if (outerNodeId == 0)
                {
                    return(false);
                }
            }

            WrappedSegment nextSegmentW = networkDictionary.RegisterSegment(nextSegmentId);

            // Release old
            ToBeReleasedNodes.Add(outerNodeW);
            ToBeReleasedSegments.Add(nextSegmentW);

            // Return values
            outerNodeW   = networkDictionary.RegisterNode(outerNodeId);
            endDirection = directions;

            /* After merging the roads, we release the segment and intersection inbetween. When I was debugging this method, I tried to release them after
             * everything is done. It might not be necessary.*/
            return(true);
        }
		// be aware:
		//   (1) path-finding works from target to start. the "next" segment is always the previous and the "previous" segment is always the next segment on the path!
		//   (2) when I use the term "lane index from right" this holds for right-hand traffic systems. On maps where you activate left-hand traffic, the "lane index from right" values represent lane indices starting from the left side.

		// 1
		private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, ref NetNode nextNode, byte connectOffset, bool isMiddle) {
			//mCurrentState = 0;
#if DEBUGPF
			//bool debug = Options.disableSomething1 && item.m_position.m_segment == 1459 && nextNodeId == 19630;
			//bool debug = Options.disableSomething1 && (item.m_position.m_segment == 3833 || item.m_position.m_segment == 9649);
			bool debug = Options.disableSomething1;
#endif
#if DEBUGPF
			/*if (m_queuedPathFindCount > 100 && Options.disableSomething1)
				Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: processItemMain RUNNING! item: {item.m_position.m_segment}, {item.m_position.m_lane} nextNodeId: {nextNodeId}");*/
#endif
			//Log.Message($"THREAD #{Thread.CurrentThread.ManagedThreadId} Path finder: " + this._pathFindIndex + " vehicle types: " + this._vehicleTypes);
#if DEBUGPF
			//bool debug = isTransportVehicle && isMiddle && item.m_position.m_segment == 13550;
			List<String> logBuf = null;
			if (debug)
				logBuf = new List<String>();
			//bool debug = nextNodeId == 12732;
#else
			bool debug = false;
#endif
			//mCurrentState = 1;
			NetManager instance = Singleton<NetManager>.instance;
			bool isPedestrianLane = false;
			bool isBicycleLane = false;
			bool isCenterPlatform = false;
			int similarLaneIndexFromLeft = 0; // similar index, starting with 0 at leftmost lane
			NetInfo prevSegmentInfo = instance.m_segments.m_buffer[(int)item.m_position.m_segment].Info;
			int prevSimiliarLaneCount = 0;
			if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
				NetInfo.Lane prevLane = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
				isPedestrianLane = (prevLane.m_laneType == NetInfo.LaneType.Pedestrian);
				isBicycleLane = (prevLane.m_laneType == NetInfo.LaneType.Vehicle && prevLane.m_vehicleType == VehicleInfo.VehicleType.Bicycle);
				isCenterPlatform = prevLane.m_centerPlatform;
				prevSimiliarLaneCount = prevLane.m_similarLaneCount;
				if ((byte)(prevLane.m_finalDirection & NetInfo.Direction.Forward) != 0) {
					similarLaneIndexFromLeft = prevLane.m_similarLaneIndex;
				} else {
					similarLaneIndexFromLeft = prevLane.m_similarLaneCount - prevLane.m_similarLaneIndex - 1;
				}
				//debug = Options.disableSomething1 && (prevLane.m_vehicleType & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None;
			}
			int firstSimilarLaneIndexFromLeft = similarLaneIndexFromLeft;
			//mCurrentState = 2;
			ushort prevSegmentId = item.m_position.m_segment;
			if (isMiddle) {
				//mCurrentState = 3;
				for (int i = 0; i < 8; i++) {
					ushort nextSegmentId = nextNode.GetSegment(i);
					if (nextSegmentId <= 0)
						continue;
					this.ProcessItemCosts(false, debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, !isPedestrianLane, isPedestrianLane);
				}
				//mCurrentState = 4;
			} else if (isPedestrianLane) {
				//mCurrentState = 5;
				int prevLaneIndex = (int)item.m_position.m_lane;
				if (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {
					bool flag4 = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
					bool flag5 = isCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;
					ushort num2 = prevSegmentId;
					ushort num3 = prevSegmentId;
					int laneIndex;
					int laneIndex2;
					uint leftLaneId;
					uint rightLaneId;
					instance.m_segments.m_buffer[(int)prevSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, flag5, out laneIndex, out laneIndex2, out leftLaneId, out rightLaneId);
					if (leftLaneId == 0u || rightLaneId == 0u) {
						ushort leftSegment;
						ushort rightSegment;
						instance.m_segments.m_buffer[(int)prevSegmentId].GetLeftAndRightSegments(nextNodeId, out leftSegment, out rightSegment);
						int num6 = 0;
						//mCurrentState = 6;
						while (leftSegment != 0 && leftSegment != prevSegmentId && leftLaneId == 0u) {
							int num7;
							int num8;
							uint num9;
							uint num10;
							instance.m_segments.m_buffer[(int)leftSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, flag5, out num7, out num8, out num9, out num10);
							if (num10 != 0u) {
								num2 = leftSegment;
								laneIndex = num8;
								leftLaneId = num10;
							} else {
								leftSegment = instance.m_segments.m_buffer[(int)leftSegment].GetLeftSegment(nextNodeId);
							}
							if (++num6 == 8) {
								break;
							}
						}
						//mCurrentState = 7;
						num6 = 0;
						while (rightSegment != 0 && rightSegment != prevSegmentId && rightLaneId == 0u) {
							int num11;
							int num12;
							uint num13;
							uint num14;
							instance.m_segments.m_buffer[(int)rightSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, flag5, out num11, out num12, out num13, out num14);
							if (num13 != 0u) {
								num3 = rightSegment;
								laneIndex2 = num11;
								rightLaneId = num13;
							} else {
								rightSegment = instance.m_segments.m_buffer[(int)rightSegment].GetRightSegment(nextNodeId);
							}
							if (++num6 == 8) {
								break;
							}
						}
						//mCurrentState = 8;
					}
					if (leftLaneId != 0u && (num2 != prevSegmentId || flag4 || flag5)) {
						this.ProcessItemPedBicycle(item, nextNodeId, num2, ref instance.m_segments.m_buffer[(int)num2], connectOffset, laneIndex, leftLaneId); // ped
					}
					if (rightLaneId != 0u && rightLaneId != leftLaneId && (num3 != prevSegmentId || flag4 || flag5)) {
						this.ProcessItemPedBicycle(item, nextNodeId, num3, ref instance.m_segments.m_buffer[(int)num3], connectOffset, laneIndex2, rightLaneId); // ped
					}
					int laneIndex3;
					uint lane3;
					if ((this._vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None && instance.m_segments.m_buffer[(int)prevSegmentId].GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out laneIndex3, out lane3)) {
						this.ProcessItemPedBicycle(item, nextNodeId, prevSegmentId, ref instance.m_segments.m_buffer[(int)prevSegmentId], connectOffset, laneIndex3, lane3); // bicycle
					}
				} else {
					//mCurrentState = 9;
					for (int j = 0; j < 8; j++) {
						ushort segment3 = nextNode.GetSegment(j);
						if (segment3 != 0 && segment3 != prevSegmentId) {
							this.ProcessItemCosts(false, debug, item, nextNodeId, segment3, ref instance.m_segments.m_buffer[(int)segment3], ref similarLaneIndexFromLeft, connectOffset, false, true);
						}
					}
					//mCurrentState = 10;
				}
				//mCurrentState = 11;
				NetInfo.LaneType laneType = this._laneTypes & ~NetInfo.LaneType.Pedestrian;
				VehicleInfo.VehicleType vehicleType = this._vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;
				if ((byte)(item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
					laneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
				}
				int num15;
				uint lane4;
				if (laneType != NetInfo.LaneType.None && vehicleType != VehicleInfo.VehicleType.None && instance.m_segments.m_buffer[(int)prevSegmentId].GetClosestLane(prevLaneIndex, laneType, vehicleType, out num15, out lane4)) {
					NetInfo.Lane lane5 = prevSegmentInfo.m_lanes[num15];
					byte connectOffset2;
					if ((instance.m_segments.m_buffer[(int)prevSegmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((byte)(lane5.m_finalDirection & NetInfo.Direction.Backward) != 0)) {
						connectOffset2 = 1;
					} else {
						connectOffset2 = 254;
					}
					//mCurrentState = 12;
					this.ProcessItemPedBicycle(item, nextNodeId, prevSegmentId, ref instance.m_segments.m_buffer[(int)prevSegmentId], connectOffset2, num15, lane4); // ped
					//mCurrentState = 13;
				}
			} else {
				//mCurrentState = 14;
				bool mayTurnAround = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
				bool pedestrianAllowed = (byte)(this._laneTypes & NetInfo.LaneType.Pedestrian) != 0;
				bool enablePedestrian = false;
				byte connectOffset3 = 0;
				if (pedestrianAllowed) {
					if (isBicycleLane) {
						connectOffset3 = connectOffset;
						enablePedestrian = (nextNode.Info.m_class.m_service == ItemClass.Service.Beautification);
					} else if (this._vehicleLane != 0u) {
						if (this._vehicleLane != item.m_laneID) {
							pedestrianAllowed = false;
						} else {
							connectOffset3 = this._vehicleOffset;
						}
					} else if (this._stablePath) {
						connectOffset3 = 128;
					} else {
						connectOffset3 = (byte)this._pathRandomizer.UInt32(1u, 254u);
					}
				}
				//mCurrentState = 15;

				// NON-STOCK CODE START //
#if DEBUGPF
				if (debug)
					logBuf.Add($"Exploring path! Segment {item.m_position.m_segment} @ node {nextNodeId}: Preparation started");
#endif

				CustomPathManager pathManager = Singleton<CustomPathManager>.instance;
				bool nextIsJunction = (nextNode.m_flags & NetNode.Flags.Junction) != NetNode.Flags.None;
				bool nextIsRealJunction = nextNode.CountSegments() > 2;
				bool nextIsTransition = (nextNode.m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;
				bool prevIsHighway = false;
				if (prevSegmentInfo.m_netAI is RoadBaseAI)
					prevIsHighway = ((RoadBaseAI)prevSegmentInfo.m_netAI).m_highwayRules;
				//mCurrentState = 16;
				NetInfo.Direction normDirection = TrafficPriority.IsLeftHandDrive() ? NetInfo.Direction.Forward : NetInfo.Direction.Backward; // direction to normalize indices to
				int prevRightSimilarLaneIndex;
				int prevLeftSimilarLaneIndex;

				NetInfo.Lane lane = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
				if ((byte)(lane.m_direction & normDirection) != 0) {
					prevRightSimilarLaneIndex = lane.m_similarLaneIndex;
					prevLeftSimilarLaneIndex = lane.m_similarLaneCount - lane.m_similarLaneIndex - 1;
				} else {
					prevRightSimilarLaneIndex = lane.m_similarLaneCount - lane.m_similarLaneIndex - 1;
					prevLeftSimilarLaneIndex = lane.m_similarLaneIndex;
				}
				bool foundForced = false;
				int totalIncomingLanes = 0;
				int totalOutgoingLanes = 0;
				bool isStrictLaneArrowPolicyEnabled = IsLaneArrowChangerEnabled() && _extVehicleType != ExtVehicleType.Emergency && (nextIsJunction || nextIsTransition) && !(Options.allRelaxed || (Options.relaxedBusses && _transportVehicle)) && (this._vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None;
				//mCurrentState = 17;
				// geometries are validated here
#if DEBUGPF
				/*if (m_queuedPathFindCount > 100 && Options.disableSomething1)
					Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Getting segment geometry of {prevSegmentId} @ {nextNodeId} START");*/
#endif
				SegmentGeometry geometry = IsMasterPathFind ? CustomRoadAI.GetSegmentGeometry(prevSegmentId, nextNodeId) : CustomRoadAI.GetSegmentGeometry(prevSegmentId);
#if DEBUGPF
				/*if (m_queuedPathFindCount > 100 && Options.disableSomething1)
					Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Getting segment geometry of {prevSegmentId} @ {nextNodeId} END");*/
#endif
				//mCurrentState = 18;
				bool prevIsOutgoingOneWay = geometry.IsOutgoingOneWay(nextNodeId);

				//mCurrentState = 19;
#if DEBUGPF
				/*if (m_queuedPathFindCount > 100 && Options.disableSomething1)
					Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Verifying segment geometry of {prevSegmentId} @ {nextNodeId} START");*/
#endif
				bool nextAreOnlyOneWayHighways = true;
				for (int k = 0; k < 8; k++) {
					ushort nextSegId = instance.m_nodes.m_buffer[nextNodeId].GetSegment(k);
					if (nextSegId == 0 || nextSegId == prevSegmentId) {
						continue;
					}

					if (IsMasterPathFind) {
						geometry.VerifyConnectedSegment(nextSegId);
					}

					if (instance.m_segments.m_buffer[nextSegId].Info.m_netAI is RoadBaseAI) {
						if (!CustomRoadAI.GetSegmentGeometry(nextSegId).IsOneWay() || !((RoadBaseAI)instance.m_segments.m_buffer[nextSegId].Info.m_netAI).m_highwayRules) {
							nextAreOnlyOneWayHighways = false;
							break;
						}
					} else {
						nextAreOnlyOneWayHighways = false;
						break;
					}
				}
				//mCurrentState = 20;
				
#if DEBUGPF
				/*if (m_queuedPathFindCount > 100 && Options.disableSomething1)
					Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Verifying segment geometry of {prevSegmentId} @ {nextNodeId} END");*/
#endif

				ushort[] incomingStraightSegmentsArray = null;
				ushort[] incomingRightSegmentsArray = null;
				ushort[] incomingLeftSegmentsArray = null;
				bool startNode = instance.m_segments.m_buffer[(int)prevSegmentId].m_startNode == nextNodeId;
				if (isStrictLaneArrowPolicyEnabled) {
					if (startNode) {
						incomingStraightSegmentsArray = geometry.StartNodeIncomingStraightSegmentsArray;
						incomingLeftSegmentsArray = geometry.StartNodeIncomingLeftSegmentsArray;
						incomingRightSegmentsArray = geometry.StartNodeIncomingRightSegmentsArray;
					} else {
						incomingStraightSegmentsArray = geometry.EndNodeIncomingStraightSegmentsArray;
						incomingLeftSegmentsArray = geometry.EndNodeIncomingLeftSegmentsArray;
						incomingRightSegmentsArray = geometry.EndNodeIncomingRightSegmentsArray;
					}
				}

				//mCurrentState = 21
				bool explorePrevSegment = Flags.getUTurnAllowed(prevSegmentId, startNode) && !Options.isStockLaneChangerUsed() && nextIsJunction && !prevIsHighway && !prevIsOutgoingOneWay && (_extVehicleType != null && (_extVehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None);
				ushort nextSegmentId = explorePrevSegment ? prevSegmentId : instance.m_segments.m_buffer[prevSegmentId].GetRightSegment(nextNodeId);
#if DEBUGPF
					if (debug)
						logBuf.Add($"Exploring path! Segment {item.m_position.m_segment} @ node {nextNodeId}: Preparation ended");
#endif

#if DEBUGPF
					if (debug)
						logBuf.Add($"pathfind @ node {nextNodeId}: Path from {nextSegmentId} to {prevSegmentId}.");
#endif
#if DEBUGPF
					if (debug)
						logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Custom part started");
#endif
				// NON-STOCK CODE END //
				//mCurrentState = 22;
				for (int k = 0; k < 8; k++) {
#if DEBUGPF
					if (debug)
						logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Segment Iteration {k}. nextSegmentId={nextSegmentId}");
#endif
					// NON-STOCK CODE START //
					int outgoingVehicleLanes = 0;
					int incomingVehicleLanes = 0;
					bool couldFindCustomPath = false;

					if (nextSegmentId == 0) {
						break;
					}
					if (!explorePrevSegment && nextSegmentId == prevSegmentId) {
						break;
					}

					//mCurrentState = 23;
					bool nextIsHighway = false;
					if (instance.m_segments.m_buffer[nextSegmentId].Info.m_netAI is RoadBaseAI)
						nextIsHighway = ((RoadBaseAI)instance.m_segments.m_buffer[nextSegmentId].Info.m_netAI).m_highwayRules;
					bool applyHighwayRules = Options.highwayRules && nextAreOnlyOneWayHighways && prevIsOutgoingOneWay && prevIsHighway && nextIsRealJunction;
					bool applyHighwayRulesAtSegment = applyHighwayRules;
					bool isUntouchable = (instance.m_segments.m_buffer[nextSegmentId].m_flags & NetSegment.Flags.Untouchable) != NetSegment.Flags.None;
					if (!isStrictLaneArrowPolicyEnabled || isUntouchable) {
#if DEBUGPF
						if (debug)
							logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: strict lane arrow policy disabled. ({nextIsJunction} || {nextIsTransition}) && !({Options.allRelaxed} || ({Options.relaxedBusses} && {_transportVehicle})) && {(this._vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None}");
#endif

							// NON-STOCK CODE END //
							//mCurrentState = 24;
						if (ProcessItemCosts(true, debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
							mayTurnAround = true;
						}
						//mCurrentState = 25;
						// NON-STOCK CODE START //
						couldFindCustomPath = true; // not of interest
					} else if (!enablePedestrian) {
						//mCurrentState = 26;

						if ((_vehicleTypes & ~VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None) {
							// handle non-car paths

#if DEBUGPF
							if (debug)
								logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Handling everything that is not a car: {this._vehicleTypes}");
#endif

							_vehicleTypes &= ~VehicleInfo.VehicleType.Car;
							if (ProcessItemCosts(false, debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
								mayTurnAround = true;
							}
							_vehicleTypes |= VehicleInfo.VehicleType.Car;
						}
#if DEBUGPF
						if (debug)
							logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: !enablePedestrian");
#endif

						//try {
						var nextSegmentInfo = instance.m_segments.m_buffer[nextSegmentId].Info;
						bool isIncomingRight = false;
						bool isIncomingStraight = false;
						bool isIncomingLeft = false;
						bool isIncomingTurn = false;

						if (nextSegmentId != prevSegmentId) {
							for (int j = 0; j < 7; ++j) {
								if (incomingRightSegmentsArray[j] == nextSegmentId)
									isIncomingRight = true;
								if (incomingLeftSegmentsArray[j] == nextSegmentId)
									isIncomingLeft = true;
								if (incomingStraightSegmentsArray[j] == nextSegmentId)
									isIncomingStraight = true;
							}
						} else {
							isIncomingTurn = true;
						}

						// we need outgoing lanes too!
						if (!isIncomingTurn && !isIncomingLeft && !isIncomingRight && !isIncomingStraight) {
#if DEBUGPF
							if (debug)
								logBuf.Add($"(PFWARN) Segment {nextSegmentId} is neither incoming left, right or straight segment @ {nextNodeId}, going to segment {prevSegmentId}");
#endif
							// recalculate geometry if segment is unknown
							geometry.VerifyConnectedSegment(nextSegmentId);

							if (!applyHighwayRulesAtSegment) {
								couldFindCustomPath = true; // not of interest
								goto nextIter;
							} else {
								// we do not stop here because we need the number of outgoing lanes in highway mode
							}
						}
						//mCurrentState = 27;
						VehicleInfo.VehicleType vehicleType2 = this._vehicleTypes;
						NetInfo.LaneType drivingEnabledLaneTypes = this._laneTypes;
						drivingEnabledLaneTypes &= ~NetInfo.LaneType.Pedestrian;
						drivingEnabledLaneTypes &= ~NetInfo.LaneType.Parking;

						//if (debug) {
						//Log.Message($"Path finding ({this._pathFindIndex}): Segment {nextSegmentId}");
						//}

						NetInfo.Direction nextDir = instance.m_segments.m_buffer[nextSegmentId].m_startNode != nextNodeId ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
						NetInfo.Direction nextDir2 = ((instance.m_segments.m_buffer[nextSegmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);

						// valid next lanes:
						int[] laneIndexes = new int[16]; // index of NetNode.Info.m_lanes
						uint[] laneIds = new uint[16]; // index of NetManager.m_lanes.m_buffer
						uint[] indexByRightSimilarLaneIndex = new uint[16];
						uint[] indexByLeftSimilarLaneIndex = new uint[16];

						uint curLaneI = 0;
						uint curLaneId = instance.m_segments.m_buffer[nextSegmentId].m_lanes;
						int laneIndex = 0;
#if DEBUG
						uint wIter = 0;
#endif
						//mCurrentState = 28;
						while (laneIndex < nextSegmentInfo.m_lanes.Length && curLaneId != 0u) {
							//mCurrentState = 29;
#if DEBUGPF
							if (debug)
								logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Lane Iteration {laneIndex}. nextSegmentId={nextSegmentId}, curLaneId={curLaneId}");
#endif

#if DEBUG
							++wIter;
							if (wIter >= 20) {
								Log.Error("Too many iterations in ProcessItemMain!");
								break;
							}
#endif

							// determine valid lanes based on lane arrows
							NetInfo.Lane nextLane = nextSegmentInfo.m_lanes[laneIndex];
							bool incomingLane = (byte)(nextLane.m_finalDirection & nextDir2) != 0;
							bool compatibleLane = nextLane.CheckType(drivingEnabledLaneTypes, _vehicleTypes);

							if (incomingLane && compatibleLane) {
								++incomingVehicleLanes;
#if DEBUGPF
								if (debug)
									logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex} is compatible (prevSegment: {prevSegmentId}). laneTypes: {_laneTypes.ToString()}, vehicleTypes: {_vehicleTypes.ToString()}, incomingLanes={incomingVehicleLanes}, isIncomingRight? {isIncomingRight}, isIncomingLeft? {isIncomingLeft}, isIncomingStraight? {isIncomingStraight}");
#endif

								// calculate current similar lane index starting from right line
								int nextRightSimilarLaneIndex;
								int nextLeftSimilarLaneIndex;
								if ((byte)(nextLane.m_direction & normDirection) != 0) {
									nextRightSimilarLaneIndex = nextLane.m_similarLaneIndex;
									nextLeftSimilarLaneIndex = nextLane.m_similarLaneCount - nextLane.m_similarLaneIndex - 1;
								} else {
									nextRightSimilarLaneIndex = nextLane.m_similarLaneCount - nextLane.m_similarLaneIndex - 1;
									nextLeftSimilarLaneIndex = nextLane.m_similarLaneIndex;
								}

								bool hasLeftArrow = ((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Left) == NetLane.Flags.Left;
								bool hasRightArrow = ((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Right) == NetLane.Flags.Right;
								bool hasForwardArrow = ((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Forward) != NetLane.Flags.None || ((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.LeftForwardRight) == NetLane.Flags.None;
#if DEBUGPF
								if (debug) {
									if (hasLeftArrow) {
										logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex} has LEFT arrow. isIncomingRight? {isIncomingRight}, isIncomingLeft? {isIncomingLeft}, isIncomingStraight? {isIncomingStraight}");
									}

									if (hasRightArrow) {
										logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex} has RIGHT arrow. isIncomingRight? {isIncomingRight}, isIncomingLeft? {isIncomingLeft}, isIncomingStraight? {isIncomingStraight}");
									}

									if (hasForwardArrow) {
										logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex} has FORWARD arrow. isIncomingRight? {isIncomingRight}, isIncomingLeft? {isIncomingLeft}, isIncomingStraight? {isIncomingStraight}");
									}
								}
#endif

								bool isValidIncomingRight = isIncomingRight && hasLeftArrow;
								bool isValidIncomingLeft = isIncomingLeft && hasRightArrow;
								bool isValidIncomingStraight = isIncomingStraight && hasForwardArrow;
								bool isValidIncomingTurn = isIncomingTurn && ((TrafficPriority.IsLeftHandDrive() && hasRightArrow) || (!TrafficPriority.IsLeftHandDrive() && hasLeftArrow));

#if DEBUGPF
								if (debug)
									logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex}. isValidIncomingRight? {isValidIncomingRight}, isValidIncomingLeft? {isValidIncomingLeft}, isValidIncomingStraight? {isValidIncomingStraight} isValidIncomingTurn? {isValidIncomingTurn}");
#endif

									// add valid next lanes
								if (applyHighwayRulesAtSegment || isValidIncomingRight || isValidIncomingLeft || isValidIncomingStraight || isValidIncomingTurn) {
									laneIndexes[curLaneI] = laneIndex;
									laneIds[curLaneI] = curLaneId;
									indexByRightSimilarLaneIndex[nextRightSimilarLaneIndex] = curLaneI + 1;
									indexByLeftSimilarLaneIndex[nextLeftSimilarLaneIndex] = curLaneI + 1;
#if DEBUGPF
									if (debug)
										logBuf.Add($"Adding lane #{curLaneI} (id {curLaneId}, idx {laneIndex}), right sim. idx: {nextRightSimilarLaneIndex}, left sim. idx.: {nextLeftSimilarLaneIndex}");
#endif
									curLaneI++;
								}
							}

							if (!incomingLane && compatibleLane) {
								// outgoing lane
								++outgoingVehicleLanes;
							}

							curLaneId = instance.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_nextLane;
							laneIndex++;
						} // foreach lane
						//mCurrentState = 30;

						if (curLaneI > 0) {
							//mCurrentState = 31;
							// we found compatible lanes
							var nextLaneIndex = 0;
							var nextLaneId = 0u;
							int nextLaneI = -1;
							int nextCompatibleLaneCount = Convert.ToInt32(curLaneI);

#if DEBUGPF
							if (debug) {
								logBuf.Add($"Compatible lanes found.");
								logBuf.Add($"next segment: {nextSegmentId}, number of next lanes: {nextCompatibleLaneCount}, prev. segment: {prevSegmentId}, prev. lane ID: {item.m_laneID}, prev. lane idx: {item.m_position.m_lane}, prev. right sim. idx: {prevRightSimilarLaneIndex}, prev. left sim. idx: {prevLeftSimilarLaneIndex}, laneTypes: {_laneTypes.ToString()}, vehicleTypes: {_vehicleTypes.ToString()}, incomingLanes={incomingVehicleLanes}, isIncomingRight? {isIncomingRight}, isIncomingLeft? {isIncomingLeft}, isIncomingStraight? {isIncomingStraight}");
							}
#endif

							// mix of incoming/outgoing lanes on the right sight of prev segment is not allowed in highway mode
							if (totalIncomingLanes > 0 && totalOutgoingLanes > 0) {
#if DEBUGPF
								if (debug)
									logBuf.Add($"{totalIncomingLanes} incoming lanes and {totalOutgoingLanes} outgoing lanes found. Disabling highway rules.");
#endif
								applyHighwayRulesAtSegment = false;
							}

							
							if (applyHighwayRulesAtSegment) {
								//mCurrentState = 32;
								int numRightLanes = Math.Max(totalIncomingLanes, totalOutgoingLanes);
#if DEBUGPF
								if (debug)
									logBuf.Add($"Applying highway rules. {numRightLanes} right lanes found ({totalIncomingLanes} incoming, {totalOutgoingLanes} outgoing).");
#endif
								int nextLeftSimilarIndex;
								if (totalOutgoingLanes > 0) {
									nextLeftSimilarIndex = prevLeftSimilarLaneIndex + numRightLanes; // lane splitting
#if DEBUGPF
									if (debug)
										logBuf.Add($"Performing lane split. nextLeftSimilarIndex={nextLeftSimilarIndex} = prevLeftSimilarIndex({prevLeftSimilarLaneIndex}) + numRightLanes({numRightLanes})");
#endif
								} else {
									nextLeftSimilarIndex = prevLeftSimilarLaneIndex - numRightLanes; // lane merging
#if DEBUGPF
									if (debug)
										logBuf.Add($"Performing lane merge. nextLeftSimilarIndex={nextLeftSimilarIndex} = prevLeftSimilarIndex({prevLeftSimilarLaneIndex}) - numRightLanes({numRightLanes})");
#endif
								}

								if (nextLeftSimilarIndex >= 0 && nextLeftSimilarIndex < nextCompatibleLaneCount) {
									// enough lanes available
									nextLaneI = Convert.ToInt32(indexByLeftSimilarLaneIndex[nextLeftSimilarIndex]) - 1;
#if DEBUGPF
									if (debug)
										logBuf.Add($"Next lane within bounds. nextLaneI={nextLaneI}");
#endif
								} else {
									if (nextLeftSimilarIndex < 0) {
										// too few lanes at prevSegment or nextSegment: sort right
										if (totalIncomingLanes >= prevSimiliarLaneCount)
											nextLaneI = Convert.ToInt32(indexByRightSimilarLaneIndex[prevRightSimilarLaneIndex]) - 1;
									} else {
										if (totalOutgoingLanes >= nextCompatibleLaneCount)
											nextLaneI = Convert.ToInt32(indexByRightSimilarLaneIndex[0]) - 1;
									}
#if DEBUGPF
									if (debug)
										logBuf.Add($"Next lane out of bounds. nextLaneI={nextLaneI}, isIncomingLeft={isIncomingLeft}, prevRightSimilarIndex={prevRightSimilarLaneIndex}, prevLeftSimilarIndex={prevLeftSimilarLaneIndex}");
#endif
								}

								if (nextLaneI < 0 || nextLaneI >= nextCompatibleLaneCount) {
#if DEBUGPF
									if (debug)
										Log.Error($"(PFERR) Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right, {prevLeftSimilarLaneIndex} from left: Highway lane selector cannot find suitable lane! isIncomingLeft={isIncomingLeft} isIncomingRight={isIncomingRight} totalIncomingLanes={totalIncomingLanes}");
#endif
									couldFindCustomPath = true; // not of interest for us
									//mCurrentState = 33;
									goto nextIter; // no path to this lane
								}
							} else if (nextCompatibleLaneCount == 1) {
								//mCurrentState = 34;
								nextLaneI = 0;
#if DEBUGPF
								if (debug)
									logBuf.Add($"Single target lane found. nextLaneI={nextLaneI}");
#endif
							} else {
								//mCurrentState = 35;
								// lane matching
								int prevSimilarLaneCount = lane.m_similarLaneCount;

#if DEBUGPF
								if (debug)
									logBuf.Add($"Multiple target lanes found. prevSimilarLaneCount={prevSimilarLaneCount}");
#endif

								int minNextRightSimilarIndex = -1;
								int maxNextRightSimilarIndex = -1;
								if (nextIsRealJunction) {
									// at junctions: try to match distinct lanes (1-to-1, n-to-1)
									minNextRightSimilarIndex = prevRightSimilarLaneIndex;
									maxNextRightSimilarIndex = prevRightSimilarLaneIndex;

									// vehicles may change lanes at straight segments?w
									if (isIncomingStraight && Flags.getStraightLaneChangingAllowed(nextSegmentId, Singleton<NetManager>.instance.m_segments.m_buffer[nextSegmentId].m_startNode == nextNodeId)) {
										minNextRightSimilarIndex = Math.Max(0, minNextRightSimilarIndex - 1);
										maxNextRightSimilarIndex = Math.Min(nextCompatibleLaneCount - 1, maxNextRightSimilarIndex + 1);
#if DEBUGPF
										if (debug)
											logBuf.Add($"Next is incoming straight. Allowing lane changes! minNextRightSimilarIndex={minNextRightSimilarIndex}, maxNextRightSimilarIndex={maxNextRightSimilarIndex}");
#endif
									}
#if DEBUGPF
									if (debug)
										logBuf.Add($"Next is junction. minNextRightSimilarIndex={minNextRightSimilarIndex}, maxNextRightSimilarIndex={maxNextRightSimilarIndex}");
#endif
								} else {
									// lane merging/splitting
									//mCurrentState = 36;
									HandleLaneMergesAndSplits(prevRightSimilarLaneIndex, nextCompatibleLaneCount, prevSimilarLaneCount, out minNextRightSimilarIndex, out maxNextRightSimilarIndex);
									//mCurrentState = 37;
#if DEBUGPF
									if (debug)
										logBuf.Add($"Next is not a junction. nextRightSimilarLaneIndex=HandleLaneMergesAndSplits({prevRightSimilarLaneIndex}, {nextCompatibleLaneCount}, {prevSimilarLaneCount})= min. {minNextRightSimilarIndex} max. {maxNextRightSimilarIndex}");
#endif
								}

								// find best matching lane(s)
								for (int nextRightSimilarIndex = minNextRightSimilarIndex; nextRightSimilarIndex <= maxNextRightSimilarIndex; ++nextRightSimilarIndex) {
#if DEBUGPF
									if (debug)
										logBuf.Add($"current right similar index = {nextRightSimilarIndex}, min. {minNextRightSimilarIndex} max. {maxNextRightSimilarIndex}");
#endif
									//mCurrentState = 38;
									nextLaneI = FindNthCompatibleLane(ref indexByRightSimilarLaneIndex, nextRightSimilarIndex);
									//mCurrentState = 39;

#if DEBUGPF
									if (debug)
										logBuf.Add($"(*) nextLaneI = {nextLaneI}");
#endif
									if (nextLaneI < 0) {
										continue;
									}

									// go to matched lane
									nextLaneIndex = laneIndexes[nextLaneI];
									nextLaneId = laneIds[nextLaneI];

#if DEBUGPF
									if (debug)
										logBuf.Add($"Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane idx {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right. There are {curLaneI} candidate lanes. We choose lane {nextLaneI} (index {nextLaneIndex}, {nextRightSimilarIndex} compatible from right). lhd: {TrafficPriority.IsLeftHandDrive()}, ped: {pedestrianAllowed}, magical flag4: {mayTurnAround}");
#endif

									//mCurrentState = 40;
									if (ProcessItemCosts(true, debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian, nextLaneIndex, nextLaneId, out foundForced)) {
										mayTurnAround = true;
									}
									//mCurrentState = 41;
									couldFindCustomPath = true;
								}

								goto nextIter;
							}

							//mCurrentState = 42;
							if (nextLaneI < 0) {
#if DEBUGPF
								if (debug)
									Log.Error($"(PFERR) Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: nextLaneI < 0!");
#endif
								//mCurrentState = 43;
								goto nextIter;
							}

							//mCurrentState = 44;
							// go to matched lane
							nextLaneIndex = laneIndexes[nextLaneI];
							nextLaneId = laneIds[nextLaneI];

#if DEBUGPF
							if (debug)
								logBuf.Add($"Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: nextLaneIndex={nextLaneIndex} nextLaneId={nextLaneId}");
#endif

							//mCurrentState = 45;
							if (IsMasterPathFind && applyHighwayRulesAtSegment) {
								// udpate highway mode arrows
#if DEBUGPF
								/*if (Options.disableSomething1)
									Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Setting highway arrows @ lane {nextLaneId}: START");*/
#endif
								Flags.LaneArrows? prevHighwayArrows = Flags.getHighwayLaneArrowFlags(nextLaneId);
								Flags.LaneArrows newHighwayArrows = Flags.LaneArrows.None;
								if (prevHighwayArrows != null)
									newHighwayArrows = (Flags.LaneArrows)prevHighwayArrows;
								if (isIncomingRight)
									newHighwayArrows |= Flags.LaneArrows.Left;
								else if (isIncomingLeft)
									newHighwayArrows |= Flags.LaneArrows.Right;
								else if (isIncomingStraight)
									newHighwayArrows |= Flags.LaneArrows.Forward;

								if (newHighwayArrows != prevHighwayArrows && newHighwayArrows != Flags.LaneArrows.None)
									Flags.setHighwayLaneArrowFlags(nextLaneId, newHighwayArrows);
#if DEBUGPF
								/*if (Options.disableSomething1)
									Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Setting highway arrows @ lane {nextLaneId} to {newHighwayArrows.ToString()}: END");*/
#endif
							}
							//mCurrentState = 46;

							if (ProcessItemCosts(true, debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian, nextLaneIndex, nextLaneId, out foundForced)) {
								mayTurnAround = true;
							}
							//mCurrentState = 47;

							if (foundForced) {
#if DEBUGPF
								if (debug)
									logBuf.Add($"Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: FORCED LANE FOUND!");
#endif
								couldFindCustomPath = true;
							}
						} else {
							// no compatible lanes found
							//mCurrentState = 48;
#if DEBUGPF
							if (debug)
								Log.Error($"(PFERR) Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: No lane arrows defined");
#endif
							couldFindCustomPath = true; // the player did not set lane arrows. this is ok...
														/*if (ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
															blocked = true;
														}*/
						}
						/*} catch (Exception e) {
							Log.Error($"(PFERR) Error occurred in custom path-finding (main): {e.ToString()}");

							couldFindCustomPath = true; // not of interest for us
														// stock code fallback
							if (this.ProcessItemSub(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
								blocked = true;
							}
						}*/
						// NON-STOCK CODE END
					} else {
						//mCurrentState = 49;
						// pedestrians

						// stock code:
						if (this.ProcessItemCosts(false, debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
							mayTurnAround = true;
						}
						couldFindCustomPath = true; // not of interest for us
						//mCurrentState = 50;
					}

					nextIter:
					//mCurrentState = 51;
					if (!couldFindCustomPath) {
#if DEBUGPF
						if (debug)
							logBuf.Add($"(PFERR) Could not find custom path from segment {nextSegmentId} to segment {prevSegmentId}, lane {item.m_position.m_lane}, off {item.m_position.m_offset} at node {nextNodeId}!");
#endif
						// stock code:
						/*if (this.ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
							blocked = true;
						}*/
					}

					if (nextSegmentId == prevSegmentId)
						similarLaneIndexFromLeft = firstSimilarLaneIndexFromLeft; // u-turning does not "consume" a lane

					nextSegmentId = instance.m_segments.m_buffer[(int)nextSegmentId].GetRightSegment(nextNodeId);
					if (nextSegmentId != prevSegmentId) {
						totalIncomingLanes += incomingVehicleLanes;
						totalOutgoingLanes += outgoingVehicleLanes;
					}

					if (explorePrevSegment && nextSegmentId == prevSegmentId)
						break;
					//mCurrentState = 52;
				} // foreach segment
				//mCurrentState = 53;
#if DEBUGPF
				if (debug)
					logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Custom part finished");
#endif
				if (mayTurnAround && (this._vehicleTypes & VehicleInfo.VehicleType.Tram) == VehicleInfo.VehicleType.None) {
					// turn-around for vehicles (if street is blocked)
#if DEBUGPF
					if (debug)
						logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Road may be blocked");
#endif
					// vehicles may turn around if the street is blocked
					nextSegmentId = item.m_position.m_segment;
					//mCurrentState = 54;
					this.ProcessItemCosts(false, debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, false);
					//mCurrentState = 55;
				}
				//mCurrentState = 56;
				// NON-STOCK CODE START
				/*if (foundForced)
					return;*/
				// NON-STOCK CODE END
				if (pedestrianAllowed) {
					// turn-around for pedestrians
#if DEBUGPF
						if (debug)
							logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Ped allowed");
#endif
					nextSegmentId = item.m_position.m_segment;
					int laneIndex4;
					uint lane6;
					if (instance.m_segments.m_buffer[(int)nextSegmentId].GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, this._vehicleTypes, out laneIndex4, out lane6)) {
						//mCurrentState = 57;
						this.ProcessItemPedBicycle(item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], connectOffset3, laneIndex4, lane6); // ped
						//mCurrentState = 58;
					}
				}
			}
			if (nextNode.m_lane != 0u) {
				bool targetDisabled = (nextNode.m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None;
				ushort segment4 = instance.m_lanes.m_buffer[(int)((UIntPtr)nextNode.m_lane)].m_segment;
				if (segment4 != 0 && segment4 != item.m_position.m_segment) {
#if DEBUGPF
						if (debug)
							logBuf.Add($"Exploring path from {segment4} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}: handling special lanes");
#endif
					//mCurrentState = 59;
					this.ProcessItem2(item, nextNodeId, targetDisabled, segment4, ref instance.m_segments.m_buffer[(int)segment4], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);
					//mCurrentState = 60;
				}
			}
			//mCurrentState = 61;
#if DEBUGPF
			if (debug) {
				foreach (String toLog in logBuf) {
					Log._Debug($"Pathfinder ({this._pathFindIndex}) for unit {unitId}: " + toLog);
				}
			}
#endif
			//mCurrentState = 62;
		}
Пример #29
0
        public static bool Prefix(ref NetNode __instance, ushort nodeID, BuildingInfo newBuilding, float heightOffset)
        {
            float num = 0f;

            if ((object)newBuilding != null)
            {
                NetInfo info = __instance.Info;
                if ((object)info != null)
                {
                    num = info.m_netAI.GetNodeBuildingAngle(nodeID, ref __instance);
                }
            }
            BuildingInfo buildingInfo = null;

            if (__instance.m_building != 0)
            {
                buildingInfo = Singleton <BuildingManager> .instance.m_buildings.m_buffer[__instance.m_building].Info;
            }
            if ((object)newBuilding != buildingInfo)
            {
                if (__instance.m_building != 0)
                {
                    Singleton <BuildingManager> .instance.ReleaseBuilding(__instance.m_building);

                    __instance.m_building = 0;
                }
                if ((object)newBuilding != null)
                {
                    Vector3 position = __instance.m_position;
                    position.y += heightOffset;
                    // NON-STOCK CODE STARTS
                    if (CSURUtil.IsCSUROffset(__instance.Info))
                    {
                        float laneOffset  = 0;
                        float startOffset = 0;
                        float endOffset   = 0;
                        if (CSURUtil.IsCSURSLane(__instance.Info, ref laneOffset, ref startOffset, ref endOffset))
                        {
                            bool lht = false;
                            if (__instance.CountSegments() != 0)
                            {
                                float collisionHalfWidth = 0;
                                float vehicleLaneNum     = CSURUtil.CountCSURSVehicleLanes(__instance.Info);
                                float otherLaneNum       = CSURUtil.CountCSURSOtherLanes(__instance.Info);
                                float laneNum            = otherLaneNum + vehicleLaneNum;
                                if (CSURUtil.isStartNode(nodeID))
                                {
                                    if (startOffset != 0)
                                    {
                                        collisionHalfWidth = startOffset * 3.75f - laneNum * 1.875f + 1.875f + otherLaneNum * 3.75f;
                                    }
                                }
                                else
                                {
                                    if (endOffset != 0)
                                    {
                                        collisionHalfWidth = endOffset * 3.75f - laneNum * 1.875f + 1.875f + otherLaneNum * 3.75f;
                                    }
                                }
                                NetSegment mysegment = CSURUtil.GetSameInfoSegment(__instance);
                                Vector3    direction = CSURUtil.CheckNodeEq(mysegment.m_startNode, __instance) ? mysegment.m_startDirection : -mysegment.m_endDirection;
                                if ((mysegment.m_flags & NetSegment.Flags.Invert) != 0)
                                {
                                    lht = true;
                                }
                                // normal to the right hand side
                                Vector3 normal = new Vector3(direction.z, 0, -direction.x).normalized;
                                position = position + (lht ? -collisionHalfWidth : collisionHalfWidth) * normal;
                            }
                        }
                        else
                        {
                            bool lht = false;
                            if (__instance.CountSegments() != 0)
                            {
                                float      collisionHalfWidth = Mathf.Max(3f, (__instance.Info.m_halfWidth + __instance.Info.m_pavementWidth) / 2f);
                                NetSegment mysegment          = CSURUtil.GetSameInfoSegment(__instance);
                                Vector3    direction          = CSURUtil.CheckNodeEq(mysegment.m_startNode, __instance) ? mysegment.m_startDirection : -mysegment.m_endDirection;
                                if ((mysegment.m_flags & NetSegment.Flags.Invert) != 0)
                                {
                                    lht = true;
                                }
                                // normal to the right hand side
                                Vector3 normal = new Vector3(direction.z, 0, -direction.x).normalized;
                                position = position + (lht ? -collisionHalfWidth : collisionHalfWidth) * normal;
                            }
                        }
                    }
                    // NON-STOCK CODE ENDS
                    num *= 6.28318548f;
                    if ((object)buildingInfo != null || TestNodeBuilding(nodeID, newBuilding, position, num))
                    {
                        Randomizer randomizer = new Randomizer(nodeID);
                        if (Singleton <BuildingManager> .instance.CreateBuilding(out __instance.m_building, ref randomizer, newBuilding, position, num, 0, __instance.m_buildIndex + 1))
                        {
                            Singleton <BuildingManager> .instance.m_buildings.m_buffer[__instance.m_building].m_flags |= (Building.Flags.Untouchable | Building.Flags.FixedHeight);
                        }
                    }
                }
            }
            else if (__instance.m_building != 0)
            {
                BuildingManager instance  = Singleton <BuildingManager> .instance;
                Vector3         position2 = __instance.m_position;
                position2.y += heightOffset;
                // NON-STOCK CODE STARTS
                if (CSURUtil.IsCSUROffset(__instance.Info))
                {
                    float laneOffset  = 0;
                    float startOffset = 0;
                    float endOffset   = 0;
                    if (CSURUtil.IsCSURSLane(__instance.Info, ref laneOffset, ref startOffset, ref endOffset))
                    {
                        bool lht = false;
                        if (__instance.CountSegments() != 0)
                        {
                            float collisionHalfWidth = 0;
                            float vehicleLaneNum     = CSURUtil.CountCSURSVehicleLanes(__instance.Info);
                            float otherLaneNum       = CSURUtil.CountCSURSOtherLanes(__instance.Info);
                            float laneNum            = otherLaneNum + vehicleLaneNum;
                            if (CSURUtil.isStartNode(nodeID))
                            {
                                if (startOffset != 0)
                                {
                                    collisionHalfWidth = startOffset * 3.75f - laneNum * 1.875f + 1.875f + otherLaneNum * 3.75f;
                                }
                            }
                            else
                            {
                                if (endOffset != 0)
                                {
                                    collisionHalfWidth = endOffset * 3.75f - laneNum * 1.875f + 1.875f + otherLaneNum * 3.75f;
                                }
                            }
                            NetSegment mysegment = CSURUtil.GetSameInfoSegment(__instance);
                            Vector3    direction = CSURUtil.CheckNodeEq(mysegment.m_startNode, __instance) ? mysegment.m_startDirection : -mysegment.m_endDirection;
                            if ((mysegment.m_flags & NetSegment.Flags.Invert) != 0)
                            {
                                lht = true;
                            }
                            // normal to the right hand side
                            Vector3 normal = new Vector3(direction.z, 0, -direction.x).normalized;
                            position2 = position2 + (lht ? -collisionHalfWidth : collisionHalfWidth) * normal;
                        }
                    }
                    else
                    {
                        bool lht = false;
                        if (__instance.CountSegments() != 0)
                        {
                            float      collisionHalfWidth = Mathf.Max(3f, (__instance.Info.m_halfWidth + __instance.Info.m_pavementWidth) / 2f);
                            NetSegment mysegment          = CSURUtil.GetSameInfoSegment(__instance);
                            Vector3    direction          = CSURUtil.CheckNodeEq(mysegment.m_startNode, __instance) ? mysegment.m_startDirection : -mysegment.m_endDirection;
                            if ((mysegment.m_flags & NetSegment.Flags.Invert) != 0)
                            {
                                lht = true;
                            }
                            // normal to the right hand side
                            Vector3 normal = new Vector3(direction.z, 0, -direction.x).normalized;
                            position2 = position2 + (lht ? -collisionHalfWidth : collisionHalfWidth) * normal;
                        }
                    }
                }
                // NON-STOCK CODE ENDS
                num *= 6.28318548f;
                // NON-STOCK CODE STARTS
                if (CSURUtil.IsCSUROffset(__instance.Info) && (instance.m_buildings.m_buffer[__instance.m_building].m_position != position2 || instance.m_buildings.m_buffer[__instance.m_building].m_angle != num))
                {
                    RemoveFromGrid(__instance.m_building, ref instance.m_buildings.m_buffer[__instance.m_building]);
                    instance.m_buildings.m_buffer[__instance.m_building].m_position = position2;
                    instance.m_buildings.m_buffer[__instance.m_building].m_angle    = num;
                    AddToGrid(__instance.m_building, ref instance.m_buildings.m_buffer[__instance.m_building]);
                    instance.m_buildings.m_buffer[__instance.m_building].CalculateBuilding(__instance.m_building);
                    Singleton <BuildingManager> .instance.UpdateBuildingRenderer(__instance.m_building, true);
                }
                else
                {
                    if (instance.m_buildings.m_buffer[__instance.m_building].m_position.y != position2.y || instance.m_buildings.m_buffer[__instance.m_building].m_angle != num)
                    {
                        instance.m_buildings.m_buffer[__instance.m_building].m_position.y = position2.y;
                        instance.m_buildings.m_buffer[__instance.m_building].m_angle      = num;
                        instance.UpdateBuilding(__instance.m_building);
                    }
                }
                // NON-STOCK CODE ENDS
            }
            return(false);
        }
        void SetNodeMarkers(ushort nodeId, FastList <NodeLaneMarker> nodeMarkers)
        {
            NetNode node             = NetManager.instance.m_nodes.m_buffer[nodeId];
            int     offsetMultiplier = node.CountSegments() <= 2 ? 3 : 1;
            ushort  segmentId        = node.m_segment0;

            for (int i = 0; i < 8 && segmentId != 0; i++)
            {
                NetSegment     segment   = NetManager.instance.m_segments.m_buffer[segmentId];
                bool           isEndNode = segment.m_endNode == nodeId;
                Vector3        offset    = segment.FindDirection(segmentId, nodeId) * offsetMultiplier;
                NetInfo.Lane[] lanes     = segment.Info.m_lanes;
                uint           laneId    = segment.m_lanes;
                for (int j = 0; j < lanes.Length && laneId != 0; j++)
                {
                    //if ((lanes[j].m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None)
                    if ((lanes[j].m_laneType & NetInfo.LaneType.Vehicle) == NetInfo.LaneType.Vehicle)
                    {
                        Vector3           pos     = Vector3.zero;
                        NetInfo.Direction laneDir = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? lanes[j].m_finalDirection : NetInfo.InvertDirection(lanes[j].m_finalDirection);

                        bool isSource = false;
                        if (isEndNode)
                        {
                            if ((laneDir & (NetInfo.Direction.Forward | NetInfo.Direction.Avoid)) == NetInfo.Direction.Forward)
                            {
                                isSource = true;
                            }
                            pos = NetManager.instance.m_lanes.m_buffer[laneId].m_bezier.d;
                        }
                        else
                        {
                            if ((laneDir & (NetInfo.Direction.Backward | NetInfo.Direction.Avoid)) == NetInfo.Direction.Backward)
                            {
                                isSource = true;
                            }
                            pos = NetManager.instance.m_lanes.m_buffer[laneId].m_bezier.a;
                        }

                        nodeMarkers.Add(new NodeLaneMarker()
                        {
                            m_lane     = laneId,
                            m_node     = nodeId,
                            m_position = pos + offset,
                            m_color    = colors[nodeMarkers.m_size],
                            m_isSource = isSource,
                        });
                    }

                    laneId = NetManager.instance.m_lanes.m_buffer[laneId].m_nextLane;
                }

                segmentId = segment.GetRightSegment(nodeId);
                if (segmentId == node.m_segment0)
                {
                    segmentId = 0;
                }
            }

            for (int i = 0; i < nodeMarkers.m_size; i++)
            {
                if (!nodeMarkers.m_buffer[i].m_isSource)
                {
                    continue;
                }

                uint[] connections = RoadManager.GetLaneConnections(nodeMarkers.m_buffer[i].m_lane);
                if (connections == null || connections.Length == 0)
                {
                    continue;
                }

                for (int j = 0; j < nodeMarkers.m_size; j++)
                {
                    if (nodeMarkers.m_buffer[j].m_isSource)
                    {
                        continue;
                    }

                    if (connections.Contains(nodeMarkers.m_buffer[j].m_lane))
                    {
                        nodeMarkers.m_buffer[i].m_connections.Add(nodeMarkers.m_buffer[j]);
                    }
                }
            }
        }
        protected override void OnToolUpdate()
        {
            base.OnToolUpdate();

            if (Input.GetKeyUp(KeyCode.PageDown))
            {
                InfoManager.instance.SetCurrentMode(InfoManager.InfoMode.Traffic, InfoManager.SubInfoMode.Default);
            }
            else if (Input.GetKeyUp(KeyCode.PageUp))
            {
                InfoManager.instance.SetCurrentMode(InfoManager.InfoMode.None, InfoManager.SubInfoMode.Default);
            }

            if (m_toolController.IsInsideUI)
            {
                return;
            }

            if (m_selectedNode != 0)
            {
                HandleIntersectionRouting();
                return;
            }

            if (m_hoveredSegment != 0)
            {
                HandleLaneCustomization();
            }

            if (!RayCastSegmentAndNode(out m_hoveredSegment, out m_hoveredNode))
            {
                // clear lanes
                if (Input.GetMouseButtonUp(1))
                {
                    m_selectedLaneMarkers.Clear();
                    if (OnEndLaneCustomization != null)
                    {
                        OnEndLaneCustomization();
                    }
                }

                m_segments.Clear();
                m_hoveredLaneMarkers.Clear();
                return;
            }


            if (m_hoveredSegment != 0)
            {
                NetSegment segment   = NetManager.instance.m_segments.m_buffer[m_hoveredSegment];
                NetNode    startNode = NetManager.instance.m_nodes.m_buffer[segment.m_startNode];
                NetNode    endNode   = NetManager.instance.m_nodes.m_buffer[segment.m_endNode];
                Ray        mouseRay  = Camera.main.ScreenPointToRay(Input.mousePosition);

                if (startNode.CountSegments() > 1)
                {
                    Bounds bounds = startNode.m_bounds;
                    if (m_hoveredNode != 0)
                    {
                        bounds.extents /= 2f;
                    }
                    if (bounds.IntersectRay(mouseRay))
                    {
                        m_hoveredSegment = 0;
                        m_hoveredNode    = segment.m_startNode;
                    }
                }

                if (m_hoveredSegment != 0 && endNode.CountSegments() > 1)
                {
                    Bounds bounds = endNode.m_bounds;
                    if (m_hoveredNode != 0)
                    {
                        bounds.extents /= 2f;
                    }
                    if (bounds.IntersectRay(mouseRay))
                    {
                        m_hoveredSegment = 0;
                        m_hoveredNode    = segment.m_endNode;
                    }
                }

                if (m_hoveredSegment != 0)
                {
                    m_hoveredNode = 0;
                    if (!m_segments.ContainsKey(m_hoveredSegment))
                    {
                        m_segments.Clear();
                        SetSegments(m_hoveredSegment);
                        SetLaneMarkers();
                    }
                }
                else if (Input.GetMouseButtonUp(1))
                {
                    // clear lane selection
                    m_selectedLaneMarkers.Clear();
                    if (OnEndLaneCustomization != null)
                    {
                        OnEndLaneCustomization();
                    }
                }
            }
            else if (m_hoveredNode != 0 && NetManager.instance.m_nodes.m_buffer[m_hoveredNode].CountSegments() < 2)
            {
                m_hoveredNode = 0;
            }

            if (m_hoveredSegment == 0)
            {
                m_segments.Clear();
                m_hoveredLaneMarkers.Clear();
            }

            if (Input.GetMouseButtonUp(0))
            {
                m_selectedNode = m_hoveredNode;
                m_hoveredNode  = 0;

                if (m_selectedNode != 0)
                {
                    SetNodeMarkers(m_selectedNode, true);
                }
            }
        }
        private Mesh GetJunctionMesh(ushort nodeId, Vector3 position, NetNode node, NetInfo info)
        {
            Vector3 m          = new Vector3(0, -0.25f, 0);
            Bezier3 leftBezier = new Bezier3(m, m, m, m);
            Bezier3 rightBezier;
            List <CombineInstance> combineInstances = new List <CombineInstance>();

            for (int index1 = 0; index1 < node.CountSegments(); index1++)
            {
                var        segment1    = node.GetSegment(index1);
                NetSegment netSegment1 = Singleton <NetManager> .instance.m_segments.m_buffer[(int)segment1];
                if (netSegment1.Info != null)
                {
                    Vector3 cornerPos1       = Vector3.zero;
                    Vector3 cornerDirection1 = Vector3.zero;
                    Vector3 cornerPos2       = Vector3.zero;
                    Vector3 cornerDirection2 = Vector3.zero;
                    Vector3 vector3_3        = (int)nodeId != (int)netSegment1.m_startNode
                        ? netSegment1.m_endDirection
                        : netSegment1.m_startDirection;
                    float  num1      = -4f;
                    ushort segmentID = 0;
                    for (int index2 = 0; index2 < 8; ++index2)
                    {
                        ushort segment2 = node.GetSegment(index2);
                        if (segment2 != (ushort)0 && (int)segment2 != (int)segment1)
                        {
                            NetSegment netSegment2 =
                                Singleton <NetManager> .instance.m_segments.m_buffer[(int)segment2];
                            if (netSegment2.Info != null)
                            {
                                Vector3 vector3_4 = (int)nodeId != (int)netSegment2.m_startNode
                                    ? netSegment2.m_endDirection
                                    : netSegment2.m_startDirection;
                                float num2 = (float)((double)vector3_3.x * (double)vector3_4.x +
                                                     (double)vector3_3.z * (double)vector3_4.z);
                                if ((double)vector3_4.z * (double)vector3_3.x -
                                    (double)vector3_4.x * (double)vector3_3.z < 0.0)
                                {
                                    if ((double)num2 > (double)num1)
                                    {
                                        num1      = num2;
                                        segmentID = segment2;
                                    }
                                }
                                else
                                {
                                    float num3 = -2f - num2;
                                    if ((double)num3 > (double)num1)
                                    {
                                        num1      = num3;
                                        segmentID = segment2;
                                    }
                                }
                            }
                        }
                    }

                    bool start1 = (int)netSegment1.m_startNode == (int)nodeId;
                    bool smooth;
                    netSegment1.CalculateCorner(segment1, true, start1, false, out cornerPos1, out cornerDirection1,
                                                out smooth);
                    if (segmentID != (ushort)0)
                    {
                        NetSegment netSegment2 =
                            Singleton <NetManager> .instance.m_segments.m_buffer[(int)segmentID];
                        bool start2 = (int)netSegment2.m_startNode == (int)nodeId;
                        netSegment2.CalculateCorner(segmentID, true, start2, true, out cornerPos2,
                                                    out cornerDirection2,
                                                    out smooth);
                    }


                    NetSegment.CalculateMiddlePoints(cornerPos1, -cornerDirection1, cornerPos2, -cornerDirection2,
                                                     true,
                                                     true, out Vector3 middlePos1, out Vector3 middlePos2);

                    rightBezier = new Bezier3(
                        cornerPos2 - position,
                        middlePos2 - position,
                        middlePos1 - position,
                        cornerPos1 - position
                        );
                    combineInstances.Add(new CombineInstance
                    {
                        mesh      = BendMesh(node.Info.m_nodes[0].m_nodeMesh, leftBezier, rightBezier, info),
                        transform = Matrix4x4.identity
                    });
                }
            }

            Mesh junction = new Mesh();

            junction.CombineMeshes(combineInstances.ToArray());
            return(junction);
        }
Пример #33
0
        public void CustomNodeSimulationStep(ushort nodeID, ref NetNode data)
        {
            if ((Singleton <SimulationManager> .instance.m_currentFrameIndex & 4095u) >= 3840u)
            {
                data.m_finalCounter += data.m_tempCounter;

                data.m_tempCounter = 0;

                var date         = DateTime.Now;
                var resetCounter = false;
                if (StatisticsFixManager.instance.SaveData.resetTimes.ContainsKey(nodeID))
                {
                    date = StatisticsFixManager.instance.SaveData.resetTimes[nodeID];

                    if (CityEventManager.CITY_TIME.Year != date.Year ||
                        CityEventManager.CITY_TIME.StartOfWeek(DayOfWeek.Monday).DayOfYear !=
                        date.StartOfWeek(DayOfWeek.Monday).DayOfYear)
                    {
                        resetCounter = true;
                    }
                }
                else
                {
                    resetCounter = true;
                }

                if (resetCounter)
                {
                    StatisticsFixManager.instance.SaveData.resetTimes[nodeID] =
                        CityEventManager.CITY_TIME;
                    data.m_tempCounter  = 0;
                    data.m_finalCounter = 0;
                }
                if (StatisticsFixManager.instance.SaveData.lastWeekPassengerCount == null)
                {
                    StatisticsFixManager.instance.SaveData.lastWeekPassengerCount = new Dictionary <ushort, ushort>();
                }

                StatisticsFixManager.instance.SaveData.lastWeekPassengerCount[nodeID] = data.m_finalCounter;
            }

            if (this.m_publicTransportAccumulation != 0)
            {
                NetManager instance = Singleton <NetManager> .instance;
                int        num      = 0;
                for (int i = 0; i < 8; i++)
                {
                    ushort segment = data.GetSegment(i);
                    if (segment != 0 && (instance.m_segments.m_buffer[(int)segment].m_flags & NetSegment.Flags.PathFailed) == NetSegment.Flags.None)
                    {
                        num += this.m_publicTransportAccumulation >> 1;
                    }
                }
                if (num != 0)
                {
                    int budget = Singleton <EconomyManager> .instance.GetBudget(this.m_info.m_class);

                    int productionRate = PlayerBuildingAI.GetProductionRate(100, budget);
                    num = num * productionRate / 100;
                }
                if (num != 0)
                {
                    Singleton <ImmaterialResourceManager> .instance.AddResource(ImmaterialResourceManager.Resource.PublicTransport, num, data.m_position, this.m_publicTransportRadius);
                }
            }
            if ((data.m_problems & Notification.Problem.LineNotConnected) != Notification.Problem.None && data.CountSegments() <= 1 && (data.m_flags & NetNode.Flags.Temporary) == NetNode.Flags.None)
            {
                GuideController properties = Singleton <GuideManager> .instance.m_properties;
                if (properties != null)
                {
                    Singleton <NetManager> .instance.m_transportNodeNotConnected.Activate(properties.m_lineNotFinished, nodeID, Notification.Problem.LineNotConnected, false);
                }
            }
        }
		// be aware:
		//   (1) path-finding works from target to start. the "next" segment is always the previous and the "previous" segment is always the next segment on the path!
		//   (2) when I use the term "lane index from right" this holds for right-hand traffic systems. On maps where you activate left-hand traffic, the "lane index from right" values represent lane indices starting from the left side.

		// 1
		private void ProcessItemMain(BufferItem item, ushort nextNodeId, ref NetNode nextNode, byte connectOffset, bool isMiddle) {
#if DEBUG
			//bool debug = nodeID == 28311u && item.m_position.m_segment == 33016;
			//bool debug = nodeID == 26128u && item.m_position.m_segment == 4139 && nextSegmentId == 27106;
			//bool debug = nodeID == 13630u && item.m_position.m_segment == 35546u;
			
#endif
#if DEBUG
			//bool debug = isTransportVehicle && isMiddle && item.m_position.m_segment == 13550;
			bool debug = false;
#else
			bool debug = false;
#endif
			NetManager instance = Singleton<NetManager>.instance;
			bool isPedestrianLane = false;
			bool isBicycleLane = false;
			int similarLaneIndexFromLeft = 0; // similar index, starting with 0 at leftmost lane
			NetInfo prevSegmentInfo = instance.m_segments.m_buffer[(int)item.m_position.m_segment].Info;
			if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
				NetInfo.Lane prevLane = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
				isPedestrianLane = (prevLane.m_laneType == NetInfo.LaneType.Pedestrian);
				isBicycleLane = (prevLane.m_laneType == NetInfo.LaneType.Vehicle && prevLane.m_vehicleType == VehicleInfo.VehicleType.Bicycle);
				if ((byte)(prevLane.m_finalDirection & NetInfo.Direction.Forward) != 0) {
					similarLaneIndexFromLeft = prevLane.m_similarLaneIndex;
				} else {
					similarLaneIndexFromLeft = prevLane.m_similarLaneCount - prevLane.m_similarLaneIndex - 1;
				}
			}

			ushort prevSegment = item.m_position.m_segment;
			if (isMiddle) {
				for (int i = 0; i < 8; i++) {
					ushort nextSegmentId = nextNode.GetSegment(i);
					if (nextSegmentId <= 0)
						continue;
					this.ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, !isPedestrianLane, isPedestrianLane);
				}
			} else if (isPedestrianLane) {
				int prevLaneIndex = (int)item.m_position.m_lane;
				if (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {
					bool flag3 = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
					int laneIndex;
					int laneIndex2;
					uint leftLaneId;
					uint rightLaneId;
					instance.m_segments.m_buffer[(int)prevSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, out laneIndex, out laneIndex2, out leftLaneId, out rightLaneId);
					ushort num4 = prevSegment;
					ushort num5 = prevSegment;
					if (leftLaneId == 0u || rightLaneId == 0u) {
						ushort leftSegment;
						ushort rightSegment;
						instance.m_segments.m_buffer[(int)prevSegment].GetLeftAndRightSegments(nextNodeId, out leftSegment, out rightSegment);
						int num6 = 0;
						while (leftSegment != 0 && leftSegment != prevSegment && leftLaneId == 0u) {
							int num7;
							int num8;
							uint num9;
							uint num10;
							instance.m_segments.m_buffer[(int)leftSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, out num7, out num8, out num9, out num10);
							if (num10 != 0u) {
								num4 = leftSegment;
								laneIndex = num8;
								leftLaneId = num10;
							} else {
								leftSegment = instance.m_segments.m_buffer[(int)leftSegment].GetLeftSegment(nextNodeId);
							}
							if (++num6 == 8) {
								break;
							}
						}
						num6 = 0;
						while (rightSegment != 0 && rightSegment != prevSegment && rightLaneId == 0u) {
							int num11;
							int num12;
							uint num13;
							uint num14;
							instance.m_segments.m_buffer[(int)rightSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, out num11, out num12, out num13, out num14);
							if (num13 != 0u) {
								num5 = rightSegment;
								laneIndex2 = num11;
								rightLaneId = num13;
							} else {
								rightSegment = instance.m_segments.m_buffer[(int)rightSegment].GetRightSegment(nextNodeId);
							}
							if (++num6 == 8) {
								break;
							}
						}
					}
					if (leftLaneId != 0u && (num4 != prevSegment || flag3)) {
						this.ProcessItemPedBicycle(item, nextNodeId, num4, ref instance.m_segments.m_buffer[(int)num4], connectOffset, laneIndex, leftLaneId); // ped
					}
					if (rightLaneId != 0u && rightLaneId != leftLaneId && (num5 != prevSegment || flag3)) {
						this.ProcessItemPedBicycle(item, nextNodeId, num5, ref instance.m_segments.m_buffer[(int)num5], connectOffset, laneIndex2, rightLaneId); // ped
					}
					int laneIndex3;
					uint lane3;
					if ((this._vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None && instance.m_segments.m_buffer[(int)prevSegment].GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out laneIndex3, out lane3)) {
						this.ProcessItemPedBicycle(item, nextNodeId, prevSegment, ref instance.m_segments.m_buffer[(int)prevSegment], connectOffset, laneIndex3, lane3); // bicycle
					}
				} else {
					for (int j = 0; j < 8; j++) {
						ushort segment3 = nextNode.GetSegment(j);
						if (segment3 != 0 && segment3 != prevSegment) {
							this.ProcessItem(debug, item, nextNodeId, segment3, ref instance.m_segments.m_buffer[(int)segment3], ref similarLaneIndexFromLeft, connectOffset, false, true);
						}
					}
				}
				NetInfo.LaneType laneType = this._laneTypes & ~NetInfo.LaneType.Pedestrian;
				VehicleInfo.VehicleType vehicleType = this._vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;
				if ((byte)(item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
					laneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
				}
				int num15;
				uint lane4;
				if (laneType != NetInfo.LaneType.None && vehicleType != VehicleInfo.VehicleType.None && instance.m_segments.m_buffer[(int)prevSegment].GetClosestLane(prevLaneIndex, laneType, vehicleType, out num15, out lane4)) {
					NetInfo.Lane lane5 = prevSegmentInfo.m_lanes[num15];
					byte connectOffset2;
					if ((instance.m_segments.m_buffer[(int)prevSegment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((byte)(lane5.m_finalDirection & NetInfo.Direction.Backward) != 0)) {
						connectOffset2 = 1;
					} else {
						connectOffset2 = 254;
					}
					this.ProcessItemPedBicycle(item, nextNodeId, prevSegment, ref instance.m_segments.m_buffer[(int)prevSegment], connectOffset2, num15, lane4); // ped
				}
			} else {
				bool blocked = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
				bool pedestrianAllowed = (byte)(this._laneTypes & NetInfo.LaneType.Pedestrian) != 0;
				bool enablePedestrian = false;
				byte connectOffset3 = 0;
				if (pedestrianAllowed) {
					if (isBicycleLane) {
						connectOffset3 = connectOffset;
						enablePedestrian = (nextNode.Info.m_class.m_service == ItemClass.Service.Beautification);
					} else if (this._vehicleLane != 0u) {
						if (this._vehicleLane != item.m_laneID) {
							pedestrianAllowed = false;
						} else {
							connectOffset3 = this._vehicleOffset;
						}
					} else if (this._stablePath) {
						connectOffset3 = 128;
					} else {
						connectOffset3 = (byte)this._pathRandomizer.UInt32(1u, 254u);
					}
				}

				// NON-STOCK CODE START //
				CustomPathManager pathManager = Singleton<CustomPathManager>.instance;
				bool nextIsJunction = nextNode.CountSegments() > 2;
				bool prevIsOutgoingOneWay = TrafficLightsManual.SegmentIsOutgoingOneWay(prevSegment, nextNodeId);
				bool prevIsHighway = false;
				if (prevSegmentInfo.m_netAI is RoadBaseAI)
					prevIsHighway = ((RoadBaseAI)prevSegmentInfo.m_netAI).m_highwayRules;

				NetInfo.Direction normDirection = TrafficPriority.LeftHandDrive ? NetInfo.Direction.Forward : NetInfo.Direction.Backward; // direction to normalize indices to
				int prevRightSimilarLaneIndex;
				int prevLeftSimilarLaneIndex;

				NetInfo.Lane lane = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
				if ((byte)(lane.m_direction & normDirection) != 0) {
					prevRightSimilarLaneIndex = lane.m_similarLaneIndex;
					prevLeftSimilarLaneIndex = lane.m_similarLaneCount - lane.m_similarLaneIndex - 1;
				} else {
					prevRightSimilarLaneIndex = lane.m_similarLaneCount - lane.m_similarLaneIndex - 1;
					prevLeftSimilarLaneIndex = lane.m_similarLaneIndex;
				}
				bool foundForced = false;
				byte totalIncomingLanes = 0;
				// NON-STOCK CODE END //

				ushort nextSegmentId = instance.m_segments.m_buffer[(int)item.m_position.m_segment].GetRightSegment(nextNodeId);

				bool hasStraight = false;
				bool hasLeft = false;
				bool hasRight = false;
				int leftRemaining = 0;
				int rightRemaining = 0;
				if (instance.m_segments.m_buffer[(int)prevSegment].m_startNode == nextNodeId) {
					hasStraight = CustomRoadAI.segmentGeometries[prevSegment].startNodeHasStraightSegment;
					leftRemaining = CustomRoadAI.segmentGeometries[prevSegment].startNodeNumLeftSegments;
					rightRemaining = CustomRoadAI.segmentGeometries[prevSegment].startNodeNumRightSegments;
				} else {
					hasStraight = CustomRoadAI.segmentGeometries[prevSegment].endNodeHasStraightSegment;
					leftRemaining = CustomRoadAI.segmentGeometries[prevSegment].endNodeNumLeftSegments;
					rightRemaining = CustomRoadAI.segmentGeometries[prevSegment].endNodeNumRightSegments;
				}
				hasLeft = leftRemaining > 0;
				hasRight = rightRemaining > 0;

				if (debug) {
					Log.Message($"Pathfind ({this._pathFindIndex}) @ node {nextNodeId}: hasStraight: {hasStraight}, hasLeft: {hasLeft}, rightRemaining: {rightRemaining}");
                }

				for (int k = 0; k < 8; k++) {
					if (nextSegmentId == 0 || nextSegmentId == item.m_position.m_segment) {
						break;
					}

					/*if (Options.nodesOverlay) {
						bool isRealRight = TrafficPriority.IsRightSegment(prevSegment, nextSegmentId, nextNodeId);
						bool isRealLeft = TrafficPriority.IsLeftSegment(prevSegment, nextSegmentId, nextNodeId);
						bool isRealStraight = !isRealRight && !isRealLeft;
						if (rightRemaining > 0) {
							if (!isRealRight)
								Log.Warning($"k={k}: segment {nextSegmentId} ({prevSegment}) is not right. rightRemaining={rightRemaining}. hasStraight={hasStraight}, hasLeft={hasLeft}. realRSL: {isRealLeft},{isRealStraight},{isRealRight}");
						} else if (rightRemaining < 0) {
							if (!isRealLeft)
								Log.Warning($"k={k}: segment {nextSegmentId} ({prevSegment}) is not left. rightRemaining={rightRemaining}. hasStraight={hasStraight}, hasLeft={hasLeft}. realRSL: {isRealLeft},{isRealStraight},{isRealRight}");
						} else if (!hasLeft) {
							if (!isRealStraight)
								Log.Warning($"k={k}: segment {nextSegmentId} ({prevSegment}) is not straight. rightRemaining={rightRemaining}. hasStraight={hasStraight}, hasLeft={hasLeft}. realRSL: {isRealLeft},{isRealStraight},{isRealRight}");
						}
					}*/

					// NON-STOCK CODE START //
					byte incomingVehicleLanes = 0;
					if (Options.allRelaxed || (Options.relaxedBusses && _transportVehicle)) {
						// NON-STOCK CODE END //
						if (ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
							blocked = true;
						}
						// NON-STOCK CODE START //
					}  else if (!enablePedestrian) {
						try {
							var nextSegment = instance.m_segments.m_buffer[nextSegmentId];
							var nextSegmentInfo = nextSegment.Info;
							bool isRight = rightRemaining > 0;
							bool isStraight = rightRemaining == 0 && hasStraight;
							bool isLeft = !isRight & !isStraight;

							VehicleInfo.VehicleType vehicleType2 = this._vehicleTypes;
							NetInfo.LaneType drivingEnabledLaneTypes = this._laneTypes;
							drivingEnabledLaneTypes &= ~NetInfo.LaneType.Pedestrian;
							drivingEnabledLaneTypes &= ~NetInfo.LaneType.Parking;

							if (debug) {
								Log.Message($"Path finding ({this._pathFindIndex}): Segment {nextSegmentId}: rightRemaining: {rightRemaining}");
							}

							NetInfo.Direction nextDir = nextSegment.m_startNode != nextNodeId ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
							NetInfo.Direction nextDir2 = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);

							// valid next lanes:
							int[] laneIndexes = new int[16]; // index of NetNode.Info.m_lanes
							uint[] laneIds = new uint[16]; // index of NetManager.m_lanes.m_buffer
							uint[] indexByRightSimilarLaneIndex = new uint[16];
							uint[] indexByLeftSimilarLaneIndex = new uint[16];

							bool laneArrowsDefined = false;
							uint curLaneI = 0;
							uint curLaneId = nextSegment.m_lanes;
							int i = 0;
							while (i < nextSegmentInfo.m_lanes.Length && curLaneId != 0u) {
								// determine valid lanes based on lane arrows
								NetInfo.Lane nextLane = nextSegmentInfo.m_lanes[i];

								if ((byte)(nextLane.m_finalDirection & nextDir2) != 0 && nextLane.CheckType(_laneTypes, _vehicleTypes)) {
									if (nextLane.CheckType(drivingEnabledLaneTypes, _vehicleTypes))
										++incomingVehicleLanes;
									if (debug) {
										Log.Message($"Path finding ({this._pathFindIndex}): Segment {nextSegmentId}, lane {curLaneId}, {i} is compatible (prevSegment: {prevSegment}). laneTypes: {_laneTypes.ToString()}, vehicleTypes: {_vehicleTypes.ToString()}, incomingLanes={incomingVehicleLanes}");
									}

									// calculate current similar lane index starting from right line
									int nextRightSimilarLaneIndex;
									int nextLeftSimilarLaneIndex;
									if ((byte)(nextLane.m_direction & normDirection) != 0) {
										nextRightSimilarLaneIndex = nextLane.m_similarLaneIndex;
										nextLeftSimilarLaneIndex = nextLane.m_similarLaneCount - nextLane.m_similarLaneIndex - 1;
									} else {
										nextRightSimilarLaneIndex = nextLane.m_similarLaneCount - nextLane.m_similarLaneIndex - 1;
										nextLeftSimilarLaneIndex = nextLane.m_similarLaneIndex;
									}

									if (((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.LeftForwardRight) != NetLane.Flags.None) {
										laneArrowsDefined = true;
									}

									if (isRight) {// TrafficPriority.IsLeftSegment(nextSegmentId, item.m_position.m_segment, targetNodeId)) {
										if (((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Left) ==
											NetLane.Flags.Left) {
											laneIndexes[curLaneI] = i;
											laneIds[curLaneI] = curLaneId;
											indexByRightSimilarLaneIndex[nextRightSimilarLaneIndex] = curLaneI + 1;
											indexByLeftSimilarLaneIndex[nextLeftSimilarLaneIndex] = curLaneI + 1;
											curLaneI++;
										}
									} else if (isLeft) { // if (TrafficPriority.IsRightSegment(nextSegmentId, item.m_position.m_segment, targetNodeId)) {
										if (((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Right) ==
											NetLane.Flags.Right) {
											laneIndexes[curLaneI] = i;
											laneIds[curLaneI] = curLaneId;
											indexByRightSimilarLaneIndex[nextRightSimilarLaneIndex] = curLaneI + 1;
											indexByLeftSimilarLaneIndex[nextLeftSimilarLaneIndex] = curLaneI + 1;
											curLaneI++;
										}
									} else {
										if (((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Forward) != NetLane.Flags.None || ((NetLane.Flags)instance.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.LeftForwardRight) == NetLane.Flags.None) { // valid if straight segment and no arrows given or forward arrow is set
											laneIndexes[curLaneI] = i;
											laneIds[curLaneI] = curLaneId;
											indexByRightSimilarLaneIndex[nextRightSimilarLaneIndex] = curLaneI + 1;
											indexByLeftSimilarLaneIndex[nextLeftSimilarLaneIndex] = curLaneI + 1;
											curLaneI++;
										}
									}
								}

								curLaneId = instance.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_nextLane;
								i++;
							} // foreach lane

							if (laneArrowsDefined) {
								var newLaneIndex = 0;
								var newLaneId = 0u;
								int nextLaneI = -1;
								int nextCompatibleLaneCount = Convert.ToInt32(curLaneI);

								if (debug) {
									Log.Message($"Path finding ({this._pathFindIndex}): Lane arrows defined.");
								}

								if (curLaneI > 0) {
									if (Options.highwayRules && prevIsOutgoingOneWay && prevIsHighway && nextIsJunction) {
										if (isLeft) {
											// right segment joins with highway: find directly matching lanes from right
											nextLaneI = Convert.ToInt32(indexByRightSimilarLaneIndex[prevRightSimilarLaneIndex]) - 1;
										} else if (isRight) {
											// left segment joins with highway: find directly matching lanes from left
											nextLaneI = Convert.ToInt32(indexByLeftSimilarLaneIndex[prevLeftSimilarLaneIndex]) - 1;
										} else {
											// straight segment
											if (hasLeft && hasRight) {
#if DEBUG
												if (debug)
													Log.Message($"Pathfind ({this._pathFindIndex}) prevLeftSimilarLaneIndex: {prevLeftSimilarLaneIndex}, totalIncomingLanes: {totalIncomingLanes}, node: {nextNodeId}, prev segment: {prevSegment}, next segment: {nextSegmentId}");
#endif
												int nextLeftSimilarIndex = prevLeftSimilarLaneIndex - totalIncomingLanes;
												if (nextLeftSimilarIndex >= 0 && nextLeftSimilarIndex < curLaneI)
													nextLaneI = Convert.ToInt32(indexByLeftSimilarLaneIndex[nextLeftSimilarIndex]) - 1;
											} else if (hasLeft) {
												// sort right
												nextLaneI = Convert.ToInt32(indexByLeftSimilarLaneIndex[prevLeftSimilarLaneIndex]) - 1;
											} else {
												// sort left
												nextLaneI = Convert.ToInt32(indexByRightSimilarLaneIndex[prevRightSimilarLaneIndex]) - 1;
											}
										}
										if (nextLaneI < 0 || nextLaneI >= nextCompatibleLaneCount)
											goto nextIter; // no path to this lane
									} else if (curLaneI == 1) {
										nextLaneI = 0;
									} else {
										// lane matching
										int prevSimilarLaneCount = lane.m_similarLaneCount;

										int nextRightSimilarLaneIndex = -1;
										int x = 0;
										if (nextIsJunction) {
											// at junctions: try to match distinct lanes (1-to-1, n-to-1)
											x = prevRightSimilarLaneIndex;
										} else {
											HandleLaneMergesAndSplits(prevRightSimilarLaneIndex, nextCompatibleLaneCount, prevSimilarLaneCount, out nextRightSimilarLaneIndex, out x);
										}

										// find best matching lane
										for (int j = 0; j < 16; ++j) {
											if (indexByRightSimilarLaneIndex[j] == 0)
												continue;
											nextLaneI = Convert.ToInt32(indexByRightSimilarLaneIndex[j]) - 1;
											nextRightSimilarLaneIndex = j;
											if (x == 0) { // matching lane found
												break;
											}
											--x;
										}
#if DEBUG
										if (debug) {
											Log.Message($"Path finding ({this._pathFindIndex}): Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right. There are {curLaneI} candidate lanes. We choose lane {nextLaneI} (index {newLaneIndex}, id {newLaneId}, {nextRightSimilarLaneIndex} from right). lhd: {TrafficPriority.LeftHandDrive}, ped: {pedestrianAllowed}, magical flag4: {blocked}");
										}
#endif
									}
								} else {
#if DEBUG
									if (debug) {
										Log.Message($"Path finding ({this._pathFindIndex}): Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: No compatible lanes found");
									}
#endif
									goto nextIter;
								}

								// go to matched lane
								newLaneIndex = laneIndexes[nextLaneI];
								newLaneId = laneIds[nextLaneI];

								if (ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian, newLaneIndex, newLaneId, out foundForced))
									blocked = true;

								if (foundForced) {
#if DEBUG
									if (debug) {
										Log.Message($"Path finding ({this._pathFindIndex}): Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: FORCED LANE FOUND!");
									}
#endif
								}
							} else {
#if DEBUG
								if (debug) {
									Log.Message($"Path finding ({this._pathFindIndex}): Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: No lane arrows defined");
								}
#endif

								if (ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
									blocked = true;
								}
							}
						} catch (Exception e) {
							Log.Error($"Error occurred in custom path-finding (main): {e.ToString()}");

							// stock code fallback
							if (this.ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
								blocked = true;
							}
						}
						// NON-STOCK CODE END
					} else {
						// stock code:
						if (this.ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, enablePedestrian)) {
							blocked = true;
						}
					}

					nextIter:
					nextSegmentId = instance.m_segments.m_buffer[(int)nextSegmentId].GetRightSegment(nextNodeId);
					totalIncomingLanes += incomingVehicleLanes;
					--rightRemaining;
				} // foreach segment
				if (blocked) {
#if DEBUG
						if (debug) {
							Log.Message($"Path finding ({this._pathFindIndex}): Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Road may be blocked");
						}
#endif
					// vehicles may turn around if the street is blocked
					nextSegmentId = item.m_position.m_segment;
					this.ProcessItem(debug, item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromLeft, connectOffset, true, false);
				}
				// NON-STOCK CODE START
				/*if (foundForced)
					return;*/
				// NON-STOCK CODE END
				if (pedestrianAllowed) {
#if DEBUG
						if (debug) {
							Log.Message($"Path finding ({this._pathFindIndex}): Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevRightSimilarLaneIndex} from right: Ped allowed");
						}
#endif
					nextSegmentId = item.m_position.m_segment;
					int laneIndex4;
					uint lane6;
					if (instance.m_segments.m_buffer[(int)nextSegmentId].GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, this._vehicleTypes, out laneIndex4, out lane6)) {
						this.ProcessItemPedBicycle(item, nextNodeId, nextSegmentId, ref instance.m_segments.m_buffer[(int)nextSegmentId], connectOffset3, laneIndex4, lane6); // ped
					}
				}
			}
			if (nextNode.m_lane != 0u) {
				bool targetDisabled = (nextNode.m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None;
				ushort segment4 = instance.m_lanes.m_buffer[(int)((UIntPtr)nextNode.m_lane)].m_segment;
				if (segment4 != 0 && segment4 != item.m_position.m_segment) {
#if DEBUG
						if (debug) {
							Log.Message($"Path finding ({this._pathFindIndex}): Exploring path from {segment4} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}: handling special lanes");
						}
#endif
					this.ProcessItem(item, nextNodeId, targetDisabled, segment4, ref instance.m_segments.m_buffer[(int)segment4], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);
				}
			}
		}