Пример #1
0
 private ExtCitizenInstanceManager()
 {
     ExtInstances = new ExtCitizenInstance[CitizenManager.MAX_INSTANCE_COUNT];
     for (uint i = 0; i < CitizenManager.MAX_INSTANCE_COUNT; ++i)
     {
         ExtInstances[i] = new ExtCitizenInstance((ushort)i);
     }
 }
Пример #2
0
        internal void OnReleaseVehicle(ushort vehicleId)
        {
#if DEBUG
            //Log._Debug($"VehicleStateManager.OnReleaseVehicle({vehicleId}) called.");
#endif
            VehicleState state = _GetVehicleState(vehicleId);
            state.VehicleType = ExtVehicleType.None;
            ExtCitizenInstance driverExtInstance = state.GetDriverExtInstance();
            if (driverExtInstance != null)
            {
                //driverExtInstance.FailedParkingAttempts = 0;
                driverExtInstance.Reset();
            }
            //state.DriverInstanceId = 0;
#if USEPATHWAITCOUNTER
            state.PathWaitCounter = 0;
#endif
            state.Valid = false;
            //VehicleStates[vehicleId].Reset();
        }
        /// <summary>
        /// Lightweight simulation step method.
        /// This method is occasionally being called for different cars.
        /// </summary>
        /// <param name="vehicleId"></param>
        /// <param name="vehicleData"></param>
        /// <param name="physicsLodRefPos"></param>
        public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos)
        {
#if DEBUG
            bool vehDebug  = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
            bool debug     = GlobalConfig.Instance.Debug.Switches[2] && vehDebug;
            bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && vehDebug;
#endif

            if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0)
            {
                PathManager pathManager   = Singleton <PathManager> .instance;
                byte        pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;

                // NON-STOCK CODE START
                ExtPathState mainPathState = ExtPathState.Calculating;
                if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0)
                {
                    mainPathState = ExtPathState.Failed;
                }
                else if ((pathFindFlags & PathUnit.FLAG_READY) != 0)
                {
                    mainPathState = ExtPathState.Ready;
                }

#if DEBUG
                if (debug)
                {
                    Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path: {vehicleData.m_path}, mainPathState={mainPathState}");
                }
#endif
                ExtSoftPathState finalPathState = ExtSoftPathState.None;
#if BENCHMARK
                using (var bm = new Benchmark(null, "UpdateCarPathState")) {
#endif
                finalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);
                if (Options.prohibitPocketCars && VehicleStateManager.Instance.VehicleStates[vehicleId].vehicleType == ExtVehicleType.PassengerCar)
                {
                    ushort driverInstanceId = CustomPassengerCarAI.GetDriverInstanceId(vehicleId, ref vehicleData);
                    finalPathState = AdvancedParkingManager.Instance.UpdateCarPathState(vehicleId, ref vehicleData, ref Singleton <CitizenManager> .instance.m_instances.m_buffer[driverInstanceId], ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId], mainPathState);

#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Applied Parking AI logic. Path: {vehicleData.m_path}, mainPathState={mainPathState}, finalPathState={finalPathState}");
                    }
#endif
                }
#if BENCHMARK
            }
#endif

                switch (finalPathState)
                {
                case ExtSoftPathState.Ready:
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding succeeded for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindSuccess");
                    }
#endif

                    vehicleData.m_pathPositionIndex = 255;
                    vehicleData.m_flags            &= ~Vehicle.Flags.WaitingPath;
                    vehicleData.m_flags            &= ~Vehicle.Flags.Arriving;
                    this.PathfindSuccess(vehicleId, ref vehicleData);
                    this.TrySpawn(vehicleId, ref vehicleData);
                    break;

                case ExtSoftPathState.Ignore:
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result shall be ignored for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- ignoring");
                    }
#endif
                    return;

                case ExtSoftPathState.Calculating:
                default:
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result undetermined for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- continue");
                    }
#endif
                    break;

                case ExtSoftPathState.FailedHard:
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): HARD path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindFailure");
                    }
#endif
                    vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
                    Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path);

                    vehicleData.m_path = 0u;
                    this.PathfindFailure(vehicleId, ref vehicleData);
                    return;

                case ExtSoftPathState.FailedSoft:
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): SOFT path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.InvalidPath");
                    }
#endif
                    // path mode has been updated, repeat path-finding
                    vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
                    this.InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData);
                    break;
                }
                // NON-STOCK CODE END
            }
            else
            {
                if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0)
                {
                    this.TrySpawn(vehicleId, ref vehicleData);
                }
            }

            // NON-STOCK CODE START
#if BENCHMARK
            using (var bm = new Benchmark(null, "UpdateVehiclePosition")) {
#endif
            VehicleStateManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData);
#if BENCHMARK
        }
#endif
            if (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0)
            {
#if BENCHMARK
                using (var bm = new Benchmark(null, "LogTraffic")) {
#endif
                // Advanced AI traffic measurement
                VehicleStateManager.Instance.LogTraffic(vehicleId);
#if BENCHMARK
            }
#endif
            }
            // NON-STOCK CODE END

            Vector3 lastFramePosition = vehicleData.GetLastFramePosition();
            int lodPhysics;
            if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f)
            {
                lodPhysics = 2;
            }
            else if (Vector3.SqrMagnitude(Singleton <SimulationManager> .instance.m_simulationView.m_position - lastFramePosition) >= 250000f)
            {
                lodPhysics = 1;
            }
            else
            {
                lodPhysics = 0;
            }
            this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics);
            if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0)
            {
                VehicleManager vehManager = Singleton <VehicleManager> .instance;
                ushort         trailerId  = vehicleData.m_trailingVehicle;
                int            numIters   = 0;
                while (trailerId != 0)
                {
                    ushort      trailingVehicle = vehManager.m_vehicles.m_buffer[(int)trailerId].m_trailingVehicle;
                    VehicleInfo info            = vehManager.m_vehicles.m_buffer[(int)trailerId].Info;
                    info.m_vehicleAI.SimulationStep(trailerId, ref vehManager.m_vehicles.m_buffer[(int)trailerId], vehicleId, ref vehicleData, lodPhysics);
                    trailerId = trailingVehicle;
                    if (++numIters > 16384)
                    {
                        CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);

                        break;
                    }
                }
            }

            int privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service);
            int maxBlockCounter     = (privateServiceIndex == -1) ? 150 : 100;
            if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0)
            {
                Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId);
            }
            else if ((int)vehicleData.m_blockCounter >= maxBlockCounter)
            {
                // NON-STOCK CODE START
                bool mayDespawn = true;
#if BENCHMARK
                using (var bm = new Benchmark(null, "MayDespawn")) {
#endif
                mayDespawn = VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData);
#if BENCHMARK
            }
#endif

                if (mayDespawn)
                {
                    // NON-STOCK CODE END
                    Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId);
                }                 // NON-STOCK CODE
            }
        }
        public void CustomSimulationStep(ushort vehicleId,
                                         ref Vehicle vehicleData,
                                         Vector3 physicsLodRefPos)
        {
#if DEBUG
            bool vehDebug = DebugSettings.VehicleId == 0 ||
                            DebugSettings.VehicleId == vehicleId;
            bool logParkingAi = DebugSwitch.BasicParkingAILog.Get() && vehDebug;
#else
            var logParkingAi = false;
#endif

            if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0)
            {
                PathManager pathManager   = Singleton <PathManager> .instance;
                byte        pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;

                // NON-STOCK CODE START
                ExtPathState mainPathState = ExtPathState.Calculating;
                if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0)
                {
                    mainPathState = ExtPathState.Failed;
                }
                else if ((pathFindFlags & PathUnit.FLAG_READY) != 0)
                {
                    mainPathState = ExtPathState.Ready;
                }

#if DEBUG
                uint logVehiclePath = vehicleData.m_path;
                Log._DebugIf(
                    logParkingAi,
                    () => $"CustomCarAI.CustomSimulationStep({vehicleId}): " +
                    $"Path: {logVehiclePath}, mainPathState={mainPathState}");
#endif

                IExtVehicleManager extVehicleManager = Constants.ManagerFactory.ExtVehicleManager;
                ExtSoftPathState   finalPathState    = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);
                if (Options.parkingAI &&
                    extVehicleManager.ExtVehicles[vehicleId].vehicleType == ExtVehicleType.PassengerCar)
                {
                    ushort driverInstanceId = extVehicleManager.GetDriverInstanceId(vehicleId, ref vehicleData);
                    finalPathState = AdvancedParkingManager.Instance.UpdateCarPathState(
                        vehicleId,
                        ref vehicleData,
                        ref Singleton <CitizenManager> .instance.m_instances.m_buffer[driverInstanceId],
                        ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId],
                        mainPathState);

#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): " +
                                   $"Applied Parking AI logic. Path: {vehicleData.m_path}, " +
                                   $"mainPathState={mainPathState}, finalPathState={finalPathState}");
                    }
#endif
                }

                switch (finalPathState)
                {
                case ExtSoftPathState.Ready: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding " +
                            $"succeeded for vehicle {vehicleId} (finalPathState={finalPathState}). " +
                            $"Path: {vehicleData.m_path} -- calling CarAI.PathfindSuccess");
                    }
#endif

                    vehicleData.m_pathPositionIndex = 255;
                    vehicleData.m_flags            &= ~Vehicle.Flags.WaitingPath;
                    vehicleData.m_flags            &= ~Vehicle.Flags.Arriving;
                    PathfindSuccess(vehicleId, ref vehicleData);
                    TrySpawn(vehicleId, ref vehicleData);
                    break;
                }

                case ExtSoftPathState.Ignore: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding " +
                            $"result shall be ignored for vehicle {vehicleId} " +
                            $"(finalPathState={finalPathState}). Path: {vehicleData.m_path} -- ignoring");
                    }
#endif
                    return;
                }

                case ExtSoftPathState.Calculating:
                default: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding " +
                            $"result undetermined for vehicle {vehicleId} (finalPathState={finalPathState}). " +
                            $"Path: {vehicleData.m_path} -- continue");
                    }
#endif
                    break;
                }

                case ExtSoftPathState.FailedHard: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleId}): HARD path-finding " +
                            $"failure for vehicle {vehicleId} (finalPathState={finalPathState}). " +
                            $"Path: {vehicleData.m_path} -- calling CarAI.PathfindFailure");
                    }
#endif
                    vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
                    Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path);

                    vehicleData.m_path = 0u;
                    PathfindFailure(vehicleId, ref vehicleData);
                    return;
                }

                case ExtSoftPathState.FailedSoft: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleId}): SOFT path-finding " +
                            $"failure for vehicle {vehicleId} (finalPathState={finalPathState}). " +
                            $"Path: {vehicleData.m_path} -- calling CarAI.InvalidPath");
                    }
#endif

                    // path mode has been updated, repeat path-finding
                    vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
                    InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData);
                    break;
                }
                }

                // NON-STOCK CODE END
            }
            else
            {
                if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0)
                {
                    TrySpawn(vehicleId, ref vehicleData);
                }
            }

            // NON-STOCK CODE START
            IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
            extVehicleMan.UpdateVehiclePosition(vehicleId, ref vehicleData);

            if (Options.advancedAI &&
                (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0)
            {
                extVehicleMan.LogTraffic(vehicleId, ref vehicleData);
            }

            // NON-STOCK CODE END
            Vector3 lastFramePosition = vehicleData.GetLastFramePosition();
            int     lodPhysics;
            if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1100f * 1100f)
            {
                lodPhysics = 2;
            }
            else if (Vector3.SqrMagnitude(
                         Singleton <SimulationManager> .instance.m_simulationView.m_position -
                         lastFramePosition) >= 500f * 500f)
            {
                lodPhysics = 1;
            }
            else
            {
                lodPhysics = 0;
            }

            SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics);
            if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0)
            {
                VehicleManager vehManager = Singleton <VehicleManager> .instance;
                ushort         trailerId  = vehicleData.m_trailingVehicle;
                int            numIters   = 0;
                while (trailerId != 0)
                {
                    ushort      trailingVehicle = vehManager.m_vehicles.m_buffer[trailerId].m_trailingVehicle;
                    VehicleInfo info            = vehManager.m_vehicles.m_buffer[trailerId].Info;

                    info.m_vehicleAI.SimulationStep(
                        trailerId,
                        ref vehManager.m_vehicles.m_buffer[trailerId],
                        vehicleId,
                        ref vehicleData,
                        lodPhysics);

                    trailerId = trailingVehicle;
                    if (++numIters > 16384)
                    {
                        CODebugBase <LogChannel> .Error(
                            LogChannel.Core,
                            $"Invalid list detected!\n{Environment.StackTrace}");

                        break;
                    }
                }
            }

            int privateServiceIndex = ItemClass.GetPrivateServiceIndex(m_info.m_class.m_service);
            int maxBlockCounter     = (privateServiceIndex == -1) ? 150 : 100;

            if ((vehicleData.m_flags & (Vehicle.Flags.Spawned
                                        | Vehicle.Flags.WaitingPath
                                        | Vehicle.Flags.WaitingSpace)) == 0 &&
                vehicleData.m_cargoParent == 0)
            {
                Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId);
            }
            else if (vehicleData.m_blockCounter >= maxBlockCounter)
            {
                // NON-STOCK CODE START
                if (VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData))
                {
                    // NON-STOCK CODE END
                    Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId);
                } // NON-STOCK CODE
            }
        }
        public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen, Vector3 startPos, Vector3 endPos, VehicleInfo vehicleInfo, bool enableTransport, bool ignoreCost)
        {
#if DEBUG
            bool citDebug  = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen;
            bool debug     = GlobalConfig.Instance.Debug.Switches[2] && citDebug;
            bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;

            if (debug)
            {
                Log.Warning($"CustomCitizenAI.ExtStartPathFind({instanceID}): called for citizen {instanceData.m_citizen}, startPos={startPos}, endPos={endPos}, sourceBuilding={instanceData.m_sourceBuilding}, targetBuilding={instanceData.m_targetBuilding}, pathMode={extInstance.pathMode}");
            }
#endif

            // NON-STOCK CODE START
            CitizenManager citizenManager  = Singleton <CitizenManager> .instance;
            ushort         parkedVehicleId = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;
            ushort         homeId          = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_homeBuilding;
            CarUsagePolicy carUsageMode    = CarUsagePolicy.Allowed;

            // disallow car usage if citizen is on a walking tour
            if ((instanceData.m_flags & CitizenInstance.Flags.OnTour) != CitizenInstance.Flags.None)
            {
                carUsageMode = CarUsagePolicy.Forbidden;
                vehicleInfo  = null;                // TODO check if citizens may use bikes on walking tours
            }

#if BENCHMARK
            using (var bm = new Benchmark(null, "ParkingAI.Preparation")) {
#endif
            if (Options.prohibitPocketCars)
            {
                switch (extInstance.pathMode)
                {
                case ExtPathMode.RequiresWalkingPathToParkedCar:
                case ExtPathMode.CalculatingWalkingPathToParkedCar:
                case ExtPathMode.WalkingToParkedCar:
                case ExtPathMode.ApproachingParkedCar:
                    if (parkedVehicleId == 0 || carUsageMode == CarUsagePolicy.Forbidden)
                    {
                        // parked vehicle not present or citizen is on a walking tour
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode} but no parked vehicle present OR citizen is on a walking tour (carUsageMode={carUsageMode}). Change to 'None'.");
                        }
#endif

                        extInstance.Reset();
                    }
                    else
                    {
#if DEBUG
                        if (fineDebug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}.  Change to 'CalculatingWalkingPathToParkedCar'.");
                        }
#endif
                        extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToParkedCar;
                    }
                    break;

                case ExtPathMode.RequiresWalkingPathToTarget:
                case ExtPathMode.CalculatingWalkingPathToTarget:
                case ExtPathMode.WalkingToTarget:
#if DEBUG
                    if (fineDebug)
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'CalculatingWalkingPathToTarget'.");
                    }
