예제 #1
0
 public bool IsCargoTruckAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.CargoTruck));
 }
예제 #2
0
 public bool IsRoadVehicleAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.RoadVehicle));
 }
예제 #3
0
 public bool IsBicycleAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.Bicycle));
 }
예제 #4
0
 public bool IsTaxiAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.Taxi));
 }
예제 #5
0
 public bool IsCableCarAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.CableCar));
 }
예제 #6
0
        // TODO improve & remove
        public void Housekeeping(bool mayDelete, bool calculateAutoPedLight)
        {
#if DEBUGHK
            bool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;
#endif

            // we intentionally never delete vehicle types (because we may want to retain traffic light states if a segment is upgraded or replaced)

            ICustomSegmentLight mainLight = MainSegmentLight;
            ushort nodeId = NodeId;
            HashSet <ExtVehicleType>           setupLights     = new HashSet <ExtVehicleType>();
            IDictionary <byte, ExtVehicleType> allAllowedTypes = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypesAsDict(SegmentId, nodeId, VehicleRestrictionsMode.Restricted);            // TODO improve
            ExtVehicleType allAllowedMask = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypes(SegmentId, nodeId, VehicleRestrictionsMode.Restricted);
            SeparateVehicleTypes = ExtVehicleType.None;
#if DEBUGHK
            if (debug)
            {
                Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping started @ seg. {SegmentId}, node {nodeId}, allAllowedTypes={allAllowedTypes.DictionaryToString()}, allAllowedMask={allAllowedMask}");
            }
#endif
            //bool addPedestrianLight = false;
            uint    separateLanes = 0;
            int     defaultLanes  = 0;
            NetInfo segmentInfo   = null;
            Constants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate(ushort segId, ref NetSegment segment) {
                VehicleTypeByLaneIndex = new ExtVehicleType?[segment.Info.m_lanes.Length];
                segmentInfo            = segment.Info;
                return(true);
            });
            HashSet <byte> laneIndicesWithoutSeparateLights = new HashSet <byte>(allAllowedTypes.Keys);           // TODO improve

            // check if separate traffic lights are required
            bool separateLightsRequired = false;
            foreach (KeyValuePair <byte, ExtVehicleType> e in allAllowedTypes)
            {
                if (e.Value != allAllowedMask)
                {
                    separateLightsRequired = true;
                    break;
                }
            }

            // set up vehicle-separated traffic lights
            if (separateLightsRequired)
            {
                foreach (KeyValuePair <byte, ExtVehicleType> e in allAllowedTypes)
                {
                    byte           laneIndex    = e.Key;
                    NetInfo.Lane   laneInfo     = segmentInfo.m_lanes[laneIndex];
                    ExtVehicleType allowedTypes = e.Value;
                    ExtVehicleType defaultMask  = Constants.ManagerFactory.VehicleRestrictionsManager.GetDefaultAllowedVehicleTypes(SegmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Unrestricted);

#if DEBUGHK
                    if (debug)
                    {
                        Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Processing lane {laneIndex} with allowedTypes={allowedTypes}, defaultMask={defaultMask}");
                    }
#endif

                    if (laneInfo.m_vehicleType == VehicleInfo.VehicleType.Car && allowedTypes == defaultMask)
                    {
#if DEBUGHK
                        if (debug)
                        {
                            Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Allowed types equal default mask. Ignoring lane.");
                        }
#endif
                        // no vehicle restrictions applied, generic lights are handled further below
                        ++defaultLanes;
                        continue;
                    }

                    ExtVehicleType mask = allowedTypes & ~ExtVehicleType.Emergency;

#if DEBUGHK
                    if (debug)
                    {
                        Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Trying to add {mask} light");
                    }
#endif

                    ICustomSegmentLight segmentLight;
                    if (!CustomLights.TryGetValue(mask, out segmentLight))
                    {
                        // add a new light
                        segmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red);
                        if (mainLight != null)
                        {
                            segmentLight.CurrentMode = mainLight.CurrentMode;
                            segmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false);
                        }

#if DEBUGHK
                        if (debug)
                        {
                            Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Light for mask {mask} does not exist. Created new light: {segmentLight} (mainLight: {mainLight})");
                        }
#endif

                        CustomLights.Add(mask, segmentLight);
                        VehicleTypes.AddFirst(mask);
                    }

                    mainVehicleType = mask;
                    VehicleTypeByLaneIndex[laneIndex] = mask;
                    laneIndicesWithoutSeparateLights.Remove(laneIndex);
                    ++separateLanes;
                    //addPedestrianLight = true;
                    setupLights.Add(mask);
                    SeparateVehicleTypes |= mask;

#if DEBUGHK
                    if (debug)
                    {
                        Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Finished processing lane {laneIndex}: mainVehicleType={mainVehicleType}, VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()}, laneIndicesWithoutSeparateLights={laneIndicesWithoutSeparateLights.CollectionToString()}, numLights={separateLanes}, SeparateVehicleTypes={SeparateVehicleTypes}");
                    }
#endif
                }
            }

            if (separateLanes == 0 || defaultLanes > 0)
            {
#if DEBUGHK
                if (debug)
                {
                    Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Adding default main vehicle light: {DEFAULT_MAIN_VEHICLETYPE}");
                }
#endif

                // generic traffic lights
                ICustomSegmentLight defaultSegmentLight;
                if (!CustomLights.TryGetValue(DEFAULT_MAIN_VEHICLETYPE, out defaultSegmentLight))
                {
                    defaultSegmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red);
                    if (mainLight != null)
                    {
                        defaultSegmentLight.CurrentMode = mainLight.CurrentMode;
                        defaultSegmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false);
                    }
                    CustomLights.Add(DEFAULT_MAIN_VEHICLETYPE, defaultSegmentLight);
                    VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE);
                }
                mainVehicleType = DEFAULT_MAIN_VEHICLETYPE;
                setupLights.Add(DEFAULT_MAIN_VEHICLETYPE);

                foreach (byte laneIndex in laneIndicesWithoutSeparateLights)
                {
                    VehicleTypeByLaneIndex[laneIndex] = ExtVehicleType.None;
                }

#if DEBUGHK
                if (debug)
                {
                    Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Added default main vehicle light: {defaultSegmentLight}");
                }
#endif
                //addPedestrianLight = true;
            }
            else
            {
                //addPedestrianLight = allAllowedMask == ExtVehicleType.None || (allAllowedMask & ~ExtVehicleType.RailVehicle) != ExtVehicleType.None;
            }

#if DEBUGHK
            if (debug)
            {
                Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Created all necessary lights. VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()}, CustomLights={CustomLights.DictionaryToString()}");
            }
#endif

            if (mayDelete)
            {
                // delete traffic lights for non-existing vehicle-separated configurations
                HashSet <ExtVehicleType> vehicleTypesToDelete = new HashSet <ExtVehicleType>();
                foreach (KeyValuePair <ExtVehicleType, ICustomSegmentLight> e in CustomLights)
                {
                    /*if (e.Key == DEFAULT_MAIN_VEHICLETYPE) {
                     *      continue;
                     * }*/
                    if (!setupLights.Contains(e.Key))
                    {
                        vehicleTypesToDelete.Add(e.Key);
                    }
                }

#if DEBUGHK
                if (debug)
                {
                    Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Going to delete unnecessary lights now: vehicleTypesToDelete={vehicleTypesToDelete.CollectionToString()}");
                }
#endif

                foreach (ExtVehicleType vehicleType in vehicleTypesToDelete)
                {
                    CustomLights.Remove(vehicleType);
                    VehicleTypes.Remove(vehicleType);
                }
            }

            if (CustomLights.ContainsKey(DEFAULT_MAIN_VEHICLETYPE) && VehicleTypes.First.Value != DEFAULT_MAIN_VEHICLETYPE)
            {
                VehicleTypes.Remove(DEFAULT_MAIN_VEHICLETYPE);
                VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE);
            }

            //if (addPedestrianLight) {
#if DEBUGHK
            if (debug)
            {
                Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: adding pedestrian light");
            }
#endif
            if (InternalPedestrianLightState == null)
            {
                InternalPedestrianLightState = RoadBaseAI.TrafficLightState.Red;
            }

            /*} else {
             *      InternalPedestrianLightState = null;
             * }*/

            OnChange(calculateAutoPedLight);
#if DEBUGHK
            if (debug)
            {
                Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Housekeeping complete. VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()} CustomLights={CustomLights.DictionaryToString()}");
            }
#endif
        }
예제 #7
0
 public bool IsPassengerTrainAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.PassengerTrain));
 }
