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); } }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
/// <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); }