#endif
                    extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget;
                    break;

                case ExtPathMode.RequiresCarPath:
                case ExtPathMode.DrivingToTarget:
                case ExtPathMode.DrivingToKnownParkPos:
                case ExtPathMode.DrivingToAltParkPos:
                case ExtPathMode.CalculatingCarPathToAltParkPos:
                case ExtPathMode.CalculatingCarPathToKnownParkPos:
                case ExtPathMode.CalculatingCarPathToTarget:
                    if (parkedVehicleId == 0 || carUsageMode == CarUsagePolicy.Forbidden)
                    {
                        // parked vehicle not present or citizen is on a walking tour

#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode} but no parked vehicle present OR citizen is on a walking tour (carUsageMode={carUsageMode}). Change to 'None'.");
                        }
#endif

                        extInstance.Reset();
                    }
                    else
                    {
#if DEBUG
                        if (fineDebug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}.  Change to 'RequiresCarPath'.");
                        }
#endif

                        extInstance.pathMode = ExtPathMode.RequiresCarPath;
                    }
                    break;

                default:
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'None'.");
                    }
#endif
                    extInstance.Reset();
                    break;
                }

                /*
                 * the following holds:
                 * - pathMode is now either CalculatingWalkingPathToParkedCar, CalculatingWalkingPathToTarget, RequiresCarPath or None.
                 * - if pathMode is CalculatingWalkingPathToParkedCar or RequiresCarPath: parked car is present and citizen is not on a walking tour
                 */

                /*
                 * reuse parked vehicle info
                 */
                if (parkedVehicleId != 0)
                {
                    vehicleInfo = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
                }

                /*
                 * check if the citizen must use their car on their current path
                 */
                if (parkedVehicleId != 0 &&                         // parked car present
                    carUsageMode != CarUsagePolicy.Forbidden &&     // cititzen is not on a walking tour
                    extInstance.pathMode == ExtPathMode.None &&     // initiating a new path
                    homeId != 0 &&                                  // home building present
                    instanceData.m_targetBuilding == homeId         // current target is home
                    )
                {
                    /*
                     * citizen travels back home
                     * -> check if their car should be returned
                     */
                    if ((extCitizen.lastTransportMode & ExtCitizen.ExtTransportMode.Car) != ExtCitizen.ExtTransportMode.None)
                    {
                        /*
                         * citizen travelled by car
                         * -> return car back home
                         */
                        extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar;

#if DEBUG
                        if (fineDebug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen used their car before and is not at home. Forcing to walk to parked car.");
                        }
#endif
                    }
                    else
                    {
                        /*
                         * citizen travelled by other means of transport
                         * -> check distance between home and parked car. if too far away: force to take the car back home
                         */
                        float distHomeToParked = (Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position - Singleton <BuildingManager> .instance.m_buildings.m_buffer[homeId].m_position).magnitude;

                        if (distHomeToParked > GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToHome)
                        {
                            /*
                             * force to take car back home
                             */
                            extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar;

#if DEBUG
                            if (fineDebug)
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen wants to go home and parked car is too far away ({distHomeToParked}). Forcing walking to parked car.");
                            }
#endif
                        }
                    }
                }

                /*
                 * modify path-finding constraints (vehicleInfo, endPos) if citizen is forced to walk
                 */
                if (extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToParkedCar || extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToTarget)
                {
                    /*
                     * vehicle must not be used since we need a walking path to either
                     * 1. a parked car or
                     * 2. the target building
                     */
                    vehicleInfo  = null;
                    carUsageMode = CarUsagePolicy.Forbidden;

                    if (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar)
                    {
                        /*
                         * walk to parked car
                         * -> end position is parked car
                         */
                        endPos = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;
#if DEBUG
                        if (fineDebug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen shall go to parked vehicle @ {endPos}");
                        }
#endif
                    }
                }
                else if (extInstance.pathMode == ExtPathMode.RequiresCarPath)
                {
                    /*
                     * citizen stands in front of their parked vehicle
                     * -> find a car-only path now
                     */
                    carUsageMode = CarUsagePolicy.Forced;
                    startPos     = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;                       // force to start from the parked car

#if DEBUG
                    if (fineDebug)
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is forced to drive their car");
                    }
#endif
                }
            }
#if BENCHMARK
        }
#endif
#if DEBUG
            if (fineDebug)
            {
                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is allowed to drive their car? {carUsageMode}");
            }
#endif
            // NON-STOCK CODE END

            /*
             * semi-stock code: determine path-finding parameters (laneTypes, vehicleTypes, extVehicleType, etc.)
             */
            NetInfo.LaneType laneTypes           = NetInfo.LaneType.Pedestrian;
            VehicleInfo.VehicleType vehicleTypes = VehicleInfo.VehicleType.None;
            bool randomParking            = false;
            bool combustionEngine         = false;
            ExtVehicleType extVehicleType = ExtVehicleType.None;
            if (vehicleInfo != null)
            {
                if (vehicleInfo.m_class.m_subService == ItemClass.SubService.PublicTransportTaxi)
                {
                    if ((instanceData.m_flags & CitizenInstance.Flags.CannotUseTaxi) == CitizenInstance.Flags.None && Singleton <DistrictManager> .instance.m_districts.m_buffer[0].m_productionData.m_finalTaxiCapacity != 0u)
                    {
                        SimulationManager instance = Singleton <SimulationManager> .instance;
                        if (instance.m_isNightTime || instance.m_randomizer.Int32(2u) == 0)
                        {
                            laneTypes     |= (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
                            vehicleTypes  |= vehicleInfo.m_vehicleType;
                            extVehicleType = ExtVehicleType.Taxi;                             // NON-STOCK CODE
                            // NON-STOCK CODE START
                            if (Options.prohibitPocketCars)
                            {
                                extInstance.pathMode = ExtPathMode.TaxiToTarget;
                            }
                            // NON-STOCK CODE END
                        }
                    }
                }
                else
                // NON-STOCK CODE START
                if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Car)
                {
                    if (carUsageMode != CarUsagePolicy.Forbidden)
                    {
                        extVehicleType   = ExtVehicleType.PassengerCar;
                        laneTypes       |= NetInfo.LaneType.Vehicle;
                        vehicleTypes    |= vehicleInfo.m_vehicleType;
                        combustionEngine = vehicleInfo.m_class.m_subService == ItemClass.SubService.ResidentialLow;
                    }
                }
                else if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle)
                {
                    extVehicleType = ExtVehicleType.Bicycle;
                    laneTypes     |= NetInfo.LaneType.Vehicle;
                    vehicleTypes  |= vehicleInfo.m_vehicleType;
                }
                // NON-STOCK CODE END
            }

            // NON-STOCK CODE START
            ExtPathType extPathType   = ExtPathType.None;
            PathUnit.Position endPosA = default(PathUnit.Position);
            bool calculateEndPos      = true;
            bool allowRandomParking   = true;
#if BENCHMARK
            using (var bm = new Benchmark(null, "ParkingAI.Main")) {
#endif
            if (Options.prohibitPocketCars)
            {
                // Parking AI

                if (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresCarPath)
                {
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Setting startPos={startPos} for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode}");
                    }
#endif

                    if (instanceData.m_targetBuilding == 0 || (Singleton <BuildingManager> .instance.m_buildings.m_buffer[instanceData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None)
                    {
                        /*
                         * the citizen is starting their journey and the target is not an outside connection
                         * -> find a suitable parking space near the target
                         */

#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Finding parking space at target for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode} parkedVehicleId={parkedVehicleId}");
                        }
#endif

                        // find a parking space in the vicinity of the target
                        bool    calcEndPos;
                        Vector3 parkPos;
                        if (AdvancedParkingManager.Instance.FindParkingSpaceForCitizen(endPos, vehicleInfo, ref extInstance, homeId, instanceData.m_targetBuilding == homeId, 0, false, out parkPos, ref endPosA, out calcEndPos) && extInstance.CalculateReturnPath(parkPos, endPos))
                        {
                            // success
                            extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingCarPathToKnownParkPos;
                            calculateEndPos      = calcEndPos;                              // if true, the end path position still needs to be calculated
                            allowRandomParking   = false;                                   // find a direct path to the calculated parking position
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Finding known parking space for citizen instance {instanceID}, parked vehicle {parkedVehicleId} succeeded and return path {extInstance.returnPathId} ({extInstance.returnPathState}) is calculating. PathMode={extInstance.pathMode}");
                            }
#endif

                            /*if (! extInstance.CalculateReturnPath(parkPos, endPos)) {
                             *      // TODO retry?
                             *      if (debug)
                             *              Log._Debug($"CustomCitizenAI.CustomStartPathFind: [PFFAIL] Could not calculate return path for citizen instance {instanceID}, parked vehicle {parkedVehicleId}. Calling OnPathFindFailed.");
                             *      CustomHumanAI.OnPathFindFailure(extInstance);
                             *      return false;
                             * }*/
                        }
                    }

                    if (extInstance.pathMode == ExtPathMode.RequiresCarPath)
                    {
                        /*
                         * no known parking space found (pathMode has not been updated in the block above)
                         * -> calculate direct path to target
                         */
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} is still at CurrentPathMode={extInstance.pathMode} (no parking space found?). Setting it to CalculatingCarPath. parkedVehicleId={parkedVehicleId}");
                        }
#endif
                        extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingCarPathToTarget;
                    }
                }

                /*
                 * determine path type from path mode
                 */
                extPathType = extInstance.GetPathType();

                /*
                 * the following holds:
                 * - pathMode is now either CalculatingWalkingPathToParkedCar, CalculatingWalkingPathToTarget, CalculatingCarPathToTarget, CalculatingCarPathToKnownParkPos or None.
                 */
            }
#if BENCHMARK
        }
#endif

            /*
             * enable random parking if exact parking space was not calculated yet
             */
            if (extVehicleType == ExtVehicleType.PassengerCar || extVehicleType == ExtVehicleType.Bicycle)
            {
                if (allowRandomParking &&
                    instanceData.m_targetBuilding != 0 &&
                    (
                        Singleton <BuildingManager> .instance.m_buildings.m_buffer[instanceData.m_targetBuilding].Info.m_class.m_service > ItemClass.Service.Office ||
                        (instanceData.m_flags & CitizenInstance.Flags.TargetIsNode) != 0
                    ))
                {
                    randomParking = true;
                }
            }
            // NON-STOCK CODE END

            /*
             * determine the path position of the parked vehicle
             */
            PathUnit.Position parkedVehiclePathPos = default(PathUnit.Position);
            if (parkedVehicleId != 0 && extVehicleType == ExtVehicleType.PassengerCar)
            {
                Vector3 position = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;
                CustomPathManager.FindPathPositionWithSpiralLoop(position, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, false, false, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, out parkedVehiclePathPos);
            }
            bool allowUnderground = (instanceData.m_flags & (CitizenInstance.Flags.Underground | CitizenInstance.Flags.Transition)) != CitizenInstance.Flags.None;

#if DEBUG
            if (debug)
            {
                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Requesting path-finding for citizen instance {instanceID}, citizen {instanceData.m_citizen}, extVehicleType={extVehicleType}, extPathType={extPathType}, startPos={startPos}, endPos={endPos}, sourceBuilding={instanceData.m_sourceBuilding}, targetBuilding={instanceData.m_targetBuilding} pathMode={extInstance.pathMode}");
            }
#endif

            /*
             * determine start & end path positions
             */
            bool foundEndPos   = !calculateEndPos || FindPathPosition(instanceID, ref instanceData, endPos, Options.prohibitPocketCars && (instanceData.m_targetBuilding == 0 || (Singleton <BuildingManager> .instance.m_buildings.m_buffer[instanceData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None) ? NetInfo.LaneType.Pedestrian : laneTypes, vehicleTypes, false, out endPosA);         // NON-STOCK CODE: with Parking AI enabled, the end position must be a pedestrian position
            bool foundStartPos = false;
            PathUnit.Position startPosA;

            if (Options.prohibitPocketCars && (extInstance.pathMode == ExtPathMode.CalculatingCarPathToTarget || extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos))
            {
                /*
                 * citizen will enter their car now
                 * -> find a road start position
                 */
                foundStartPos = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, laneTypes & ~NetInfo.LaneType.Pedestrian, vehicleTypes, allowUnderground, false, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, out startPosA);
            }
            else
            {
                foundStartPos = FindPathPosition(instanceID, ref instanceData, startPos, laneTypes, vehicleTypes, allowUnderground, out startPosA);
            }

            /*
             * start path-finding
             */
            if (foundStartPos &&            // TODO probably fails if vehicle is parked too far away from road
                foundEndPos                 // NON-STOCK CODE
                )
            {
                if (enableTransport)
                {
                    /*
                     * public transport usage is allowed for this path
                     */
                    if ((instanceData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None)
                    {
                        if (carUsageMode != CarUsagePolicy.Forced)                           // NON-STOCK CODE

                        /*
                         * citizen may use public transport
                         */
                        {
                            laneTypes |= NetInfo.LaneType.PublicTransport;

                            uint citizenId = instanceData.m_citizen;
                            if (citizenId != 0u && (citizenManager.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Evacuating) != Citizen.Flags.None)
                            {
                                laneTypes |= NetInfo.LaneType.EvacuationTransport;
                            }
                        }
                    }
                    else if (Options.prohibitPocketCars)                         // TODO check for incoming connection

                    /*
                     * citizen tried to use public transport but waiting time was too long
                     * -> add public transport demand for source building
                     */
                    {
                        if (instanceData.m_sourceBuilding != 0)
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} cannot uses public transport from building {instanceData.m_sourceBuilding} to {instanceData.m_targetBuilding}. Incrementing public transport demand.");
                            }
#endif
                            ExtBuildingManager.Instance.ExtBuildings[instanceData.m_sourceBuilding].AddPublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandWaitingIncrement, true);
                        }
                    }
                }

                PathUnit.Position dummyPathPos = default(PathUnit.Position);
                uint path;
                // NON-STOCK CODE START
                PathCreationArgs args;
                args.extPathType         = extPathType;
                args.extVehicleType      = extVehicleType;
                args.vehicleId           = 0;
                args.buildIndex          = Singleton <SimulationManager> .instance.m_currentBuildIndex;
                args.startPosA           = startPosA;
                args.startPosB           = dummyPathPos;
                args.endPosA             = endPosA;
                args.endPosB             = dummyPathPos;
                args.vehiclePosition     = parkedVehiclePathPos;
                args.laneTypes           = laneTypes;
                args.vehicleTypes        = vehicleTypes;
                args.maxLength           = 20000f;
                args.isHeavyVehicle      = false;
                args.hasCombustionEngine = combustionEngine;
                args.ignoreBlocked       = false;
                args.ignoreFlooded       = false;
                args.ignoreCosts         = ignoreCost;
                args.randomParking       = randomParking;
                args.stablePath          = false;
                args.skipQueue           = false;

                if ((instanceData.m_flags & CitizenInstance.Flags.OnTour) != 0)
                {
                    args.stablePath = true;
                    args.maxLength  = 160000f;
                    //args.laneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
                }
                else
                {
                    args.stablePath = false;
                    args.maxLength  = 20000f;
                }

                bool res = CustomPathManager._instance.CreatePath(out path, ref Singleton <SimulationManager> .instance.m_randomizer, args);
                // NON-STOCK CODE END

                if (res)
                {
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Path-finding starts for citizen instance {instanceID}, path={path}, extVehicleType={extVehicleType}, startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, laneType={laneTypes}, vehicleType={vehicleTypes}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}, vehiclePos.m_segment={parkedVehiclePathPos.m_segment}, vehiclePos.m_lane={parkedVehiclePathPos.m_lane}, vehiclePos.m_offset={parkedVehiclePathPos.m_offset}");
                    }
#endif

                    if (instanceData.m_path != 0u)
                    {
                        Singleton <PathManager> .instance.ReleasePath(instanceData.m_path);
                    }
                    instanceData.m_path   = path;
                    instanceData.m_flags |= CitizenInstance.Flags.WaitingPath;
                    return(true);
                }
            }

#if DEBUG
            if (Options.prohibitPocketCars)
            {
                if (debug)
                {
                    Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): CustomCitizenAI.CustomStartPathFind: [PFFAIL] failed for citizen instance {instanceID} (CurrentPathMode={extInstance.pathMode}). startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, startPosA.offset={startPosA.m_offset}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}, endPosA.offset={endPosA.m_offset}, foundStartPos={foundStartPos}, foundEndPos={foundEndPos}");
                }
            }