예제 #8
0
        /// <summary>
        /// coppies vehicle restrictions of the current segment
        /// and applies them to all segments until the next junction.
        /// </summary>
        /// <param name="sortedLaneIndex">if provided only current lane is considered</param>
        /// <param name="vehicleTypes">
        /// if provided only bits for which vehicleTypes is set are considered.
        /// </param>
        private void ApplyRestrictionsToAllSegments(
            int?sortedLaneIndex         = null,
            ExtVehicleType?vehicleTypes = null)
        {
            NetManager netManager = Singleton <NetManager> .instance;

            NetInfo selectedSegmentInfo = netManager.m_segments.m_buffer[SelectedSegmentId].Info;

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

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

                ushort  segmentId   = data.SegVisitData.CurSeg.segmentId;
                NetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;

                byte selectedLaneIndex = data.InitLanePos.laneIndex;

                NetInfo.Lane selectedLaneInfo = selectedSegmentInfo.m_lanes[selectedLaneIndex];

                uint laneId    = data.CurLanePos.laneId;
                byte laneIndex = data.CurLanePos.laneIndex;

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

                // apply restrictions of selected segment & lane
                ExtVehicleType mask =
                    VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(
                        SelectedSegmentId,
                        selectedSegmentInfo,
                        selectedLaneIndex,
                        selectedLaneInfo,
                        VehicleRestrictionsMode.Configured);;

                if (vehicleTypes != null)
                {
                    ExtVehicleType currentMask =
                        VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(
                            segmentId,
                            segmentInfo,
                            laneIndex,
                            laneInfo,
                            VehicleRestrictionsMode.Configured);

                    // only apply changes where types is 1. that means:
                    // for bits where types is 0, use currentMask,
                    // for bits where types is 1, use initial mask.
                    ExtVehicleType types2 = (ExtVehicleType)vehicleTypes; //cast
                    mask = (types2 & mask) | (~types2 & currentMask);
                }

                VehicleRestrictionsManager.Instance.SetAllowedVehicleTypes(
                    segmentId,
                    segmentInfo,
                    laneIndex,
                    laneInfo,
                    laneId,
                    mask);

                RefreshCurrentRestrictedSegmentIds(segmentId);

                return(true);
            }

            SegmentLaneTraverser.Traverse(
                SelectedSegmentId,
                SegmentTraverser.TraverseDirection.AnyDirection,
                SegmentTraverser.TraverseSide.AnySide,
                SegmentLaneTraverser.LaneStopCriterion.LaneCount,
                SegmentTraverser.SegmentStopCriterion.Junction,
                VehicleRestrictionsManager.LANE_TYPES,
                VehicleRestrictionsManager.VEHICLE_TYPES,
                LaneVisitorFun);
        }
예제 #9
0
        public bool CustomStartPathFind(ushort vehicleId, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays)
        {
            /// NON-STOCK CODE START ///
            ExtVehicleType?vehicleType = VehicleStateManager.DetermineVehicleType(vehicleId, ref vehicleData);

            if (vehicleType == ExtVehicleType.CargoTrain)
            {
                vehicleType = ExtVehicleType.CargoVehicle;
            }
#if DEBUG
            /*bool reversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;
             * ushort frontVehicleId;
             * if (reversed) {
             *      frontVehicleId = vehicleData.GetLastVehicle(vehicleId);
             * } else {
             *      frontVehicleId = vehicleId;
             * }
             * Log._Debug($"CustomTrainAI.CustomStartPathFind. vehicleID={vehicleId}. reversed={reversed} frontVehicleId={frontVehicleId} type={this.GetType().ToString()} vehicleType={vehicleType} target={vehicleData.m_targetBuilding}");*/
#endif
            /// NON-STOCK CODE END ///

            VehicleInfo info = this.m_info;
            if ((vehicleData.m_flags & Vehicle.Flags.Spawned) == 0 && Vector3.Distance(startPos, endPos) < 100f)
            {
                startPos = endPos;
            }
            bool allowUnderground;
            bool allowUnderground2;
            if (info.m_vehicleType == VehicleInfo.VehicleType.Metro)
            {
                allowUnderground  = true;
                allowUnderground2 = true;
            }
            else
            {
                allowUnderground  = ((vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0);
                allowUnderground2 = false;
            }
            PathUnit.Position startPosA;
            PathUnit.Position startPosB;
            float             num;
            float             num2;
            PathUnit.Position endPosA;
            PathUnit.Position endPosB;
            float             num3;
            float             num4;
            if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out num, out num2) &&
                CustomPathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground2, false, 32f, out endPosA, out endPosB, out num3, out num4))
            {
                if (!startBothWays || num2 > num * 1.2f)
                {
                    startPosB = default(PathUnit.Position);
                }
                if (!endBothWays || num4 > num3 * 1.2f)
                {
                    endPosB = default(PathUnit.Position);
                }
                uint path;
                bool res = false;
                if (vehicleType == null)
                {
                    res = Singleton <PathManager> .instance.CreatePath(out path, ref Singleton <SimulationManager> .instance.m_randomizer, Singleton <SimulationManager> .instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, false, false, true, false);
                }
                else
                {
                    res = Singleton <CustomPathManager> .instance.CreatePath((ExtVehicleType)vehicleType, out path, ref Singleton <SimulationManager> .instance.m_randomizer, Singleton <SimulationManager> .instance.m_currentBuildIndex, ref startPosA, ref startPosB, ref endPosA, ref endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, false, false, true, false);
                }
                if (res)
                {
                    if (vehicleData.m_path != 0u)
                    {
                        Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path);
                    }
                    vehicleData.m_path   = path;
                    vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
                    return(true);
                }
            }
            return(false);
        }
 public void Record()
 {
     allowedVehicleTypes_ = VehicleRestrictionsManager.Instance.GetAllowedVehicleTypesRaw(SegmentId, LaneIndex);
 }
