From 91dca4e553c77ce4a70bb379dc95cd48363d07c2 Mon Sep 17 00:00:00 2001 From: Victor-Philipp Negoescu Date: Fri, 13 Jul 2018 21:39:00 +0200 Subject: [PATCH] 1.10.9: New path-finding implementation works --- TLM/TLM/Custom/PathFinding/CustomPathFind2.cs | 844 +++++++++++++++--- TLM/TLM/State/ConfigData/Debug.cs | 6 +- TLM/TLM/State/ConfigData/PathFinding.cs | 4 +- TLM/TLM/State/GlobalConfig.cs | 2 +- TLM/TLM/TLM.csproj | 8 +- TLM/TLM/Traffic/Data/SegmentEndFlags.cs | 18 +- TLM/TLM/TrafficManagerMod.cs | 6 +- 7 files changed, 766 insertions(+), 122 deletions(-) diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs index bac6d55b..16525fae 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs @@ -7,10 +7,12 @@ using System.Reflection; using System.Text; using System.Threading; +using TrafficManager.Manager; using TrafficManager.Manager.Impl; using TrafficManager.State; using TrafficManager.Traffic; using TrafficManager.Traffic.Data; +using TrafficManager.TrafficLight; using UnityEngine; using static TrafficManager.Custom.PathFinding.CustomPathManager; @@ -19,12 +21,25 @@ public class CustomPathFind2 : PathFind { private const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = 0.003921569f; private const float TICKET_COST_CONVERSION_FACTOR = BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f; - private static readonly CustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance; - private static readonly JunctionRestrictionsManager junctionManager = JunctionRestrictionsManager.Instance; - private static readonly VehicleRestrictionsManager vehicleRestrictionsManager = VehicleRestrictionsManager.Instance; - private static readonly SpeedLimitManager speedLimitManager = SpeedLimitManager.Instance; - private static readonly TrafficMeasurementManager trafficMeasurementManager = TrafficMeasurementManager.Instance; - private static readonly RoutingManager routingManager = RoutingManager.Instance; +#if ROUTING + private readonly RoutingManager m_routingManager = RoutingManager.Instance; +#endif +#if JUNCTIONRESTRICTIONS + private readonly JunctionRestrictionsManager m_junctionManager = JunctionRestrictionsManager.Instance; +#endif +#if VEHICLERESTRICTIONS + private readonly VehicleRestrictionsManager m_vehicleRestrictionsManager = VehicleRestrictionsManager.Instance; +#endif +#if SPEEDLIMITS + private readonly SpeedLimitManager m_speedLimitManager = SpeedLimitManager.Instance; +#endif +#if CUSTOMTRAFFICLIGHTS + private readonly CustomSegmentLightsManager m_customTrafficLightsManager = CustomSegmentLightsManager.Instance; +#endif +#if ADVANCEDAI && ROUTING + private readonly TrafficMeasurementManager m_trafficMeasurementManager = TrafficMeasurementManager.Instance; +#endif + private GlobalConfig m_conf = null; private struct BufferItem { public PathUnit.Position m_position; @@ -34,8 +49,18 @@ private struct BufferItem { public uint m_laneID; public NetInfo.Direction m_direction; public NetInfo.LaneType m_lanesUsed; - //public VehicleInfo.VehicleType m_vehiclesUsed; - //public float m_trafficRand; +#if PARKINGAI + public VehicleInfo.VehicleType m_vehiclesUsed; +#endif +#if ADVANCEDAI && ROUTING + public float m_trafficRand; +#endif + } + + private enum LaneChangingCostCalculationMode { + None, + ByLaneDistance, + ByGivenDistance } // private stock fields @@ -115,6 +140,7 @@ private bool m_terminated { // custom fields private PathUnitQueueItem m_queueItem; + private bool m_isHeavyVehicle; #if DEBUG public uint m_failedPathFinds = 0; public uint m_succeededPathFinds = 0; @@ -123,6 +149,11 @@ private bool m_terminated { private ushort m_startSegmentA; private ushort m_startSegmentB; #endif +#if ROUTING + private bool m_isRoadVehicle; + private bool m_isLaneArrowObeyingEntity; + //private bool m_isLaneConnectionObeyingEntity; +#endif private void Awake() { Type stockPathFindType = typeof(PathFind); @@ -209,6 +240,8 @@ public bool ExtCalculatePath(uint unit, bool skipQueue) { } private void PathFindImplementation(uint unit, ref PathUnit data) { + m_conf = GlobalConfig.Instance; // NON-STOCK CODE + NetManager netManager = Singleton.instance; m_laneTypes = (NetInfo.LaneType)m_pathUnits.m_buffer[unit].m_laneTypes; @@ -218,7 +251,8 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { m_pathRandomizer = new Randomizer(unit); m_carBanMask = NetSegment.Flags.CarBan; - if ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IS_HEAVY) != 0) { + m_isHeavyVehicle = (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IS_HEAVY) != 0; // NON-STOCK CODE (refactored) + if (m_isHeavyVehicle) { m_carBanMask |= NetSegment.Flags.HeavyBan; } @@ -241,6 +275,23 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { m_laneTypes |= NetInfo.LaneType.TransportVehicle; } +#if ROUTING + m_isRoadVehicle = + (m_queueItem.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None; + + m_isLaneArrowObeyingEntity = +#if DEBUG + ! Options.allRelaxed && // debug option: all vehicle may ignore lane arrows +#endif + (! Options.relaxedBusses || m_queueItem.vehicleType != ExtVehicleType.Bus) && // option: busses may ignore lane arrows + (m_vehicleTypes & LaneArrowManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None && + (m_queueItem.vehicleType & LaneArrowManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None; + + //m_isLaneConnectionObeyingEntity = + // (m_vehicleTypes & LaneConnectionManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None && + // (m_queueItem.vehicleType & LaneConnectionManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None; +#endif + int posCount = m_pathUnits.m_buffer[unit].m_positionCount & 0xF; int vehiclePosIndicator = m_pathUnits.m_buffer[unit].m_positionCount >> 4; BufferItem bufferItemStartA = default(BufferItem); @@ -252,7 +303,11 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { m_startOffsetA = data.m_position00.m_offset; bufferItemStartA.m_laneID = m_startLaneA; bufferItemStartA.m_position = data.m_position00; - GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed); + GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed +#if PARKINGAI + , out bufferItemStartA.m_vehiclesUsed +#endif + ); bufferItemStartA.m_comparisonValue = 0f; bufferItemStartA.m_duration = 0f; } else { @@ -272,7 +327,11 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { m_startOffsetB = data.m_position02.m_offset; bufferItemStartB.m_laneID = m_startLaneB; bufferItemStartB.m_position = data.m_position02; - GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed); + GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed +#if PARKINGAI + , out bufferItemStartB.m_vehiclesUsed +#endif + ); bufferItemStartB.m_comparisonValue = 0f; bufferItemStartB.m_duration = 0f; } else { @@ -288,7 +347,11 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { m_endLaneA = PathManager.GetLaneID(data.m_position01); bufferItemEndA.m_laneID = m_endLaneA; bufferItemEndA.m_position = data.m_position01; - GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed); + GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed +#if PARKINGAI + , out bufferItemEndA.m_vehiclesUsed +#endif + ); bufferItemEndA.m_methodDistance = 0.01f; bufferItemEndA.m_comparisonValue = 0f; bufferItemEndA.m_duration = 0f; @@ -301,7 +364,11 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { m_endLaneB = PathManager.GetLaneID(data.m_position03); bufferItemEndB.m_laneID = m_endLaneB; bufferItemEndB.m_position = data.m_position03; - GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed); + GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed +#if PARKINGAI + , out bufferItemEndB.m_vehiclesUsed +#endif + ); bufferItemEndB.m_methodDistance = 0.01f; bufferItemEndB.m_comparisonValue = 0f; bufferItemEndB.m_duration = 0f; @@ -386,22 +453,21 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { } } + ushort startNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode; + ushort endNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode; + if ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) { - ushort startNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode; ProcessItemMain(candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], startNodeId, ref netManager.m_nodes.m_buffer[startNodeId], 0, false); } if ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None) { - ushort endNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode; ProcessItemMain(candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], endNodeId, ref netManager.m_nodes.m_buffer[endNodeId], 255, false); } int numIter = 0; ushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes; if (specialNodeId != 0) { - ushort startNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode; - ushort endNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode; - bool nodesDisabled = ((netManager.m_nodes.m_buffer[startNode2].m_flags | netManager.m_nodes.m_buffer[endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None; + bool nodesDisabled = ((netManager.m_nodes.m_buffer[startNodeId].m_flags | netManager.m_nodes.m_buffer[endNodeId].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None; while (specialNodeId != 0) { NetInfo.Direction direction = NetInfo.Direction.None; @@ -440,6 +506,10 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { float duration = (m_laneTypes != NetInfo.LaneType.Pedestrian && (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance; m_pathUnits.m_buffer[unit].m_length = duration; m_pathUnits.m_buffer[unit].m_speed = (byte)Mathf.Clamp(finalBufferItem.m_methodDistance * 100f / Mathf.Max(0.01f, finalBufferItem.m_duration), 0f, 255f); +#if PARKINGAI + m_pathUnits.m_buffer[unit].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; + m_pathUnits.m_buffer[unit].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; +#endif uint currentPathUnitId = unit; int currentItemPositionCount = 0; @@ -508,6 +578,10 @@ private void PathFindImplementation(uint unit, ref PathUnit data) { m_pathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY; m_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId; m_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount; +#if PARKINGAI + m_pathUnits.m_buffer[currentPathUnitId].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // (this is not accurate!) + m_pathUnits.m_buffer[currentPathUnitId].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // (this is not accurate!) +#endif sumOfPositionCounts += currentItemPositionCount; Singleton.instance.m_pathUnitCount = (int)(m_pathUnits.ItemCount() - 1); } finally { @@ -536,18 +610,51 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne NetManager netManager = Singleton.instance; ushort prevSegmentId = item.m_position.m_segment; + byte prevLaneIndex = item.m_position.m_lane; + bool prevIsPedestrianLane = false; bool prevIsBicycleLane = false; bool prevIsCenterPlatform = false; bool prevIsElevated = false; +#if ADVANCEDAI && ROUTING + // NON-STOCK CODE START + bool prevIsCarLane = false; + // NON-STOCK CODE END +#endif int prevRelSimilarLaneIndex = 0; + // NON-STOCK CODE START + float prevMaxSpeed = 1f; + float prevLaneSpeed = 1f; + // NON-STOCK CODE END + NetInfo prevSegmentInfo = prevSegment.Info; - if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) { + if (prevLaneIndex < prevSegmentInfo.m_lanes.Length) { NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane]; prevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian); prevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle); prevIsCenterPlatform = prevLaneInfo.m_centerPlatform; prevIsElevated = prevLaneInfo.m_elevated; + +#if ADVANCEDAI && ROUTING + // NON-STOCK CODE START + if (Options.advancedAI) { + prevIsCarLane = + (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && + (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None + ; + } + // NON-STOCK CODE END +#endif + + // NON-STOCK CODE START +#if SPEEDLIMITS + prevMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(prevSegmentId, prevLaneIndex, item.m_laneID, prevLaneInfo); +#else + prevMaxSpeed = prevLaneInfo.m_speedLimit; +#endif + prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); + // NON-STOCK CODE END + prevRelSimilarLaneIndex = (((prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? (prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1) : prevLaneInfo.m_similarLaneIndex); } @@ -555,13 +662,12 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne for (int i = 0; i < 8; i++) { ushort nextSegmentId = nextNode.GetSegment(i); if (nextSegmentId != 0) { - ProcessItemCosts(item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane); + ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, true, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane); } } } else if (prevIsPedestrianLane) { // gwe are going to a pedestrian lane if (!prevIsElevated) { - int prevLaneIndex = item.m_position.m_lane; if (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) { bool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None; bool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None; @@ -623,24 +729,24 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne } if (leftLaneId != 0 && (nextLeftSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) { - ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, nextLeftSegmentId, ref netManager.m_segments.m_buffer[nextLeftSegmentId], nextNodeId, ref nextNode, leftLaneIndex, leftLaneId, ref netManager.m_lanes.m_buffer[leftLaneId], connectOffset, connectOffset); + ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextLeftSegmentId, ref netManager.m_segments.m_buffer[nextLeftSegmentId], nextNodeId, ref nextNode, leftLaneIndex, leftLaneId, ref netManager.m_lanes.m_buffer[leftLaneId], connectOffset, connectOffset); } if (rightLaneId != 0 && rightLaneId != leftLaneId && (nextRightSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) { - ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, nextRightSegmentId, ref netManager.m_segments.m_buffer[nextRightSegmentId], nextNodeId, ref nextNode, rightLaneIndex, rightLaneId, ref netManager.m_lanes.m_buffer[rightLaneId], connectOffset, connectOffset); + ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextRightSegmentId, ref netManager.m_segments.m_buffer[nextRightSegmentId], nextNodeId, ref nextNode, rightLaneIndex, rightLaneId, ref netManager.m_lanes.m_buffer[rightLaneId], connectOffset, connectOffset); } int nextLaneIndex; uint nextLaneId; if ((m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None && prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) { - ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], connectOffset, connectOffset); + ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], connectOffset, connectOffset); } } else { // we are going from pedestrian lane to a beautification node for (int j = 0; j < 8; j++) { ushort nextSegmentId = nextNode.GetSegment(j); if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) { - ProcessItemCosts(item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true); + ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true); } } } @@ -683,12 +789,13 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne if (m_randomParking) { nextItem.m_comparisonValue += (float)m_pathRandomizer.Int32(300u) / m_maxLength; } - ProcessItemPedBicycle(nextItem, ref prevSegment, ref prevLane, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, sameSegLaneIndex, sameSegLaneId, ref netManager.m_lanes.m_buffer[sameSegLaneId], sameSegConnectOffset, 128); + ProcessItemPedBicycle(nextItem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, sameSegLaneIndex, sameSegLaneId, ref netManager.m_lanes.m_buffer[sameSegLaneId], sameSegConnectOffset, 128); } } } else { // We are going to a non-pedestrian lane + bool nextIsBeautificationNode = nextNode.Info.m_class.m_service == ItemClass.Service.Beautification; // NON-STOCK CODE (refactored) bool allowPedestrian = (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None; // allow switching from pedestrian lane to a non-pedestrian lane? bool allowBicycle = false; // allow switching from a pedestrian lane to a bike lane? byte switchConnectOffset = 0; // lane switching offset @@ -696,7 +803,7 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne if (prevIsBicycleLane) { // we are going to a bicycle lane switchConnectOffset = connectOffset; - allowBicycle = (nextNode.Info.m_class.m_service == ItemClass.Service.Beautification); + allowBicycle = nextIsBeautificationNode; } else if (m_vehicleLane != 0) { // there is a parked vehicle position if (m_vehicleLane != item.m_laneID) { @@ -728,40 +835,131 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne } ushort nextSegmentId = 0; - if ((m_vehicleTypes & (VehicleInfo.VehicleType.Ferry | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None) { + if ((m_vehicleTypes & (VehicleInfo.VehicleType.Ferry +#if !ROUTING + | VehicleInfo.VehicleType.Monorail +#endif + )) != VehicleInfo.VehicleType.None) { + // ferry (/ monorail) + bool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None; for (int k = 0; k < 8; k++) { nextSegmentId = nextNode.GetSegment(k); if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) { - ProcessItemCosts(item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle); + ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle); } } - if (isUturnAllowedHere && (m_vehicleTypes & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None) { + if (isUturnAllowedHere +#if !ROUTING + && (m_vehicleTypes & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None +#endif + ) { nextSegmentId = prevSegmentId; - ProcessItemCosts(item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, false); + ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, false); } } else { - bool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None; - nextSegmentId = prevSegment.GetRightSegment(nextNodeId); - for (int l = 0; l < 8; l++) { - if (nextSegmentId == 0) { - break; - } + // road vehicles / trams / trains / metros (/ monorails) / etc. - if (nextSegmentId == prevSegmentId) { - break; - } +#if ROUTING + bool exploreUturn = false; +#else + bool exploreUturn = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None; +#endif + +#if ROUTING + bool prevIsRouted = false; + uint laneRoutingIndex = 0; + bool nextIsStartNode = nextNodeId == prevSegment.m_startNode; + if (nextIsStartNode || nextNodeId == prevSegment.m_endNode) { + laneRoutingIndex = m_routingManager.GetLaneEndRoutingIndex(item.m_laneID, nextIsStartNode); + prevIsRouted = m_routingManager.laneEndBackwardRoutings[laneRoutingIndex].routed; + } - if (ProcessItemCosts(item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle)) { - isUturnAllowedHere = true; + if (allowBicycle || !prevIsRouted) { + /* + * pedestrian to bicycle lane switch or no routing information available: + * if pedestrian lanes should be explored (allowBicycle == true): do this here + * if previous segment has custom routing (prevIsRouted == true): do NOT explore vehicle lanes here, else: vanilla exploration of vehicle lanes + */ + // NON-STOCK CODE END +#endif + nextSegmentId = prevSegment.GetRightSegment(nextNodeId); + for (int l = 0; l < 8; l++) { + if (nextSegmentId == 0) { + break; + } + + if (nextSegmentId == prevSegmentId) { + break; + } + + if (ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, +#if ROUTING + !prevIsRouted // NON-STOCK CODE +#else + true +#endif + , allowBicycle)) { + exploreUturn = true; // allow exceptional u-turns + } + + nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId); } +#if ROUTING + } // NON-STOCK CODE +#endif + + // NON-STOCK CODE START + float segmentSelectionCost = 1f; + float laneSelectionCost = 1f; + float laneChangingCost = 1f; + bool enableAdvancedAI = false; + // NON-STOCK CODE END - nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId); +#if ADVANCEDAI && ROUTING + /* + * ======================================================================================================= + * Calculate Advanced Vehicle AI cost factors + * ======================================================================================================= + */ + if ( + Options.advancedAI && + m_isRoadVehicle && + prevIsCarLane && + !m_stablePath + ) { + enableAdvancedAI = true; + CalculateAdvancedAiCostFactors(ref item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, ref segmentSelectionCost, ref laneSelectionCost, ref laneChangingCost); } +#endif + +#if ROUTING + if (prevIsRouted) { + exploreUturn = false; // custom routing processes regular u-turns + if (ProcessItemRouted(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed +#if ADVANCEDAI + , enableAdvancedAI, laneChangingCost, +#endif + segmentSelectionCost, laneSelectionCost, nextNodeId, ref nextNode, false, m_routingManager.segmentRoutings[prevSegmentId], m_routingManager.laneEndBackwardRoutings[laneRoutingIndex], connectOffset)) { + exploreUturn = true; // allow exceptional u-turns + } + } else if (! exploreUturn) { + // no exceptional u-turns allowed: allow regular u-turns + exploreUturn = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None; + } +#endif - if (isUturnAllowedHere && (m_vehicleTypes & VehicleInfo.VehicleType.Tram) == VehicleInfo.VehicleType.None) { - ProcessItemCosts(item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, prevSegmentId, ref prevSegment, ref prevRelSimilarLaneIndex, connectOffset, true, false); + if (exploreUturn && (m_vehicleTypes & VehicleInfo.VehicleType.Tram) == VehicleInfo.VehicleType.None) { + ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, +#if ADVANCEDAI && ROUTING + enableAdvancedAI, laneChangingCost, +#endif + nextNodeId, ref nextNode, false, prevSegmentId, ref prevSegment, +#if ROUTING + segmentSelectionCost, laneSelectionCost, null, +#endif + ref prevRelSimilarLaneIndex, connectOffset, true, false); } } @@ -769,7 +967,7 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne int nextLaneIndex; uint nextLaneId; if (prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, m_vehicleTypes, out nextLaneIndex, out nextLaneId)) { - ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], switchConnectOffset, switchConnectOffset); + ProcessItemPedBicycle(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], switchConnectOffset, switchConnectOffset); } } } @@ -778,13 +976,13 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne bool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled; ushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment; if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) { - ProcessItemPublicTransport(item, ref prevSegment, ref prevLane, nextNodeId, targetDisabled, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset); + ProcessItemPublicTransport(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, targetDisabled, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset); } } } // 2 - private void ProcessItemPublicTransport(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) { + private void ProcessItemPublicTransport(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) { if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) { return; } @@ -797,29 +995,23 @@ private void ProcessItemPublicTransport(BufferItem item, ref NetSegment prevSegm NetInfo nextSegmentInfo = nextSegment.Info; NetInfo prevSegmentInfo = prevSegment.Info; int nextNumLanes = nextSegmentInfo.m_lanes.Length; - float prevMaxSpeed = 1f; - float prevSpeed = 1f; + // float prevMaxSpeed = 1f; // stock code commented + // float prevLaneSpeed = 1f; // stock code commented NetInfo.LaneType prevLaneType = NetInfo.LaneType.None; if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) { NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane]; -#if SPEEDLIMITS - // NON-STOCK CODE START - prevMaxSpeed = speedLimitManager.GetLockFreeGameSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); - // NON-STOCK CODE END -#else - prevMaxSpeed = prevLaneInfo.m_speedLimit; -#endif + // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented + // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented prevLaneType = prevLaneInfo.m_laneType; if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) { prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle); } - prevSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); } float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length; float offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength; float methodDistance = item.m_methodDistance + offsetLength; - float comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * m_maxLength); + float comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength); float duration = item.m_duration + offsetLength / prevMaxSpeed; Vector3 b = prevLane.CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR); @@ -860,14 +1052,14 @@ private void ProcessItemPublicTransport(BufferItem item, ref NetSegment prevSegm nextItem.m_methodDistance = methodDistance + distance; } - if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < 1000f) && !m_stablePath) { + if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance) return; } float nextMaxSpeed; #if SPEEDLIMITS // NON-STOCK CODE START - nextMaxSpeed = speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); + nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); // NON-STOCK CODE END #else nextMaxSpeed = nextLaneInfo.m_speedLimit; @@ -910,13 +1102,56 @@ private void ProcessItemPublicTransport(BufferItem item, ref NetSegment prevSegm nextItem.m_laneID = nextLaneId; nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType); +#if PARKINGAI + nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType); +#endif +#if ADVANCEDAI && ROUTING + // NON-STOCK CODE START + nextItem.m_trafficRand = item.m_trafficRand; + // NON-STOCK CODE END +#endif AddBufferItem(nextItem, item.m_position); } } - // 3 - private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, ushort nextSegmentId, ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian) { +#if ADVANCEDAI && ROUTING + // 3a (non-routed, no adv. AI) + private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian) { + return ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, isMiddle, nextSegmentId, ref nextSegment, ref laneIndexFromInner, connectOffset, enableVehicle, enablePedestrian, false); + } +#endif + +#if ROUTING || ADVANCEDAI + // 3b (non-routed, adv. AI toggleable) + private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian +#if ADVANCEDAI && ROUTING + , bool enableAdvancedAI +#endif + ) { + return ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, +#if ADVANCEDAI && ROUTING + enableAdvancedAI, 1f, +#endif + nextNodeId, ref nextNode, isMiddle, nextSegmentId, ref nextSegment, +#if ROUTING + 1f, 1f, null, +#endif + ref laneIndexFromInner, connectOffset, enableVehicle, enablePedestrian); + } +#endif + + // 3c + private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, +#if ADVANCEDAI && ROUTING + bool enableAdvancedAI, float laneChangingCost, +#endif + ushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment, +#if ROUTING + float segmentSelectionCost, float laneSelectionCost, LaneTransitionData? transition, +#endif + ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian + ) { bool blocked = false; if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) { return blocked; @@ -926,25 +1161,24 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N NetInfo nextSegmentInfo = nextSegment.Info; NetInfo prevSegmentInfo = prevSegment.Info; int nextNumLanes = nextSegmentInfo.m_lanes.Length; - uint nextLaneId = nextSegment.m_lanes; NetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward; NetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir); - float prevMaxSpeed = 1f; - float prevLaneSpeed = 1f; + // float prevMaxSpeed = 1f; // stock code commented + // float prevLaneSpeed = 1f; // stock code commented NetInfo.LaneType prevLaneType = NetInfo.LaneType.None; VehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None; +#if ADVANCEDAI && ROUTING + int prevOuterSimilarLaneIndex = 0; +#endif if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) { NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane]; prevLaneType = prevLaneInfo.m_laneType; prevVehicleType = prevLaneInfo.m_vehicleType; -#if SPEEDLIMITS - // NON-STOCK CODE START - prevMaxSpeed = speedLimitManager.GetLockFreeGameSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); - // NON-STOCK CODE END -#else - prevMaxSpeed = prevLaneInfo.m_speedLimit; + // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented + // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented +#if ADVANCEDAI && ROUTING + prevOuterSimilarLaneIndex = m_routingManager.CalcOuterSimilarLaneIndex(prevLaneInfo); #endif - prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); } bool acuteTurningAngle = false; @@ -966,7 +1200,13 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N float duration = item.m_duration + offsetLength / prevMaxSpeed; if (!m_stablePath) { - offsetLength *= (float)(new Randomizer(m_pathFindIndex << 16 | item.m_position.m_segment).Int32(900, 1000 + prevSegment.m_trafficDensity * 10) + m_pathRandomizer.Int32(20u)) * 0.001f; +#if ADVANCEDAI && ROUTING + if (!enableAdvancedAI) { +#endif + offsetLength *= (float)(new Randomizer(m_pathFindIndex << 16 | item.m_position.m_segment).Int32(900, 1000 + prevSegment.m_trafficDensity * 10) + m_pathRandomizer.Int32(20u)) * 0.001f; +#if ADVANCEDAI && ROUTING + } +#endif } if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevVehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Car && (prevSegment.m_flags & m_carBanMask) != NetSegment.Flags.None) { @@ -977,7 +1217,13 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N offsetLength *= 0.95f; } - float comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength); +#if ROUTING + offsetLength *= segmentSelectionCost; + offsetLength *= laneSelectionCost; +#endif + + float baseLength = offsetLength / (prevLaneSpeed * m_maxLength); // NON-STOCK CODE + float comparisonValue = item.m_comparisonValue + baseLength; // NON-STOCK CODE (refactored) if (!m_ignoreCost) { int ticketCost = prevLane.m_ticketCost; if (ticketCost != 0) { @@ -991,7 +1237,7 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N Vector3 b = prevLane.CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR); int newLaneIndexFromInner = laneIndexFromInner; - bool transitionNode = (nextNode.m_flags & NetNode.Flags.Transition) != NetNode.Flags.None; + bool isTransition = (nextNode.m_flags & NetNode.Flags.Transition) != NetNode.Flags.None; NetInfo.LaneType allowedLaneTypes = m_laneTypes; VehicleInfo.VehicleType allowedVehicleTypes = m_vehicleTypes; @@ -1005,7 +1251,35 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N allowedLaneTypes &= ~NetInfo.LaneType.Pedestrian; } - for (int nextLaneIndex = 0; nextLaneIndex < nextNumLanes && nextLaneId != 0; nextLaneIndex++) { + // NON-STOCK CODE START + bool applyTransportTransferPenalty = + Options.realisticPublicTransport && + !m_stablePath && + (allowedLaneTypes & (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian)) == (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian) && + m_conf.PathFinding.PublicTransportTransitionMinPenalty >= 0 && + m_conf.PathFinding.PublicTransportTransitionMaxPenalty > m_conf.PathFinding.PublicTransportTransitionMinPenalty + ; + + int nextLaneIndex = 0; + uint nextLaneId = nextSegment.m_lanes; + int maxNextLaneIndex = nextNumLanes - 1; +#if ADVANCEDAI && ROUTING + byte laneDist = 0; +#endif +#if ROUTING + if (transition != null) { + LaneTransitionData trans = (LaneTransitionData)transition; + nextLaneIndex = trans.laneIndex; + nextLaneId = trans.laneId; + maxNextLaneIndex = nextLaneIndex; +#if ADVANCEDAI + laneDist = trans.distance; +#endif + } +#endif + // NON-STOCK CODE END + + for (; nextLaneIndex <= maxNextLaneIndex && nextLaneId != 0; nextLaneIndex++) { NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex]; if ((nextLaneInfo.m_finalDirection & nextFinalDir) != NetInfo.Direction.None) { if (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) && (nextSegmentId != item.m_position.m_segment || nextLaneIndex != item.m_position.m_lane) && (nextLaneInfo.m_finalDirection & nextFinalDir) != NetInfo.Direction.None) { @@ -1017,14 +1291,14 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N Vector3 a = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? netManager.m_lanes.m_buffer[nextLaneId].m_bezier.a : netManager.m_lanes.m_buffer[nextLaneId].m_bezier.d; float transitionCost = Vector3.Distance(a, b); - if (transitionNode) { + if (isTransition) { transitionCost *= 2f; } float nextMaxSpeed; #if SPEEDLIMITS // NON-STOCK CODE START - nextMaxSpeed = speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); + nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); // NON-STOCK CODE END #else nextMaxSpeed = nextLaneInfo.m_speedLimit; @@ -1040,12 +1314,42 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N nextItem.m_methodDistance = methodDistance + transitionCost; } - if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < 1000f) && !m_stablePath) { + if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance) nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane; continue; } - nextItem.m_comparisonValue = comparisonValue + transitionCostOverMeanMaxSpeed; + // NON-STOCK CODE START + if (applyTransportTransferPenalty) { + if ( + isMiddle && + (nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None && + (item.m_lanesUsed & NetInfo.LaneType.PublicTransport) != NetInfo.LaneType.None && + nextLaneInfo.m_laneType == NetInfo.LaneType.PublicTransport + ) { + // apply penalty when switching between public transport lines + float transportTransitionPenalty = (m_conf.PathFinding.PublicTransportTransitionMinPenalty + ((float)nextNode.m_maxWaitTime * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR) * (m_conf.PathFinding.PublicTransportTransitionMaxPenalty - m_conf.PathFinding.PublicTransportTransitionMinPenalty)) / (0.5f * this.m_maxLength); + transitionCostOverMeanMaxSpeed += transportTransitionPenalty; + } else if ( + (nextLaneId == this.m_startLaneA || nextLaneId == this.m_startLaneB) && + (item.m_lanesUsed & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport)) == NetInfo.LaneType.Pedestrian + ) { + // account for public tranport transition costs on non-PT paths + float transportTransitionPenalty = (2f * m_conf.PathFinding.PublicTransportTransitionMaxPenalty) / (0.5f * this.m_maxLength); + transitionCostOverMeanMaxSpeed += transportTransitionPenalty; + } + } + // NON-STOCK CODE END + +#if ADVANCEDAI && ROUTING + if (enableAdvancedAI) { + nextItem.m_comparisonValue = transitionCostOverMeanMaxSpeed; + } else { +#endif + nextItem.m_comparisonValue = comparisonValue + transitionCostOverMeanMaxSpeed; +#if ADVANCEDAI && ROUTING + } +#endif nextItem.m_duration = duration + transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f); nextItem.m_direction = nextDir; @@ -1082,18 +1386,45 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N blocked = true; } - nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType); nextItem.m_laneID = nextLaneId; + nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType); +#if PARKINGAI + nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType); +#endif +#if ADVANCEDAI && ROUTING + // NON-STOCK CODE START + nextItem.m_trafficRand = item.m_trafficRand; + // NON-STOCK CODE END +#endif if ((nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None && (nextLaneInfo.m_vehicleType & m_vehicleTypes) != VehicleInfo.VehicleType.None) { - int firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget; - int lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget; - if (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) { - nextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength); - } - if (!m_transportVehicle && nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle) { - nextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength); +#if ADVANCEDAI && ROUTING + if (enableAdvancedAI) { + if (m_queueItem.vehicleId != 0 || (nextLaneId != m_startLaneA && nextLaneId != m_startLaneB)) { + if (laneDist != 0) { + // apply lane changing costs + comparisonValue *= + 1f + + laneDist * + laneChangingCost * + (laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f); // additional costs for changing multiple lanes at once + } + } + + nextItem.m_comparisonValue += comparisonValue; + } else { +#endif + int firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget; + int lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget; + if (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) { + nextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength); + } + if (!m_transportVehicle && nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle) { + nextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength); + } +#if ADVANCEDAI && ROUTING } +#endif } AddBufferItem(nextItem, item.m_position); @@ -1112,11 +1443,39 @@ private bool ProcessItemCosts(BufferItem item, ref NetSegment prevSegment, ref N } // 4 - private void ProcessItemPedBicycle(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextSegmentId, ref NetSegment nextSegment, ushort nextNodeId, ref NetNode nextNode, int nextLaneIndex, uint nextLaneId, ref NetLane nextLane, byte connectOffset, byte laneSwitchOffset) { + private void ProcessItemPedBicycle(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextSegmentId, ref NetSegment nextSegment, ushort nextNodeId, ref NetNode nextNode, int nextLaneIndex, uint nextLaneId, ref NetLane nextLane, byte connectOffset, byte laneSwitchOffset) { if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) { return; } + // NON-STOCK CODE START +#if JUNCTIONRESTRICTIONS || CUSTOMTRAFFICLIGHTS + if (Options.junctionRestrictionsEnabled || Options.timedLightsEnabled) { + bool nextIsStartNode = nextNodeId == nextSegment.m_startNode; + if (nextIsStartNode || nextNodeId == nextSegment.m_endNode) { +#if JUNCTIONRESTRICTIONS + if (Options.junctionRestrictionsEnabled) { + // check if pedestrians are not allowed to cross here + if (!m_junctionManager.IsPedestrianCrossingAllowed(nextSegmentId, nextIsStartNode)) { + return; + } + } +#endif + +#if CUSTOMTRAFFICLIGHTS + if (Options.timedLightsEnabled) { + // check if pedestrian light won't change to green + ICustomSegmentLights lights = m_customTrafficLightsManager.GetSegmentLights(nextSegmentId, nextIsStartNode, false); + if (lights != null && lights.InvalidPedestrianLight) { + return; + } + } +#endif + } + } +#endif + // NON-STOCK CODE END + NetInfo nextSegmentInfo = nextSegment.Info; NetInfo prevSegmentInfo = prevSegment.Info; int nextNumLanes = nextSegmentInfo.m_lanes.Length; @@ -1135,29 +1494,23 @@ private void ProcessItemPedBicycle(BufferItem item, ref NetSegment prevSegment, offset = (byte)(((direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) ? 255 : 0); } - float prevMaxSpeed = 1f; - float prevSpeed = 1f; + // float prevMaxSpeed = 1f; // stock code commented + // float prevLaneSpeed = 1f; // stock code commented NetInfo.LaneType prevLaneType = NetInfo.LaneType.None; if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) { NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane]; -#if SPEEDLIMITS - // NON-STOCK CODE START - prevMaxSpeed = speedLimitManager.GetLockFreeGameSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); - // NON-STOCK CODE END -#else - prevMaxSpeed = prevLaneInfo.m_speedLimit; -#endif + // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented + // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented prevLaneType = prevLaneInfo.m_laneType; if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) { prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle); } - prevSpeed = CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); } float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length; float offsetLength = (float)Mathf.Abs(laneSwitchOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength; float methodDistance = item.m_methodDistance + offsetLength; - float comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * m_maxLength); + float comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength); float duration = item.m_duration + offsetLength / prevMaxSpeed; if (!m_ignoreCost) { @@ -1184,14 +1537,14 @@ private void ProcessItemPedBicycle(BufferItem item, ref NetSegment prevSegment, nextItem.m_methodDistance = methodDistance + distance; } - if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < 1000f) && !m_stablePath) { + if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance) return; } float nextMaxSpeed; #if SPEEDLIMITS // NON-STOCK CODE START - nextMaxSpeed = speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); + nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); // NON-STOCK CODE END #else nextMaxSpeed = nextLaneInfo.m_speedLimit; @@ -1234,11 +1587,183 @@ private void ProcessItemPedBicycle(BufferItem item, ref NetSegment prevSegment, nextItem.m_laneID = nextLaneId; nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType); +#if PARKINGAI + nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType); +#endif +#if ADVANCEDAI && ROUTING + // NON-STOCK CODE START + nextItem.m_trafficRand = item.m_trafficRand; + // NON-STOCK CODE END +#endif AddBufferItem(nextItem, item.m_position); } } +#if ROUTING + // 5 (custom: process routed vehicle paths) + private bool ProcessItemRouted(BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, +#if ADVANCEDAI && ROUTING + bool enableAdvancedAI, float laneChangingCost, +#endif + float segmentSelectionCost, float laneSelectionCost, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, SegmentRoutingData prevSegmentRouting, LaneEndRoutingData prevLaneEndRouting, byte connectOffset) { + /* + * ======================================================================================================= + * Fetch lane end transitions, check if there are any present + * ======================================================================================================= + */ + LaneTransitionData[] laneTransitions = prevLaneEndRouting.transitions; + if (laneTransitions == null) { + return false; + } + + ushort prevSegmentId = item.m_position.m_segment; + int prevLaneIndex = item.m_position.m_lane; + NetInfo prevSegmentInfo = prevSegment.Info; + if (prevLaneIndex >= prevSegmentInfo.m_lanes.Length) { + return false; + } + NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane]; + +#if VEHICLERESTRICTIONS + /* + * ======================================================================================================= + * Check vehicle restrictions, especially bans + * ======================================================================================================= + */ + bool canUseLane = CanUseLane(prevSegmentId, prevSegmentInfo, prevLaneIndex, prevLaneInfo); + if (! canUseLane && Options.vehicleRestrictionsAggression == VehicleRestrictionsAggression.Strict) { + // vehicle is strictly prohibited to use this lane + return false; + } +#endif + + bool strictLaneRouting = + m_isLaneArrowObeyingEntity && + nextNode.Info.m_class.m_service != ItemClass.Service.Beautification && + (nextNode.m_flags & NetNode.Flags.Untouchable) == NetNode.Flags.None + ; + bool prevIsCarLane = + (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && + (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None + ; + + /* + * ======================================================================================================= + * Check if u-turns may be performed + * ======================================================================================================= + */ + bool isUturnAllowedHere = false; // is u-turn allowed at this place? + if ((this.m_vehicleTypes & (VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None) { // is vehicle able to perform a u-turn? +#if JUNCTIONRESTRICTIONS + if (Options.junctionRestrictionsEnabled) { + bool nextIsStartNode = nextNodeId == prevSegment.m_startNode; + bool prevIsOutgoingOneWay = nextIsStartNode ? prevSegmentRouting.startNodeOutgoingOneWay : prevSegmentRouting.endNodeOutgoingOneWay; + + // determine if the vehicle may u-turn at the target node, according to customization + isUturnAllowedHere = + m_isRoadVehicle && // only road vehicles may perform u-turns + prevIsCarLane && // u-turns for road vehicles only + !m_isHeavyVehicle && // only small vehicles may perform u-turns + !prevIsOutgoingOneWay && // do not u-turn on one-ways + m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode) // only do u-turns if allowed + ; + } else { +#endif + isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None; +#if JUNCTIONRESTRICTIONS + } +#endif + } + +#if VEHICLERESTRICTIONS + /* + * ======================================================================================================= + * Apply vehicle restriction costs + * ======================================================================================================= + */ + if (!canUseLane) { + laneSelectionCost *= VehicleRestrictionsManager.PATHFIND_PENALTIES[(int)Options.vehicleRestrictionsAggression]; + } +#endif + + /* + * ======================================================================================================= + * Apply costs for large vehicles using inner lanes on highways + * ======================================================================================================= + */ + if (Options.preferOuterLane && + m_isHeavyVehicle && + m_isRoadVehicle && + prevIsCarLane && + prevSegmentRouting.highway && + prevLaneInfo.m_similarLaneCount > 1 && + m_pathRandomizer.Int32(m_conf.PathFinding.HeavyVehicleInnerLanePenaltySegmentSel) == 0) { + + int prevOuterSimilarLaneIndex = m_routingManager.CalcOuterSimilarLaneIndex(prevLaneInfo); + float prevRelOuterLane = ((float)prevOuterSimilarLaneIndex / (float)(prevLaneInfo.m_similarLaneCount - 1)); + laneSelectionCost *= 1f + m_conf.PathFinding.HeavyVehicleMaxInnerLanePenalty * prevRelOuterLane; + } + + /* + * ======================================================================================================= + * Explore available lane end routings + * ======================================================================================================= + */ + NetManager netManager = Singleton.instance; + bool blocked = false; + bool uturnExplored = false; + for (int k = 0; k < laneTransitions.Length; ++k) { + ushort nextSegmentId = laneTransitions[k].segmentId; + + if (nextSegmentId == 0) { + continue; + } + + if (nextSegmentId == prevSegmentId) { + if (!isUturnAllowedHere) { + // prevent double/forbidden exploration of previous segment by vanilla code during this method execution + continue; + } + // we are going to explore a regular u-turn + uturnExplored = true; + } + + if (laneTransitions[k].type == LaneEndTransitionType.Invalid) { + continue; + } + + // allow vehicles to ignore strict lane routing when moving off + bool relaxedLaneRouting = + m_isRoadVehicle && + (m_queueItem.vehicleType & (ExtVehicleType.Service | ExtVehicleType.PublicTransport | ExtVehicleType.Emergency)) != ExtVehicleType.None && + m_queueItem.vehicleId == 0 && + (laneTransitions[k].laneId == m_startLaneA || laneTransitions[k].laneId == m_startLaneB); + + if ( + !relaxedLaneRouting && + (strictLaneRouting && laneTransitions[k].type == LaneEndTransitionType.Relaxed) + ) { + continue; + } + + bool foundForced = false; + int dummy = -1; // not required when using custom routing + if ( + ProcessItemCosts(item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, +#if ADVANCEDAI && ROUTING + enableAdvancedAI, laneChangingCost, +#endif + nextNodeId, ref nextNode, isMiddle, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], segmentSelectionCost, laneSelectionCost, laneTransitions[k], ref dummy, connectOffset, true, false) + ) { + blocked = true; + } + } + + return blocked && !uturnExplored; + } +#endif + private void AddBufferItem(BufferItem item, PathUnit.Position target) { uint laneLocation = m_laneLocation[item.m_laneID]; uint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index @@ -1306,20 +1831,127 @@ private float CalculateLaneSpeed(float maxSpeed, byte startOffset, byte endOffse return maxSpeed; } - private void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType type) { + private void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType laneType +#if PARKINGAI + , out VehicleInfo.VehicleType vehicleType +#endif + ) { NetManager netManager = Singleton.instance; NetInfo info = netManager.m_segments.m_buffer[pathPos.m_segment].Info; if (info.m_lanes.Length > pathPos.m_lane) { direction = info.m_lanes[pathPos.m_lane].m_finalDirection; - type = info.m_lanes[pathPos.m_lane].m_laneType; + laneType = info.m_lanes[pathPos.m_lane].m_laneType; +#if PARKINGAI + vehicleType = info.m_lanes[pathPos.m_lane].m_vehicleType; +#endif if ((netManager.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) { direction = NetInfo.InvertDirection(direction); } } else { direction = NetInfo.Direction.None; - type = NetInfo.LaneType.None; + laneType = NetInfo.LaneType.None; +#if PARKINGAI + vehicleType = VehicleInfo.VehicleType.None; +#endif + } + } + +#if VEHICLERESTRICTIONS + private bool CanUseLane(ushort segmentId, NetInfo segmentInfo, int laneIndex, NetInfo.Lane laneInfo) { + if (!Options.vehicleRestrictionsEnabled || + m_queueItem.vehicleType == ExtVehicleType.None || + m_queueItem.vehicleType == ExtVehicleType.Tram || + (laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None) { + return true; } + + ExtVehicleType allowedTypes = m_vehicleRestrictionsManager.GetAllowedVehicleTypes(segmentId, segmentInfo, (uint)laneIndex, laneInfo, VehicleRestrictionsMode.Configured); + + return ((allowedTypes & m_queueItem.vehicleType) != ExtVehicleType.None); } +#endif + +#if ADVANCEDAI && ROUTING + private void CalculateAdvancedAiCostFactors(ref BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, ref float segmentSelectionCost, ref float laneSelectionCost, ref float laneChangingCost) { + NetInfo prevSegmentInfo = prevSegment.Info; + bool nextIsJunction = (nextNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction; + + if (nextIsJunction) { + /* + * ======================================================================================================= + * Calculate costs for randomized lane selection behind junctions and highway transitions + * ======================================================================================================= + */ + // TODO check if highway transitions are actually covered by this code + if ( + !m_isHeavyVehicle && + m_pathRandomizer.Int32(m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel) == 0 && + m_pathRandomizer.Int32((uint)prevSegmentInfo.m_lanes.Length) == 0 + ) { + // randomized lane selection at junctions + laneSelectionCost *= 1f + m_conf.AdvancedVehicleAI.LaneRandomizationCostFactor; + } + + /* + * ======================================================================================================= + * Calculate junction costs + * ======================================================================================================= + */ + // TODO if (prevSegmentRouting.highway) ? + segmentSelectionCost *= 1f + m_conf.AdvancedVehicleAI.JunctionBaseCost; + } + + bool nextIsStartNode = prevSegment.m_startNode == nextNodeId; + bool nextIsEndNode = nextNodeId == prevSegment.m_endNode; + if (nextIsStartNode || nextIsEndNode) { // next node is a regular node + /* + * ======================================================================================================= + * Calculate traffic measurement costs for segment selection + * ======================================================================================================= + */ + NetInfo.Direction prevFinalDir = nextIsStartNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward; + prevFinalDir = ((prevSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? prevFinalDir : NetInfo.InvertDirection(prevFinalDir); + TrafficMeasurementManager.SegmentDirTrafficData prevDirTrafficData = + m_trafficMeasurementManager.segmentDirTrafficData[m_trafficMeasurementManager.GetDirIndex(item.m_position.m_segment, prevFinalDir)]; + + float segmentTraffic = Mathf.Clamp(1f - (float)prevDirTrafficData.meanSpeed / (float)TrafficMeasurementManager.REF_REL_SPEED + item.m_trafficRand, 0, 1f); + + segmentSelectionCost *= 1f + + m_conf.AdvancedVehicleAI.TrafficCostFactor * + segmentTraffic; + + if ( + m_conf.AdvancedVehicleAI.LaneDensityRandInterval > 0 && + nextIsJunction && + (nextNode.m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut) + ) { + item.m_trafficRand = 0.01f * ((float)m_pathRandomizer.Int32((uint)m_conf.AdvancedVehicleAI.LaneDensityRandInterval + 1u) - m_conf.AdvancedVehicleAI.LaneDensityRandInterval / 2f); + } + + if ( + m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost > 0 && + (Singleton.instance.m_nodes.m_buffer[nextIsStartNode ? prevSegment.m_endNode : prevSegment.m_startNode].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction // check previous node + ) { + /* + * ======================================================================================================= + * Calculate lane changing base cost factor when in front of junctions + * ======================================================================================================= + */ + laneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost; + } + + /* + * ======================================================================================================= + * Calculate general lane changing base cost factor + * ======================================================================================================= + */ + if (m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost > 0 && m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost > m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost) { + float rand = (float)m_pathRandomizer.Int32(101u) / 100f; + laneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost + rand * (m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost - m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost); + } + } + } +#endif private void PathFindThread() { while (true) { diff --git a/TLM/TLM/State/ConfigData/Debug.cs b/TLM/TLM/State/ConfigData/Debug.cs index 290e5fc6..3e40a82d 100644 --- a/TLM/TLM/State/ConfigData/Debug.cs +++ b/TLM/TLM/State/ConfigData/Debug.cs @@ -8,8 +8,8 @@ namespace TrafficManager.State.ConfigData { #if DEBUG public class Debug { public bool[] Switches = { - false, // 0: path-find debug log - false, // 1: path-find costs debug log + false, // 0: - + false, // 1: - false, // 2: parking ai debug log (basic) false, // 3: do not actually repair stuck vehicles/cims, just report false, // 4: parking ai debug log (extended) @@ -19,7 +19,7 @@ public class Debug { false, // 8: debug routing false, // 9: debug vehicle to segment end linking false, // 10: prevent routing recalculation on global configuration reload - false, // 11: disable custom routing + false, // 11: - false, // 12: pedestrian path-find debug log false, // 13: priority rules debug false, // 14: disable GUI overlay of citizens having a valid path diff --git a/TLM/TLM/State/ConfigData/PathFinding.cs b/TLM/TLM/State/ConfigData/PathFinding.cs index 529cb916..6f34bf21 100644 --- a/TLM/TLM/State/ConfigData/PathFinding.cs +++ b/TLM/TLM/State/ConfigData/PathFinding.cs @@ -43,11 +43,11 @@ public class PathFinding { /// /// Minimum penalty for entering public transport vehicles /// - public float PublicTransportTransitionMinPenalty = 250f; + public float PublicTransportTransitionMinPenalty = 0f; /// /// Maximum penalty for entering public transport vehicles /// - public float PublicTransportTransitionMaxPenalty = 500f; + public float PublicTransportTransitionMaxPenalty = 100f; } } diff --git a/TLM/TLM/State/GlobalConfig.cs b/TLM/TLM/State/GlobalConfig.cs index 69c48021..05db2240 100644 --- a/TLM/TLM/State/GlobalConfig.cs +++ b/TLM/TLM/State/GlobalConfig.cs @@ -21,7 +21,7 @@ namespace TrafficManager.State { public class GlobalConfig : GenericObservable { public const string FILENAME = "TMPE_GlobalConfig.xml"; public const string BACKUP_FILENAME = FILENAME + ".bak"; - private static int LATEST_VERSION = 14; + private static int LATEST_VERSION = 15; #if DEBUG private static uint lastModificationCheckFrame = 0; #endif diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index da3e0e2a..348dec89 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -18,7 +18,7 @@ full false bin\Debug\ - DEBUG;QUEUEDSTATS + DEBUG;PF2;QUEUEDSTATS;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS prompt 0 true @@ -27,7 +27,7 @@ pdbonly true bin\Release\ - QUEUEDSTATS + PF2;QUEUEDSTATS;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS prompt 4 true @@ -35,7 +35,7 @@ true bin\DebugGeometry\ - DEBUG;QUEUEDSTATS;DEBUGGEO;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGTTL;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGHK + DEBUG;PF2;QUEUEDSTATS;DEBUGGEO;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGTTL;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGHK;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS true 0 full @@ -57,7 +57,7 @@ true bin\PF2_Debug\ - DEBUG;QUEUEDSTATS;DEBUGGEO;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGTTL;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGHK;PF2;PARKINGAI;SPEEDLIMITS + DEBUG;QUEUEDSTATS;DEBUGGEO;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGTTL;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGHK;PF2;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS true 0 full diff --git a/TLM/TLM/Traffic/Data/SegmentEndFlags.cs b/TLM/TLM/Traffic/Data/SegmentEndFlags.cs index 8127113b..8913c847 100644 --- a/TLM/TLM/Traffic/Data/SegmentEndFlags.cs +++ b/TLM/TLM/Traffic/Data/SegmentEndFlags.cs @@ -17,22 +17,34 @@ public struct SegmentEndFlags { public TernaryBool pedestrianCrossingAllowed; private bool defaultEnterWhenBlockedAllowed; + private bool defaultUturnAllowed; //private bool defaultPedestrianCrossingAllowed; public void UpdateDefaults(SegmentEndGeometry segmentEndGeometry) { NodeGeometry nodeGeo = NodeGeometry.Get(segmentEndGeometry.NodeId()); bool newDefaultEnterWhenBlockedAllowed = false; - NetNode.Flags _nodeFlags = NetNode.Flags.None; + bool newDefaultUturnAllowed = false; + //NetNode.Flags _nodeFlags = NetNode.Flags.None; Constants.ServiceFactory.NetService.ProcessNode(segmentEndGeometry.NodeId(), delegate (ushort nodeId, ref NetNode node) { - _nodeFlags = node.m_flags; + //_nodeFlags = node.m_flags; int numOutgoing = 0; int numIncoming = 0; node.CountLanes(nodeId, 0, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, true, ref numOutgoing, ref numIncoming); newDefaultEnterWhenBlockedAllowed = numOutgoing == 1 || numIncoming == 1; + + if (Options.allowUTurns) { + newDefaultUturnAllowed = + (node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition | NetNode.Flags.Bend | NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None && + node.Info?.m_class?.m_service != ItemClass.Service.Beautification + ; + } else { + newDefaultUturnAllowed = (node.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None; + } return true; }); defaultEnterWhenBlockedAllowed = newDefaultEnterWhenBlockedAllowed; + defaultUturnAllowed = newDefaultUturnAllowed; //Log._Debug($"SegmentEndFlags.UpdateDefaults: this={this} _nodeFlags={_nodeFlags} defaultEnterWhenBlockedAllowed={defaultEnterWhenBlockedAllowed}"); } @@ -45,7 +57,7 @@ public bool IsUturnAllowed() { } public bool GetDefaultUturnAllowed() { - return Options.allowUTurns; + return defaultUturnAllowed; } public bool IsLaneChangingAllowedWhenGoingStraight() { diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index b7eb0aa8..6598b0e2 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -7,12 +7,12 @@ namespace TrafficManager { public class TrafficManagerMod : IUserMod { - public static readonly string Version = "1.10.9-alpha1"; + public static readonly string Version = "1.10.9"; - public static readonly uint GameVersion = 176284432u; + public static readonly uint GameVersion = 176415504u; public static readonly uint GameVersionA = 1u; public static readonly uint GameVersionB = 10u; - public static readonly uint GameVersionC = 0u; + public static readonly uint GameVersionC = 1u; public static readonly uint GameVersionBuild = 3u; public string Name => "Traffic Manager: President Edition [" + Version + "]";