#endif
            return(false);
        }
        public string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance data, out InstanceID target)
        {
            if ((data.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None)
            {
                target = InstanceID.Empty;
                return(Locale.Get("CITIZEN_STATUS_CONFUSED"));
            }
            CitizenManager instance  = Singleton <CitizenManager> .instance;
            uint           citizen   = data.m_citizen;
            bool           isStudent = false;
            ushort         homeId    = 0;
            ushort         workId    = 0;
            ushort         vehicleId = 0;

            if (citizen != 0u)
            {
                homeId    = instance.m_citizens.m_buffer[(int)((UIntPtr)citizen)].m_homeBuilding;
                workId    = instance.m_citizens.m_buffer[(int)((UIntPtr)citizen)].m_workBuilding;
                vehicleId = instance.m_citizens.m_buffer[(int)((UIntPtr)citizen)].m_vehicle;
                isStudent = ((instance.m_citizens.m_buffer[(int)((UIntPtr)citizen)].m_flags & Citizen.Flags.Student) != Citizen.Flags.None);
            }
            ushort targetBuilding = data.m_targetBuilding;

            if (targetBuilding == 0)
            {
                target = InstanceID.Empty;
                return(Locale.Get("CITIZEN_STATUS_CONFUSED"));
            }
            bool   isOutsideConnection = (Singleton <BuildingManager> .instance.m_buildings.m_buffer[(int)targetBuilding].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None;
            bool   hangsAround         = data.m_path == 0u && (data.m_flags & CitizenInstance.Flags.HangAround) != CitizenInstance.Flags.None;
            String ret = "";

            if (vehicleId != 0)
            {
                VehicleManager instance2 = Singleton <VehicleManager> .instance;
                VehicleInfo    info      = instance2.m_vehicles.m_buffer[(int)vehicleId].Info;
                if (info.m_class.m_service == ItemClass.Service.Residential && info.m_vehicleType != VehicleInfo.VehicleType.Bicycle)
                {
                    if (info.m_vehicleAI.GetOwnerID(vehicleId, ref instance2.m_vehicles.m_buffer[(int)vehicleId]).Citizen == citizen)
                    {
                        if (isOutsideConnection)
                        {
                            target = InstanceID.Empty;
                            return(Locale.Get("CITIZEN_STATUS_DRIVINGTO_OUTSIDE"));
                        }

                        if (targetBuilding == homeId)
                        {
                            target = InstanceID.Empty;
                            return(Locale.Get("CITIZEN_STATUS_DRIVINGTO_HOME"));
                        }
                        else if (targetBuilding == workId)
                        {
                            target = InstanceID.Empty;
                            return(Locale.Get((!isStudent) ? "CITIZEN_STATUS_DRIVINGTO_WORK" : "CITIZEN_STATUS_DRIVINGTO_SCHOOL"));
                        }
                        else
                        {
                            target          = InstanceID.Empty;
                            target.Building = targetBuilding;
                            return(Locale.Get("CITIZEN_STATUS_DRIVINGTO"));
                        }
                    }
                }
                else if (info.m_class.m_service == ItemClass.Service.PublicTransport || info.m_class.m_service == ItemClass.Service.Disaster)
                {
                    if ((data.m_flags & CitizenInstance.Flags.WaitingTaxi) != CitizenInstance.Flags.None)
                    {
                        target = InstanceID.Empty;
                        return(Locale.Get("CITIZEN_STATUS_WAITING_TAXI"));
                    }
                    if (isOutsideConnection)
                    {
                        target = InstanceID.Empty;
                        return(Locale.Get("CITIZEN_STATUS_TRAVELLINGTO_OUTSIDE"));
                    }
                    if (targetBuilding == homeId)
                    {
                        target = InstanceID.Empty;
                        return(Locale.Get("CITIZEN_STATUS_TRAVELLINGTO_HOME"));
                    }
                    if (targetBuilding == workId)
                    {
                        target = InstanceID.Empty;
                        return(Locale.Get((!isStudent) ? "CITIZEN_STATUS_TRAVELLINGTO_WORK" : "CITIZEN_STATUS_TRAVELLINGTO_SCHOOL"));
                    }
                    target          = InstanceID.Empty;
                    target.Building = targetBuilding;
                    return(Locale.Get("CITIZEN_STATUS_TRAVELLINGTO"));
                }
            }
            if (isOutsideConnection)
            {
                target = InstanceID.Empty;
                return(Locale.Get("CITIZEN_STATUS_GOINGTO_OUTSIDE"));
            }

            if (targetBuilding == homeId)
            {
                if (hangsAround)
                {
                    target = InstanceID.Empty;
                    return(Locale.Get("CITIZEN_STATUS_AT_HOME"));
                }
                target = InstanceID.Empty;
                ret    = Locale.Get("CITIZEN_STATUS_GOINGTO_HOME");
            }
            else if (targetBuilding == workId)
            {
                if (hangsAround)
                {
                    target = InstanceID.Empty;
                    return(Locale.Get((!isStudent) ? "CITIZEN_STATUS_AT_WORK" : "CITIZEN_STATUS_AT_SCHOOL"));
                }
                target = InstanceID.Empty;
                ret    = Locale.Get((!isStudent) ? "CITIZEN_STATUS_GOINGTO_WORK" : "CITIZEN_STATUS_GOINGTO_SCHOOL");
            }
            else
            {
                if (hangsAround)
                {
                    target          = InstanceID.Empty;
                    target.Building = targetBuilding;
                    return(Locale.Get("CITIZEN_STATUS_VISITING"));
                }
                target          = InstanceID.Empty;
                target.Building = targetBuilding;
                ret             = Locale.Get("CITIZEN_STATUS_GOINGTO");
            }

            // NON-STOCK CODE START
            if (Options.prohibitPocketCars)
            {
                ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
                ret = CustomHumanAI.EnrichLocalizedStatus(ret, extInstance);
            }
            // NON-STOCK CODE END
            return(ret);
        }
        public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceCar)
        {
            if (citizenData.m_citizen == 0u)
            {
                return(null);
            }

            // NON-STOCK CODE START
            bool forceTaxi = false;

            if (Options.prohibitPocketCars)
            {
                ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
                if (extInstance.PathMode == ExtPathMode.TaxiToTarget)
                {
                    forceTaxi = true;
                }
            }
            // NON-STOCK CODE END

            Citizen.AgeGroup ageGroup = CustomCitizenAI.GetAgeGroup(citizenData.Info.m_agePhase);

            int carProb;
            int bikeProb;
            int taxiProb;

            // NON-STOCK CODE START
            if (forceTaxi)
            {
                carProb  = 0;
                bikeProb = 0;
                taxiProb = 100;
            }
            else
            // NON-STOCK CODE END
            if (forceCar || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None)
            {
                carProb  = 100;
                bikeProb = 0;
                taxiProb = 0;
            }
            else
            {
                carProb  = GetCarProbability(instanceID, ref citizenData, ageGroup);
                bikeProb = GetBikeProbability(instanceID, ref citizenData, ageGroup);
                taxiProb = GetTaxiProbability(instanceID, ref citizenData, ageGroup);
            }
            Randomizer randomizer = new Randomizer(citizenData.m_citizen);
            bool       useCar     = randomizer.Int32(100u) < carProb;
            bool       useBike    = randomizer.Int32(100u) < bikeProb;
            bool       useTaxi    = randomizer.Int32(100u) < taxiProb;

            ItemClass.Service    service    = ItemClass.Service.Residential;
            ItemClass.SubService subService = ItemClass.SubService.ResidentialLow;
            if (!useCar && useTaxi)
            {
                service    = ItemClass.Service.PublicTransport;
                subService = ItemClass.SubService.PublicTransportTaxi;
            }
            // NON-STOCK CODE START
            VehicleInfo carInfo = null;

            if (Options.prohibitPocketCars && useCar && !useTaxi)
            {
                ExtCitizenInstance extInstance     = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
                ushort             parkedVehicleId = Singleton <CitizenManager> .instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;
                if (parkedVehicleId != 0)
                {
#if DEBUG
                    if (GlobalConfig.Instance.DebugSwitches[2])
                    {
                        Log._Debug($"CustomResidentAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info.");
                    }
#endif
                    carInfo = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
                }
            }
            if (carInfo == null && (useCar || useTaxi))
            {
                // NON-STOCK CODE END
                carInfo = Singleton <VehicleManager> .instance.GetRandomVehicleInfo(ref randomizer, service, subService, ItemClass.Level.Level1);
            }

            VehicleInfo bikeInfo = Singleton <VehicleManager> .instance.GetRandomVehicleInfo(ref randomizer, ItemClass.Service.Residential, ItemClass.SubService.ResidentialHigh, (ageGroup != Citizen.AgeGroup.Child)?ItemClass.Level.Level2 : ItemClass.Level.Level1);

            if (useBike && bikeInfo != null)
            {
                return(bikeInfo);
            }
            if ((useCar || useTaxi) && carInfo != null)
            {
                return(carInfo);
            }
            return(null);
        }
        public bool IsAtOutsideConnection(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, Vector3 startPos)
        {
#if DEBUG
            bool citDebug =
                (GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == extInstance.GetCitizenId()) &&
                (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&
                (GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == Singleton <CitizenManager> .instance.m_instances.m_buffer[extInstance.instanceId].m_sourceBuilding) &&
                (GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == Singleton <CitizenManager> .instance.m_instances.m_buffer[extInstance.instanceId].m_targetBuilding)
            ;
            bool debug     = GlobalConfig.Instance.Debug.Switches[2] && citDebug;
            bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;

            if (debug)
            {
                Log._Debug($"ExtCitizenInstanceManager.IsAtOutsideConnection({extInstance.instanceId}): called. Path: {instanceData.m_path} sourceBuilding={instanceData.m_sourceBuilding}");
            }
#endif

            bool ret =
                (Singleton <BuildingManager> .instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None &&
                (startPos - Singleton <BuildingManager> .instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].m_position).magnitude <= GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance;

#if DEBUG
            if (debug)
            {
                Log._Debug($"ExtCitizenInstanceManager.IsAtOutsideConnection({instanceId}): ret={ret}");
            }
#endif
            return(ret);
        }
Пример #9
0
        public void CustomSimulationStep(ushort instanceId,
                                         ref CitizenInstance instanceData,
                                         Vector3 physicsLodRefPos)
        {
#if DEBUG
            bool citizenDebug = (DebugSettings.CitizenInstanceId == 0 ||
                                 DebugSettings.CitizenInstanceId == instanceId) &&
                                (DebugSettings.CitizenId == 0 ||
                                 DebugSettings.CitizenId == instanceData.m_citizen) &&
                                (DebugSettings.SourceBuildingId == 0 ||
                                 DebugSettings.SourceBuildingId == instanceData.m_sourceBuilding) &&
                                (DebugSettings.TargetBuildingId == 0 ||
                                 DebugSettings.TargetBuildingId == instanceData.m_targetBuilding);
            bool logParkingAi = DebugSwitch.BasicParkingAILog.Get() && citizenDebug;
#else
            var logParkingAi = false;
#endif
            CitizenManager citizenManager = Singleton <CitizenManager> .instance;
            uint           citizenId      = instanceData.m_citizen;

            if ((instanceData.m_flags & (CitizenInstance.Flags.Blown
                                         | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None &&
                (instanceData.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None)
            {
                citizenManager.ReleaseCitizenInstance(instanceId);
                if (citizenId != 0u)
                {
                    citizenManager.ReleaseCitizen(citizenId);
                }

                return;
            }

            Citizen[] citizensBuffer = citizenManager.m_citizens.m_buffer;
            if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None)
            {
                PathManager pathManager   = Singleton <PathManager> .instance;
                byte        pathFindFlags = pathManager.m_pathUnits.m_buffer[instanceData.m_path].m_pathFindFlags;

                // NON-STOCK CODE START
                ExtPathState mainPathState = ExtPathState.Calculating;
                if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || instanceData.m_path == 0)
                {
                    mainPathState = ExtPathState.Failed;
                }
                else if ((pathFindFlags & PathUnit.FLAG_READY) != 0)
                {
                    mainPathState = ExtPathState.Ready;
                }

                if (logParkingAi)
                {
                    Log._Debug(
                        $"CustomHumanAI.CustomSimulationStep({instanceId}): " +
                        $"Path: {instanceData.m_path}, mainPathState={mainPathState}");
                }

                ExtSoftPathState finalPathState;
                using (var bm = Benchmark.MaybeCreateBenchmark(
                           null,
                           "ConvertPathStateToSoftPathState+UpdateCitizenPathState"))
                {
                    finalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);

                    if (Options.parkingAI)
                    {
                        finalPathState = AdvancedParkingManager.Instance.UpdateCitizenPathState(
                            instanceId,
                            ref instanceData,
                            ref ExtCitizenInstanceManager
                            .Instance.ExtInstances[
                                instanceId],
                            ref ExtCitizenManager
                            .Instance.ExtCitizens[
                                citizenId],
                            ref citizensBuffer[
                                instanceData.m_citizen],
                            mainPathState);
                        if (logParkingAi)
                        {
                            Log._Debug(
                                $"CustomHumanAI.CustomSimulationStep({instanceId}): " +
                                $"Applied Parking AI logic. Path: {instanceData.m_path}, " +
                                $"mainPathState={mainPathState}, finalPathState={finalPathState}, " +
                                $"extCitizenInstance={ExtCitizenInstanceManager.Instance.ExtInstances[instanceId]}");
                        }
                    } // if Options.parkingAi
                }

                switch (finalPathState)
                {
                case ExtSoftPathState.Ready: {
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomHumanAI.CustomSimulationStep({instanceId}): Path-finding " +
                            $"succeeded for citizen instance {instanceId} " +
                            $"(finalPathState={finalPathState}). Path: {instanceData.m_path} " +
                            "-- calling HumanAI.PathfindSuccess");
                    }

                    if (citizenId == 0 ||
                        citizensBuffer[instanceData.m_citizen].m_vehicle == 0)
                    {
                        Spawn(instanceId, ref instanceData);
                    }

                    instanceData.m_pathPositionIndex = 255;
                    instanceData.m_flags            &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags            &= ~(CitizenInstance.Flags.HangAround
                                                         | CitizenInstance.Flags.Panicking
                                                         | CitizenInstance.Flags.SittingDown
                                                         | CitizenInstance.Flags.Cheering);

                    // NON-STOCK CODE START (transferred from ResidentAI.PathfindSuccess)
                    const Citizen.Flags CTZ_MASK = Citizen.Flags.Tourist
                                                   | Citizen.Flags.MovingIn
                                                   | Citizen.Flags.DummyTraffic;
                    if (citizenId != 0 &&
                        (citizensBuffer[citizenId].m_flags & CTZ_MASK) == Citizen.Flags.MovingIn)
                    {
                        StatisticBase statisticBase = Singleton <StatisticsManager>
                                                      .instance.Acquire <StatisticInt32>(StatisticType.MoveRate);

                        statisticBase.Add(1);
                    }

                    // NON-STOCK CODE END
                    PathfindSuccess(instanceId, ref instanceData);
                    break;
                }

                case ExtSoftPathState.Ignore: {
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomHumanAI.CustomSimulationStep({instanceId}): " +
                            "Path-finding result shall be ignored for citizen instance " +
                            $"{instanceId} (finalPathState={finalPathState}). " +
                            $"Path: {instanceData.m_path} -- ignoring");
                    }

                    return;
                }

                case ExtSoftPathState.Calculating:
                default: {
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomHumanAI.CustomSimulationStep({instanceId}): " +
                            $"Path-finding result undetermined for citizen instance {instanceId} " +
                            $"(finalPathState={finalPathState}). " +
                            $"Path: {instanceData.m_path} -- continue");
                    }

                    break;
                }

                case ExtSoftPathState.FailedHard: {
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomHumanAI.CustomSimulationStep({instanceId}): " +
                            $"HARD path-finding failure for citizen instance {instanceId} " +
                            $"(finalPathState={finalPathState}). Path: {instanceData.m_path} " +
                            "-- calling HumanAI.PathfindFailure");
                    }

                    instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround
                                              | CitizenInstance.Flags.Panicking
                                              | CitizenInstance.Flags.SittingDown
                                              | CitizenInstance.Flags.Cheering);
                    Singleton <PathManager> .instance.ReleasePath(instanceData.m_path);

                    instanceData.m_path = 0u;
                    PathfindFailure(instanceId, ref instanceData);
                    return;
                }

                case ExtSoftPathState.FailedSoft: {
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomHumanAI.CustomSimulationStep({instanceId}): " +
                            $"SOFT path-finding failure for citizen instance {instanceId} " +
                            $"(finalPathState={finalPathState}). Path: {instanceData.m_path} " +
                            "-- calling HumanAI.InvalidPath");
                    }

                    // path mode has been updated, repeat path-finding
                    instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround
                                              | CitizenInstance.Flags.Panicking
                                              | CitizenInstance.Flags.SittingDown
                                              | CitizenInstance.Flags.Cheering);
                    InvalidPath(instanceId, ref instanceData);
                    break;
                }
                }

                // NON-STOCK CODE END
            }

            // NON-STOCK CODE START
            using (var bm = Benchmark.MaybeCreateBenchmark(null, "ExtSimulationStep")) {
                if (Options.parkingAI)
                {
                    if (ExtSimulationStep(
                            instanceId,
                            ref instanceData,
                            ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceId],
                            physicsLodRefPos))
                    {
                        return;
                    }
                }
            }

            // NON-STOCK CODE END
            base.SimulationStep(instanceId, ref instanceData, physicsLodRefPos);

            VehicleManager vehicleManager = Singleton <VehicleManager> .instance;
            ushort         vehicleId      = 0;
            if (instanceData.m_citizen != 0u)
            {
                vehicleId = citizensBuffer[instanceData.m_citizen].m_vehicle;
            }

            if (vehicleId != 0)
            {
                Vehicle[]   vehiclesBuffer = vehicleManager.m_vehicles.m_buffer;
                VehicleInfo vehicleInfo    = vehiclesBuffer[vehicleId].Info;

                if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle)
                {
                    vehicleInfo.m_vehicleAI.SimulationStep(
                        vehicleId,
                        ref vehiclesBuffer[vehicleId],
                        vehicleId,
                        ref vehiclesBuffer[vehicleId],
                        0);
                    vehicleId = 0;
                }
            }

            if (vehicleId != 0 ||
                (instanceData.m_flags & (CitizenInstance.Flags.Character
                                         | CitizenInstance.Flags.WaitingPath
                                         | CitizenInstance.Flags.Blown
                                         | CitizenInstance.Flags.Floating)) !=
                CitizenInstance.Flags.None)
            {
                return;
            }

            instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround
                                      | CitizenInstance.Flags.Panicking
                                      | CitizenInstance.Flags.SittingDown);
            ArriveAtDestination(instanceId, ref instanceData, false);
            citizenManager.ReleaseCitizenInstance(instanceId);
        }