예제 #11
0
        public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget)
        {
            ExtVehicleType?vehicleType = VehicleStateManager.DetermineVehicleType(vehicleID, ref vehicleData);

#if PATHRECALC
            VehicleState state           = VehicleStateManager._GetVehicleState(vehicleID);
            bool         recalcRequested = state.PathRecalculationRequested;
            state.PathRecalculationRequested = false;
#endif

            /*if (vehicleType == null) {
             *      Log._Debug($"CustomCarAI.CustomStartPathFind: Could not determine ExtVehicleType from class type. typeof this={this.GetType().ToString()}");
             * } else {
             *      Log._Debug($"CustomCarAI.CustomStartPathFind: vehicleType={vehicleType}. typeof this={this.GetType().ToString()}");
             * }*/

            VehicleInfo       info             = this.m_info;
            bool              allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
            PathUnit.Position startPosA;
            PathUnit.Position startPosB;
            float             num;
            float             num2;
            PathUnit.Position endPosA;
            PathUnit.Position endPosB;
            float             num3;
            float             num4;
            if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out num, out num2) &&
                CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out num3, out num4))
            {
                if (!startBothWays || num < 10f)
                {
                    startPosB = default(PathUnit.Position);
                }
                if (!endBothWays || num3 < 10f)
                {
                    endPosB = default(PathUnit.Position);
                }
                uint path;
                bool res = false;
                if (vehicleType == null)
                {
                    res = Singleton <CustomPathManager> .instance.CreatePath(out path, ref Singleton <SimulationManager> .instance.m_randomizer, Singleton <SimulationManager> .instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false);
                }
                else
                {
                    res = Singleton <CustomPathManager> .instance.CreatePath(
#if PATHRECALC
                        recalcRequested,
#endif
                        (ExtVehicleType)vehicleType, out path, ref Singleton <SimulationManager> .instance.m_randomizer, Singleton <SimulationManager> .instance.m_currentBuildIndex, ref startPosA, ref startPosB, ref endPosA, ref endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false);
                }
                if (res)
                {
                    if (vehicleData.m_path != 0u)
                    {
                        Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path);
                    }
                    vehicleData.m_path   = path;
                    vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
                    return(true);
                }
            }
            return(false);
        }
        internal static float CalcMaxSpeed(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position position, Vector3 pos, float maxSpeed, bool isRecklessDriver)
        {
            var     netManager   = Singleton <NetManager> .instance;
            NetInfo segmentInfo  = netManager.m_segments.m_buffer[(int)position.m_segment].Info;
            bool    highwayRules = (segmentInfo.m_netAI is RoadBaseAI && ((RoadBaseAI)segmentInfo.m_netAI).m_highwayRules);

            if (!highwayRules)
            {
                if (netManager.m_treatWetAsSnow)
                {
                    DistrictManager districtManager = Singleton <DistrictManager> .instance;
                    byte            district        = districtManager.GetDistrict(pos);
                    DistrictPolicies.CityPlanning cityPlanningPolicies = districtManager.m_districts.m_buffer[(int)district].m_cityPlanningPolicies;
                    if ((cityPlanningPolicies & DistrictPolicies.CityPlanning.StuddedTires) != DistrictPolicies.CityPlanning.None)
                    {
                        if (Options.strongerRoadConditionEffects)
                        {
                            if (maxSpeed > ICY_ROADS_STUDDED_MIN_SPEED)
                            {
                                maxSpeed = ICY_ROADS_STUDDED_MIN_SPEED + (float)(255 - netManager.m_segments.m_buffer[(int)position.m_segment].m_wetness) * 0.0039215686f * (maxSpeed - ICY_ROADS_STUDDED_MIN_SPEED);
                            }
                        }
                        else
                        {
                            maxSpeed *= 1f - (float)netManager.m_segments.m_buffer[(int)position.m_segment].m_wetness * 0.0005882353f;                             // vanilla: -15% .. �
                        }
                        districtManager.m_districts.m_buffer[(int)district].m_cityPlanningPoliciesEffect |= DistrictPolicies.CityPlanning.StuddedTires;
                    }
                    else
                    {
                        if (Options.strongerRoadConditionEffects)
                        {
                            if (maxSpeed > ICY_ROADS_MIN_SPEED)
                            {
                                maxSpeed = ICY_ROADS_MIN_SPEED + (float)(255 - netManager.m_segments.m_buffer[(int)position.m_segment].m_wetness) * 0.0039215686f * (maxSpeed - ICY_ROADS_MIN_SPEED);
                            }
                        }
                        else
                        {
                            maxSpeed *= 1f - (float)netManager.m_segments.m_buffer[(int)position.m_segment].m_wetness * 0.00117647066f;                             // vanilla: -30% .. �
                        }
                    }
                }
                else
                {
                    if (Options.strongerRoadConditionEffects)
                    {
                        float minSpeed = Math.Min(maxSpeed * WET_ROADS_FACTOR, WET_ROADS_MAX_SPEED);
                        if (maxSpeed > minSpeed)
                        {
                            maxSpeed = minSpeed + (float)(255 - netManager.m_segments.m_buffer[(int)position.m_segment].m_wetness) * 0.0039215686f * (maxSpeed - minSpeed);
                        }
                    }
                    else
                    {
                        maxSpeed *= 1f - (float)netManager.m_segments.m_buffer[(int)position.m_segment].m_wetness * 0.0005882353f;                         // vanilla: -15% .. �
                    }
                }

                if (Options.strongerRoadConditionEffects)
                {
                    float minSpeed = Math.Min(maxSpeed * BROKEN_ROADS_FACTOR, BROKEN_ROADS_MAX_SPEED);
                    if (maxSpeed > minSpeed)
                    {
                        maxSpeed = minSpeed + (float)netManager.m_segments.m_buffer[(int)position.m_segment].m_condition * 0.0039215686f * (maxSpeed - minSpeed);
                    }
                }
                else
                {
                    maxSpeed *= 1f + (float)netManager.m_segments.m_buffer[(int)position.m_segment].m_condition * 0.0005882353f;                     // vanilla: � .. +15 %
                }
            }

            ExtVehicleType?vehicleType = CustomVehicleAI.DetermineVehicleTypeFromVehicle(vehicleId, ref vehicleData);
            float          vehicleRand = Math.Min(1f, (float)(vehicleId % 101) * 0.01f);    // we choose 101 because it's a prime number

            if (isRecklessDriver)
            {
                maxSpeed *= 1.5f + vehicleRand * 1.5f;                 // woohooo, 1.5 .. 3
            }
            else if ((vehicleType & ExtVehicleType.PassengerCar) != ExtVehicleType.None)
            {
                maxSpeed *= 0.8f + vehicleRand * 0.3f;                 // a little variance, 0.8 .. 1.1
            }
            else if ((vehicleType & ExtVehicleType.Taxi) != ExtVehicleType.None)
            {
                maxSpeed *= 0.9f + vehicleRand * 0.4f;            // a little variance, 0.9 .. 1.3
            }
            maxSpeed = Math.Max(MIN_SPEED, maxSpeed);             // at least 10 km/h

            return(maxSpeed);
        }
        /// <summary>
        /// Handles vehicle path information in order to manage special nodes (nodes with priority signs or traffic lights).
        /// Data like "vehicle X is on segment S0 and is going to segment S1" is collected.
        /// </summary>
        /// <param name="vehicleId"></param>
        /// <param name="vehicleData"></param>
        internal static void HandleVehicle(ushort vehicleId, ref Vehicle vehicleData, bool addTraffic, bool realTraffic, byte maxUpcomingPathPositions, bool debug = false)
        {
            if (maxUpcomingPathPositions <= 0)
            {
                maxUpcomingPathPositions = 1;                 // we need at least one upcoming path position
            }
            var netManager          = Singleton <NetManager> .instance;
            var lastFrameData       = vehicleData.GetLastFrameData();
            var lastFrameVehiclePos = lastFrameData.m_position;

#if DEBUGV
            var camPos = Camera.main.transform.position;
            //debug = (lastFrameVehiclePos - camPos).sqrMagnitude < CloseLod;
            debug = false;
            List <String> logBuffer = new List <String>();
            bool          logme     = false;
#endif
            if ((vehicleData.m_flags & Vehicle.Flags.Created) == Vehicle.Flags.None)
            {
                TrafficPriority.RemoveVehicleFromSegments(vehicleId);
                return;
            }

            if (vehicleData.Info.m_vehicleType != VehicleInfo.VehicleType.Car &&
                vehicleData.Info.m_vehicleType != VehicleInfo.VehicleType.Train &&
                vehicleData.Info.m_vehicleType != VehicleInfo.VehicleType.Tram)
            {
                //Log._Debug($"HandleVehicle does not handle vehicles of type {vehicleData.Info.m_vehicleType}");
                return;
            }
#if DEBUGV
            logBuffer.Add("Calculating prio info for vehicleId " + vehicleId);
#endif

            ExtVehicleType?vehicleType = CustomVehicleAI.DetermineVehicleTypeFromVehicle(vehicleId, ref vehicleData);
            if (vehicleType == null)
            {
                Log.Warning($"Could not determine vehicle type of vehicle {vehicleId}!");
            }

            if (vehicleType == null || vehicleType == ExtVehicleType.None)
            {
                return;
            }

            // add vehicle to our vehicle list
            VehiclePosition vehiclePos = TrafficPriority.GetVehiclePosition(vehicleId);

            // we extract the segment information directly from the vehicle
            var                      currentPathUnitId        = vehicleData.m_path;
            List <ushort>            realTimeDestinationNodes = new List <ushort>();            // current and upcoming node ids
            List <PathUnit.Position> realTimePositions        = new List <PathUnit.Position>(); // current and upcoming vehicle positions

#if DEBUGV
            logBuffer.Add("* vehicleId " + vehicleId + ". currentPathId: " + currentPathUnitId + " pathPositionIndex: " + vehicleData.m_pathPositionIndex);
#endif

            if (currentPathUnitId > 0)
            {
                // vehicle has a path...
                if ((Singleton <PathManager> .instance.m_pathUnits.m_buffer[currentPathUnitId].m_pathFindFlags & PathUnit.FLAG_READY) != 0)
                {
                    // The path(unit) is established and is ready for use: get the vehicle's current position in terms of segment and lane
                    realTimePositions.Add(Singleton <PathManager> .instance.m_pathUnits.m_buffer[currentPathUnitId].GetPosition(vehicleData.m_pathPositionIndex >> 1));
                    if (realTimePositions[0].m_offset == 0)
                    {
                        realTimeDestinationNodes.Add(netManager.m_segments.m_buffer[realTimePositions[0].m_segment].m_startNode);
                    }
                    else
                    {
                        realTimeDestinationNodes.Add(netManager.m_segments.m_buffer[realTimePositions[0].m_segment].m_endNode);
                    }

                    if (maxUpcomingPathPositions > 0)
                    {
                        // evaluate upcoming path units
                        byte i          = 0;
                        uint pathUnitId = currentPathUnitId;
                        int  pathPos    = (byte)((vehicleData.m_pathPositionIndex >> 1) + 1);
                        while (true)
                        {
                            if (pathPos > 11)
                            {
                                // go to next path unit
                                pathPos    = 0;
                                pathUnitId = Singleton <PathManager> .instance.m_pathUnits.m_buffer[pathUnitId].m_nextPathUnit;
#if DEBUGV
                                logBuffer.Add("* vehicleId " + vehicleId + ". Going to next path unit (1). pathUnitId=" + pathUnitId);
#endif
                                if (pathUnitId <= 0)
                                {
                                    break;
                                }
                            }

                            PathUnit.Position nextRealTimePosition = default(PathUnit.Position);
                            if (!Singleton <PathManager> .instance.m_pathUnits.m_buffer[pathUnitId].GetPosition(pathPos, out nextRealTimePosition))                             // if this returns false, there is no next path unit
                            {
#if DEBUGV
                                logBuffer.Add("* vehicleId " + vehicleId + ". No next path unit! pathPos=" + pathPos + ", pathUnitId=" + pathUnitId);
#endif
                                break;
                            }

                            ushort destNodeId = 0;
                            if (nextRealTimePosition.m_segment > 0)
                            {
                                if (nextRealTimePosition.m_offset == 0)
                                {
                                    destNodeId = netManager.m_segments.m_buffer[nextRealTimePosition.m_segment].m_startNode;
                                }
                                else
                                {
                                    destNodeId = netManager.m_segments.m_buffer[nextRealTimePosition.m_segment].m_endNode;
                                }
                            }

#if DEBUGV
                            logBuffer.Add("* vehicleId " + vehicleId + ". Next path unit! node " + destNodeId + ", seg. " + nextRealTimePosition.m_segment + ", pathUnitId=" + pathUnitId + ", pathPos: " + pathPos);
#endif

                            realTimePositions.Add(nextRealTimePosition);
                            realTimeDestinationNodes.Add(destNodeId);

                            if (i >= maxUpcomingPathPositions - 1)
                            {
                                break;                                 // we calculate up to 2 upcoming path units at the moment
                            }
                            ++pathPos;
                            ++i;
                        }
                    }

                    // please don't ask why we use "m_pathPositionIndex >> 1" (which equals to "m_pathPositionIndex / 2") here (Though it would
                    // be interesting to know why they used such an ugly indexing scheme!!). I assume the oddness of m_pathPositionIndex relates
                    // to the car's position on the segment. If it is even the car might be in the segment's first half and if it is odd, it might
                    // be in the segment's second half.
#if DEBUGV
                    logBuffer.Add("* vehicleId " + vehicleId + ". *INFO* rtPos.seg=" + realTimePositions[0].m_segment + " nrtPos.seg=" + (realTimePositions.Count > 1 ? "" + realTimePositions[1].m_segment : "n/a"));
#endif
                }
            }

            // we have seen the car!
            vehiclePos.LastFrame = Singleton <SimulationManager> .instance.m_currentFrameIndex;

#if DEBUGV
            logBuffer.Add("* vehicleId " + vehicleId + ". ToNode: " + vehiclePos.ToNode + ". FromSegment: " + vehiclePos.FromSegment /* + ". FromLaneId: " + TrafficPriority.Vehicles[vehicleId].FromLaneId*/);
#endif
            if (addTraffic && vehicleData.m_leadingVehicle == 0 && realTimePositions.Count > 0 && realTimePositions[0].m_segment != 0)
            {
                // add traffic to lane
                uint laneId = PathManager.GetLaneID(realTimePositions[0]);
                //Log._Debug($"HandleVehicle: adding traffic to segment {realTimePositions[0].m_segment}, lane {realTimePositions[0].m_lane}");
                CustomRoadAI.AddTraffic(laneId, Singleton <NetManager> .instance.m_segments.m_buffer[realTimePositions[0].m_segment].Info.m_lanes[realTimePositions[0].m_lane], (ushort)Mathf.RoundToInt(vehicleData.CalculateTotalLength(vehicleId)), (ushort)Mathf.RoundToInt(lastFrameData.m_velocity.magnitude), realTraffic);
            }

#if DEBUGV
            logBuffer.Add("* vehicleId " + vehicleId + ". Real time positions: " + realTimePositions.Count + ", Destination nodes: " + realTimeDestinationNodes.Count);
#endif
            if (realTimePositions.Count >= 1)
            {
                // we found a valid path unit
                var sourceLaneIndex = realTimePositions[0].m_lane;

                if (
                    !vehiclePos.Valid ||
                    vehiclePos.ToNode != realTimeDestinationNodes[0] ||
                    vehiclePos.FromSegment != realTimePositions[0].m_segment ||
                    vehiclePos.FromLaneIndex != sourceLaneIndex)
                {
                    // vehicle information is not up-to-date. remove the car from old priority segments (if existing)...
                    TrafficPriority.RemoveVehicleFromSegments(vehicleId);

                    if (realTimePositions.Count >= 2)
                    {
                        // save vehicle information for priority rule handling
                        vehiclePos.Valid                     = true;
                        vehiclePos.CarState                  = VehicleJunctionTransitState.None;
                        vehiclePos.WaitTime                  = 0;
                        vehiclePos.Stopped                   = false;
                        vehiclePos.ToNode                    = realTimeDestinationNodes[0];
                        vehiclePos.FromSegment               = realTimePositions[0].m_segment;
                        vehiclePos.FromLaneIndex             = realTimePositions[0].m_lane;
                        vehiclePos.ToSegment                 = realTimePositions[1].m_segment;
                        vehiclePos.ToLaneIndex               = realTimePositions[1].m_lane;
                        vehiclePos.ReduceSpeedByValueToYield = UnityEngine.Random.Range(16f, 28f);
                        vehiclePos.OnEmergency               = (vehicleData.m_flags & Vehicle.Flags.Emergency2) != Vehicle.Flags.None;
                        vehiclePos.VehicleType               = (ExtVehicleType)vehicleType;

#if DEBUGV
                        logBuffer.Add($"* vehicleId {vehicleId}. Setting current position to: from {vehiclePos.FromSegment} (lane {vehiclePos.FromLaneIndex}), going over {vehiclePos.ToNode}, to {vehiclePos.ToSegment} (lane {vehiclePos.ToLaneIndex})");
#endif

                        //if (!Options.disableSomething) {
                        // add the vehicle to upcoming priority segments that have timed traffic lights
                        for (int i = 0; i < realTimePositions.Count - 1; ++i)
                        {
                            var prioritySegment = TrafficPriority.GetPrioritySegment(realTimeDestinationNodes[i], realTimePositions[i].m_segment);
                            if (prioritySegment == null)
                            {
                                continue;
                            }

                            // add upcoming segments only if there is a timed traffic light
                            TrafficLightSimulation nodeSim = TrafficLightSimulation.GetNodeSimulation(realTimeDestinationNodes[i]);
                            if (i > 0 && (nodeSim == null || !nodeSim.IsTimedLight() || !nodeSim.IsTimedLightActive()))
                            {
                                continue;
                            }

                            VehiclePosition upcomingVehiclePos = new VehiclePosition();
                            upcomingVehiclePos.Valid                     = true;
                            upcomingVehiclePos.CarState                  = VehicleJunctionTransitState.None;
                            upcomingVehiclePos.LastFrame                 = vehiclePos.LastFrame;
                            upcomingVehiclePos.ToNode                    = realTimeDestinationNodes[i];
                            upcomingVehiclePos.FromSegment               = realTimePositions[i].m_segment;
                            upcomingVehiclePos.FromLaneIndex             = realTimePositions[i].m_lane;
                            upcomingVehiclePos.ToSegment                 = realTimePositions[i + 1].m_segment;
                            upcomingVehiclePos.ToLaneIndex               = realTimePositions[i + 1].m_lane;
                            upcomingVehiclePos.ReduceSpeedByValueToYield = UnityEngine.Random.Range(16f, 28f);
                            upcomingVehiclePos.OnEmergency               = (vehicleData.m_flags & Vehicle.Flags.Emergency2) != Vehicle.Flags.None;
                            upcomingVehiclePos.VehicleType               = (ExtVehicleType)vehicleType;
#if DEBUGV
                            logBuffer.Add($"* vehicleId {vehicleId}. Adding future position: from {upcomingVehiclePos.FromSegment}  (lane {upcomingVehiclePos.FromLaneIndex}), going over {upcomingVehiclePos.ToNode}, to {upcomingVehiclePos.ToSegment} (lane {upcomingVehiclePos.ToLaneIndex})");
#endif

                            prioritySegment.AddVehicle(vehicleId, upcomingVehiclePos);
                        }
                    }
                    //}
                }
                else
                {
#if DEBUGV
                    logBuffer.Add($"* vehicleId {vehicleId}. Nothing has changed. from {vehiclePos.FromSegment} (lane {vehiclePos.FromLaneIndex}), going over {vehiclePos.ToNode}, to {vehiclePos.ToSegment} (lane {vehiclePos.ToLaneIndex})");
                    logme = false;
#endif
                }
            }
            else
            {
#if DEBUGV
                logBuffer.Add($"* vehicleId {vehicleId}. Insufficient path unit positions.");
#endif
                TrafficPriority.RemoveVehicleFromSegments(vehicleId);
            }

#if DEBUGV
            if (logme)
            {
                Log._Debug("vehicleId: " + vehicleId + " ============================================");
                foreach (String logBuf in logBuffer)
                {
                    Log._Debug(logBuf);
                }
                Log._Debug("vehicleId: " + vehicleId + " ============================================");
            }
#endif
        }
        /// <summary>
        /// Determines the allowed vehicle types for the given segment and lane.
        /// </summary>
        /// <param name="segmentId"></param>
        /// <param name="laneIndex"></param>
        /// <param name="segmetnInfo"></param>
        /// <param name="laneInfo"></param>
        /// <returns></returns>
        internal static ExtVehicleType GetAllowedVehicleTypes(ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo)
        {
            if (Flags.IsInitDone())
            {
                ExtVehicleType?[] fastArray = Flags.laneAllowedVehicleTypesArray[segmentId];
                if (fastArray != null && fastArray.Length > laneIndex && fastArray[laneIndex] != null)
                {
                    return((ExtVehicleType)fastArray[laneIndex]);
                }
            }

            // manage cached default vehicle types
            if (defaultVehicleTypeCache == null)
            {
                defaultVehicleTypeCache = new ExtVehicleType?[NetManager.MAX_SEGMENT_COUNT][];
            }

            ExtVehicleType?[] cachedDefaultTypes = defaultVehicleTypeCache[segmentId];
            if (cachedDefaultTypes == null || cachedDefaultTypes.Length != segmentInfo.m_lanes.Length)
            {
                defaultVehicleTypeCache[segmentId] = cachedDefaultTypes = new ExtVehicleType?[segmentInfo.m_lanes.Length];
            }

            ExtVehicleType?defaultVehicleType = cachedDefaultTypes[laneIndex];

            if (defaultVehicleType == null)
            {
                ExtVehicleType ret = ExtVehicleType.None;
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Bicycle;
                }
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Tram) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Tram;
                }
                if ((laneInfo.m_laneType & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None)
                {
                    ret |= ExtVehicleType.RoadPublicTransport | ExtVehicleType.Emergency;
                }
                else if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.RoadVehicle;
                }
                if ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro)) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.RailVehicle;
                }
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Ship;
                }
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Plane) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Plane;
                }
                cachedDefaultTypes[laneIndex] = ret;
                return(ret);
            }
            else
            {
                return((ExtVehicleType)defaultVehicleType);
            }
        }
