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; } }
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); }
/* 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); }
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; }
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); }
static void Postfix(ref NetNode data) { if (data.CountSegments() != 2) { return; } ushort nodeID = NetUtil.GetID(data); ref NetNodeExt netNodeExt = ref NetworkExtensionManager.Instance.NodeBuffer[nodeID];
private bool CheckForSegmentConnectionThroughNode(ushort seg, NetNode node) { for (int i = 0; i < node.CountSegments(); i++) { if (node.GetSegment(i) == seg) { return(true); } } return(false); }
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); }
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; } } } } } }
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)); } } } }
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); } }
/* 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); }
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); }
// 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; }
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); }
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); } } }