Пример #10
0
        internal bool ExtSimulationStep(ushort instanceID, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, Vector3 physicsLodRefPos)
        {
            // check if the citizen has reached a parked car or target
            if (extInstance.pathMode == ExtPathMode.WalkingToParkedCar || extInstance.pathMode == ExtPathMode.ApproachingParkedCar)
            {
                ushort parkedVehicleId = Singleton <CitizenManager> .instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;
                if (parkedVehicleId == 0)
                {
                    // citizen is reaching their parked car but does not own a parked car
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log.Warning($"CustomHumanAI.CustomSimulationStep({instanceID}): Citizen instance {instanceID} was walking to / reaching their parked car ({extInstance.pathMode}) but parked car has disappeared. RESET.");
                    }
#endif

                    extInstance.Reset();

                    instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);
                    this.InvalidPath(instanceID, ref instanceData);
                    return(true);
                }
                else
                {
                    ParkedCarApproachState approachState = AdvancedParkingManager.Instance.CitizenApproachingParkedCarSimulationStep(instanceID, ref instanceData, ref extInstance, physicsLodRefPos, ref Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId]);
                    switch (approachState)
                    {
                    case ParkedCarApproachState.None:
                    default:
                        break;

                    case ParkedCarApproachState.Approaching:
                        // citizen approaches their parked car
                        return(true);

                    case ParkedCarApproachState.Approached:
                        // citizen reached their parked car
#if DEBUG
                        if (GlobalConfig.Instance.Debug.Switches[4])
                        {
                            Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Citizen instance {instanceID} arrived at parked car. PathMode={extInstance.pathMode}");
                        }
#endif
                        if (instanceData.m_path != 0)
                        {
                            Singleton <PathManager> .instance.ReleasePath(instanceData.m_path);

                            instanceData.m_path = 0;
                        }
                        instanceData.m_flags = instanceData.m_flags & (CitizenInstance.Flags.Created | CitizenInstance.Flags.Cheering | CitizenInstance.Flags.Deleted | CitizenInstance.Flags.Underground | CitizenInstance.Flags.CustomName | CitizenInstance.Flags.Character | CitizenInstance.Flags.BorrowCar | CitizenInstance.Flags.HangAround | CitizenInstance.Flags.InsideBuilding | CitizenInstance.Flags.WaitingPath | CitizenInstance.Flags.TryingSpawnVehicle | CitizenInstance.Flags.CannotUseTransport | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.OnPath | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.AtTarget | CitizenInstance.Flags.RequireSlowStart | CitizenInstance.Flags.Transition | CitizenInstance.Flags.RidingBicycle | CitizenInstance.Flags.OnBikeLane | CitizenInstance.Flags.CannotUseTaxi | CitizenInstance.Flags.CustomColor | CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating | CitizenInstance.Flags.TargetFlags);
                        if (!this.StartPathFind(instanceID, ref instanceData))
                        {
                            instanceData.Unspawn(instanceID);
                            extInstance.Reset();
                        }

                        return(true);

                    case ParkedCarApproachState.Failure:
#if DEBUG
                        if (GlobalConfig.Instance.Debug.Switches[2])
                        {
                            Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Citizen instance {instanceID} failed to arrive at parked car. PathMode={extInstance.pathMode}");
                        }
#endif
                        // repeat path-finding
                        instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                        instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);
                        this.InvalidPath(instanceID, ref instanceData);
                        return(true);
                    }
                }
            }
            else if ((extInstance.pathMode == ExtCitizenInstance.ExtPathMode.WalkingToTarget ||
                      extInstance.pathMode == ExtCitizenInstance.ExtPathMode.PublicTransportToTarget ||
                      extInstance.pathMode == ExtCitizenInstance.ExtPathMode.TaxiToTarget)
                     )
            {
                AdvancedParkingManager.Instance.CitizenApproachingTargetSimulationStep(instanceID, ref instanceData, ref extInstance);
            }
            return(false);
        }
Пример #11
0
        public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instanceData, Vector3 physicsLodRefPos)
        {
            uint citizenId = instanceData.m_citizen;

            if ((instanceData.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None && (instanceData.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None)
            {
                Singleton <CitizenManager> .instance.ReleaseCitizenInstance(instanceID);

                if (citizenId != 0u)
                {
                    Singleton <CitizenManager> .instance.ReleaseCitizen(citizenId);
                }
                return;
            }

            if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None)
            {
                PathManager pathManager   = Singleton <PathManager> .instance;
                byte        pathFindFlags = pathManager.m_pathUnits.m_buffer[instanceData.m_path].m_pathFindFlags;

                // NON-STOCK CODE START
                ExtPathState mainPathState = ExtPathState.Calculating;
                if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || instanceData.m_path == 0)
                {
                    mainPathState = ExtPathState.Failed;
                }
                else if ((pathFindFlags & PathUnit.FLAG_READY) != 0)
                {
                    mainPathState = ExtPathState.Ready;
                }

#if DEBUG
                if (GlobalConfig.Instance.Debug.Switches[2])
                {
                    Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path: {instanceData.m_path}, mainPathState={mainPathState}");
                }
#endif

                ExtSoftPathState finalPathState = ExtSoftPathState.None;
#if BENCHMARK
                using (var bm = new Benchmark(null, "ConvertPathStateToSoftPathState+UpdateCitizenPathState")) {
#endif
                finalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);
                if (Options.prohibitPocketCars)
                {
                    finalPathState = AdvancedParkingManager.Instance.UpdateCitizenPathState(instanceID, ref instanceData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], ref Singleton <CitizenManager> .instance.m_citizens.m_buffer[instanceData.m_citizen], mainPathState);
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Applied Parking AI logic. Path: {instanceData.m_path}, mainPathState={mainPathState}, finalPathState={finalPathState}, extCitizenInstance={ExtCitizenInstanceManager.Instance.ExtInstances[instanceID]}");
                    }
#endif
                }
#if BENCHMARK
            }
#endif

                switch (finalPathState)
                {
                case ExtSoftPathState.Ready:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding succeeded for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.PathfindSuccess");
                    }
#endif
                    this.Spawn(instanceID, ref instanceData);
                    instanceData.m_pathPositionIndex = 255;
                    instanceData.m_flags            &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags            &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);
                    // NON-STOCK CODE START (transferred from ResidentAI.PathfindSuccess)
                    if (citizenId != 0 && (Singleton <CitizenManager> .instance.m_citizens.m_buffer[citizenId].m_flags & (Citizen.Flags.Tourist | Citizen.Flags.MovingIn | Citizen.Flags.DummyTraffic)) == Citizen.Flags.MovingIn)
                    {
                        StatisticBase statisticBase = Singleton <StatisticsManager> .instance.Acquire <StatisticInt32>(StatisticType.MoveRate);

                        statisticBase.Add(1);
                    }
                    // NON-STOCK CODE END
                    this.PathfindSuccess(instanceID, ref instanceData);
                    break;

                case ExtSoftPathState.Ignore:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding result shall be ignored for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- ignoring");
                    }
#endif
                    return;

                case ExtSoftPathState.Calculating:
                default:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding result undetermined for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- continue");
                    }
#endif
                    break;

                case ExtSoftPathState.FailedHard:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): HARD path-finding failure for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.PathfindFailure");
                    }
#endif
                    instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);
                    Singleton <PathManager> .instance.ReleasePath(instanceData.m_path);

                    instanceData.m_path = 0u;
                    this.PathfindFailure(instanceID, ref instanceData);
                    return;

                case ExtSoftPathState.FailedSoft:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): SOFT path-finding failure for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.InvalidPath");
                    }
#endif
                    // path mode has been updated, repeat path-finding
                    instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);
                    this.InvalidPath(instanceID, ref instanceData);
                    return;
                }
                // NON-STOCK CODE END
            }

            // NON-STOCK CODE START
#if BENCHMARK
            using (var bm = new Benchmark(null, "ExtSimulationStep")) {
#endif
            if (Options.prohibitPocketCars)
            {
                if (ExtSimulationStep(instanceID, ref instanceData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], physicsLodRefPos))
                {
                    return;
                }
            }
#if BENCHMARK
        }
#endif
            // NON-STOCK CODE END

            base.SimulationStep(instanceID, ref instanceData, physicsLodRefPos);

            CitizenManager citizenManager = Singleton <CitizenManager> .instance;
            VehicleManager vehicleManager = Singleton <VehicleManager> .instance;
            ushort vehicleId = 0;
            if (instanceData.m_citizen != 0u)
            {
                vehicleId = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_vehicle;
            }
            if (vehicleId != 0)
            {
                VehicleInfo vehicleInfo = vehicleManager.m_vehicles.m_buffer[(int)vehicleId].Info;
                if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle)
                {
                    vehicleInfo.m_vehicleAI.SimulationStep(vehicleId, ref vehicleManager.m_vehicles.m_buffer[(int)vehicleId], vehicleId, ref vehicleManager.m_vehicles.m_buffer[(int)vehicleId], 0);
                    vehicleId = 0;
                }
            }
            if (vehicleId == 0 && (instanceData.m_flags & (CitizenInstance.Flags.Character | CitizenInstance.Flags.WaitingPath | CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) == CitizenInstance.Flags.None)
            {
                instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown);
                this.ArriveAtDestination(instanceID, ref instanceData, false);
                citizenManager.ReleaseCitizenInstance(instanceID);
            }
        }
        /// <summary>
        /// Lightweight simulation step method.
        /// This method is occasionally being called for different cars.
        /// </summary>
        /// <param name="vehicleId"></param>
        /// <param name="vehicleData"></param>
        /// <param name="physicsLodRefPos"></param>
        public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos)
        {
            PathManager pathMan = Singleton <PathManager> .instance;

#if DEBUG
            /*if (!GlobalConfig.Instance.DebugSwitches[0]) {
             *      Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}) called. flags: {vehicleData.m_flags} pfFlags: {pathMan.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags}");
             * }*/
#endif

            // NON-STOCK CODE START
            VehicleState       state             = null;
            ExtCitizenInstance driverExtInstance = null;
            bool prohibitPocketCars = Options.prohibitPocketCars;
            if (prohibitPocketCars)
            {
                // check for valid driver and update return path state
                state = VehicleStateManager.Instance._GetVehicleState(vehicleData.GetFirstVehicle(vehicleId));
                if (state.VehicleType == ExtVehicleType.PassengerCar)
                {
                    driverExtInstance = state.GetDriverExtInstance();
                    if (driverExtInstance == null)
                    {
                        prohibitPocketCars = false;
                    }
                    else
                    {
                        driverExtInstance.UpdateReturnPathState();
                    }
                }
                else
                {
                    prohibitPocketCars = false;
                }
            }
            // NON-STOCK CODE END

            if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0 &&
                (!prohibitPocketCars || driverExtInstance.ReturnPathState != ExtCitizenInstance.ExtPathState.Calculating))                    // NON-STOCK CODE: Parking AI: wait for the return path to be calculated
            {
                PathManager pathManager   = Singleton <PathManager> .instance;
                byte        pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;

                bool pathFindFailed    = (pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0;              // path == 0: non-stock code!
                bool pathFindSucceeded = (pathFindFlags & PathUnit.FLAG_READY) != 0;

#if USEPATHWAITCOUNTER
                if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0)
                {
                    VehicleState state = VehicleStateManager.Instance._GetVehicleState(vehicleId);
                    state.PathWaitCounter = 0;                     // NON-STOCK CODE
                }
#endif

                if (prohibitPocketCars)
                {
                    if (driverExtInstance.ReturnPathState == ExtPathState.Failed)
                    {
                        // no walking path from parking position to target found. flag main path as 'failed'.
#if DEBUG
                        if (GlobalConfig.Instance.DebugSwitches[2])
                        {
                            Log._Debug($"CustomCarAI.CustomSimulationStep: Return path {driverExtInstance.ReturnPathId} FAILED. Forcing path-finding to fail.");
                        }
#endif
                        pathFindSucceeded = false;
                        pathFindFailed    = true;
                    }

                    driverExtInstance.ReleaseReturnPath();

                    if (pathFindSucceeded)
                    {
                        CustomPassengerCarAI.OnPathFindSuccess(vehicleId, ref vehicleData, driverExtInstance);
                    }
                    else if (pathFindFailed)
                    {
                        CustomPassengerCarAI.OnPathFindFailure(driverExtInstance, vehicleId);
                    }
                }

                if (pathFindSucceeded)
                {
                    vehicleData.m_pathPositionIndex = 255;
                    vehicleData.m_flags            &= ~Vehicle.Flags.WaitingPath;
                    vehicleData.m_flags            &= ~Vehicle.Flags.Arriving;
                    this.PathfindSuccess(vehicleId, ref vehicleData);
                    this.TrySpawn(vehicleId, ref vehicleData);
                }
                else if (pathFindFailed)
                {
                    vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
                    Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path);

                    vehicleData.m_path = 0u;
                    this.PathfindFailure(vehicleId, ref vehicleData);
                    return;
                }
#if USEPATHWAITCOUNTER
                else
                {
                    VehicleState state = VehicleStateManager.Instance._GetVehicleState(vehicleId);
                    state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1);                   // NON-STOCK CODE
                }