예제 #15
0
		public static void setLaneAllowedVehicleTypes(ushort segmentId, uint laneIndex, uint laneId, ExtVehicleType vehicleTypes) {
			if (segmentId <= 0 || laneIndex < 0 || laneId <= 0)
				return;
			if ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) {
				return;
			}
			if (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & NetLane.Flags.Created) == NetLane.Flags.None)
				return;
			NetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;
			if (laneIndex >= segmentInfo.m_lanes.Length) {
				return;
			}

			try {
				Monitor.Enter(laneAllowedVehicleTypesLock);
				Log._Debug($"Flags.setLaneAllowedVehicleTypes: setting allowed vehicles of lane index {laneIndex} @ seg. {segmentId} to {vehicleTypes.ToString()}");

				laneAllowedVehicleTypes[laneId] = vehicleTypes;

				// save allowed vehicle types into the fast-access array.
				// (1) ensure that the array is defined and large enough
				if (laneAllowedVehicleTypesArray[segmentId] == null) {
					laneAllowedVehicleTypesArray[segmentId] = new ExtVehicleType?[segmentInfo.m_lanes.Length];
				} else if (laneAllowedVehicleTypesArray[segmentId].Length < segmentInfo.m_lanes.Length) {
					var oldArray = laneAllowedVehicleTypesArray[segmentId];
					laneAllowedVehicleTypesArray[segmentId] = new ExtVehicleType?[segmentInfo.m_lanes.Length];
					Array.Copy(oldArray, laneAllowedVehicleTypesArray[segmentId], oldArray.Length);
				}
				// (2) insert the custom speed limit
				laneAllowedVehicleTypesArray[segmentId][laneIndex] = vehicleTypes;
			} finally {
				Monitor.Exit(laneAllowedVehicleTypesLock);
			}
		}
        /// <summary>
        /// Determines the default set of allowed vehicle types for a given segment and lane.
        /// </summary>
        /// <param name="segmentId"></param>
        /// <param name="segmentInfo"></param>
        /// <param name="laneIndex"></param>
        /// <param name="laneInfo"></param>
        /// <returns></returns>
        public static ExtVehicleType GetDefaultAllowedVehicleTypes(ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo)
        {
#if TRACE
            Singleton <CodeProfiler> .instance.Start("VehicleRestrictionsManager.GetDefaultAllowedVehicleTypes");
#endif
            // manage cached default vehicle types
            if (defaultVehicleTypeCache == null)
            {
                defaultVehicleTypeCache = new ExtVehicleType?[NetManager.MAX_SEGMENT_COUNT][];
            }

            ExtVehicleType?[] cachedDefaultTypes = defaultVehicleTypeCache[segmentId];
            if (cachedDefaultTypes == null || cachedDefaultTypes.Length != segmentInfo.m_lanes.Length)
            {
                defaultVehicleTypeCache[segmentId] = cachedDefaultTypes = new ExtVehicleType?[segmentInfo.m_lanes.Length];
            }

            ExtVehicleType?defaultVehicleType = cachedDefaultTypes[laneIndex];
            if (defaultVehicleType == null)
            {
                ExtVehicleType ret = ExtVehicleType.None;
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Bicycle;
                }
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Tram) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Tram;
                }
                if ((laneInfo.m_laneType & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None)
                {
                    ret |= ExtVehicleType.RoadPublicTransport | ExtVehicleType.Service | ExtVehicleType.Emergency;
                }
                else if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.RoadVehicle;
                }
                if ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro)) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.RailVehicle;
                }
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Ship;
                }
                if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Plane) != VehicleInfo.VehicleType.None)
                {
                    ret |= ExtVehicleType.Plane;
                }
                cachedDefaultTypes[laneIndex] = ret;
