diff --git a/README.md b/README.md index d4f838aa..102418f4 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ User manual: http://www.viathinksoft.de/tmpe/wiki # Changelog 1.10.10, 07/14/2018 +- Parking AI: Improved park & ride behavior +- Parking AI: Walking paths from parking position to destination building take public transportation into account - Bugfix: Parking AI causes unnecessary path-findings (#183, thanks to Sipke82 for reporting) - Bugfix: Prohibiting cims from crossing the road also affect paths where crossing is unnecessary (#168, thanks to aubergine10 for reporting) diff --git a/TLM/TLM/Custom/AI/CustomCitizenAI.cs b/TLM/TLM/Custom/AI/CustomCitizenAI.cs index 86f62530..02bd376b 100644 --- a/TLM/TLM/Custom/AI/CustomCitizenAI.cs +++ b/TLM/TLM/Custom/AI/CustomCitizenAI.cs @@ -40,143 +40,172 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData ushort homeId = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_homeBuilding; CarUsagePolicy carUsageMode = CarUsagePolicy.Allowed; - bool isOnWalkingTour = (instanceData.m_flags & CitizenInstance.Flags.OnTour) != CitizenInstance.Flags.None; - if (isOnWalkingTour) { - vehicleInfo = null; - } - - bool startAtRoadConnection = false; +#if BENCHMARK + using (var bm = new Benchmark(null, "ParkingAI.Preparation")) { +#endif if (Options.prohibitPocketCars) { - ItemClass.Service sourceBuildingService = Singleton.instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].Info.m_class.m_service; - - /* - * force car usage if citizen is at a road outside connection, - * probibit car usage if citizen is at a non-road outside connection - */ - if (Constants.ManagerFactory.ExtCitizenInstanceManager.IsAtOutsideConnection(instanceID, ref instanceData, ref citizenManager.m_citizens.m_buffer[instanceData.m_citizen])) { - if (sourceBuildingService == ItemClass.Service.Road) { - if (instanceData.Info.m_agePhase > Citizen.AgePhase.Child && !ignoreCost && !isOnWalkingTour) { + switch (extInstance.pathMode) { + case ExtPathMode.RequiresWalkingPathToParkedCar: + case ExtPathMode.CalculatingWalkingPathToParkedCar: + case ExtPathMode.WalkingToParkedCar: + case ExtPathMode.ApproachingParkedCar: + if (parkedVehicleId == 0) { + /* + * Parked vehicle not present but citizen wants to reach it + * -> Reset path mode + */ #if DEBUG if (debug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a road outside connection: Setting path mode to 'RequiresCarPath'"); + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode} but no parked vehicle present. Change to 'None'."); #endif - extInstance.pathMode = ExtPathMode.RequiresCarPath; - startAtRoadConnection = true; + + extInstance.Reset(); } else { + /* + * Parked vehicle is present and citizen wants to reach it + * -> Prohibit car usage + */ #if DEBUG - if (debug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a road outside connection but is not allowed to use a car (agePhase={instanceData.Info.m_agePhase}, ignoreCost={ignoreCost}, isOnWalkingTour={isOnWalkingTour}): ABORTING PATH-FINDING"); + if (fineDebug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'CalculatingWalkingPathToParkedCar'."); #endif - extInstance.Reset(); - return false; + extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToParkedCar; + carUsageMode = CarUsagePolicy.Forbidden; } - } else { + break; + case ExtPathMode.RequiresWalkingPathToTarget: + case ExtPathMode.CalculatingWalkingPathToTarget: + case ExtPathMode.WalkingToTarget: + /* + * Citizen walks to target + * -> Reset path mode + */ #if DEBUG - if (debug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a non-road outside connection: Setting path mode to 'RequiresWalkingPathToTarget'"); + if (fineDebug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'CalculatingWalkingPathToTarget'."); #endif - extInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget; - } - } else if (ignoreCost /* = we are a mascot */ || isOnWalkingTour) { - // disallow car usage if citizen is a mascot or on a walking tour + extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget; + carUsageMode = CarUsagePolicy.Forbidden; + 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) { + /* + * Citizen wants to drive to target but parked vehicle is not present + * -> Reset path mode + */ + #if DEBUG - if (debug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen ignores cost ({ignoreCost}) or is on a walking tour ({isOnWalkingTour}): Setting path mode to 'RequiresWalkingPathToTarget'"); -#endif - extInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget; - } - } - -#if BENCHMARK - using (var bm = new Benchmark(null, "ParkingAI.Preparation")) { + if (debug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode} but no parked vehicle present. Change to 'None'."); #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 + + extInstance.Reset(); + } else { + /* + * Citizen wants to drive to target and parked vehicle is present + * -> Force parked car usage + */ + #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'."); + if (fineDebug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'RequiresCarPath'."); #endif - extInstance.Reset(); - } else { + extInstance.pathMode = ExtPathMode.RequiresCarPath; + carUsageMode = CarUsagePolicy.ForcedParked; + startPos = Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position; // force to start from the parked car + } + break; + default: #if DEBUG - if (fineDebug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'CalculatingWalkingPathToParkedCar'."); + if (debug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'None'."); #endif - extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToParkedCar; - } - break; - case ExtPathMode.RequiresWalkingPathToTarget: - case ExtPathMode.CalculatingWalkingPathToTarget: - case ExtPathMode.WalkingToTarget: + extInstance.Reset(); + break; + } + + if (extInstance.pathMode == ExtPathMode.None) { + if ((instanceData.m_flags & CitizenInstance.Flags.OnTour) != CitizenInstance.Flags.None || ignoreCost) { + /* + * Citizen is on a walking tour or is a mascot + * -> Prohibit car usage + */ #if DEBUG - if (fineDebug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'CalculatingWalkingPathToTarget'."); + if (debug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen ignores cost ({ignoreCost}) or is on a walking tour ({(instanceData.m_flags & CitizenInstance.Flags.OnTour) != CitizenInstance.Flags.None}): Setting path mode 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 (!startAtRoadConnection && (parkedVehicleId == 0 || carUsageMode == CarUsagePolicy.Forbidden)) { - // parked vehicle not present or citizen is on a walking tour + carUsageMode = CarUsagePolicy.Forbidden; + extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget; + } else { + /* + * Citizen is not on a walking tour and is not a mascot + * -> Check if citizen is located at an outside connection and make them obey Parking AI restrictions + */ + if (instanceData.m_sourceBuilding != 0) { + ItemClass.Service sourceBuildingService = Singleton.instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].Info.m_class.m_service; + + if (Constants.ManagerFactory.ExtCitizenInstanceManager.IsAtOutsideConnection(instanceID, ref instanceData, ref citizenManager.m_citizens.m_buffer[instanceData.m_citizen])) { + if (sourceBuildingService == ItemClass.Service.Road) { + if (vehicleInfo != null) { + /* + * Citizen is located at a road outside connection and can spawn a car + * -> Force car usage + */ #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'."); + if (debug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a road outside connection: Setting path mode to 'RequiresCarPath' and carUsageMode to 'ForcedPocket'"); #endif - - extInstance.Reset(); - } else { + extInstance.pathMode = ExtPathMode.RequiresCarPath; + carUsageMode = CarUsagePolicy.ForcedPocket; + } else { + /* + * Citizen is located at a non-road outside connection and cannot spawn a car + * -> Path-finding fails + */ #if DEBUG - if (fineDebug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'RequiresCarPath'."); + if (debug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a road outside connection but does not have a car template: ABORTING PATH-FINDING"); #endif - - extInstance.pathMode = ExtPathMode.RequiresCarPath; - } - break; - default: + return false; + } + } else { + /* + * Citizen is located at a non-road outside connection + * -> Prohibit car usage + */ #if DEBUG - if (debug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'None'."); + if (debug) + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a non-road outside connection: Setting path mode to 'CalculatingWalkingPathToTarget'"); #endif - extInstance.Reset(); - break; + extInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget; + carUsageMode = CarUsagePolicy.Forbidden; + } + } + } } + } + if ((carUsageMode == CarUsagePolicy.Allowed || carUsageMode == CarUsagePolicy.ForcedParked) && parkedVehicleId != 0) { /* - * 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 + */ + vehicleInfo = Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info; /* - * reuse parked vehicle info + * Check if the citizen should return their car back home */ - if (carUsageMode != CarUsagePolicy.Forbidden && parkedVehicleId != 0) { - vehicleInfo = Singleton.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 - ) { + if (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 @@ -187,6 +216,7 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData * -> return car back home */ extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar; + carUsageMode = CarUsagePolicy.Forbidden; #if DEBUG if (fineDebug) @@ -204,6 +234,7 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData * force to take car back home */ extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar; + carUsageMode = CarUsagePolicy.Forbidden; #if DEBUG if (fineDebug) @@ -212,43 +243,39 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData } } } + } + /* + * 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 + * - carUsageMode is valid + * - if pathMode is RequiresCarPath: carUsageMode is either ForcedParked or ForcedPocket + */ + + /* + * modify path-finding constraints (vehicleInfo, endPos) if citizen is forced to walk + */ + if (extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToParkedCar || extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToTarget) { /* - * modify path-finding constraints (vehicleInfo, endPos) if citizen is forced to walk + * vehicle must not be used since we need a walking path to either + * 1. a parked car or + * 2. the target building */ - 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 - */ - carUsageMode = CarUsagePolicy.Forbidden; - - if (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar) { - /* - * walk to parked car - * -> end position is parked car - */ - endPos = Singleton.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) { + + if (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar) { /* - * citizen stands in front of their parked vehicle - * -> find a car-only path now + * walk to parked car + * -> end position is parked car */ - carUsageMode = CarUsagePolicy.Forced; - startPos = Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position; // force to start from the parked car - + endPos = Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position; #if DEBUG if (fineDebug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is forced to drive their car"); + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen shall go to parked vehicle @ {endPos}"); #endif } } + } #if BENCHMARK } #endif @@ -274,7 +301,7 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData laneTypes |= (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle); vehicleTypes |= vehicleInfo.m_vehicleType; extVehicleType = ExtVehicleType.Taxi; // NON-STOCK CODE - // NON-STOCK CODE START + // NON-STOCK CODE START if (Options.prohibitPocketCars) { extInstance.pathMode = ExtPathMode.TaxiToTarget; } @@ -306,81 +333,84 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData #if BENCHMARK using (var bm = new Benchmark(null, "ParkingAI.Main")) { #endif - if (Options.prohibitPocketCars) { - // Parking AI + 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.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 (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresCarPath) { #if DEBUG if (debug) - Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Setting startPos={startPos} for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode}"); + 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 ( - instanceData.m_targetBuilding == 0 || - (Singleton.instance.m_buildings.m_buffer[instanceData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None + AdvancedParkingManager.Instance.FindParkingSpaceForCitizen(endPos, vehicleInfo, ref extInstance, homeId, instanceData.m_targetBuilding == homeId, 0, false, out parkPos, ref endPosA, out calcEndPos) && + extInstance.CalculateReturnPath(parkPos, endPos) ) { - /* - * the citizen is starting their journey and the target is not an outside connection - * -> find a suitable parking space near the target - */ - + // 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 parking space at target for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode} parkedVehicleId={parkedVehicleId}"); + 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 - - // 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 (! extInstance.CalculateReturnPath(parkPos, endPos)) { + // TODO retry? 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; - }*/ - } + 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 (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}"); + 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; - } + extInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingCarPathToTarget; } + } - /* - * determine path type from path mode - */ - extPathType = extInstance.GetPathType(); + /* + * determine path type from path mode + */ + extPathType = extInstance.GetPathType(); - /* - * the following holds: - * - pathMode is now either CalculatingWalkingPathToParkedCar, CalculatingWalkingPathToTarget, CalculatingCarPathToTarget, CalculatingCarPathToKnownParkPos or None. - */ - } + /* + * 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 && @@ -410,7 +440,7 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData /* * determine start & end path positions - */ + */ bool foundEndPos = !calculateEndPos || FindPathPosition(instanceID, ref instanceData, endPos, Options.prohibitPocketCars && (instanceData.m_targetBuilding == 0 || (Singleton.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; @@ -435,18 +465,16 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData 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; + /* + * 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; - } + 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 /* @@ -503,7 +531,7 @@ public bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData 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}"); + Log._Debug($"CustomCitizenAI.ExtStartPathFind({instanceID}): Path-finding starts for citizen instance {instanceID}, path={path}, extVehicleType={extVehicleType}, extPathType={extPathType}, 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) { diff --git a/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs b/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs index 1b020bb4..a481acef 100644 --- a/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs +++ b/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs @@ -172,10 +172,12 @@ public bool ExtStartPathFind(ushort vehicleID, ref Vehicle vehicleData, ushort d skipQueue = true; bool allowTourists = false; + bool searchAtCurrentPos = false; if (driverExtInstance.pathMode == ExtPathMode.ParkingFailed) { // previous parking attempt failed driverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToAltParkPos; allowTourists = true; + searchAtCurrentPos = true; #if DEBUG if (debug) @@ -217,7 +219,7 @@ public bool ExtStartPathFind(ushort vehicleID, ref Vehicle vehicleData, ushort d bool calcEndPos; Vector3 parkPos; - if (AdvancedParkingManager.Instance.FindParkingSpaceForCitizen(endPos, vehicleData.Info, ref driverExtInstance, homeId, targetBuildingId == homeId, vehicleID, allowTourists, out parkPos, ref endPosA, out calcEndPos)) { + if (AdvancedParkingManager.Instance.FindParkingSpaceForCitizen(searchAtCurrentPos ? vehicleData.GetLastFramePosition() : endPos, vehicleData.Info, ref driverExtInstance, homeId, targetBuildingId == homeId, vehicleID, allowTourists, out parkPos, ref endPosA, out calcEndPos)) { calculateEndPos = calcEndPos; allowRandomParking = false; movingToParkingPos = true; @@ -548,7 +550,9 @@ internal bool ExtParkVehicle(ushort vehicleID, ref Vehicle vehicleData, uint dri 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 (driverExtInstance.failedParkingAttempts > 1) { + ExtBuildingManager.Instance.ExtBuildings[targetBuildingId].AddParkingSpaceDemand(GlobalConfig.Instance.ParkingAI.FailedParkingSpaceDemandIncrement * (uint)(driverExtInstance.failedParkingAttempts - 1)); + } } if (! foundParkingSpace) { diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs index b641de5a..2638d173 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs @@ -819,12 +819,21 @@ private void ProcessItemMain(BufferItem item, ref NetSegment prevSegment, ref Ne } else { // pocket car spawning #if PARKINGAI - if (Options.prohibitPocketCars && - m_queueItem.vehicleType == ExtVehicleType.PassengerCar && - (m_queueItem.pathType == ExtCitizenInstance.ExtPathType.WalkingOnly || - (m_queueItem.pathType == ExtCitizenInstance.ExtPathType.DrivingOnly && item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB))) { - /* disallow pocket cars on walking paths, allow only if a driving path is required and we reached the start segment */ - allowPedestrian = false; + if ( + Options.prohibitPocketCars + ) { + if ( + (m_queueItem.pathType == ExtCitizenInstance.ExtPathType.WalkingOnly && prevIsCarLane) || + ( + m_queueItem.pathType == ExtCitizenInstance.ExtPathType.DrivingOnly && + m_queueItem.vehicleType == ExtVehicleType.PassengerCar && + ((item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB) || !prevIsCarLane) + ) + ) { + /* allow pocket cars only if an instant driving path is required and we are at the start segment */ + /* disallow pocket cars on walking paths */ + allowPedestrian = false; + } } else { #endif switchConnectOffset = (byte)m_pathRandomizer.UInt32(1u, 254u); diff --git a/TLM/TLM/Manager/IAdvancedParkingManager.cs b/TLM/TLM/Manager/IAdvancedParkingManager.cs index da1a55e9..c03c4188 100644 --- a/TLM/TLM/Manager/IAdvancedParkingManager.cs +++ b/TLM/TLM/Manager/IAdvancedParkingManager.cs @@ -17,9 +17,13 @@ public enum CarUsagePolicy { /// Allowed, /// - /// Citizens are forced to use their car + /// Citizens are forced to use their parked car /// - Forced, + ForcedParked, + /// + /// Citizens are forced to use a pocket car + /// + ForcedPocket, /// /// Citizens are forbidden to use their car /// diff --git a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs index 911c5ecc..4063ac30 100644 --- a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs +++ b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs @@ -530,7 +530,7 @@ protected ExtSoftPathState OnCitizenPathFindSuccess(ushort instanceId, ref Citiz bool usesPublicTransport = (laneTypes & (byte)(NetInfo.LaneType.PublicTransport)) != 0; bool usesCar = (laneTypes & (byte)(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0 && (vehicleTypes & (ushort)(VehicleInfo.VehicleType.Car)) != 0; - if (usesPublicTransport && usesCar && extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos) { + if (usesPublicTransport && usesCar && (extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos || extInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos)) { /* * when using public transport together with a car (assuming a "source -> walk -> drive -> walk -> use public transport -> walk -> target" path) * discard parking space information since the cim has to park near the public transport stop @@ -563,7 +563,6 @@ protected ExtSoftPathState OnCitizenPathFindSuccess(ushort instanceId, ref Citiz Log._Debug($"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Path for citizen instance {instanceId} contains passenger car section. Ensuring that citizen is allowed to use their car."); #endif - // check if citizen is at an outside connection ushort sourceBuildingId = instanceData.m_sourceBuilding; ushort homeId = Singleton.instance.m_citizens.m_buffer[instanceData.m_citizen].m_homeBuilding; @@ -686,8 +685,33 @@ protected ExtSoftPathState OnCitizenPathFindSuccess(ushort instanceId, ref Citiz if (debug) Log._Debug($"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Path for citizen instance {instanceId} contains passenger car section and citizen should stand in front of their car."); #endif - - if (parkedVehicleId == 0) { + if ( + Constants.ManagerFactory.ExtCitizenInstanceManager.IsAtOutsideConnection(instanceId, ref instanceData, ref citizenData) && + Singleton.instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].Info.m_class.m_service == ItemClass.Service.Road + ) { + // car path calculated starting at road outside connection: success + if (extInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos) { + extInstance.pathMode = ExtPathMode.DrivingToAltParkPos; + extInstance.parkingPathStartPosition = null; +#if DEBUG + if (debug) + Log._Debug($"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Path to an alternative parking position is READY! CurrentPathMode={extInstance.pathMode}"); +#endif + } else if (extInstance.pathMode == ExtPathMode.CalculatingCarPathToTarget) { + extInstance.pathMode = ExtPathMode.DrivingToTarget; +#if DEBUG + if (debug) + Log._Debug($"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Car path is READY! CurrentPathMode={extInstance.pathMode}"); +#endif + } else if (extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos) { + extInstance.pathMode = ExtPathMode.DrivingToKnownParkPos; +#if DEBUG + if (debug) + Log._Debug($"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Car path to known parking position is READY! CurrentPathMode={extInstance.pathMode}"); +#endif + } + return ExtSoftPathState.Ready; + } else if (parkedVehicleId == 0) { // error! could not find/spawn parked car #if DEBUG if (debug) @@ -1670,7 +1694,11 @@ public string EnrichLocalizedCarStatus(string ret, ref ExtCitizenInstance driver if (driverExtInstance.IsValid()) { switch (driverExtInstance.pathMode) { case ExtPathMode.DrivingToAltParkPos: - ret = Translation.GetString("Driving_to_another_parking_spot") + " (#" + driverExtInstance.failedParkingAttempts + "), " + ret; + if (driverExtInstance.failedParkingAttempts <= 1) { + ret = Translation.GetString("Looking_for_a_parking_spot") + ", " + ret; + } else { + ret = Translation.GetString("Driving_to_another_parking_spot") + " (#" + driverExtInstance.failedParkingAttempts + "), " + ret; + } break; case ExtPathMode.CalculatingCarPathToKnownParkPos: case ExtPathMode.DrivingToKnownParkPos: diff --git a/TLM/TLM/Traffic/Data/ExtCitizenInstance.cs b/TLM/TLM/Traffic/Data/ExtCitizenInstance.cs index f3e45bf4..9fd815a7 100644 --- a/TLM/TLM/Traffic/Data/ExtCitizenInstance.cs +++ b/TLM/TLM/Traffic/Data/ExtCitizenInstance.cs @@ -344,7 +344,7 @@ internal bool CalculateReturnPath(Vector3 parkPos, Vector3 targetPos) { args.endPosA = targetPathPos; args.endPosB = dummyPathPos; args.vehiclePosition = dummyPathPos; - args.laneTypes = NetInfo.LaneType.Pedestrian; + args.laneTypes = NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport; args.vehicleTypes = VehicleInfo.VehicleType.None; args.maxLength = 20000f; args.isHeavyVehicle = false;