#endif
            }
            else
            {
                if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0)
                {
                    this.TrySpawn(vehicleId, ref vehicleData);
                }
            }

            /// NON-STOCK CODE START ///
            VehicleStateManager vehStateManager = VehicleStateManager.Instance;
            if (Options.prioritySignsEnabled || Options.timedLightsEnabled)
            {
                // update vehicle position for timed traffic lights and priority signs
                try {
                    vehStateManager.UpdateVehiclePos(vehicleId, ref vehicleData);
                } catch (Exception e) {
                    Log.Error("CarAI CustomSimulationStep Error: " + e.ToString());
                }
            }

            if (!Options.isStockLaneChangerUsed())
            {
                // Advanced AI traffic measurement
                try {
                    vehStateManager.LogTraffic(vehicleId, ref vehicleData, true);
                } catch (Exception e) {
                    Log.Error("CarAI CustomSimulationStep Error: " + e.ToString());
                }
            }
            /// NON-STOCK CODE END ///

            Vector3 lastFramePosition = vehicleData.GetLastFramePosition();
            int     lodPhysics;
            if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f)
            {
                lodPhysics = 2;
            }
            else if (Vector3.SqrMagnitude(Singleton <SimulationManager> .instance.m_simulationView.m_position - lastFramePosition) >= 250000f)
            {
                lodPhysics = 1;
            }
            else
            {
                lodPhysics = 0;
            }
            this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics);
            if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0)
            {
                VehicleManager vehManager = Singleton <VehicleManager> .instance;
                ushort         num        = vehicleData.m_trailingVehicle;
                int            num2       = 0;
                while (num != 0)
                {
                    ushort      trailingVehicle = vehManager.m_vehicles.m_buffer[(int)num].m_trailingVehicle;
                    VehicleInfo info            = vehManager.m_vehicles.m_buffer[(int)num].Info;
                    info.m_vehicleAI.SimulationStep(num, ref vehManager.m_vehicles.m_buffer[(int)num], vehicleId, ref vehicleData, lodPhysics);
                    num = trailingVehicle;
                    if (++num2 > 16384)
                    {
                        CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);

                        break;
                    }
                }
            }
#if PATHRECALC
            ushort recalcSegmentId = 0;
#endif
            int privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service);
            int maxBlockCounter     = (privateServiceIndex == -1) ? 150 : 100;
            if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0)
            {
                Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId);
            }
            else if ((int)vehicleData.m_blockCounter >= maxBlockCounter && Options.enableDespawning)
            {
                Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId);
            }
#if PATHRECALC
            else if (vehicleData.m_leadingVehicle == 0 && CustomVehicleAI.ShouldRecalculatePath(vehicleId, ref vehicleData, maxBlockCounter, out recalcSegmentId))
            {
                CustomVehicleAI.MarkPathRecalculation(vehicleId, recalcSegmentId);
                InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData);
            }
#endif
        }
        public static bool Prefix(CarAI __instance,
                                  ushort vehicleID,
                                  ref Vehicle data,
                                  Vector3 physicsLodRefPos)
        {
            #if DEBUG
            bool vehDebug = DebugSettings.VehicleId == 0 ||
                            DebugSettings.VehicleId == vehicleID;
            bool logParkingAi = DebugSwitch.BasicParkingAILog.Get() && vehDebug;
#else
            var logParkingAi = false;
#endif

            if ((data.m_flags & Vehicle.Flags.WaitingPath) != 0)
            {
                PathManager pathManager   = Singleton <PathManager> .instance;
                byte        pathFindFlags = pathManager.m_pathUnits.m_buffer[data.m_path].m_pathFindFlags;

                // NON-STOCK CODE START
                ExtPathState mainPathState = ExtPathState.Calculating;
                if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || data.m_path == 0)
                {
                    mainPathState = ExtPathState.Failed;
                }
                else if ((pathFindFlags & PathUnit.FLAG_READY) != 0)
                {
                    mainPathState = ExtPathState.Ready;
                }

#if DEBUG
                uint logVehiclePath = data.m_path;
                Log._DebugIf(
                    logParkingAi,
                    () => $"CustomCarAI.CustomSimulationStep({vehicleID}): " +
                    $"Path: {logVehiclePath}, mainPathState={mainPathState}");
#endif

                IExtVehicleManager extVehicleManager = Constants.ManagerFactory.ExtVehicleManager;
                ExtSoftPathState   finalPathState    = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);
                if (Options.parkingAI &&
                    extVehicleManager.ExtVehicles[vehicleID].vehicleType == ExtVehicleType.PassengerCar)
                {
                    ushort driverInstanceId = extVehicleManager.GetDriverInstanceId(vehicleID, ref data);
                    finalPathState = AdvancedParkingManager.Instance.UpdateCarPathState(
                        vehicleID,
                        ref data,
                        ref driverInstanceId.ToCitizenInstance(),
                        ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId],
                        mainPathState);

#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleID}): " +
                                   $"Applied Parking AI logic. Path: {data.m_path}, " +
                                   $"mainPathState={mainPathState}, finalPathState={finalPathState}");
                    }
#endif
                }

                switch (finalPathState)
                {
                case ExtSoftPathState.Ready: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleID}): Path-finding " +
                            $"succeeded for vehicle {vehicleID} (finalPathState={finalPathState}). " +
                            $"Path: {data.m_path} -- calling CarAI.PathfindSuccess");
                    }
#endif

                    data.m_pathPositionIndex = 255;
                    data.m_flags            &= ~Vehicle.Flags.WaitingPath;
                    data.m_flags            &= ~Vehicle.Flags.Arriving;
                    GameConnectionManager.Instance.VehicleAIConnection.PathfindSuccess(__instance, vehicleID, ref data);
                    __instance.TrySpawn(vehicleID, ref data);
                    break;
                }

                case ExtSoftPathState.Ignore: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleID}): Path-finding " +
                            $"result shall be ignored for vehicle {vehicleID} " +
                            $"(finalPathState={finalPathState}). Path: {data.m_path} -- ignoring");
                    }
#endif
                    return(false);
                }

                case ExtSoftPathState.Calculating:
                default: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleID}): Path-finding " +
                            $"result undetermined for vehicle {vehicleID} (finalPathState={finalPathState}). " +
                            $"Path: {data.m_path} -- continue");
                    }
#endif
                    break;
                }

                case ExtSoftPathState.FailedHard: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleID}): HARD path-finding " +
                            $"failure for vehicle {vehicleID} (finalPathState={finalPathState}). " +
                            $"Path: {data.m_path} -- calling CarAI.PathfindFailure");
                    }
#endif
                    data.m_flags &= ~Vehicle.Flags.WaitingPath;
                    Singleton <PathManager> .instance.ReleasePath(data.m_path);

                    data.m_path = 0u;
                    GameConnectionManager.Instance.VehicleAIConnection.PathfindFailure(__instance, vehicleID, ref data);
                    return(false);
                }

                case ExtSoftPathState.FailedSoft: {
#if DEBUG
                    if (logParkingAi)
                    {
                        Log._Debug(
                            $"CustomCarAI.CustomSimulationStep({vehicleID}): SOFT path-finding " +
                            $"failure for vehicle {vehicleID} (finalPathState={finalPathState}). " +
                            $"Path: {data.m_path} -- calling CarAI.InvalidPath");
                    }
#endif

                    // path mode has been updated, repeat path-finding
                    data.m_flags &= ~Vehicle.Flags.WaitingPath;
                    GameConnectionManager.Instance.VehicleAIConnection.InvalidPath(__instance, vehicleID, ref data, vehicleID, ref data);
                    break;
                }
                }

                // NON-STOCK CODE END
            }
            else
            {
                if ((data.m_flags & Vehicle.Flags.WaitingSpace) != 0)
                {
                    __instance.TrySpawn(vehicleID, ref data);
                }
            }

            // NON-STOCK CODE START
            IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
            extVehicleMan.UpdateVehiclePosition(vehicleID, ref data);

            if (Options.advancedAI &&
                (data.m_flags & Vehicle.Flags.Spawned) != 0)
            {
                extVehicleMan.LogTraffic(vehicleID, ref data);
            }

            // NON-STOCK CODE END
            Vector3 lastFramePosition = data.GetLastFramePosition();
            int     lodPhysics;
            if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1100f * 1100f)
            {
                lodPhysics = 2;
            }
            else if (Vector3.SqrMagnitude(
                         Singleton <SimulationManager> .instance.m_simulationView.m_position -
                         lastFramePosition) >= 500f * 500f)
            {
                lodPhysics = 1;
            }
            else
            {
                lodPhysics = 0;
            }

            __instance.SimulationStep(vehicleID, ref data, vehicleID, ref data, lodPhysics);
            if (data.m_leadingVehicle == 0 && data.m_trailingVehicle != 0)
            {
                ushort trailerId = data.m_trailingVehicle;
                int    numIters  = 0;
                while (trailerId != 0)
                {
                    ref Vehicle trailer = ref trailerId.ToVehicle();

                    trailer.Info.m_vehicleAI.SimulationStep(
                        trailerId,
                        ref trailer,
                        vehicleID,
                        ref data,
                        lodPhysics);

                    trailerId = trailer.m_trailingVehicle;
                    if (++numIters > 16384)
                    {
                        CODebugBase <LogChannel> .Error(
                            LogChannel.Core,
                            $"Invalid list detected!\n{Environment.StackTrace}");

                        break;
                    }
                }
            }
        public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance citizenData, ref ExtCitizenInstance extInstance, Vector3 startPos, Vector3 endPos, VehicleInfo vehicleInfo)
        {
#if DEBUG
            if (GlobalConfig.Instance.Debug.Switches[2])
            {
                Log.Warning($"CustomCitizenAI.ExtStartPathFind({instanceID}): called for citizen instance {instanceID}, citizen {citizenData.m_citizen}, startPos={startPos}, endPos={endPos}, sourceBuilding={citizenData.m_sourceBuilding}, targetBuilding={citizenData.m_targetBuilding}, pathMode={extInstance.pathMode}");
            }
#endif

            // NON-STOCK CODE START
            ExtVehicleType extVehicleType  = ExtVehicleType.None;
            ushort         parkedVehicleId = Singleton <CitizenManager> .instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;
            //bool mayUseOwnPassengerCar = true; // allowed to use a passenger car?
            bool           canUseOwnPassengerCar = false;   // allowed to use a passenger car AND given vehicle type is a passenger car?
            CarUsagePolicy carUsageMode          = CarUsagePolicy.Allowed;
            //bool forceUseCar = false;
#if BENCHMARK
            using (var bm = new Benchmark(null, "ParkingAI.Preparation")) {
#endif
            if (Options.prohibitPocketCars)
            {
                switch (extInstance.pathMode)
                {
                case ExtPathMode.RequiresWalkingPathToParkedCar:
                case ExtPathMode.CalculatingWalkingPathToParkedCar:
                case ExtPathMode.WalkingToParkedCar:
                case ExtPathMode.ApproachingParkedCar:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} has CurrentPathMode={extInstance.pathMode}. Switching to 'CalculatingWalkingPathToParkedCar'.");
                    }
#endif
                    extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToParkedCar;
                    break;

                case ExtPathMode.RequiresWalkingPathToTarget:
                case ExtPathMode.CalculatingWalkingPathToTarget:
                case ExtPathMode.WalkingToTarget:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} has CurrentPathMode={extInstance.pathMode}. Change to 'CalculatingWalkingPathToTarget'.");
                    }
#endif
                    extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget;
                    break;

                case ExtPathMode.RequiresCarPath:
                case ExtPathMode.DrivingToTarget:
                case ExtPathMode.DrivingToKnownParkPos:
                case ExtPathMode.DrivingToAltParkPos:
                case ExtPathMode.CalculatingCarPathToAltParkPos:
                case ExtPathMode.CalculatingCarPathToKnownParkPos:
                case ExtPathMode.CalculatingCarPathToTarget:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} has CurrentPathMode={extInstance.pathMode}.  Change to 'RequiresCarPath'.");
                    }
#endif
                    extInstance.pathMode = ExtPathMode.RequiresCarPath;
                    break;

                default:
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} has CurrentPathMode={extInstance.pathMode}. Change to 'None'.");
                    }
#endif
                    extInstance.Reset();
                    break;
                }

                // pathMode is now either CalculatingWalkingPathToParkedCar, CalculatingWalkingPathToTarget, RequiresCarPath or None.

                if (extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToParkedCar || extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToTarget)
                {
                    // vehicle must not be used since we need a walking path
                    vehicleInfo  = null;
                    carUsageMode = CarUsagePolicy.Forbidden;

                    if (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar)
                    {
                        // check if parked car is present
                        if (parkedVehicleId == 0)
                        {
#if DEBUG
                            if (GlobalConfig.Instance.Debug.Switches[2])
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} should go to parked car (CurrentPathMode={extInstance.pathMode}) but parked vehicle could not be found. Setting CurrentPathMode='CalculatingWalkingPathToTarget'.");
                            }
#endif
                            extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget;
                        }
                        else
                        {
                            endPos = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;
#if DEBUG
                            if (GlobalConfig.Instance.Debug.Switches[4])
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} shall go to parked vehicle @ {endPos}");
                            }
#endif
                        }
                    }
                }
                else if (parkedVehicleId != 0)
                {
                    // reuse parked vehicle info
                    vehicleInfo  = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
                    carUsageMode = CarUsagePolicy.Allowed;

                    ushort homeId = Singleton <CitizenManager> .instance.m_citizens.m_buffer[citizenData.m_citizen].m_homeBuilding;
                    if (homeId != 0 && citizenData.m_targetBuilding == homeId)
                    {
                        // check distance between home and parked car. if too far away: force to take the car back home
                        float distHomeToParked = (Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position - Singleton <BuildingManager> .instance.m_buildings.m_buffer[homeId].m_position).magnitude;

                        if (distHomeToParked > GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToHome)
                        {
                            // force to take car back home
#if DEBUG
                            if (GlobalConfig.Instance.Debug.Switches[2])
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} will try to move parkedVehicleId={parkedVehicleId} towards home. distHomeToParked={distHomeToParked}");
                            }
#endif
                            carUsageMode = CarUsagePolicy.Forced;
                        }
                    }
                }
                else
                {
                    carUsageMode = CarUsagePolicy.Allowed;
                }
            }
#if BENCHMARK
        }
#endif

            /*if (Options.parkingRestrictionsEnabled && carUsageMode == CarUsagePolicy.Allowed && parkedVehicleId != 0) {
             *      // force removal of illegaly parked vehicle
             *      PathUnit.Position parkedPathPos;
             *      if (PathManager.FindPathPosition(Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position, ItemClass.Service.Road, NetInfo.LaneType.Parking, VehicleInfo.VehicleType.Car, false, false, 32f, out parkedPathPos)) {
             *              if (! ParkingRestrictionsManager.Instance.IsParkingAllowed(parkedPathPos.m_segment, Singleton<NetManager>.instance.m_segments.m_buffer[parkedPathPos.m_segment].Info.m_lanes[parkedPathPos.m_lane].m_finalDirection)) {
             *                      carUsageMode = CarUsagePolicy.Forced;
             *                      vehicleInfo = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
             *              }
             *      }
             * }*/
            // NON-STOCK CODE END

            NetInfo.LaneType laneTypes           = NetInfo.LaneType.Pedestrian;
            VehicleInfo.VehicleType vehicleTypes = VehicleInfo.VehicleType.None;
            bool randomParking    = false;
            bool combustionEngine = false;
            if (vehicleInfo != null)
            {
                if (vehicleInfo.m_class.m_subService == ItemClass.SubService.PublicTransportTaxi)
                {
                    if ((citizenData.m_flags & CitizenInstance.Flags.CannotUseTaxi) == CitizenInstance.Flags.None && Singleton <DistrictManager> .instance.m_districts.m_buffer[0].m_productionData.m_finalTaxiCapacity != 0u)
                    {
                        SimulationManager instance = Singleton <SimulationManager> .instance;
                        if (instance.m_isNightTime || instance.m_randomizer.Int32(2u) == 0)
                        {
                            laneTypes     |= (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
                            vehicleTypes  |= vehicleInfo.m_vehicleType;
                            extVehicleType = ExtVehicleType.Taxi;                             // NON-STOCK CODE
                            // NON-STOCK CODE START
                            if (Options.prohibitPocketCars)
                            {
                                extInstance.pathMode = ExtPathMode.TaxiToTarget;
                            }
                            // NON-STOCK CODE END
                        }
                    }
                }
                else
                {
                    // NON-STOCK CODE START
                    if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Car)
                    {
                        if (carUsageMode == CarUsagePolicy.Allowed || carUsageMode == CarUsagePolicy.Forced)
                        {
                            extVehicleType        = ExtVehicleType.PassengerCar;
                            laneTypes            |= NetInfo.LaneType.Vehicle;
                            vehicleTypes         |= vehicleInfo.m_vehicleType;
                            combustionEngine      = vehicleInfo.m_class.m_subService == ItemClass.SubService.ResidentialLow;
                            canUseOwnPassengerCar = true;
                        }
                    }
                    else if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle)
                    {
                        extVehicleType = ExtVehicleType.Bicycle;
                        laneTypes     |= NetInfo.LaneType.Vehicle;
                        vehicleTypes  |= vehicleInfo.m_vehicleType;
                        if (citizenData.m_targetBuilding != 0 && Singleton <BuildingManager> .instance.m_buildings.m_buffer[citizenData.m_targetBuilding].Info.m_class.m_service > ItemClass.Service.Office)
                        {
                            randomParking = true;
                        }
                    }
                    // NON-STOCK CODE END
                }
            }
            NetInfo.LaneType startLaneType    = laneTypes;
            PathUnit.Position vehiclePosition = default(PathUnit.Position);

            // NON-STOCK CODE START
            if (Options.prohibitPocketCars)
            {
                if (carUsageMode == CarUsagePolicy.Forced && !canUseOwnPassengerCar)
                {
                    carUsageMode = CarUsagePolicy.Forbidden;
                }
            }

            PathUnit.Position endPosA = default(PathUnit.Position);
            bool calculateEndPos      = true;
            bool allowRandomParking   = true;