#if TRACE
                Singleton <CodeProfiler> .instance.Stop("VehicleRestrictionsManager.GetDefaultAllowedVehicleTypes");
#endif
                return(ret);
            }
            else
            {
#if TRACE
                Singleton <CodeProfiler> .instance.Stop("VehicleRestrictionsManager.GetDefaultAllowedVehicleTypes");
#endif
                return((ExtVehicleType)defaultVehicleType);
            }
        }
예제 #17
0
        private static void GetCustomTrafficLightState(ushort vehicleId, ref Vehicle vehicleData, ushort nodeId, ushort fromSegmentId, ushort toSegmentId, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState, TrafficLightSimulation nodeSim = null)
        {
            if (nodeSim == null)
            {
                nodeSim = TrafficLightSimulation.GetNodeSimulation(nodeId);
                if (nodeSim == null)
                {
                    Log.Error($"GetCustomTrafficLightState: node traffic light simulation not found at node {nodeId}! Vehicle {vehicleId} comes from segment {fromSegmentId} and goes to node {nodeId}");
                    throw new ApplicationException($"GetCustomTrafficLightState: node traffic light simulation not found at node {nodeId}! Vehicle {vehicleId} comes from segment {fromSegmentId} and goes to node {nodeId}");
                }
            }

            // get vehicle position

            /*VehiclePosition vehiclePos = TrafficPriority.GetVehiclePosition(vehicleId);
             * if (!vehiclePos.Valid || vehiclePos.FromSegment != fromSegmentId || vehiclePos.ToNode != nodeId) {
             *      Log._Debug($"GetTrafficLightState: Recalculating position for vehicle {vehicleId}! FromSegment={vehiclePos.FromSegment} Valid={vehiclePos.Valid}");
             *      try {
             *              HandleVehicle(vehicleId, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId], false, false);
             *      } catch (Exception e) {
             *              Log.Error("VehicleAI GetTrafficLightState Error: " + e.ToString());
             *      }
             * }
             *
             * if (!vehiclePos.Valid || vehiclePos.FromSegment != fromSegmentId || vehiclePos.ToNode != nodeId) {
             *      Log.Warning($"GetTrafficLightState: Vehicle {vehicleId} is not moving at segment {fromSegmentId} to node {nodeId}! FromSegment={vehiclePos.FromSegment} ToNode={vehiclePos.ToNode} Valid={vehiclePos.Valid}");
             *      vehicleLightState = RoadBaseAI.TrafficLightState.Red;
             *      pedestrianLightState = RoadBaseAI.TrafficLightState.Red;
             *      return;
             * }*/

            // get vehicle type
            ExtVehicleType?vehicleType = CustomVehicleAI.DetermineVehicleTypeFromVehicle(vehicleId, ref vehicleData);

            if (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Tram && vehicleType != ExtVehicleType.Tram)
            {
                Log.Warning($"vehicleType={vehicleType} ({(int)vehicleType}) for Tram");
            }
            //Log._Debug($"GetCustomTrafficLightState: Vehicle {vehicleId} is a {vehicleType}");
            if (vehicleType == null)
            {
                Log.Warning($"GetTrafficLightState: Could not determine vehicle type of vehicle {vehicleId}!");
                vehicleLightState    = RoadBaseAI.TrafficLightState.Red;
                pedestrianLightState = RoadBaseAI.TrafficLightState.Red;
                return;
            }

            // get responsible traffic light
            CustomSegmentLights lights = CustomTrafficLights.GetSegmentLights(nodeId, fromSegmentId);
            CustomSegmentLight  light  = lights == null ? null : lights.GetCustomLight((ExtVehicleType)vehicleType);

            if (lights == null || light == null)
            {
                Log.Warning($"GetTrafficLightState: No custom light for vehicleType {vehicleType} @ node {nodeId}, segment {fromSegmentId} found. lights null? {lights == null} light null? {light == null}");
                vehicleLightState    = RoadBaseAI.TrafficLightState.Red;
                pedestrianLightState = RoadBaseAI.TrafficLightState.Red;
                return;
            }

            SegmentGeometry geometry = CustomRoadAI.GetSegmentGeometry(fromSegmentId);

            // get traffic light state from responsible traffic light
            if (geometry.IsLeftSegment(toSegmentId, nodeId))
            {
                vehicleLightState = light.GetLightLeft();
            }
            else if (geometry.IsRightSegment(toSegmentId, nodeId))
            {
                vehicleLightState = light.GetLightRight();
            }
            else
            {
                vehicleLightState = light.GetLightMain();
            }

            // get traffic lights state for pedestrians
            pedestrianLightState = (lights.PedestrianLightState != null) ? (RoadBaseAI.TrafficLightState)lights.PedestrianLightState : RoadBaseAI.TrafficLightState.Red;
        }
예제 #18
0
 public bool IsBlimpAllowed(ExtVehicleType? allowedTypes)
 {
     return IsAllowed(allowedTypes, ExtVehicleType.Blimp);
 }
예제 #19
0
 public bool IsEmergencyAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.Emergency));
 }
예제 #20
0
 public bool IsCargoTrainAllowed(ExtVehicleType? allowedTypes)
 {
     return IsAllowed(allowedTypes, ExtVehicleType.CargoTrain);
 }
예제 #21
0
 public bool IsServiceAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.Service));
 }
예제 #22
0
 public bool IsPassengerCarAllowed(ExtVehicleType? allowedTypes)
 {
     return IsAllowed(allowedTypes, ExtVehicleType.PassengerCar);
 }
예제 #23
0
 public bool IsTramAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.Tram));
 }
예제 #24
0
 public bool IsRailVehicleAllowed(ExtVehicleType? allowedTypes)
 {
     return IsAllowed(allowedTypes, ExtVehicleType.RailVehicle);
 }
예제 #25
0
 public bool IsFerryAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.Ferry));
 }
        internal ExtVehicleType OnStartPathFind(ushort vehicleId, ref Vehicle vehicleData, ExtVehicleType?vehicleType)
        {
            if ((vehicleData.Info.m_vehicleType & VEHICLE_TYPES) == VehicleInfo.VehicleType.None ||
                (vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created)
            {
#if DEBUG
                if (GlobalConfig.Instance.Debug.Switches[9])
                {
                    Log._Debug($"VehicleStateManager.OnStartPathFind({vehicleId}, {vehicleType}): unhandled vehicle! type: {vehicleData.Info.m_vehicleType}");
                }
#endif
                return(ExtVehicleType.None);
            }

            /*ushort connectedVehicleId = vehicleId;
             * while (connectedVehicleId != 0) {
             #if DEBUG
             *      if (GlobalConfig.Instance.Debug.Switches[9])
             *              Log._Debug($"VehicleStateManager.OnStartPathFind({vehicleId}, {vehicleType}): overriding vehicle type for connected vehicle {connectedVehicleId} of vehicle {vehicleId} (leading)");
             #endif
             *      VehicleStates[connectedVehicleId].vehicleType = type;
             *
             *      connectedVehicleId = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId].m_leadingVehicle;
             * }*/

            ExtVehicleType ret = VehicleStates[vehicleId].OnStartPathFind(ref vehicleData, vehicleType);

            ushort connectedVehicleId = vehicleId;
            while (true)
            {
                connectedVehicleId = Singleton <VehicleManager> .instance.m_vehicles.m_buffer[connectedVehicleId].m_trailingVehicle;

                if (connectedVehicleId == 0)
                {
                    break;
                }

#if DEBUG
                if (GlobalConfig.Instance.Debug.Switches[9])
                {
                    Log._Debug($"VehicleStateManager.OnStartPathFind({vehicleId}, {vehicleType}): overriding vehicle type for connected vehicle {connectedVehicleId} of vehicle {vehicleId} (trailing)");
                }
#endif
                VehicleStates[connectedVehicleId].OnStartPathFind(ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[connectedVehicleId], vehicleType);
            }

            return(ret);
        }
예제 #27
0
 public bool IsAllowed(ExtVehicleType?allowedTypes, ExtVehicleType vehicleType)
 {
     return(allowedTypes == null || ((ExtVehicleType)allowedTypes & vehicleType) != ExtVehicleType.None);
 }
        // TODO improve & remove
        public void Housekeeping(bool mayDelete, bool calculateAutoPedLight)
        {
            // we intentionally never delete vehicle types (because we may want to retain traffic light states if a segment is upgraded or replaced)

            ICustomSegmentLight mainLight = MainSegmentLight;
            ushort nodeId = NodeId;
            HashSet <ExtVehicleType>           setupLights     = new HashSet <ExtVehicleType>();                                                                                                          // TODO improve
            IDictionary <byte, ExtVehicleType> allAllowedTypes = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypesAsDict(SegmentId, nodeId, VehicleRestrictionsMode.Restricted); // TODO improve
            ExtVehicleType allAllowedMask = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypes(SegmentId, nodeId, VehicleRestrictionsMode.Restricted);

            SeparateVehicleTypes = ExtVehicleType.None;
#if DEBUGHK
            Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}, allAllowedTypes={string.Join(", ", allAllowedTypes.Select(x => x.ToString()).ToArray())}, allAllowedMask={allAllowedMask}");
#endif
            bool addPedestrianLight = false;
            uint numLights          = 0;
            Constants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate(ushort segId, ref NetSegment segment) {
                VehicleTypeByLaneIndex = new ExtVehicleType?[segment.Info.m_lanes.Length];
                return(true);
            });
            HashSet <byte> laneIndicesWithoutSeparateLights = new HashSet <byte>(allAllowedTypes.Keys);           // TODO improve
            foreach (KeyValuePair <byte, ExtVehicleType> e in allAllowedTypes)
            {
                byte           laneIndex    = e.Key;
                ExtVehicleType allowedTypes = e.Value;

                foreach (ExtVehicleType mask in SINGLE_LANE_VEHICLETYPES)
                {
                    if (setupLights.Contains(mask))
                    {
                        ++numLights;
                        break;
                    }

                    if ((allowedTypes & mask) != ExtVehicleType.None && (allowedTypes & ~(mask | ExtVehicleType.Emergency)) == ExtVehicleType.None)
                    {
#if DEBUGHK
                        Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: adding {mask} light");
#endif

                        ICustomSegmentLight segmentLight;
                        if (!CustomLights.TryGetValue(mask, out segmentLight))
                        {
                            segmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red);
                            if (mainLight != null)
                            {
                                segmentLight.CurrentMode = mainLight.CurrentMode;
                                segmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false);
                            }
                            CustomLights.Add(mask, segmentLight);
                            VehicleTypes.AddFirst(mask);
                        }
                        mainVehicleType = mask;
                        VehicleTypeByLaneIndex[laneIndex] = mask;
                        laneIndicesWithoutSeparateLights.Remove(laneIndex);
                        ++numLights;
                        addPedestrianLight = true;
                        setupLights.Add(mask);
                        SeparateVehicleTypes |= mask;
                        break;
                    }
                }
            }

            if (allAllowedTypes.Count > numLights)
            {
#if DEBUGHK
                Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: adding default main vehicle light: {DEFAULT_MAIN_VEHICLETYPE}");
#endif

                // traffic lights for cars
                ICustomSegmentLight defaultSegmentLight;
                if (!CustomLights.TryGetValue(DEFAULT_MAIN_VEHICLETYPE, out defaultSegmentLight))
                {
                    defaultSegmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red);
                    if (mainLight != null)
                    {
                        defaultSegmentLight.CurrentMode = mainLight.CurrentMode;
                        defaultSegmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false);
                    }
                    CustomLights.Add(DEFAULT_MAIN_VEHICLETYPE, defaultSegmentLight);
                    VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE);
                }
                mainVehicleType = DEFAULT_MAIN_VEHICLETYPE;

                foreach (byte laneIndex in laneIndicesWithoutSeparateLights)
                {
                    VehicleTypeByLaneIndex[laneIndex] = ExtVehicleType.None;
                }
                addPedestrianLight = true;
            }
            else
            {
                addPedestrianLight = allAllowedMask == ExtVehicleType.None || (allAllowedMask & ~ExtVehicleType.RailVehicle) != ExtVehicleType.None;
            }

            if (mayDelete)
            {
                // delete traffic lights for non-existing configurations
                HashSet <ExtVehicleType> vehicleTypesToDelete = new HashSet <ExtVehicleType>();
                foreach (KeyValuePair <ExtVehicleType, ICustomSegmentLight> e in CustomLights)
                {
                    if (e.Key == DEFAULT_MAIN_VEHICLETYPE)
                    {
                        continue;
                    }
                    if (!setupLights.Contains(e.Key))
                    {
                        vehicleTypesToDelete.Add(e.Key);
                    }
                }

                foreach (ExtVehicleType vehicleType in vehicleTypesToDelete)
                {
#if DEBUGHK
                    Log._Debug($"Deleting traffic light for {vehicleType} at segment {SegmentId}, node {nodeId}");
#endif
                    CustomLights.Remove(vehicleType);
                    VehicleTypes.Remove(vehicleType);
                }
            }

            if (CustomLights.ContainsKey(DEFAULT_MAIN_VEHICLETYPE) && VehicleTypes.First.Value != DEFAULT_MAIN_VEHICLETYPE)
            {
                VehicleTypes.Remove(DEFAULT_MAIN_VEHICLETYPE);
                VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE);
            }

            if (addPedestrianLight)
            {
#if DEBUGHK
                Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: adding pedestrian light");
#endif
                if (InternalPedestrianLightState == null)
                {
                    InternalPedestrianLightState = RoadBaseAI.TrafficLightState.Red;
                }
            }
            else
            {
                InternalPedestrianLightState = null;
            }

            OnChange(calculateAutoPedLight);
#if DEBUGHK
            Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: Housekeeping complete. VehicleTypeByLaneIndex={string.Join("; ", VehicleTypeByLaneIndex.Select(x => x == null ? "null" : x.ToString()).ToArray())} CustomLights={string.Join("; ", CustomLights.Select(x => x.Key.ToString()).ToArray())}");
#endif
        }
예제 #29
0
 public bool IsBusAllowed(ExtVehicleType?allowedTypes)
 {
     return(IsAllowed(allowedTypes, ExtVehicleType.Bus));
 }