#if BENCHMARK
            using (var bm = new Benchmark(null, "ParkingAI.Main")) {
#endif
            if (Options.prohibitPocketCars)
            {
                // Parking AI

                if (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresCarPath)
                {
                    if (canUseOwnPassengerCar)
                    {
                        ushort homeId = Singleton <CitizenManager> .instance.m_citizens.m_buffer[citizenData.m_citizen].m_homeBuilding;

                        startLaneType &= ~NetInfo.LaneType.Pedestrian;                                                               // force to use the car from the beginning
                        startPos       = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position; // force to start from the parked car

#if DEBUG
                        if (GlobalConfig.Instance.Debug.Switches[2])
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Setting startLaneType={startLaneType}, startPos={startPos} for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode}");
                        }
#endif

                        if (citizenData.m_targetBuilding == 0 || (Singleton <BuildingManager> .instance.m_buildings.m_buffer[citizenData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None)
                        {
                            // the citizen is starting their journey and the target is not an outside connection: find a suitable parking space near the target

#if DEBUG
                            if (GlobalConfig.Instance.Debug.Switches[2])
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Finding parking space at target for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode} parkedVehicleId={parkedVehicleId}");
                            }
#endif

                            // find a parking space in the vicinity of the target
                            bool    calcEndPos;
                            Vector3 parkPos;
                            if (AdvancedParkingManager.Instance.FindParkingSpaceForCitizen(endPos, vehicleInfo, ref extInstance, homeId, citizenData.m_targetBuilding == homeId, 0, false, out parkPos, ref endPosA, out calcEndPos) && extInstance.CalculateReturnPath(parkPos, endPos))
                            {
                                // success
                                extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingCarPathToKnownParkPos;
                                calculateEndPos      = calcEndPos;                                  // if true, the end path position still needs to be calculated
                                allowRandomParking   = false;                                       // find a direct path to the calculated parking position
#if DEBUG
                                if (GlobalConfig.Instance.Debug.Switches[2])
                                {
                                    Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Finding known parking space for citizen instance {instanceID}, parked vehicle {parkedVehicleId} succeeded and return path {extInstance.returnPathId} ({extInstance.returnPathState}) is calculating. PathMode={extInstance.pathMode}");
                                }
#endif

                                /*if (! extInstance.CalculateReturnPath(parkPos, endPos)) {
                                 *      // TODO retry?
                                 *      if (GlobalConfig.Instance.Debug.Switches[2])
                                 *              Log._Debug($"CustomCitizenAI.CustomStartPathFind: [PFFAIL] Could not calculate return path for citizen instance {instanceID}, parked vehicle {parkedVehicleId}. Calling OnPathFindFailed.");
                                 *      CustomHumanAI.OnPathFindFailure(extInstance);
                                 *      return false;
                                 * }*/
                            }
                        }

                        if (extInstance.pathMode == ExtPathMode.RequiresCarPath)
                        {
                            // no known parking space found. calculate direct path to target
#if DEBUG
                            if (GlobalConfig.Instance.Debug.Switches[2])
                            {
                                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} is still at CurrentPathMode={extInstance.pathMode} (no parking space found?). Setting it to CalculatingCarPath. parkedVehicleId={parkedVehicleId}");
                            }
#endif
                            extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingCarPathToTarget;
                        }
                    }
                    else if (extInstance.pathMode == ExtPathMode.RequiresCarPath)
                    {
#if DEBUG
                        if (GlobalConfig.Instance.Debug.Switches[2])
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} requires car path but no parked car present. Citizen will need to walk to target.");
                        }
#endif
                        extInstance.Reset();
                    }
                }
            }
#if BENCHMARK
        }
#endif

            if (canUseOwnPassengerCar)
            {
                if (allowRandomParking && citizenData.m_targetBuilding != 0 && Singleton <BuildingManager> .instance.m_buildings.m_buffer[citizenData.m_targetBuilding].Info.m_class.m_service > ItemClass.Service.Office)
                {
                    randomParking = true;
                }
            }

            // determine path type
            ExtPathType extPathType = ExtPathType.None;
            if (Options.prohibitPocketCars)
            {
                extPathType = extInstance.GetPathType();
            }

            // NON-STOCK CODE END

            if (parkedVehicleId != 0 && canUseOwnPassengerCar)
            {
                Vector3 position = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;
                CustomPathManager.FindPathPosition(position, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, false, false, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, out vehiclePosition);
            }
            bool allowUnderground = (citizenData.m_flags & (CitizenInstance.Flags.Underground | CitizenInstance.Flags.Transition)) != CitizenInstance.Flags.None;

#if DEBUG
            if (GlobalConfig.Instance.Debug.Switches[2])
            {
                Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Requesting path-finding for citizen instance {instanceID}, citizen {citizenData.m_citizen}, extVehicleType={extVehicleType}, extPathType={extPathType}, startPos={startPos}, endPos={endPos}, sourceBuilding={citizenData.m_sourceBuilding}, targetBuilding={citizenData.m_targetBuilding} pathMode={extInstance.pathMode}");
            }
#endif

            bool foundEndPos   = !calculateEndPos || FindPathPosition(instanceID, ref citizenData, endPos, Options.prohibitPocketCars && (citizenData.m_targetBuilding == 0 || (Singleton <BuildingManager> .instance.m_buildings.m_buffer[citizenData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None) ? NetInfo.LaneType.Pedestrian : (laneTypes | NetInfo.LaneType.Pedestrian), vehicleTypes, false, out endPosA);         // NON-STOCK CODE: with Parking AI enabled, the end position must be a pedestrian position
            bool foundStartPos = false;
            PathUnit.Position startPosA;

            if (Options.prohibitPocketCars && (extInstance.pathMode == ExtPathMode.CalculatingCarPathToTarget || extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos))
            {
                foundStartPos = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, laneTypes & ~NetInfo.LaneType.Pedestrian, vehicleTypes, allowUnderground, false, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, out startPosA);
            }
            else
            {
                foundStartPos = FindPathPosition(instanceID, ref citizenData, startPos, startLaneType, vehicleTypes, allowUnderground, out startPosA);
            }

            if (foundStartPos &&            // TODO probably fails if vehicle is parked too far away from road
                foundEndPos                 // NON-STOCK CODE
                )
            {
                bool canUseTransport = (citizenData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None;
                if (canUseTransport)
                {
                    if (carUsageMode != CarUsagePolicy.Forced)                       // NON-STOCK CODE
                    {
                        laneTypes |= NetInfo.LaneType.PublicTransport;

                        CitizenManager citizenManager = Singleton <CitizenManager> .instance;
                        uint           citizenId      = citizenManager.m_instances.m_buffer[instanceID].m_citizen;
                        if (citizenId != 0u && (citizenManager.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Evacuating) != Citizen.Flags.None)
                        {
                            laneTypes |= NetInfo.LaneType.EvacuationTransport;
                        }
                    }
                }
                else if (Options.prohibitPocketCars)                     // TODO check for incoming connection
                // cim tried to use public transport but waiting time was too long
                {
                    if (citizenData.m_sourceBuilding != 0)
                    {
#if DEBUG
                        if (GlobalConfig.Instance.Debug.Switches[2])
                        {
                            Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} cannot uses public transport from building {citizenData.m_sourceBuilding} to {citizenData.m_targetBuilding}. Incrementing public transport demand.");
                        }
#endif
                        ExtBuildingManager.Instance.ExtBuildings[citizenData.m_sourceBuilding].AddPublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandWaitingIncrement, true);
                    }
                }

                PathUnit.Position dummyPathPos = default(PathUnit.Position);
                uint path;
                // NON-STOCK CODE START
                PathCreationArgs args;
                args.extPathType         = extPathType;
                args.extVehicleType      = extVehicleType;
                args.vehicleId           = 0;
                args.buildIndex          = Singleton <SimulationManager> .instance.m_currentBuildIndex;
                args.startPosA           = startPosA;
                args.startPosB           = dummyPathPos;
                args.endPosA             = endPosA;
                args.endPosB             = dummyPathPos;
                args.vehiclePosition     = vehiclePosition;
                args.laneTypes           = laneTypes;
                args.vehicleTypes        = vehicleTypes;
                args.maxLength           = 20000f;
                args.isHeavyVehicle      = false;
                args.hasCombustionEngine = combustionEngine;
                args.ignoreBlocked       = false;
                args.ignoreFlooded       = false;
                args.randomParking       = randomParking;
                args.stablePath          = false;
                args.skipQueue           = false;

                bool res = CustomPathManager._instance.CreatePath(out path, ref Singleton <SimulationManager> .instance.m_randomizer, args);
                // NON-STOCK CODE END

                if (res)
                {
#if DEBUG
                    if (GlobalConfig.Instance.Debug.Switches[2])
                    {
                        Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Path-finding starts for citizen instance {instanceID}, path={path}, extVehicleType={extVehicleType}, startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, laneType={laneTypes}, vehicleType={vehicleTypes}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}, vehiclePos.m_segment={vehiclePosition.m_segment}, vehiclePos.m_lane={vehiclePosition.m_lane}, vehiclePos.m_offset={vehiclePosition.m_offset}");
                    }
#endif

                    if (citizenData.m_path != 0u)
                    {
                        Singleton <PathManager> .instance.ReleasePath(citizenData.m_path);
                    }
                    citizenData.m_path   = path;
                    citizenData.m_flags |= CitizenInstance.Flags.WaitingPath;
                    return(true);
                }
            }

#if DEBUG
            if (Options.prohibitPocketCars)
            {
                if (GlobalConfig.Instance.Debug.Switches[2])
                {
                    Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): CustomCitizenAI.CustomStartPathFind: [PFFAIL] failed for citizen instance {instanceID} (CurrentPathMode={extInstance.pathMode}). startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, startPosA.offset={startPosA.m_offset}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}, endPosA.offset={endPosA.m_offset}, foundStartPos={foundStartPos}, foundEndPos={foundEndPos}");
                }
            }
#endif
            return(false);
        }
Пример #15
0
        public bool ExtStartPathFind(ushort vehicleID, ref Vehicle vehicleData, ushort driverInstanceId, ref ExtCitizenInstance driverExtInstance, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget)
        {
#if DEBUG
            bool citDebug  = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == driverExtInstance.GetCitizenId();
            bool debug     = GlobalConfig.Instance.Debug.Switches[2] && citDebug;
            bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;

            if (debug)
            {
                Log.Warning($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): called for vehicle {vehicleID}, driverInstanceId={driverInstanceId}, startPos={startPos}, endPos={endPos}, sourceBuilding={vehicleData.m_sourceBuilding}, targetBuilding={vehicleData.m_targetBuilding} pathMode={driverExtInstance.pathMode}");
            }
#endif

            PathUnit.Position startPosA = default(PathUnit.Position);
            PathUnit.Position startPosB = default(PathUnit.Position);
            PathUnit.Position endPosA   = default(PathUnit.Position);
            float sqrDistA = 0f;
            float sqrDistB;

            CitizenManager citizenManager = Singleton <CitizenManager> .instance;
            ushort targetBuildingId       = citizenManager.m_instances.m_buffer[(int)driverInstanceId].m_targetBuilding;
            uint driverCitizenId          = citizenManager.m_instances.m_buffer[(int)driverInstanceId].m_citizen;

            // NON-STOCK CODE START
            bool calculateEndPos    = true;
            bool allowRandomParking = true;
            bool movingToParkingPos = false;
            bool foundStartingPos   = false;
            bool skipQueue          = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
            ExtPathType extPathType = ExtPathType.None;
#if BENCHMARK
            using (var bm = new Benchmark(null, "ParkingAI")) {
#endif
            if (Options.prohibitPocketCars)
            {
                //if (driverExtInstance != null) {
#if DEBUG
                if (debug)
                {
                    Log.Warning($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): PathMode={driverExtInstance.pathMode} for vehicle {vehicleID}, driver citizen instance {driverExtInstance.instanceId}!");
                }
#endif

                if (targetBuildingId != 0 && (Singleton <BuildingManager> .instance.m_buildings.m_buffer[targetBuildingId].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None)
                {
                    // target is outside connection
                    driverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToTarget;
                }
                else
                {
                    if (driverExtInstance.pathMode == ExtPathMode.DrivingToTarget || driverExtInstance.pathMode == ExtPathMode.DrivingToKnownParkPos || driverExtInstance.pathMode == ExtPathMode.ParkingFailed)
                    {
                        skipQueue = true;
                    }

                    bool allowTourists = false;
                    if (driverExtInstance.pathMode == ExtPathMode.ParkingFailed)
                    {
                        // previous parking attempt failed
                        driverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToAltParkPos;
                        allowTourists = true;

#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Vehicle {vehicleID} shall move to an alternative parking position! CurrentPathMode={driverExtInstance.pathMode} FailedParkingAttempts={driverExtInstance.failedParkingAttempts}");
                        }
#endif

                        if (driverExtInstance.parkingPathStartPosition != null)
                        {
                            startPosA        = (PathUnit.Position)driverExtInstance.parkingPathStartPosition;
                            foundStartingPos = true;
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Setting starting pos for {vehicleID} to segment={startPosA.m_segment}, laneIndex={startPosA.m_lane}, offset={startPosA.m_offset}");
                            }
#endif
                        }
                        startBothWays = false;

                        if (driverExtInstance.failedParkingAttempts > GlobalConfig.Instance.ParkingAI.MaxParkingAttempts)
                        {
                            // maximum number of parking attempts reached
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Reached maximum number of parking attempts for vehicle {vehicleID}! GIVING UP.");
                            }
#endif
                            driverExtInstance.Reset();

                            // pocket car fallback
                            //vehicleData.m_flags |= Vehicle.Flags.Parking;
                            return(false);
                        }
                        else
                        {
#if DEBUG
                            if (fineDebug)
                            {
                                Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Increased number of parking attempts for vehicle {vehicleID}: {driverExtInstance.failedParkingAttempts}/{GlobalConfig.Instance.ParkingAI.MaxParkingAttempts}");
                            }
#endif
                        }
                    }
                    else
                    {
                        driverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToKnownParkPos;
                    }

                    ushort homeId = Singleton <CitizenManager> .instance.m_citizens.m_buffer[driverCitizenId].m_homeBuilding;
                    bool calcEndPos;
                    Vector3 parkPos;

                    if (AdvancedParkingManager.Instance.FindParkingSpaceForCitizen(endPos, vehicleData.Info, ref driverExtInstance, homeId, targetBuildingId == homeId, vehicleID, allowTourists, out parkPos, ref endPosA, out calcEndPos))
                    {
                        calculateEndPos    = calcEndPos;
                        allowRandomParking = false;
                        movingToParkingPos = true;

                        if (!driverExtInstance.CalculateReturnPath(parkPos, endPos))
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Could not calculate return path for citizen instance {driverExtInstance.instanceId}, vehicle {vehicleID}. Resetting instance.");
                            }
#endif
                            driverExtInstance.Reset();
                            return(false);
                        }
                    }
                    else if (driverExtInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos)
                    {
                        // no alternative parking spot found: abort
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): No alternative parking spot found for vehicle {vehicleID}, citizen instance {driverExtInstance.instanceId} with CurrentPathMode={driverExtInstance.pathMode}! GIVING UP.");
                        }