예제 #30
0
        internal static IDictionary <uint, ExtVehicleType> GetAllLaneAllowedVehicleTypes()
        {
            IDictionary <uint, ExtVehicleType> ret = new Dictionary <uint, ExtVehicleType>();

            for (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId)
            {
                // Begin local function
                bool ForEachLane(ushort segId, ref NetSegment segment)
                {
                    if ((segment.m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) !=
                        NetSegment.Flags.Created)
                    {
                        return(true);
                    }

                    ExtVehicleType?[] allowedTypes = laneAllowedVehicleTypesArray[segId];
                    if (allowedTypes == null)
                    {
                        return(true);
                    }

                    Constants.ServiceFactory.NetService.IterateSegmentLanes(
                        segId,
                        ref segment,
                        (uint laneId,
                         ref NetLane lane,
                         NetInfo.Lane laneInfo,
                         ushort sId,
                         ref NetSegment seg,
                         byte laneIndex) =>
                    {
                        if (laneInfo.m_vehicleType == VehicleInfo.VehicleType.None)
                        {
                            return(true);
                        }

                        if (laneIndex >= allowedTypes.Length)
                        {
                            return(true);
                        }

                        ExtVehicleType?allowedType = allowedTypes[laneIndex];

                        if (allowedType == null)
                        {
                            return(true);
                        }

                        ret.Add(laneId, (ExtVehicleType)allowedType);
                        return(true);
                    });
                    return(true);
                }

                // ↑↑↑
                // end local function
                Constants.ServiceFactory.NetService.ProcessSegment(
                    (ushort)segmentId,
                    ForEachLane);
            }

            return(ret);
        }
		// PathFind
		protected void PathFindImplementation(uint unit, ref PathUnit data) {
			//pfCurrentState = 0;

			NetManager instance = Singleton<NetManager>.instance;
			this._laneTypes = (NetInfo.LaneType)this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_laneTypes;
			this._vehicleTypes = (VehicleInfo.VehicleType)this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_vehicleTypes;
			this._maxLength = this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_length;
			this._pathFindIndex = (this._pathFindIndex + 1u & 32767u);
			this._pathRandomizer = new Randomizer(unit);
			this._isHeavyVehicle = ((this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags & 16) != 0);
			this._ignoreBlocked = ((this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags & 32) != 0);
			this._stablePath = ((this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags & 64) != 0);
			this._transportVehicle = ((byte)(this._laneTypes & NetInfo.LaneType.TransportVehicle) != 0);
			this._extVehicleType = pathUnitExtVehicleType[unit];
			if ((byte)(this._laneTypes & NetInfo.LaneType.Vehicle) != 0) {
				this._laneTypes |= NetInfo.LaneType.TransportVehicle;
			}
			int num = (int)(this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_positionCount & 15);
			int num2 = this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_positionCount >> 4;
			BufferItem bufferItemStartA;
			if (data.m_position00.m_segment != 0 && num >= 1) {
				this._startLaneA = PathManager.GetLaneID(data.m_position00);
				this._startOffsetA = data.m_position00.m_offset;
				bufferItemStartA.m_laneID = this._startLaneA;
				bufferItemStartA.m_position = data.m_position00;
				this.GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed);
				bufferItemStartA.m_comparisonValue = 0f;
			} else {
				this._startLaneA = 0u;
				this._startOffsetA = 0;
				bufferItemStartA = default(BufferItem);
			}
			BufferItem bufferItemStartB;
			if (data.m_position02.m_segment != 0 && num >= 3) {
				this._startLaneB = PathManager.GetLaneID(data.m_position02);
				this._startOffsetB = data.m_position02.m_offset;
				bufferItemStartB.m_laneID = this._startLaneB;
				bufferItemStartB.m_position = data.m_position02;
				this.GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed);
				bufferItemStartB.m_comparisonValue = 0f;
			} else {
				this._startLaneB = 0u;
				this._startOffsetB = 0;
				bufferItemStartB = default(BufferItem);
			}
			BufferItem bufferItemEndA;
			if (data.m_position01.m_segment != 0 && num >= 2) {
				this._endLaneA = PathManager.GetLaneID(data.m_position01);
				bufferItemEndA.m_laneID = this._endLaneA;
				bufferItemEndA.m_position = data.m_position01;
				this.GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed);
				bufferItemEndA.m_methodDistance = 0f;
				bufferItemEndA.m_comparisonValue = 0f;
			} else {
				this._endLaneA = 0u;
				bufferItemEndA = default(BufferItem);
			}
			BufferItem bufferItemEndB;
			if (data.m_position03.m_segment != 0 && num >= 4) {
				this._endLaneB = PathManager.GetLaneID(data.m_position03);
				bufferItemEndB.m_laneID = this._endLaneB;
				bufferItemEndB.m_position = data.m_position03;
				this.GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed);
				bufferItemEndB.m_methodDistance = 0f;
				bufferItemEndB.m_comparisonValue = 0f;
			} else {
				this._endLaneB = 0u;
				bufferItemEndB = default(BufferItem);
			}
			if (data.m_position11.m_segment != 0 && num2 >= 1) {
				this._vehicleLane = PathManager.GetLaneID(data.m_position11);
				this._vehicleOffset = data.m_position11.m_offset;
			} else {
				this._vehicleLane = 0u;
				this._vehicleOffset = 0;
			}
			BufferItem finalBufferItem = default(BufferItem);
			byte startOffset = 0;
			this._bufferMinPos = 0;
			this._bufferMaxPos = -1;
			if (this._pathFindIndex == 0u) {
				uint num3 = 4294901760u;
				for (int i = 0; i < 262144; i++) {
					this._laneLocation[i] = num3;
				}
			}
			for (int j = 0; j < 1024; j++) {
				this._bufferMin[j] = 0;
				this._bufferMax[j] = -1;
			}
			if (bufferItemEndA.m_position.m_segment != 0) {
				this._bufferMax[0]++;
				this._buffer[++this._bufferMaxPos] = bufferItemEndA;
			}
			if (bufferItemEndB.m_position.m_segment != 0) {
				this._bufferMax[0]++;
				this._buffer[++this._bufferMaxPos] = bufferItemEndB;
			}
			bool canFindPath = false;
#if DEBUGPF
			uint wIter = 0;
#endif
			//Log.Message($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: STARTING MAIN LOOP! bufferMinPos: {this._bufferMinPos}, bufferMaxPos: {this._bufferMaxPos}, startA: {bufferItemStartA.m_position.m_segment}, startB: {bufferItemStartB.m_position.m_segment}, endA: {bufferItemEndA.m_position.m_segment}, endB: {bufferItemEndB.m_position.m_segment}");
			while (this._bufferMinPos <= this._bufferMaxPos) {
				//pfCurrentState = 1;
#if DEBUGPF
				/*if (m_queuedPathFindCount > 100 && Options.disableSomething1)
					Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: MAIN LOOP RUNNING! bufferMinPos: {this._bufferMinPos}, bufferMaxPos: {this._bufferMaxPos}, startA: {bufferItemStartA.m_position.m_segment}, startB: {bufferItemStartB.m_position.m_segment}, endA: {bufferItemEndA.m_position.m_segment}, endB: {bufferItemEndB.m_position.m_segment}");*/
#endif
				int num4 = this._bufferMin[this._bufferMinPos];
				int num5 = this._bufferMax[this._bufferMinPos];
				if (num4 > num5) {
					this._bufferMinPos++;
				} else {
					this._bufferMin[this._bufferMinPos] = num4 + 1;
					BufferItem candidateItem = this._buffer[(this._bufferMinPos << 6) + num4];
					if (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {
						// we reached startA
						if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this._startOffsetA) {
							finalBufferItem = candidateItem;
							startOffset = this._startOffsetA;
							canFindPath = true;
							break;
						}
						if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this._startOffsetA) {
							finalBufferItem = candidateItem;
							startOffset = this._startOffsetA;
							canFindPath = true;
							break;
						}
					}
					if (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {
						// we reached startB
						if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this._startOffsetB) {
							finalBufferItem = candidateItem;
							startOffset = this._startOffsetB;
							canFindPath = true;
							break;
						}
						if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this._startOffsetB) {
							finalBufferItem = candidateItem;
							startOffset = this._startOffsetB;
							canFindPath = true;
							break;
						}
					}

					// explore the path
					if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0) {
						ushort startNode = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_startNode;
						this.ProcessItemMain(unit, candidateItem, startNode, ref instance.m_nodes.m_buffer[(int)startNode], 0, false);
					}

					if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0) {
						ushort endNode = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_endNode;
						this.ProcessItemMain(unit, candidateItem, endNode, ref instance.m_nodes.m_buffer[(int)endNode], 255, false);
					}

					// handle special nodes (e.g. bus stops)
					int num6 = 0;
					ushort specialNodeId = instance.m_lanes.m_buffer[(int)((UIntPtr)candidateItem.m_laneID)].m_nodes;
					if (specialNodeId != 0) {
						ushort startNode2 = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_startNode;
						ushort endNode2 = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_endNode;
						bool flag2 = ((instance.m_nodes.m_buffer[(int)startNode2].m_flags | instance.m_nodes.m_buffer[(int)endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;
						while (specialNodeId != 0) {
							NetInfo.Direction direction = NetInfo.Direction.None;
							byte laneOffset = instance.m_nodes.m_buffer[(int)specialNodeId].m_laneOffset;
							if (laneOffset <= candidateItem.m_position.m_offset) {
								direction |= NetInfo.Direction.Forward;
							}
							if (laneOffset >= candidateItem.m_position.m_offset) {
								direction |= NetInfo.Direction.Backward;
							}
							if ((byte)(candidateItem.m_direction & direction) != 0 && (!flag2 || (instance.m_nodes.m_buffer[(int)specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {
								this.ProcessItemMain(unit, candidateItem, specialNodeId, ref instance.m_nodes.m_buffer[(int)specialNodeId], laneOffset, true);
							}
							specialNodeId = instance.m_nodes.m_buffer[(int)specialNodeId].m_nextLaneNode;
							if (++num6 == 32768) {
								Log.Warning("Special loop: Too many iterations");
								break;
							}
						}
					}
				}
#if DEBUGPF
				++wIter;
				if (wIter > 1000000) {
					Log.Error("Too many iterations in PathFindImpl.");
					break;
				}
#endif
			}
			//pfCurrentState = 2;
			/*if (m_queuedPathFindCount > 100 && Options.disableSomething0)
				Log.Message($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: MAIN LOOP FINISHED! bufferMinPos: {this._bufferMinPos}, bufferMaxPos: {this._bufferMaxPos}, startA: {bufferItemStartA.m_position.m_segment}, startB: {bufferItemStartB.m_position.m_segment}, endA: {bufferItemEndA.m_position.m_segment}, endB: {bufferItemEndB.m_position.m_segment}");*/
			if (!canFindPath) {
				// we could not find a path
				//pfCurrentState = 3;
				PathUnits.m_buffer[(int)unit].m_pathFindFlags = (byte)(PathUnits.m_buffer[(int)((UIntPtr)unit)].m_pathFindFlags | 8);
#if DEBUG
				++_failedPathFinds;
				//Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}");
#endif
				pathUnitExtVehicleType[unit] = null;

				/*PathUnit[] expr_909_cp_0 = this._pathUnits.m_buffer;
				UIntPtr expr_909_cp_1 = (UIntPtr)unit;
				expr_909_cp_0[(int)expr_909_cp_1].m_pathFindFlags = (byte)(expr_909_cp_0[(int)expr_909_cp_1].m_pathFindFlags | 8);*/
				return;
			}
			// we could calculate a valid path

			//pfCurrentState = 4;
			float totalPathLength = finalBufferItem.m_comparisonValue * this._maxLength;
			this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_length = totalPathLength;
			uint currentPathUnitId = unit;
			int currentItemPositionCount = 0;
			int sumOfPositionCounts = 0;
			PathUnit.Position currentPosition = finalBufferItem.m_position;
			if ((currentPosition.m_segment != bufferItemEndA.m_position.m_segment || currentPosition.m_lane != bufferItemEndA.m_position.m_lane || currentPosition.m_offset != bufferItemEndA.m_position.m_offset) &&
				(currentPosition.m_segment != bufferItemEndB.m_position.m_segment || currentPosition.m_lane != bufferItemEndB.m_position.m_lane || currentPosition.m_offset != bufferItemEndB.m_position.m_offset)) {
				// the found starting position differs from the desired end position
				if (startOffset != currentPosition.m_offset) {
					// the offsets differ: copy the found starting position and modify the offset to fit the desired offset
					PathUnit.Position position2 = currentPosition;
					position2.m_offset = startOffset;
					this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].SetPosition(currentItemPositionCount++, position2);
					// now we have: [desired starting position]
				}
				// add the found starting position to the path unit
				this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].SetPosition(currentItemPositionCount++, currentPosition);
				currentPosition = this._laneTarget[(int)((UIntPtr)finalBufferItem.m_laneID)]; // go to the next path position

				// now we have either [desired starting position, found starting position] or [found starting position], depending on if the found starting position matched the desired
			}
			//pfCurrentState = 5;

			// beginning with the starting position, going to the target position: assemble the path units
			for (int k = 0; k < 262144; k++) {
				//pfCurrentState = 6;
				this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].SetPosition(currentItemPositionCount++, currentPosition); // add the next path position to the current unit

				if ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) ||
					(currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) {
					// we have reached the end position

					this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_positionCount = (byte)currentItemPositionCount;
					sumOfPositionCounts += currentItemPositionCount; // add position count of last unit to sum
					if (sumOfPositionCounts != 0) {
						// for each path unit from start to target: calculate length (distance) to target
						currentPathUnitId = this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_nextPathUnit; // (we do not need to calculate the length for the starting unit since this is done before; it's the total path length)
						currentItemPositionCount = (int)this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_positionCount;
						int totalIter = 0;
						while (currentPathUnitId != 0u) {
							//pfCurrentState = 7;
							this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_length = totalPathLength * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;
							currentItemPositionCount += (int)this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_positionCount;
							currentPathUnitId = this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_nextPathUnit;
							if (++totalIter >= 262144) {
#if DEBUG
								Log.Error("THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: PathFindImplementation: Invalid list detected.");
#endif
								CODebugBase<LogChannel>.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
								break;
							}
						}
					}
#if DEBUG
					//Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Path found (pfCurrentState={pfCurrentState}) for unit {unit}");
#endif
					PathUnits.m_buffer[(int)unit].m_pathFindFlags = (byte)(PathUnits.m_buffer[(int)((UIntPtr)unit)].m_pathFindFlags | 4); // Path found
#if DEBUG
					++_succeededPathFinds;
					pathUnitExtVehicleType[unit] = null;
#endif
					return;
				}

				// We have not reached the target position yet 
				if (currentItemPositionCount == 12) {
					// the current path unit is full, we need a new one
					//pfCurrentState = 8;
					uint createdPathUnitId;
					try {
						Monitor.Enter(this._bufferLock);
						//pfCurrentState = 10;
						if (!this.PathUnits.CreateItem(out createdPathUnitId, ref this._pathRandomizer)) {
							// we failed to create a new path unit, thus the path-finding also failed
							PathUnits.m_buffer[(int)((UIntPtr)unit)].m_pathFindFlags = (byte)(PathUnits.m_buffer[(int)unit].m_pathFindFlags | 8);
#if DEBUG
							++_failedPathFinds;
							//Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}");
#endif
							pathUnitExtVehicleType[unit] = null;
							return;
						}
						this.PathUnits.m_buffer[(int)((UIntPtr)createdPathUnitId)] = this.PathUnits.m_buffer[(int)currentPathUnitId];
						this.PathUnits.m_buffer[(int)((UIntPtr)createdPathUnitId)].m_referenceCount = 1;
						this.PathUnits.m_buffer[(int)((UIntPtr)createdPathUnitId)].m_pathFindFlags = 4;
						this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_nextPathUnit = createdPathUnitId;
						this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_positionCount = (byte)currentItemPositionCount;
						sumOfPositionCounts += currentItemPositionCount;
						Singleton<PathManager>.instance.m_pathUnitCount = (int)(this.PathUnits.ItemCount() - 1u);
					} catch (Exception e) {
						Log.Error("CustomPathFind.PathFindImplementation Error: " + e.ToString());
						break;
					} finally {
						Monitor.Exit(this._bufferLock);
					}
					currentPathUnitId = createdPathUnitId;
					currentItemPositionCount = 0;
				}
				uint laneID = PathManager.GetLaneID(currentPosition);
				// NON-STOCK CODE START
				CustomRoadAI.AddTraffic(laneID, (ushort)(this._isHeavyVehicle || _extVehicleType == ExtVehicleType.Bus ? 50 : 25), (ushort)GetLaneSpeedLimit(currentPosition.m_segment, currentPosition.m_lane, laneID, Singleton<NetManager>.instance.m_segments.m_buffer[currentPosition.m_segment].Info.m_lanes[currentPosition.m_lane]), false); //SpeedLimitManager.GetLockFreeGameSpeedLimit(currentPosition.m_segment, currentPosition.m_lane, laneID, ref Singleton<NetManager>.instance.m_segments.m_buffer[currentPosition.m_segment].Info.m_lanes[currentPosition.m_lane])
				// NON-STOCK CODE END
				currentPosition = this._laneTarget[(int)((UIntPtr)laneID)];
			}
			PathUnits.m_buffer[(int)((UIntPtr)unit)].m_pathFindFlags = (byte)(PathUnits.m_buffer[(int)unit].m_pathFindFlags | 8);
#if DEBUG
			++_failedPathFinds;
#endif
			pathUnitExtVehicleType[unit] = null;
#if DEBUG
			//Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}");
#endif
		}
        /// <summary>
        /// Calculates for each segment the number of cars going to this segment.
        /// We use integer arithmetic for better performance.
        /// </summary>
        public Dictionary <ushort, uint> GetVehicleMetricGoingToSegment(float?minSpeed, ExtVehicleType?vehicleTypes = null, ExtVehicleType separateVehicleTypes = ExtVehicleType.None, bool debug = false)
        {
            Dictionary <ushort, uint> numCarsGoingToSegmentId = new Dictionary <ushort, uint>();
            VehicleManager            vehicleManager          = Singleton <VehicleManager> .instance;
            NetManager netManager = Singleton <NetManager> .instance;

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

                if (segmentId == 0 || segmentId == SegmentId)
                {
                    continue;
                }

                if (CustomRoadAI.GetSegmentGeometry(segmentId).IsIncomingOneWay(NodeId))
                {
                    continue;
                }

                numCarsGoingToSegmentId[segmentId] = 0;
            }

            List <ushort> vehicleIdsToReHandle = new List <ushort>();

            foreach (KeyValuePair <ushort, VehiclePosition> e in Vehicles)
            {
                var vehicleId = e.Key;
                var carPos    = e.Value;

                if (vehicleId <= 0 || carPos.ToSegment <= 0)
                {
                    continue;
                }
                if ((vehicleManager.m_vehicles.m_buffer[vehicleId].m_flags & Vehicle.Flags.Created) == Vehicle.Flags.None)
                {
                    vehicleIdsToReHandle.Add(vehicleId);
                    continue;
                }
                if (minSpeed != null && vehicleManager.m_vehicles.m_buffer[vehicleId].GetLastFrameVelocity().magnitude < minSpeed)
                {
                    continue;
                }
                VehiclePosition globalPos = TrafficPriority.GetVehiclePosition(vehicleId);
                if (globalPos == null || !globalPos.Valid || globalPos.LastFrame >> 7 < Singleton <SimulationManager> .instance.m_currentFrameIndex >> 7)                 // ~64 sec.
                {
                    vehicleIdsToReHandle.Add(vehicleId);
                    continue;
                }
                if (vehicleTypes != null)
                {
                    if (vehicleTypes == ExtVehicleType.None)
                    {
                        if ((globalPos.VehicleType & separateVehicleTypes) != ExtVehicleType.None)
                        {
                            // we want all vehicles that do not have separate traffic lights
                            continue;
                        }
                    }
                    else
                    {
                        if ((globalPos.VehicleType & vehicleTypes) == ExtVehicleType.None)
                        {
                            continue;
                        }
                    }
                }

                //debug = vehicleManager.m_vehicles.m_buffer[vehicleId].Info.m_vehicleType == VehicleInfo.VehicleType.Tram;
#if DEBUG
                /*if (debug) {
                 *      Log._Debug($"getNumCarsGoingToSegment: Handling vehicle {vehicleId} going from {carPos.FromSegment}/{SegmentId} to {carPos.ToSegment}. carState={globalPos.CarState}. lastUpdate={globalPos.LastCarStateUpdate}");
                 * }*/
#endif

                uint avgSegmentLength = (uint)Singleton <NetManager> .instance.m_segments.m_buffer[SegmentId].m_averageLength;
                uint normLength       = (uint)(vehicleManager.m_vehicles.m_buffer[vehicleId].CalculateTotalLength(vehicleId) * 100u) / avgSegmentLength;

#if DEBUG
                /*if (debug) {
                 *      Log._Debug($"getNumCarsGoingToSegment: NormLength of vehicle {vehicleId} going to {carPos.ToSegment}: {avgSegmentLength} -> {normLength}");
                 * }*/
#endif

                if (numCarsGoingToSegmentId.ContainsKey(carPos.ToSegment))
                {
                    /*if (carPos.OnEmergency)
                     *      numCarsGoingToSegmentId[carPos.ToSegment] += 10000f;
                     * else*/
                    numCarsGoingToSegmentId[carPos.ToSegment] += normLength;
                }
                // "else" must not happen (incoming one-way)
            }

            foreach (ushort vehicleId in vehicleIdsToReHandle)
            {
                CustomVehicleAI.HandleVehicle(vehicleId, ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[vehicleId], false, false);
            }
            return(numCarsGoingToSegmentId);
        }