#endif
                        driverExtInstance.Reset();
                        return(false);
                    }
                    else
                    {
                        // calculate a direct path to target
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): No alternative parking spot found for vehicle {vehicleID}, citizen instance {driverExtInstance.instanceId} with CurrentPathMode={driverExtInstance.pathMode}! Setting CurrentPathMode to 'CalculatingCarPath'.");
                        }
#endif
                        driverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToTarget;
                    }
                }

                extPathType = driverExtInstance.GetPathType();

                /*} else {
                 #if DEBUG
                 *      if (debug)
                 *              Log.Warning($"CustomPassengerCarAI.CustomStartPathFind: No driver citizen instance found for vehicle {vehicleID}!");
                 #endif
                 * }*/
            }
#if BENCHMARK
        }
#endif

            NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle;
            if (!movingToParkingPos)
            {
                laneTypes |= NetInfo.LaneType.Pedestrian;
            }
            // NON-STOCK CODE END

            VehicleInfo.VehicleType vehicleTypes = this.m_info.m_vehicleType;
            bool allowUnderground = (vehicleData.m_flags & Vehicle.Flags.Underground) != 0;
            bool randomParking    = false;
            bool combustionEngine = this.m_info.m_class.m_subService == ItemClass.SubService.ResidentialLow;
            if (allowRandomParking &&             // NON-STOCK CODE
                !movingToParkingPos &&
                targetBuildingId != 0 &&
                (
                    Singleton <BuildingManager> .instance.m_buildings.m_buffer[(int)targetBuildingId].Info.m_class.m_service > ItemClass.Service.Office ||
                    (citizenManager.m_instances.m_buffer[driverInstanceId].m_flags & CitizenInstance.Flags.TargetIsNode) != CitizenInstance.Flags.None
                ))
            {
                randomParking = true;
            }

#if DEBUG
            if (fineDebug)
            {
                Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Requesting path-finding for passenger car {vehicleID}, startPos={startPos}, endPos={endPos}, extPathType={extPathType}");
            }
#endif

            // NON-STOCK CODE START
            if (!foundStartingPos)
            {
                foundStartingPos = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, vehicleTypes, allowUnderground, false, 32f, out startPosA, out startPosB, out sqrDistA, out sqrDistB);
            }

            bool foundEndPos = !calculateEndPos || citizenManager.m_instances.m_buffer[(int)driverInstanceId].Info.m_citizenAI.FindPathPosition(driverInstanceId, ref citizenManager.m_instances.m_buffer[(int)driverInstanceId], endPos, Options.prohibitPocketCars && (targetBuildingId == 0 || (Singleton <BuildingManager> .instance.m_buildings.m_buffer[targetBuildingId].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None) ? NetInfo.LaneType.Pedestrian : (laneTypes | NetInfo.LaneType.Pedestrian), vehicleTypes, undergroundTarget, out endPosA);
            // NON-STOCK CODE END

            if (foundStartingPos &&
                foundEndPos)                   // NON-STOCK CODE

            {
                if (!startBothWays || sqrDistA < 10f)
                {
                    startPosB = default(PathUnit.Position);
                }
                PathUnit.Position endPosB = default(PathUnit.Position);
                SimulationManager simMan  = Singleton <SimulationManager> .instance;
                uint path;
                PathUnit.Position dummyPathPos = default(PathUnit.Position);
                // NON-STOCK CODE START
                PathCreationArgs args;
                args.extPathType         = extPathType;
                args.extVehicleType      = ExtVehicleType.PassengerCar;
                args.vehicleId           = vehicleID;
                args.buildIndex          = simMan.m_currentBuildIndex;
                args.startPosA           = startPosA;
                args.startPosB           = startPosB;
                args.endPosA             = endPosA;
                args.endPosB             = endPosB;
                args.vehiclePosition     = dummyPathPos;
                args.laneTypes           = laneTypes;
                args.vehicleTypes        = vehicleTypes;
                args.maxLength           = 20000f;
                args.isHeavyVehicle      = this.IsHeavyVehicle();
                args.hasCombustionEngine = this.CombustionEngine();
                args.ignoreBlocked       = this.IgnoreBlocked(vehicleID, ref vehicleData);
                args.ignoreFlooded       = false;
                args.ignoreCosts         = false;
                args.randomParking       = randomParking;
                args.stablePath          = false;
                args.skipQueue           = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;

                if (CustomPathManager._instance.CreatePath(out path, ref simMan.m_randomizer, args))
                {
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Path-finding starts for passenger car {vehicleID}, path={path}, startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, laneType={laneTypes}, vehicleType={vehicleTypes}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}");
                    }
#endif
                    // NON-STOCK CODE END

                    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);
        }
Пример #16
0
        public bool ExtSimulationStep(ushort instanceId,
                                      ref CitizenInstance instanceData,
                                      ref ExtCitizenInstance extInstance,
                                      Vector3 physicsLodRefPos)
        {
            IExtCitizenInstanceManager extCitInstMan = Constants.ManagerFactory.ExtCitizenInstanceManager;

#if DEBUG
            bool citizenDebug
                = (DebugSettings.CitizenInstanceId == 0 ||
                   DebugSettings.CitizenInstanceId == instanceId) &&
                  (DebugSettings.CitizenId == 0 ||
                   DebugSettings.CitizenId == instanceData.m_citizen) &&
                  (DebugSettings.SourceBuildingId == 0 ||
                   DebugSettings.SourceBuildingId == instanceData.m_sourceBuilding) &&
                  (DebugSettings.TargetBuildingId == 0 ||
                   DebugSettings.TargetBuildingId == instanceData.m_targetBuilding);
            bool logParkingAi         = DebugSwitch.BasicParkingAILog.Get() && citizenDebug;
            bool extendedLogParkingAi = DebugSwitch.ExtendedParkingAILog.Get() && citizenDebug;
#else
            var logParkingAi         = false;
            var extendedLogParkingAi = false;
#endif

#if DEBUG
            ExtPathMode logPathMode = extInstance.pathMode;
#else
            var logPathMode = 0;
#endif
            switch (extInstance.pathMode)
            {
            // check if the citizen has reached a parked car or target
            case ExtPathMode.WalkingToParkedCar:
            case ExtPathMode.ApproachingParkedCar: {
                Citizen[] citizensBuffer  = Singleton <CitizenManager> .instance.m_citizens.m_buffer;
                ushort    parkedVehicleId = citizensBuffer[instanceData.m_citizen].m_parkedVehicle;

                if (parkedVehicleId == 0)
                {
                    // citizen is reaching their parked car but does not own a parked car
                    Log._DebugOnlyWarningIf(
                        logParkingAi,
                        () => $"CustomHumanAI.ExtSimulationStep({instanceId}): " +
                        $"Citizen instance {instanceId} was walking to / reaching " +
                        $"their parked car ({logPathMode}) but parked " +
                        "car has disappeared. RESET.");

                    extCitInstMan.Reset(ref extInstance);
                    instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround
                                              | CitizenInstance.Flags.Panicking
                                              | CitizenInstance.Flags.SittingDown
                                              | CitizenInstance.Flags.Cheering);
                    InvalidPath(instanceId, ref instanceData);
                    return(true);
                }

                VehicleParked[]        parkedVehicles = Singleton <VehicleManager> .instance.m_parkedVehicles.m_buffer;
                ParkedCarApproachState approachState  =
                    AdvancedParkingManager.Instance.CitizenApproachingParkedCarSimulationStep(
                        instanceId,
                        ref instanceData,
                        ref extInstance,
                        physicsLodRefPos,
                        ref parkedVehicles[parkedVehicleId]);

                switch (approachState)
                {
                case ParkedCarApproachState.None:
                default:
                    break;

                case ParkedCarApproachState.Approaching:
                    // citizen approaches their parked car
                    return(true);

                case ParkedCarApproachState.Approached: {
                    // citizen reached their parked car
                    Log._DebugIf(
                        extendedLogParkingAi,
                        () => $"CustomHumanAI.CustomSimulationStep({instanceId}): " +
                        $"Citizen instance {instanceId} arrived at parked car. " +
                        $"PathMode={logPathMode}");

                    if (instanceData.m_path != 0)
                    {
                        Singleton <PathManager> .instance.ReleasePath(instanceData.m_path);

                        instanceData.m_path = 0;
                    }

                    instanceData.m_flags &= CitizenInstance.Flags.Created
                                            | CitizenInstance.Flags.Cheering
                                            | CitizenInstance.Flags.Deleted
                                            | CitizenInstance.Flags.Underground
                                            | CitizenInstance.Flags.CustomName
                                            | CitizenInstance.Flags.Character
                                            | CitizenInstance.Flags.BorrowCar
                                            | CitizenInstance.Flags.HangAround
                                            | CitizenInstance.Flags.InsideBuilding
                                            | CitizenInstance.Flags.WaitingPath
                                            | CitizenInstance.Flags.TryingSpawnVehicle
                                            | CitizenInstance.Flags.CannotUseTransport
                                            | CitizenInstance.Flags.Panicking
                                            | CitizenInstance.Flags.OnPath
                                            | CitizenInstance.Flags.SittingDown
                                            | CitizenInstance.Flags.AtTarget
                                            | CitizenInstance.Flags.RequireSlowStart
                                            | CitizenInstance.Flags.Transition
                                            | CitizenInstance.Flags.RidingBicycle
                                            | CitizenInstance.Flags.OnBikeLane
                                            | CitizenInstance.Flags.CannotUseTaxi
                                            | CitizenInstance.Flags.CustomColor
                                            | CitizenInstance.Flags.Blown
                                            | CitizenInstance.Flags.Floating
                                            | CitizenInstance.Flags.TargetFlags;

                    if (StartPathFind(instanceId, ref instanceData))
                    {
                        return(true);
                    }
                    else
                    {
                        instanceData.Unspawn(instanceId);
                        extCitInstMan.Reset(ref extInstance);
                        return(true);
                    }
                }

                case ParkedCarApproachState.Failure: {
                    Log._DebugIf(
                        logParkingAi,
                        () => $"CustomHumanAI.ExtSimulationStep({instanceId}): " +
                        $"Citizen instance {instanceId} failed to arrive at " +
                        $"parked car. PathMode={logPathMode}");

                    // repeat path-finding
                    instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;
                    instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround
                                              | CitizenInstance.Flags.Panicking
                                              | CitizenInstance.Flags.SittingDown
                                              | CitizenInstance.Flags.Cheering);
                    InvalidPath(instanceId, ref instanceData);
                    return(true);
                }
                }

                break;
            }

            case ExtPathMode.WalkingToTarget:
            case ExtPathMode.TaxiToTarget: {
                AdvancedParkingManager.Instance.CitizenApproachingTargetSimulationStep(
                    instanceId,
                    ref instanceData,
                    ref extInstance);
                break;
            }
            }

            return(false);
        }
Пример #17
0
        internal bool ExtParkVehicle(ushort vehicleID, ref Vehicle vehicleData, uint driverCitizenId, ushort driverCitizenInstanceId, ref ExtCitizenInstance driverExtInstance, ushort targetBuildingId, PathUnit.Position pathPos, uint nextPath, int nextPositionIndex, out byte segmentOffset)
        {
#if DEBUG
            bool citDebug  = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == driverExtInstance.GetCitizenId();
            bool debug     = GlobalConfig.Instance.Debug.Switches[2] && citDebug;
            bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;
#endif

            PathManager pathManager       = Singleton <PathManager> .instance;
            CitizenManager citizenManager = Singleton <CitizenManager> .instance;
            NetManager netManager         = Singleton <NetManager> .instance;
            VehicleManager vehicleManager = Singleton <VehicleManager> .instance;

            // NON-STOCK CODE START
            bool prohibitPocketCars = false;
            // NON-STOCK CODE END

            if (driverCitizenId != 0u)
            {
                if (Options.prohibitPocketCars && driverCitizenInstanceId != 0)
                {
                    prohibitPocketCars = true;
                }

                uint laneID = PathManager.GetLaneID(pathPos);
                segmentOffset = (byte)Singleton <SimulationManager> .instance.m_randomizer.Int32(1, 254);

                Vector3 refPos;
                Vector3 vector;
                netManager.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)segmentOffset * 0.003921569f, out refPos, out vector);
                NetInfo info           = netManager.m_segments.m_buffer[(int)pathPos.m_segment].Info;
                bool isSegmentInverted = (netManager.m_segments.m_buffer[(int)pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None;
                bool isPosNegative     = info.m_lanes[(int)pathPos.m_lane].m_position < 0f;
                vector.Normalize();
                Vector3 searchDir;
                if (isSegmentInverted != isPosNegative)
                {
                    searchDir.x = -vector.z;
                    searchDir.y = 0f;
                    searchDir.z = vector.x;
                }
                else
                {
                    searchDir.x = vector.z;
                    searchDir.y = 0f;
                    searchDir.z = -vector.x;
                }
                ushort homeID = 0;
                if (driverCitizenId != 0u)
                {
                    homeID = Singleton <CitizenManager> .instance.m_citizens.m_buffer[driverCitizenId].m_homeBuilding;
                }
                Vector3 parkPos    = default(Vector3);
                Quaternion parkRot = default(Quaternion);
                float parkOffset   = -1f;

                // NON-STOCK CODE START
                bool foundParkingSpace = false;

                if (prohibitPocketCars)
                {
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"Vehicle {vehicleID} tries to park on a parking position now (flags: {vehicleData.m_flags})! CurrentPathMode={driverExtInstance.pathMode} path={vehicleData.m_path} pathPositionIndex={vehicleData.m_pathPositionIndex} segmentId={pathPos.m_segment} laneIndex={pathPos.m_lane} offset={pathPos.m_offset} nextPath={nextPath} refPos={refPos} searchDir={searchDir} home={homeID} driverCitizenId={driverCitizenId} driverCitizenInstanceId={driverCitizenInstanceId}");
                    }
#endif

                    if (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToAltParkPos || driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToKnownParkPos)
                    {
                        // try to use previously found parking space
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Vehicle {vehicleID} tries to park on an (alternative) parking position now! CurrentPathMode={driverExtInstance.pathMode} altParkingSpaceLocation={driverExtInstance.parkingSpaceLocation} altParkingSpaceLocationId={driverExtInstance.parkingSpaceLocationId}");
                        }
#endif

                        switch (driverExtInstance.parkingSpaceLocation)
                        {
                        case ExtCitizenInstance.ExtParkingSpaceLocation.RoadSide:
                            uint parkLaneID; int parkLaneIndex;
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Vehicle {vehicleID} wants to park road-side @ segment {driverExtInstance.parkingSpaceLocationId}");
                            }
#endif
                            foundParkingSpace = AdvancedParkingManager.Instance.FindParkingSpaceRoadSideForVehiclePos(this.m_info, 0, driverExtInstance.parkingSpaceLocationId, refPos, out parkPos, out parkRot, out parkOffset, out parkLaneID, out parkLaneIndex);
                            break;

                        case ExtCitizenInstance.ExtParkingSpaceLocation.Building:
                            float maxDist = 9999f;
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Vehicle {vehicleID} wants to park @ building {driverExtInstance.parkingSpaceLocationId}");
                            }
#endif
                            foundParkingSpace = AdvancedParkingManager.Instance.FindParkingSpacePropAtBuilding(this.m_info, homeID, 0, driverExtInstance.parkingSpaceLocationId, ref Singleton <BuildingManager> .instance.m_buildings.m_buffer[driverExtInstance.parkingSpaceLocationId], pathPos.m_segment, refPos, ref maxDist, true, out parkPos, out parkRot, out parkOffset);
                            break;

                        default:
#if DEBUG
                            Log.Error($"No alternative parking position stored for vehicle {vehicleID}! PathMode={driverExtInstance.pathMode}");
#endif
                            foundParkingSpace = CustomFindParkingSpace(this.m_info, homeID, refPos, searchDir, pathPos.m_segment, out parkPos, out parkRot, out parkOffset);
                            break;
                        }
                    }
                }

                if (!foundParkingSpace)
                {
                    foundParkingSpace =                     /*prohibitPocketCars ?*/
                                        CustomFindParkingSpace(this.m_info, homeID, refPos, searchDir, pathPos.m_segment, out parkPos, out parkRot, out parkOffset) /*:
                                                                                                                                                                     * FindParkingSpace(homeID, refPos, searchDir, pathPos.m_segment, this.m_info.m_generatedInfo.m_size.x, this.m_info.m_generatedInfo.m_size.z, out parkPos, out parkRot, out parkOffset)*/;
                }

                // NON-STOCK CODE END
                ushort parkedVehicleId = 0;
                bool parkedCarCreated  = foundParkingSpace && vehicleManager.CreateParkedVehicle(out parkedVehicleId, ref Singleton <SimulationManager> .instance.m_randomizer, this.m_info, parkPos, parkRot, driverCitizenId);
                if (foundParkingSpace && parkedCarCreated)
                {
                    // we have reached a parking position
#if DEBUG
                    float sqrDist = (refPos - parkPos).sqrMagnitude;
                    if (fineDebug)
                    {
                        Log._Debug($"Vehicle {vehicleID} succeeded in parking! CurrentPathMode={driverExtInstance.pathMode} sqrDist={sqrDist}");
                    }

                    if (GlobalConfig.Instance.Debug.Switches[6] && sqrDist >= 16000)
                    {
                        Log._Debug($"CustomPassengerCarAI.CustomParkVehicle: FORCED PAUSE. Distance very large! Vehicle {vehicleID}. dist={sqrDist}");
                        Singleton <SimulationManager> .instance.SimulationPaused = true;
                    }
#endif

                    citizenManager.m_citizens.m_buffer[driverCitizenId].SetParkedVehicle(driverCitizenId, parkedVehicleId);
                    if (parkOffset >= 0f)
                    {
                        segmentOffset = (byte)(parkOffset * 255f);
                    }

                    // NON-STOCK CODE START
                    if (prohibitPocketCars)
                    {
                        if ((driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToAltParkPos || driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToKnownParkPos) && targetBuildingId != 0)
                        {
                            // decrease parking space demand of target building
                            ExtBuildingManager.Instance.ExtBuildings[targetBuildingId].ModifyParkingSpaceDemand(parkPos, GlobalConfig.Instance.ParkingAI.MinFoundParkPosParkingSpaceDemandDelta, GlobalConfig.Instance.ParkingAI.MaxFoundParkPosParkingSpaceDemandDelta);
                        }

                        //if (driverExtInstance.CurrentPathMode == ExtCitizenInstance.PathMode.DrivingToAltParkPos || driverExtInstance.CurrentPathMode == ExtCitizenInstance.PathMode.DrivingToKnownParkPos) {
                        // we have reached an (alternative) parking position and succeeded in finding a parking space
                        driverExtInstance.pathMode = ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget;
                        driverExtInstance.failedParkingAttempts  = 0;
                        driverExtInstance.parkingSpaceLocation   = ExtCitizenInstance.ExtParkingSpaceLocation.None;
                        driverExtInstance.parkingSpaceLocationId = 0;
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Vehicle {vehicleID} has reached an (alternative) parking position! CurrentPathMode={driverExtInstance.pathMode} position={parkPos}");
                        }
#endif
                        //}
                    }
                }
                else if (prohibitPocketCars)
                {
                    // could not find parking space. vehicle would despawn.
                    if (targetBuildingId != 0 && (Singleton <BuildingManager> .instance.m_buildings.m_buffer[targetBuildingId].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None)
                    {
                        // target is an outside connection
                        return(true);
                    }

                    // Find parking space in the vicinity, redo path-finding to the parking space, park the vehicle and do citizen path-finding to the current target

                    if (!foundParkingSpace && (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToAltParkPos || driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToKnownParkPos) && targetBuildingId != 0)
                    {
                        // increase parking space demand of target building
                        ExtBuildingManager.Instance.ExtBuildings[targetBuildingId].AddParkingSpaceDemand(GlobalConfig.Instance.ParkingAI.FailedParkingSpaceDemandIncrement * (uint)driverExtInstance.failedParkingAttempts);
                    }

                    if (!foundParkingSpace)
                    {
                        ++driverExtInstance.failedParkingAttempts;
                    }
                    else
                    {
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Parking failed for vehicle {vehicleID}: Parked car could not be created. ABORT.");
                        }
#endif
                        driverExtInstance.failedParkingAttempts = GlobalConfig.Instance.ParkingAI.MaxParkingAttempts + 1;
                    }
                    driverExtInstance.pathMode = ExtCitizenInstance.ExtPathMode.ParkingFailed;
                    driverExtInstance.parkingPathStartPosition = pathPos;

#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"Parking failed for vehicle {vehicleID}! (flags: {vehicleData.m_flags}) pathPos segment={pathPos.m_segment}, lane={pathPos.m_lane}, offset={pathPos.m_offset}. Trying to find parking space in the vicinity. FailedParkingAttempts={driverExtInstance.failedParkingAttempts}, CurrentPathMode={driverExtInstance.pathMode} foundParkingSpace={foundParkingSpace}");
                    }
#endif

                    // invalidate paths of all passengers in order to force path recalculation
                    uint curUnitId = vehicleData.m_citizenUnits;
                    int numIter    = 0;
                    while (curUnitId != 0u)
                    {
                        uint nextUnit = citizenManager.m_units.m_buffer[curUnitId].m_nextUnit;
                        for (int i = 0; i < 5; i++)
                        {
                            uint curCitizenId = citizenManager.m_units.m_buffer[curUnitId].GetCitizen(i);
                            if (curCitizenId != 0u)
                            {
                                ushort citizenInstanceId = citizenManager.m_citizens.m_buffer[curCitizenId].m_instance;
                                if (citizenInstanceId != 0)
                                {
#if DEBUG
                                    if (debug)
                                    {
                                        Log._Debug($"Releasing path for citizen instance {citizenInstanceId} sitting in vehicle {vehicleID} (was {citizenManager.m_instances.m_buffer[citizenInstanceId].m_path}).");
                                    }
#endif
                                    if (citizenInstanceId != driverCitizenInstanceId)
                                    {
#if DEBUG
                                        if (debug)
                                        {
                                            Log._Debug($"Resetting pathmode for passenger citizen instance {citizenInstanceId} sitting in vehicle {vehicleID} (was {ExtCitizenInstanceManager.Instance.ExtInstances[citizenInstanceId].pathMode}).");
                                        }
#endif

                                        ExtCitizenInstanceManager.Instance.ExtInstances[citizenInstanceId].Reset();
                                    }

                                    if (citizenManager.m_instances.m_buffer[citizenInstanceId].m_path != 0)
                                    {
                                        Singleton <PathManager> .instance.ReleasePath(citizenManager.m_instances.m_buffer[citizenInstanceId].m_path);

                                        citizenManager.m_instances.m_buffer[citizenInstanceId].m_path = 0u;
                                    }
                                }
                            }
                        }
                        curUnitId = nextUnit;
                        if (++numIter > CitizenManager.MAX_UNIT_COUNT)
                        {
                            CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);

                            break;
                        }
                    }
                    return(false);
                    // NON-STOCK CODE END
                }
            }
            else
            {
                segmentOffset = pathPos.m_offset;
            }

            // parking has succeeded
            if (driverCitizenId != 0u)
            {
                uint curCitizenUnitId = vehicleData.m_citizenUnits;
                int numIter           = 0;
                while (curCitizenUnitId != 0u)
                {
                    uint nextUnit = citizenManager.m_units.m_buffer[curCitizenUnitId].m_nextUnit;
                    for (int j = 0; j < 5; j++)
                    {
                        uint citId = citizenManager.m_units.m_buffer[curCitizenUnitId].GetCitizen(j);
                        if (citId != 0u)
                        {
                            ushort citizenInstanceId = citizenManager.m_citizens.m_buffer[citId].m_instance;
                            if (citizenInstanceId != 0)
                            {
                                // NON-STOCK CODE START
                                if (prohibitPocketCars)
                                {
                                    if (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget)
                                    {
#if DEBUG
                                        if (debug)
                                        {
                                            Log._Debug($"Parking succeeded: Doing nothing for citizen instance {citizenInstanceId}! path: {citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path}");
                                        }
#endif
                                        ExtCitizenInstanceManager.Instance.ExtInstances[citizenInstanceId].pathMode = ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget;
                                        continue;
                                    }
                                }
                                // NON-STOCK CODE END

                                if (pathManager.AddPathReference(nextPath))
                                {
                                    if (citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path != 0u)
                                    {
                                        pathManager.ReleasePath(citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path);
                                    }
                                    citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path = nextPath;
                                    citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_pathPositionIndex = (byte)nextPositionIndex;
                                    citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_lastPathOffset    = segmentOffset;
#if DEBUG
                                    if (debug)
                                    {
                                        Log._Debug($"Parking succeeded (default): Setting path of citizen instance {citizenInstanceId} to {nextPath}!");
                                    }
#endif
                                }
                            }
                        }
                    }
                    curCitizenUnitId = nextUnit;
                    if (++numIter > 524288)
                    {
                        CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);

                        break;
                    }
                }
            }

            if (prohibitPocketCars)
            {
                if (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget)
                {
#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"Parking succeeded (alternative parking spot): Citizen instance {driverExtInstance} has to walk for the remaining path!");
                    }
#endif

                    /*driverExtInstance.CurrentPathMode = ExtCitizenInstance.PathMode.CalculatingWalkingPathToTarget;
                     * if (debug)
                     *      Log._Debug($"Setting CurrentPathMode of vehicle {vehicleID} to {driverExtInstance.CurrentPathMode}");*/
                }
            }

            return(true);
        }
Пример #18
0
        /// <summary>
        /// Finds a free parking space in the vicinity of the given target position <paramref name="endPos"/> for the given citizen instance <paramref name="extDriverInstance"/>.
        /// </summary>
        /// <param name="endPos">target position</param>
        /// <param name="vehicleInfo">vehicle type that is being used</param>
        /// <param name="extDriverInstance">cititzen instance that is driving the car</param>
        /// <param name="homeId">Home building of the citizen (may be 0 for tourists/homeless cims)</param>
        /// <param name="vehicleId">Vehicle that is being used (used for logging)</param>
        /// <param name="allowTourists">If true, method fails if given citizen is a tourist (TODO remove this parameter)</param>
        /// <param name="parkPos">parking position (output)</param>
        /// <param name="endPathPos">sidewalk path position near parking space (output). only valid if <paramref name="calculateEndPos"/> yields false.</param>
        /// <param name="calculateEndPos">if false, a parking space path position could be calculated (TODO negate & rename parameter)</param>
        /// <returns>true if a parking space could be found, false otherwise</returns>
        public static bool FindParkingSpaceForExtInstance(Vector3 endPos, VehicleInfo vehicleInfo, ExtCitizenInstance extDriverInstance, ushort homeId, ushort vehicleId, bool allowTourists, out Vector3 parkPos, ref PathUnit.Position endPathPos, out bool calculateEndPos)
        {
            calculateEndPos = true;
            parkPos         = default(Vector3);

            if (!allowTourists)
            {
                // TODO remove this from this method
                uint citizenId = extDriverInstance.GetCitizenId();
                if (citizenId == 0 ||
                    (Singleton <CitizenManager> .instance.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Tourist) != Citizen.Flags.None)
                {
                    return(false);
                }
            }

#if DEBUG
            if (GlobalConfig.Instance.DebugSwitches[4])
            {
                Log._Debug($"Citizen instance {extDriverInstance.InstanceId} (CurrentPathMode={extDriverInstance.PathMode}) can still use their passenger car and is either not a tourist or wants to find an alternative parking spot. Finding a parking space before starting path-finding.");
            }
#endif

            ExtParkingSpaceLocation knownParkingSpaceLocation;
            ushort     knownParkingSpaceLocationId;
            Quaternion parkRot;
            float      parkOffset;

            // find a free parking space
            bool success = CustomPassengerCarAI.FindParkingSpaceInVicinity(endPos, vehicleInfo, homeId, vehicleId, out knownParkingSpaceLocation, out knownParkingSpaceLocationId, out parkPos, out parkRot, out parkOffset);

            extDriverInstance.ParkingSpaceLocation   = knownParkingSpaceLocation;
            extDriverInstance.ParkingSpaceLocationId = knownParkingSpaceLocationId;

            if (success)
            {
#if DEBUG
                if (GlobalConfig.Instance.DebugSwitches[4])
                {
                    Log._Debug($"Found a parking spot for citizen instance {extDriverInstance.InstanceId} (CurrentPathMode={extDriverInstance.PathMode}) before starting car path: {knownParkingSpaceLocation} @ {knownParkingSpaceLocationId}");
                }
#endif

                if (knownParkingSpaceLocation == ExtParkingSpaceLocation.RoadSide)
                {
                    // found segment with parking space
                    Vector3 pedPos;
                    uint    laneId;
                    int     laneIndex;
                    float   laneOffset;

#if DEBUG
                    if (GlobalConfig.Instance.DebugSwitches[2])
                    {
                        Log._Debug($"Found segment {knownParkingSpaceLocationId} for road-side parking position for citizen instance {extDriverInstance.InstanceId}!");
                    }
#endif

                    // determine nearest sidewalk position for parking position at segment
                    if (Singleton <NetManager> .instance.m_segments.m_buffer[knownParkingSpaceLocationId].GetClosestLanePosition(parkPos, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, out pedPos, out laneId, out laneIndex, out laneOffset))
                    {
                        endPathPos.m_segment = knownParkingSpaceLocationId;
                        endPathPos.m_lane    = (byte)laneIndex;
                        endPathPos.m_offset  = (byte)(parkOffset * 255f);
                        calculateEndPos      = false;

                        //extDriverInstance.CurrentPathMode = successMode;// ExtCitizenInstance.PathMode.CalculatingKnownCarPath;
#if DEBUG
                        if (GlobalConfig.Instance.DebugSwitches[2])
                        {
                            Log._Debug($"Found an parking spot sidewalk position for citizen instance {extDriverInstance.InstanceId} @ segment {knownParkingSpaceLocationId}, laneId {laneId}, laneIndex {laneIndex}, offset={endPathPos.m_offset}! CurrentPathMode={extDriverInstance.PathMode}");
                        }
#endif
                        return(true);
                    }
                    else
                    {
#if DEBUG
                        if (GlobalConfig.Instance.DebugSwitches[2])
                        {
                            Log._Debug($"Could not find an alternative parking spot sidewalk position for citizen instance {extDriverInstance.InstanceId}! CurrentPathMode={extDriverInstance.PathMode}");
                        }
#endif
                        return(false);
                    }
                }
                else if (knownParkingSpaceLocation == ExtParkingSpaceLocation.Building)
                {
                    // found a building with parking space
                    if (CustomPathManager.FindPathPositionWithSpiralLoop(parkPos, endPos, ItemClass.Service.Road, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, false, false, GlobalConfig.Instance.MaxBuildingToPedestrianLaneDistance, out endPathPos))
                    {
                        calculateEndPos = false;
                    }

                    //endPos = parkPos;

                    //extDriverInstance.CurrentPathMode = successMode;// ExtCitizenInstance.PathMode.CalculatingKnownCarPath;
#if DEBUG
                    if (GlobalConfig.Instance.DebugSwitches[2])
                    {
                        Log._Debug($"Navigating citizen instance {extDriverInstance.InstanceId} to parking building {knownParkingSpaceLocationId}! segment={endPathPos.m_segment}, laneIndex={endPathPos.m_lane}, offset={endPathPos.m_offset}. CurrentPathMode={extDriverInstance.PathMode} calculateEndPos={calculateEndPos}");
                    }
#endif
                    return(true);
                }
            }
            return(false);
        }