diff --git a/README.md b/README.md index 8b67720b..9b80a6a2 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,17 @@ A work-in-progress modification for **Cities: Skylines** to add additional traff User manual: http://www.viathinksoft.de/tmpe # Changelog +1.8.4, 12/11/2016 +- New feature: "Stay on lane": By pressing Shift + S in the Lane Connector tool you can now link connected lanes such that vehicles are not allowed to change lanes at this point. Press Shift + S again to restrict "stay on lane" to either road direction. +- U-turns are now only allowed to be performed from the innermost lane +- TMPE now detects if the number of spawned vehicles is reaching its limit (16384). If so, spawning of service/emergency vehicles is prioritized over spawning other vehicles. +- Bugfix: Bicycles cannot change from bicycle lanes to pedestrian lanes +- Bugfix: Travel probabilities set in the "Citizen Lifecycle Rebalance v2.1" mod are not obeyed (thanks to @informmanuel, @shaundoddmusic for reporting this issue) +- Bugfix: Number of tourists seems to drop when activating the mod (statistics were not updated, thanks to @hpp7117, @wjrohn for reporting this issue) +- Bugfix: When loading a second savegame a second main menu button is displayed (thanks to @Cpt. Whitepaw for reporting this issue) +- Bugfix: While path-finding is in progress vehicles do "bungee-jumping" on the current segment (thanks to @mxolsenx, @Howzitworld for reporting this issue) +- Bugfix: Cims leaving the city search for parking spaces near the outside connection which is obviously not required + 1.8.3, 12/4/2016 - Bugfix: Despite having the Parking AI activated, cims sometimes still spawn pocket cars. - Bugfix: When the Parking AI is active, bicycle lanes are not used (thanks to @informmanuel for reporting this issue) diff --git a/TLM/GlobalConfigGenerator/GlobalConfigGenerator.csproj b/TLM/GlobalConfigGenerator/GlobalConfigGenerator.csproj index 1f5cb34a..cea07e3b 100644 --- a/TLM/GlobalConfigGenerator/GlobalConfigGenerator.csproj +++ b/TLM/GlobalConfigGenerator/GlobalConfigGenerator.csproj @@ -32,6 +32,16 @@ prompt 4 + + bin\QueuedStats\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + true + diff --git a/TLM/SpiralLoopTest/SpiralLoopTest.csproj b/TLM/SpiralLoopTest/SpiralLoopTest.csproj index 41d34eca..a1ead434 100644 --- a/TLM/SpiralLoopTest/SpiralLoopTest.csproj +++ b/TLM/SpiralLoopTest/SpiralLoopTest.csproj @@ -32,6 +32,16 @@ prompt 4 + + bin\QueuedStats\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + true + diff --git a/TLM/TLM.sln b/TLM/TLM.sln index a2833dce..82f852c2 100644 --- a/TLM/TLM.sln +++ b/TLM/TLM.sln @@ -23,19 +23,26 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + QueuedStats|Any CPU = QueuedStats|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7422AE58-8B0A-401C-9404-F4A438EFFE10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7422AE58-8B0A-401C-9404-F4A438EFFE10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7422AE58-8B0A-401C-9404-F4A438EFFE10}.QueuedStats|Any CPU.ActiveCfg = QueuedStats|Any CPU + {7422AE58-8B0A-401C-9404-F4A438EFFE10}.QueuedStats|Any CPU.Build.0 = QueuedStats|Any CPU {7422AE58-8B0A-401C-9404-F4A438EFFE10}.Release|Any CPU.ActiveCfg = Release|Any CPU {7422AE58-8B0A-401C-9404-F4A438EFFE10}.Release|Any CPU.Build.0 = Release|Any CPU {48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.QueuedStats|Any CPU.ActiveCfg = QueuedStats|Any CPU + {48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.QueuedStats|Any CPU.Build.0 = QueuedStats|Any CPU {48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Release|Any CPU.ActiveCfg = Release|Any CPU {48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Release|Any CPU.Build.0 = Release|Any CPU {615EF202-3E13-4A15-A82F-594D90CD0ECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {615EF202-3E13-4A15-A82F-594D90CD0ECD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {615EF202-3E13-4A15-A82F-594D90CD0ECD}.QueuedStats|Any CPU.ActiveCfg = QueuedStats|Any CPU + {615EF202-3E13-4A15-A82F-594D90CD0ECD}.QueuedStats|Any CPU.Build.0 = QueuedStats|Any CPU {615EF202-3E13-4A15-A82F-594D90CD0ECD}.Release|Any CPU.ActiveCfg = Release|Any CPU {615EF202-3E13-4A15-A82F-594D90CD0ECD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/TLM/TLM/Custom/AI/CustomCarAI.cs b/TLM/TLM/Custom/AI/CustomCarAI.cs index 7ce0806f..5ab6e721 100644 --- a/TLM/TLM/Custom/AI/CustomCarAI.cs +++ b/TLM/TLM/Custom/AI/CustomCarAI.cs @@ -14,6 +14,7 @@ using TrafficManager.State; using TrafficManager.Manager; using TrafficManager.Traffic; +using static TrafficManager.Traffic.ExtCitizenInstance; namespace TrafficManager.Custom.AI { public class CustomCarAI : CarAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`) @@ -40,10 +41,33 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect }*/ #endif - if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { + // NON-STOCK CODE START + VehicleState state = null; + ExtCitizenInstance driverExtInstance = null; + bool prohibitPocketCars = Options.prohibitPocketCars; + if (prohibitPocketCars) { + 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)) { PathManager pathManager = Singleton.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); @@ -51,13 +75,32 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect } #endif - if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { + if (prohibitPocketCars) { + if (driverExtInstance.ReturnPathState == ExtPathState.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 ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) { // path == 0: non-stock code! + } else if (pathFindFailed) { vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton.instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; @@ -106,13 +149,13 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect } this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics); if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) { - VehicleManager instance2 = Singleton.instance; + VehicleManager vehManager = Singleton.instance; ushort num = vehicleData.m_trailingVehicle; int num2 = 0; while (num != 0) { - ushort trailingVehicle = instance2.m_vehicles.m_buffer[(int)num].m_trailingVehicle; - VehicleInfo info = instance2.m_vehicles.m_buffer[(int)num].Info; - info.m_vehicleAI.SimulationStep(num, ref instance2.m_vehicles.m_buffer[(int)num], vehicleId, ref vehicleData, lodPhysics); + 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.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); diff --git a/TLM/TLM/Custom/AI/CustomCitizenAI.cs b/TLM/TLM/Custom/AI/CustomCitizenAI.cs index 6c241a5a..8cc67245 100644 --- a/TLM/TLM/Custom/AI/CustomCitizenAI.cs +++ b/TLM/TLM/Custom/AI/CustomCitizenAI.cs @@ -224,8 +224,8 @@ public bool CustomStartPathFind(ushort instanceID, ref CitizenInstance citizenDa ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID); ushort homeId = Singleton.instance.m_citizens.m_buffer[citizenData.m_citizen].m_homeBuilding; - // if the citizen is a resident and is starting its journey: find a suitable parking space near the target - if (extInstance.PathMode == ExtCitizenInstance.ExtPathMode.ParkedCarReached) { + // if the citizen is a resident starting its journey and the target is not an outside connection: find a suitable parking space near the target + if (extInstance.PathMode == ExtCitizenInstance.ExtPathMode.ParkedCarReached && (citizenData.m_targetBuilding == 0 || (Singleton.instance.m_buildings.m_buffer[citizenData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None)) { #if DEBUG if (GlobalConfig.Instance.DebugSwitches[2]) Log._Debug($"CustomCitizenAI.CustomStartPathFind: Finding parking space at target for citizen instance {instanceID}. CurrentDepartureMode={extInstance.PathMode} parkedVehicleId={parkedVehicleId}"); diff --git a/TLM/TLM/Custom/AI/CustomHumanAI.cs b/TLM/TLM/Custom/AI/CustomHumanAI.cs index 5e8e08ba..b269d373 100644 --- a/TLM/TLM/Custom/AI/CustomHumanAI.cs +++ b/TLM/TLM/Custom/AI/CustomHumanAI.cs @@ -25,28 +25,26 @@ public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instance } // NON-STOCK CODE START + ExtCitizenInstance extInstance = null; if (Options.prohibitPocketCars) { - ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID); + // query the state of the return path + extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID); extInstance.UpdateReturnPathState(); } // NON-STOCK CODE END - if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) { + if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None + && (!Options.prohibitPocketCars || extInstance.ReturnPathState != ExtPathState.Calculating)) { PathManager pathManager = Singleton.instance; byte pathFindFlags = pathManager.m_pathUnits.m_buffer[instanceData.m_path].m_pathFindFlags; // NON-STOCK CODE START - bool pathFindFailed = (pathFindFlags & PathUnit.FLAG_FAILED) != 0; + bool pathFindFailed = (pathFindFlags & PathUnit.FLAG_FAILED) != 0 || instanceData.m_path == 0; bool pathFindSucceeded = (pathFindFlags & PathUnit.FLAG_READY) != 0; bool handleSuccess = true; if (Options.prohibitPocketCars) { - ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID); - - if (extInstance.ReturnPathState == ExtPathState.Calculating) { - // wait for the return path being calculated - return; - } else if (extInstance.ReturnPathState == ExtPathState.Failed) { + if (extInstance.ReturnPathState == ExtPathState.Failed) { #if DEBUG if (GlobalConfig.Instance.DebugSwitches[2]) Log._Debug($"CustomPassengerCarAI.CustomSimulationStep: Return path {extInstance.ReturnPathId} FAILED. Forcing path-finding to fail."); @@ -55,8 +53,8 @@ public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instance pathFindFailed = true; } - if (extInstance.ReturnPathState == ExtPathState.Ready || extInstance.ReturnPathState == ExtPathState.Failed) - extInstance.ReleaseReturnPath(); + //if (extInstance.ReturnPathState == ExtPathState.Ready || extInstance.ReturnPathState == ExtPathState.Failed) + extInstance.ReleaseReturnPath(); #if DEBUG if (GlobalConfig.Instance.DebugSwitches[4] && (pathFindFailed || pathFindSucceeded)) { @@ -165,7 +163,7 @@ public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instance } } - public bool CustomArriveAtTarget(ushort instanceID, ref CitizenInstance citizenData) { // TODO stock code + /*public bool CustomArriveAtTarget(ushort instanceID, ref CitizenInstance citizenData) { // TODO stock code if ((citizenData.m_flags & CitizenInstance.Flags.HangAround) != CitizenInstance.Flags.None) { uint citizenId = citizenData.m_citizen; if (citizenId != 0u) { @@ -184,7 +182,7 @@ public bool CustomArriveAtTarget(ushort instanceID, ref CitizenInstance citizenD this.ArriveAtDestination(instanceID, ref citizenData, true); } return true; - } + }*/ internal static bool OnPathFindFailure(ushort instanceID, ref CitizenInstance instanceData, ExtCitizenInstance extInstance) { #if DEBUG @@ -315,7 +313,6 @@ internal static bool OnPathFindSuccess(ushort instanceID, ref CitizenInstance in isAtOutsideConnection = (Singleton.instance.m_buildings.m_buffer[sourceBuildingId].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None;// Info.m_buildingAI is OutsideConnectionAI; if (isAtOutsideConnection && (instanceData.GetLastFramePosition() - Singleton.instance.m_buildings.m_buffer[sourceBuildingId].m_position).magnitude > GlobalConfig.Instance.MaxBuildingToPedestrianLaneDistance) isAtOutsideConnection = false; - //isAtOutsideConnection = Singleton.instance.m_buildings.m_buffer[sourceBuildingId].Info.m_buildingAI is OutsideConnectionAI; } #if DEBUG @@ -335,96 +332,57 @@ internal static bool OnPathFindSuccess(ushort instanceID, ref CitizenInstance in // try to spawn parked vehicle in the vicinity of the starting point. VehicleInfo vehicleInfo = null; - Citizen.AgeGroup ageGroup = CustomCitizenAI.GetAgeGroup(instanceData.Info.m_agePhase); - if (ageGroup > Citizen.AgeGroup.Child) { - PrefabAI ai = Singleton.instance.m_citizens.m_buffer[instanceData.m_citizen].GetCitizenInfo(instanceData.m_citizen).GetAI(); - if (ai is ResidentAI) { - vehicleInfo = CustomResidentAI.GetVehicleInfo(instanceID, ref instanceData, true); - } else if (ai is TouristAI) { - vehicleInfo = CustomTouristAI.GetVehicleInfo(instanceID, ref instanceData, true); - } + if (instanceData.Info.m_agePhase > Citizen.AgePhase.Child) { + // get a random car info (due to the fact we are using a different randomizer, car assignment differs from the selection in ResidentAI.GetVehicleInfo/TouristAI.GetVehicleInfo method, but this should not matter since we are reusing parked vehicle infos there) + vehicleInfo = Singleton.instance.GetRandomVehicleInfo(ref Singleton.instance.m_randomizer, ItemClass.Service.Residential, ItemClass.SubService.ResidentialLow, ItemClass.Level.Level1); } if (vehicleInfo != null) { - /*switch (vehicleInfo.GetService()) { - case ItemClass.Service.PublicTransport: - if (usesPublicTransport) { - extInstance.PathMode = ExtCitizenInstance.ExtPathMode.PublicTransportToTarget; - if (instanceData.m_sourceBuilding != 0) - ExtBuildingManager.Instance.GetExtBuilding(instanceData.m_sourceBuilding).RemovePublicTransportDemand((uint)GlobalConfig.Instance.PublicTransportDemandUsageDecrement, true); - if (instanceData.m_targetBuilding != 0) - ExtBuildingManager.Instance.GetExtBuilding(instanceData.m_targetBuilding).RemovePublicTransportDemand((uint)GlobalConfig.Instance.PublicTransportDemandUsageDecrement, false); - } else { - extInstance.PathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToTarget; - handleSoftPathFindFailure = true; - return false; - } #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using public transport/is walking. CurrentPathMode={extInstance.PathMode}"); -#endif - return true; - case ItemClass.Service.Residential:*/ -#if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using their own passenger car. CurrentPathMode={extInstance.PathMode}"); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using their own passenger car. CurrentPathMode={extInstance.PathMode}"); #endif - Vector3 currentPos; - if (sourceBuildingId != 0) { - currentPos = Singleton.instance.m_buildings.m_buffer[sourceBuildingId].m_position; -#if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from source building {sourceBuildingId} for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos} CurrentPathMode={extInstance.PathMode}"); -#endif - } else { - ushort currentBuildingId = Singleton.instance.m_citizens.m_buffer[instanceData.m_citizen].GetBuildingByLocation(); - if (currentBuildingId != 0) { - currentPos = Singleton.instance.m_buildings.m_buffer[currentBuildingId].m_position; + // determine current position vector + Vector3 currentPos; + ushort currentBuildingId = Singleton.instance.m_citizens.m_buffer[instanceData.m_citizen].GetBuildingByLocation(); + if (currentBuildingId != 0) { + currentPos = Singleton.instance.m_buildings.m_buffer[sourceBuildingId].m_position; #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from current building {currentBuildingId} for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos}. CurrentPathMode={extInstance.PathMode}"); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from source building {sourceBuildingId} for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos} CurrentPathMode={extInstance.PathMode}"); #endif - } else { - currentPos = instanceData.GetLastFramePosition(); + } else { + currentPos = instanceData.GetLastFramePosition(); #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from last frame position for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos}. Home {homeId} pos: {Singleton.instance.m_buildings.m_buffer[homeId].m_position} CurrentPathMode={extInstance.PathMode}"); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from last frame position for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos}. Home {homeId} pos: {Singleton.instance.m_buildings.m_buffer[homeId].m_position} CurrentPathMode={extInstance.PathMode}"); #endif - } - } + } - Vector3 parkPos; - if (CustomCitizenAI.TrySpawnParkedPassengerCar(instanceData.m_citizen, homeId, currentPos, vehicleInfo, out parkPos)) { - parkedVehicleId = Singleton.instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle; + // spawn a passenger car near the current position + Vector3 parkPos; + if (CustomCitizenAI.TrySpawnParkedPassengerCar(instanceData.m_citizen, homeId, currentPos, vehicleInfo, out parkPos)) { + parkedVehicleId = Singleton.instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle; #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2] && sourceBuildingId != 0) - Log._Debug($"Parked vehicle for citizen {instanceData.m_citizen} (instance {instanceID}) is {parkedVehicleId} now."); + if (GlobalConfig.Instance.DebugSwitches[2] && sourceBuildingId != 0) + Log._Debug($"Parked vehicle for citizen {instanceData.m_citizen} (instance {instanceID}) is {parkedVehicleId} now."); #endif - if (sourceBuildingId != 0) { - ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).ModifyParkingSpaceDemand(parkPos, GlobalConfig.Instance.MinSpawnedCarParkingSpaceDemandDelta, GlobalConfig.Instance.MaxSpawnedCarParkingSpaceDemandDelta); - } - } else { + if (sourceBuildingId != 0) { + ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).ModifyParkingSpaceDemand(parkPos, GlobalConfig.Instance.MinSpawnedCarParkingSpaceDemandDelta, GlobalConfig.Instance.MaxSpawnedCarParkingSpaceDemandDelta); + } + } else { #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) { - Log._Debug($">> Failed to spawn parked vehicle for citizen {instanceData.m_citizen} (citizen instance {instanceID}). homePos: {Singleton.instance.m_buildings.m_buffer[homeId].m_position}"); - } + if (GlobalConfig.Instance.DebugSwitches[2]) { + Log._Debug($">> Failed to spawn parked vehicle for citizen {instanceData.m_citizen} (citizen instance {instanceID}). homePos: {Singleton.instance.m_buildings.m_buffer[homeId].m_position}"); + } #endif - if (sourceBuildingId != 0) { - ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).AddParkingSpaceDemand(GlobalConfig.Instance.FailedSpawnParkingSpaceDemandIncrement); - } - } - /*break; - default: - extInstance.PathMode = ExtCitizenInstance.ExtPathMode.WalkingToTarget; -#if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using an UNHANDLED {vehicleInfo.GetService()} vehicle. CurrentPathMode={extInstance.PathMode}"); -#endif - return true; - }*/ + if (sourceBuildingId != 0) { + ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).AddParkingSpaceDemand(GlobalConfig.Instance.FailedSpawnParkingSpaceDemandIncrement); + } + } } else { #if DEBUG if (GlobalConfig.Instance.DebugSwitches[2]) { diff --git a/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs b/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs index 831b1a61..2d6368ae 100644 --- a/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs +++ b/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs @@ -20,64 +20,14 @@ namespace TrafficManager.Custom.AI { public class CustomPassengerCarAI : CarAI { public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { - try { - // NON-STOCK CODE START - VehicleState state = null; - ExtCitizenInstance driverExtInstance = null; - if (Options.prohibitPocketCars) { - state = VehicleStateManager.Instance._GetVehicleState(vehicleData.GetFirstVehicle(vehicleId)); - driverExtInstance = state.GetDriverExtInstance(); - /*if (driverExtInstance != null) { - driverExtInstance.UpdateReturnPathState(); - }*/ - } - // NON-STOCK CODE END - - if ((vehicleData.m_flags & Vehicle.Flags.Congestion) != 0 && Options.enableDespawning) { - Singleton.instance.ReleaseVehicle(vehicleId); - } else { - // NON-STOCK CODE START - if (Options.prohibitPocketCars) { - if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { - if (driverExtInstance != null) { - PathManager pathManager = Singleton.instance; - byte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags; - - bool pathFindFailed = (pathFindFlags & PathUnit.FLAG_FAILED) != 0; - bool pathFindSucceeded = (pathFindFlags & PathUnit.FLAG_READY) != 0; - - if (driverExtInstance.ReturnPathState == ExtPathState.Calculating) { - // wait for the return path being calculated - return; - } else if (driverExtInstance.ReturnPathState == ExtPathState.Failed) { -#if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomPassengerCarAI.CustomSimulationStep: Return path {driverExtInstance.ReturnPathId} FAILED. Forcing path-finding to fail."); -#endif - pathFindSucceeded = false; - pathFindFailed = true; - } - - if (driverExtInstance.ReturnPathState == ExtPathState.Ready || driverExtInstance.ReturnPathState == ExtPathState.Failed) - driverExtInstance.ReleaseReturnPath(); - - if (pathFindSucceeded) { - OnPathFindSuccess(vehicleId, ref vehicleData, driverExtInstance); - } else if (pathFindFailed) { - OnPathFindFailure(driverExtInstance, vehicleId); - } - } - } - } - // NON-STOCK CODE END - base.SimulationStep(vehicleId, ref vehicleData, physicsLodRefPos); - } - } catch (Exception ex) { - Log.Error("Error in CustomPassengerCarAI.SimulationStep: " + ex.ToString()); + if ((vehicleData.m_flags & Vehicle.Flags.Congestion) != 0 && Options.enableDespawning) { + Singleton.instance.ReleaseVehicle(vehicleId); + } else { + base.SimulationStep(vehicleId, ref vehicleData, physicsLodRefPos); } } - protected static void OnPathFindFailure(ExtCitizenInstance extInstance, ushort vehicleId) { + internal static void OnPathFindFailure(ExtCitizenInstance extInstance, ushort vehicleId) { #if DEBUG if (GlobalConfig.Instance.DebugSwitches[2]) Log._Debug($"CustomHumanAI.OnPathFindFailure: Path-finding failed for vehicle {vehicleId}, citizen instance {extInstance.InstanceId}. CurrentPathMode={extInstance.PathMode}"); @@ -220,11 +170,19 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto float sqrDistA = 0f; float sqrDistB; + ushort driverInstanceId = CustomPassengerCarAI.GetDriverInstance(vehicleID, ref vehicleData); + if (driverInstanceId == 0) { + return false; + } + CitizenManager citizenManager = Singleton.instance; + ushort targetBuildingId = citizenManager.m_instances.m_buffer[(int)driverInstanceId].m_targetBuilding; + // NON-STOCK CODE START bool calculateEndPos = true; bool allowRandomParking = true; bool movingToParkingPos = false; bool foundStartingPos = false; + bool skipQueue = false; ExtPathType extPathType = ExtPathType.None; if (Options.prohibitPocketCars) { VehicleState state = VehicleStateManager.Instance._GetVehicleState(vehicleData.GetFirstVehicle(vehicleID)); @@ -241,75 +199,83 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto case ExtPathMode.DrivingToTarget: case ExtPathMode.DrivingToKnownParkPos: case ExtPathMode.ParkingFailed: - bool allowTourists = false; - if (driverExtInstance.PathMode == ExtPathMode.ParkingFailed) { - // previous parking attempt failed - driverExtInstance.PathMode = ExtPathMode.CalculatingCarPathToAltParkPos; - allowTourists = true; + if (targetBuildingId != 0 && (Singleton.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 (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"Vehicle {vehicleID} shall move to an alternative parking position! CurrentPathMode={driverExtInstance.PathMode}"); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"Vehicle {vehicleID} shall move to an alternative parking position! CurrentPathMode={driverExtInstance.PathMode}"); #endif - if (driverExtInstance.ParkingPathStartPosition != null) { - startPosA = (PathUnit.Position)driverExtInstance.ParkingPathStartPosition; - foundStartingPos = true; + if (driverExtInstance.ParkingPathStartPosition != null) { + startPosA = (PathUnit.Position)driverExtInstance.ParkingPathStartPosition; + foundStartingPos = true; #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"Setting starting pos for {vehicleID} to segment={startPosA.m_segment}, laneIndex={startPosA.m_lane}, offset={startPosA.m_offset}"); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"Setting starting pos for {vehicleID} to segment={startPosA.m_segment}, laneIndex={startPosA.m_lane}, offset={startPosA.m_offset}"); #endif - } - startBothWays = false; + } + startBothWays = false; - if (driverExtInstance.FailedParkingAttempts > GlobalConfig.Instance.MaxParkingAttempts) { - // maximum number of parking attempts reached + if (driverExtInstance.FailedParkingAttempts > GlobalConfig.Instance.MaxParkingAttempts) { + // maximum number of parking attempts reached #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"Reached maximum number of parking attempts for vehicle {vehicleID}! GIVING UP."); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"Reached maximum number of parking attempts for vehicle {vehicleID}! GIVING UP."); #endif - driverExtInstance.Reset(); + driverExtInstance.Reset(); - // pocket car fallback - vehicleData.m_flags |= Vehicle.Flags.Parking; - return true; + // pocket car fallback + vehicleData.m_flags |= Vehicle.Flags.Parking; + return true; + } + } else { + driverExtInstance.PathMode = ExtPathMode.CalculatingCarPathToKnownParkPos; } - } else { - driverExtInstance.PathMode = ExtPathMode.CalculatingCarPathToKnownParkPos; - } - ushort homeId = Singleton.instance.m_citizens.m_buffer[driverExtInstance.GetCitizenId()].m_homeBuilding; - bool calcEndPos; - Vector3 parkPos; + ushort homeId = Singleton.instance.m_citizens.m_buffer[driverExtInstance.GetCitizenId()].m_homeBuilding; + bool calcEndPos; + Vector3 parkPos; - if (CustomCitizenAI.FindParkingSpaceForExtInstance(endPos, vehicleData.Info, driverExtInstance, homeId, vehicleID, allowTourists, out parkPos, ref endPosA, out calcEndPos)) { - calculateEndPos = calcEndPos; - allowRandomParking = false; - movingToParkingPos = true; + if (CustomCitizenAI.FindParkingSpaceForExtInstance(endPos, vehicleData.Info, driverExtInstance, homeId, vehicleID, allowTourists, out parkPos, ref endPosA, out calcEndPos)) { + calculateEndPos = calcEndPos; + allowRandomParking = false; + movingToParkingPos = true; - if (!driverExtInstance.CalculateReturnPath(parkPos, endPos)) { + if (!driverExtInstance.CalculateReturnPath(parkPos, endPos)) { +#if DEBUG + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"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 (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"Could not calculate return path for citizen instance {driverExtInstance.InstanceId}, vehicle {vehicleID}. Resetting instance."); + Log._Debug($"No alternative parking spot found for vehicle {vehicleID}, citizen instance {driverExtInstance.InstanceId} with CurrentPathMode={driverExtInstance.PathMode}! GIVING UP."); #endif driverExtInstance.Reset(); return false; - } - } else if (driverExtInstance.PathMode == ExtPathMode.CalculatingCarPathToAltParkPos) { - // no alternative parking spot found: abort -#if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"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 + } else { + // calculate a direct path to target #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"No alternative parking spot found for vehicle {vehicleID}, citizen instance {driverExtInstance.InstanceId} with CurrentPathMode={driverExtInstance.PathMode}! Setting CurrentPathMode to 'CalculatingCarPath'."); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"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; + driverExtInstance.PathMode = ExtPathMode.CalculatingCarPathToTarget; + } } break; } @@ -322,17 +288,8 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto #endif } } - // NON-STOCK CODE END - VehicleInfo info = this.m_info; - ushort driverInstance = CustomPassengerCarAI.GetDriverInstance(vehicleID, ref vehicleData); - if (driverInstance == 0) { - return false; - } - CitizenManager instance = Singleton.instance; - CitizenInfo citizenInfo = instance.m_instances.m_buffer[(int)driverInstance].Info; - NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle; // NON-STOCK CODE - // NON-STOCK CODE START + NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle; if (!movingToParkingPos) { laneTypes |= NetInfo.LaneType.Pedestrian; } @@ -341,11 +298,10 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto VehicleInfo.VehicleType vehicleType = this.m_info.m_vehicleType; bool allowUnderground = (vehicleData.m_flags & Vehicle.Flags.Underground) != 0; bool randomParking = false; - ushort targetBuilding = instance.m_instances.m_buffer[(int)driverInstance].m_targetBuilding; if (allowRandomParking && // NON-STOCK CODE !movingToParkingPos && - targetBuilding != 0 && - Singleton.instance.m_buildings.m_buffer[(int)targetBuilding].Info.m_class.m_service > ItemClass.Service.Office) { + targetBuildingId != 0 && + Singleton.instance.m_buildings.m_buffer[(int)targetBuildingId].Info.m_class.m_service > ItemClass.Service.Office) { randomParking = true; } @@ -356,10 +312,10 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto // NON-STOCK CODE START if (! foundStartingPos) { - foundStartingPos = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out sqrDistA, out sqrDistB); + foundStartingPos = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out sqrDistA, out sqrDistB); } - bool foundEndPos = !calculateEndPos || citizenInfo.m_citizenAI.FindPathPosition(driverInstance, ref instance.m_instances.m_buffer[(int)driverInstance], endPos, Options.prohibitPocketCars ? NetInfo.LaneType.Pedestrian : (laneTypes | NetInfo.LaneType.Pedestrian), vehicleType, undergroundTarget, out endPosA); + 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 ? NetInfo.LaneType.Pedestrian : (laneTypes | NetInfo.LaneType.Pedestrian), vehicleType, undergroundTarget, out endPosA); // NON-STOCK CODE END if (foundStartingPos && @@ -368,8 +324,8 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto // NON-STOCK CODE START bool allowEscapeTransport = true; if (Options.restrictEvacBussesToShelter) { - if (targetBuilding != 0) { - BuildingInfo targetBuildingInfo = Singleton.instance.m_buildings.m_buffer[targetBuilding].Info; + if (targetBuildingId != 0) { + BuildingInfo targetBuildingInfo = Singleton.instance.m_buildings.m_buffer[targetBuildingId].Info; allowEscapeTransport = targetBuildingInfo.GetService() == ItemClass.Service.Disaster && targetBuildingInfo.GetClassLevel() == ItemClass.Level.Level4; } else { allowEscapeTransport = false; @@ -390,7 +346,7 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto #else false #endif - , ExtVehicleType.PassengerCar, vehicleID, extPathType, out path, ref instance2.m_randomizer, instance2.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleType, 20000f, false, false, false, false, randomParking, false, allowEscapeTransport)) { + , ExtVehicleType.PassengerCar, vehicleID, extPathType, out path, ref instance2.m_randomizer, instance2.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleType, 20000f, false, false, false, skipQueue, randomParking, false, allowEscapeTransport)) { #if USEPATHWAITCOUNTER VehicleState state = VehicleStateManager.Instance._GetVehicleState(vehicleID); state.PathWaitCounter = 0; @@ -769,7 +725,7 @@ public static bool FindParkingSpaceBuilding(VehicleInfo vehicleInfo, ushort home Vector3 position = transformMatrix.MultiplyPoint(prop.m_position); if (FindParkingSpaceProp(ignoreParked, propInfo, position, building.m_angle + prop.m_radAngle, prop.m_fixedHeight, refPos, vehicleInfo.m_generatedInfo.m_size.x, vehicleInfo.m_generatedInfo.m_size.z, ref propMinDistance, ref parkPos, ref parkRot)) { // NON-STOCK CODE result = true; - if (randomize && propMinDistance <= maxDistance && rng.Int32(GlobalConfig.Instance.VicinityParkingSpaceSelectionRand) != 0) + if (randomize && propMinDistance <= maxDistance && rng.Int32(GlobalConfig.Instance.VicinityParkingSpaceSelectionRand) == 0) break; } } @@ -913,8 +869,10 @@ public bool CustomParkVehicle(ushort vehicleID, ref Vehicle vehicleData, PathUni if (driverCitizenId != 0u) { if (Options.prohibitPocketCars) { state = VehicleStateManager.Instance._GetVehicleState(vehicleData.GetFirstVehicle(vehicleID)); - driverExtInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(driverCitizenInstanceId); - prohibitPocketCars = driverExtInstance != null; // TODO why would this be null?? + if (driverCitizenInstanceId != 0) { + driverExtInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(driverCitizenInstanceId); + prohibitPocketCars = true; + } } uint laneID = PathManager.GetLaneID(pathPos); @@ -1196,9 +1154,5 @@ private static bool CustomFindParkingSpace(VehicleInfo vehicleInfo, ushort homeI } return false; } - - internal static void OnLevelLoaded() { - - } } } diff --git a/TLM/TLM/Custom/AI/CustomResidentAI.cs b/TLM/TLM/Custom/AI/CustomResidentAI.cs index d041c8d9..1a488ccf 100644 --- a/TLM/TLM/Custom/AI/CustomResidentAI.cs +++ b/TLM/TLM/Custom/AI/CustomResidentAI.cs @@ -13,7 +13,7 @@ using static TrafficManager.Traffic.ExtCitizenInstance; namespace TrafficManager.Custom.AI { - public class CustomResidentAI : HumanAI { + public class CustomResidentAI : ResidentAI { 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; @@ -123,10 +123,6 @@ public string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance da } public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) { - return CustomResidentAI.GetVehicleInfo(instanceID, ref citizenData, forceProbability); - } - - public static new VehicleInfo GetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) { if (citizenData.m_citizen == 0u) { return null; } @@ -137,15 +133,15 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID); if (extInstance.PathMode == ExtPathMode.TaxiToTarget) { forceTaxi = true; - } - - ushort parkedVehicleId = Singleton.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle; - if (parkedVehicleId != 0) { + } else { + ushort parkedVehicleId = Singleton.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."); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"CustomResidentAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info."); #endif - return Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info; + return Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info; + } } } // NON-STOCK CODE END @@ -155,18 +151,21 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c int carProb; int bikeProb; int taxiProb; + // NON-STOCK CODE START if (forceTaxi) { carProb = 0; bikeProb = 0; taxiProb = 100; - } else if (forceProbability || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) { + } else + // NON-STOCK CODE END + if (forceProbability || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) { carProb = 100; bikeProb = 0; taxiProb = 0; } else { - carProb = CustomResidentAI.GetCarProbability(instanceID, ref citizenData, ageGroup); - bikeProb = CustomResidentAI.GetBikeProbability(instanceID, ref citizenData, ageGroup); - taxiProb = CustomResidentAI.GetTaxiProbability(instanceID, ref citizenData, ageGroup); + 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; @@ -189,7 +188,8 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c return null; } - private static int GetTaxiProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { + private int GetTaxiProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { + Log.Error("CustomResidentAI.GetTaxiProbability called!"); switch (ageGroup) { case Citizen.AgeGroup.Child: return 0; @@ -206,7 +206,8 @@ private static int GetTaxiProbability(ushort instanceID, ref CitizenInstance cit } } - private static int GetBikeProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { + private int GetBikeProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { + Log.Error("CustomResidentAI.GetBikeProbability called!"); CitizenManager citizenManager = Singleton.instance; uint citizenId = citizenData.m_citizen; ushort homeId = citizenManager.m_citizens.m_buffer[(int)((UIntPtr)citizenId)].m_homeBuilding; @@ -236,18 +237,19 @@ private static int GetBikeProbability(ushort instanceID, ref CitizenInstance cit } } - private static int GetCarProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { + private int GetCarProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { + Log.Error("CustomResidentAI.GetCarProbability called!"); switch (ageGroup) { case Citizen.AgeGroup.Child: - return ResidentAI.CAR_PROBABILITY_CHILD; + return 0; case Citizen.AgeGroup.Teen: - return ResidentAI.CAR_PROBABILITY_TEEN; + return 5; case Citizen.AgeGroup.Young: - return ResidentAI.CAR_PROBABILITY_YOUNG; + return 15; case Citizen.AgeGroup.Adult: - return ResidentAI.CAR_PROBABILITY_ADULT; + return 20; case Citizen.AgeGroup.Senior: - return ResidentAI.CAR_PROBABILITY_SENIOR; + return 10; default: return 0; } diff --git a/TLM/TLM/Custom/AI/CustomTouristAI.cs b/TLM/TLM/Custom/AI/CustomTouristAI.cs index 33b7fbc7..3d20bcd1 100644 --- a/TLM/TLM/Custom/AI/CustomTouristAI.cs +++ b/TLM/TLM/Custom/AI/CustomTouristAI.cs @@ -8,6 +8,7 @@ using TrafficManager.Manager; using TrafficManager.State; using TrafficManager.Traffic; +using static TrafficManager.Traffic.ExtCitizenInstance; namespace TrafficManager.Custom.AI { public class CustomTouristAI : TouristAI { @@ -77,23 +78,25 @@ public string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance da } public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) { - return CustomTouristAI.GetVehicleInfo(instanceID, ref citizenData, forceProbability); - } - - public static new VehicleInfo GetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) { if (citizenData.m_citizen == 0u) { return null; } // NON-STOCK CODE START + bool forceTaxi = false; if (Options.prohibitPocketCars) { - ushort parkedVehicleId = Singleton.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle; - if (parkedVehicleId != 0) { + ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID); + if (extInstance.PathMode == ExtPathMode.TaxiToTarget) { + forceTaxi = true; + } else { + ushort parkedVehicleId = Singleton.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle; + if (parkedVehicleId != 0) { #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[2]) - Log._Debug($"CustomTouristAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info."); + if (GlobalConfig.Instance.DebugSwitches[2]) + Log._Debug($"CustomTouristAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info."); #endif - return Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info; + return Singleton.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info; + } } } // NON-STOCK CODE END @@ -101,14 +104,21 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c int carProb; int bikeProb; int taxiProb; + // NON-STOCK CODE START + if (forceTaxi) { + carProb = 0; + bikeProb = 0; + taxiProb = 100; + } else + // NON-STOCK CODE END if (forceProbability || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) { carProb = 100; bikeProb = 0; taxiProb = 0; } else { - carProb = CustomTouristAI.GetCarProbability(); - bikeProb = CustomTouristAI.GetBikeProbability(); - taxiProb = CustomTouristAI.GetTaxiProbability(); + carProb = GetCarProbability(); + bikeProb = GetBikeProbability(); + taxiProb = GetTaxiProbability(); } Randomizer randomizer = new Randomizer(citizenData.m_citizen); bool useCar = randomizer.Int32(100u) < carProb; @@ -131,15 +141,18 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c return null; } - private static int GetTaxiProbability() { + private int GetTaxiProbability() { + Log.Error("CustomTouristAI.GetTaxiProbability called!"); return 20; } - private static int GetBikeProbability() { + private int GetBikeProbability() { + Log.Error("CustomTouristAI.GetBikeProbability called!"); return 20; } - private static int GetCarProbability() { + private int GetCarProbability() { + Log.Error("CustomTouristAI.GetCarProbability called!"); return 20; } } diff --git a/TLM/TLM/Custom/Manager/CustomVehicleManager.cs b/TLM/TLM/Custom/Manager/CustomVehicleManager.cs index 61a51b6e..16158eba 100644 --- a/TLM/TLM/Custom/Manager/CustomVehicleManager.cs +++ b/TLM/TLM/Custom/Manager/CustomVehicleManager.cs @@ -21,6 +21,17 @@ private void ReleaseVehicleImplementation(ushort vehicleId, ref Vehicle vehicleD } public bool CustomCreateVehicle(out ushort vehicleId, ref Randomizer r, VehicleInfo info, Vector3 position, TransferManager.TransferReason type, bool transferToSource, bool transferToTarget) { + // NON-STOCK CODE START + if (this.m_vehicleCount > VehicleManager.MAX_VEHICLE_COUNT - 5) { + // prioritize service vehicles and public transport when hitting the vehicle limit + ItemClass.Service service = info.GetService(); + if (service == ItemClass.Service.Residential || service == ItemClass.Service.Industrial || service == ItemClass.Service.Commercial || service == ItemClass.Service.Office) { + vehicleId = 0; + return false; + } + } + // NON-STOCK CODE END + ushort vehId; if (this.m_vehicles.CreateItem(out vehId, ref r)) { vehicleId = vehId; diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs index d382bf01..459942a1 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs @@ -906,6 +906,7 @@ private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSe } else { // pocket car spawning if (Options.prohibitPocketCars && + _extVehicleType == ExtVehicleType.PassengerCar && (_extPathType == ExtCitizenInstance.ExtPathType.WalkingOnly || (_extPathType == ExtCitizenInstance.ExtPathType.DrivingOnly && item.m_position.m_segment != _startSegmentA && item.m_position.m_segment != _startSegmentB))) { allowPedSwitch = false; } else { @@ -1453,7 +1454,10 @@ private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSe // min/max compatible outer similar lane indices short minNextOuterSimilarIndex = -1; short maxNextOuterSimilarIndex = -1; - if (nextIsRealJunction) { + if (uturn) { + // force u-turns to happen on the most inner lane + minNextOuterSimilarIndex = maxNextOuterSimilarIndex = (short)((short)nextCompatibleLaneCount - 1); + } else if (nextIsRealJunction) { // at junctions: try to match distinct lanes /*if (prevOuterSimilarLaneIndex >= nextCompatibleLaneCount-1) { @@ -1463,7 +1467,10 @@ private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSe if (debug) logBuf.Add($"City rules: Splitting inner lane. minNextOuterSimilarLaneIndex={minNextOuterSimilarIndex} maxNextOuterSimilarLaneIndex={maxNextOuterSimilarIndex}"); #endif - } else */if (nextCompatibleLaneCount > prevSimilarLaneCount && prevOuterSimilarLaneIndex == prevSimilarLaneCount-1) { + } else */ + + + if (nextCompatibleLaneCount > prevSimilarLaneCount && prevOuterSimilarLaneIndex == prevSimilarLaneCount-1) { // merge inner lanes minNextOuterSimilarIndex = prevOuterSimilarLaneIndex; maxNextOuterSimilarIndex = (short)((short)nextCompatibleLaneCount - 1); @@ -1793,11 +1800,12 @@ private static short FindValue(ref byte[] values, int value, short length) { } /// - /// xxx Finds the value in `values` having the (n+1)th lowest index, or, if (n+1) > number of valid elements in `values` finds the value in `values` with the highest index. + /// Finds the element in with the highest associated index i satisfying i <= . + /// If no such element is found, the element with the highest index is returned. /// /// array to be queried - /// a bitmask holding all valid indices of `values` - /// query + /// a bitmask holding all valid indices of + /// query index /// private static short FindCompatibleLane(ref byte[] values, ushort validMask, short n) { short nextLaneI = -1; @@ -2335,7 +2343,7 @@ private bool ProcessItemCosts(bool allowAdvancedAI, bool ignoreLaneArrows, bool if (debug) logBuf.Add($"ProcessItemCosts: applying u-turn cost factor on deactivated advaned AI"); #endif - prevCost *= _conf.UturnLaneDistance; + prevCost *= (float)_conf.UturnLaneDistance; } } diff --git a/TLM/TLM/LoadingExtension.cs b/TLM/TLM/LoadingExtension.cs index 28c16d8a..1f5ea458 100644 --- a/TLM/TLM/LoadingExtension.cs +++ b/TLM/TLM/LoadingExtension.cs @@ -18,7 +18,7 @@ using TrafficManager.Manager; namespace TrafficManager { - public class LoadingExtension : LoadingExtensionBase { + public class LoadingExtension : LoadingExtensionBase { public class Detour { public MethodInfo OriginalMethod; public MethodInfo CustomMethod; @@ -31,7 +31,7 @@ public Detour(MethodInfo originalMethod, MethodInfo customMethod) { } } - public static LoadingExtension Instance; + //public static LoadingExtension Instance; public static bool IsPathManagerReplaced { get; private set; @@ -45,21 +45,21 @@ public static bool IsRushHourLoaded { get; private set; } = false; - public CustomPathManager CustomPathManager { get; set; } - public static bool DetourInited { get; set; } + public static CustomPathManager CustomPathManager { get; set; } + public static bool DetourInited { get; set; } public static List Detours { get; set; } - public TrafficManagerMode ToolMode { get; set; } - public TrafficManagerTool TrafficManagerTool { get; set; } + public static TrafficManagerMode ToolMode { get; set; } + public static TrafficManagerTool TrafficManagerTool { get; set; } #if !TAM - public UIBase BaseUI { get; private set; } + public static UIBase BaseUI { get; private set; } #endif - public UITransportDemand TransportDemandUI { get; private set; } + public static UITransportDemand TransportDemandUI { get; private set; } - private static bool gameLoaded = false; + private static bool gameLoaded = false; - public LoadingExtension() { - } + public LoadingExtension() { + } public void revertDetours() { if (DetourInited) { @@ -220,6 +220,117 @@ public void initDetours() { detourFailed = true; }*/ + Log.Info("Reverse-Redirection ResidentAI::GetTaxiProbability calls"); + try { + Detours.Add(new Detour(typeof(CustomResidentAI).GetMethod("GetTaxiProbability", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + new[] + { + typeof (ushort), + typeof (CitizenInstance).MakeByRefType(), + typeof (Citizen.AgeGroup) + }, + null), + typeof(ResidentAI).GetMethod("GetTaxiProbability", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + new[] + { + typeof (ushort), + typeof (CitizenInstance).MakeByRefType(), + typeof (Citizen.AgeGroup) + }, + null))); + } catch (Exception) { + Log.Error("Could not reverse-redirect ResidentAI::GetTaxiProbability"); + detourFailed = true; + } + + Log.Info("Reverse-Redirection ResidentAI::GetBikeProbability calls"); + try { + Detours.Add(new Detour(typeof(CustomResidentAI).GetMethod("GetBikeProbability", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + new[] + { + typeof (ushort), + typeof (CitizenInstance).MakeByRefType(), + typeof (Citizen.AgeGroup) + }, + null), + typeof(ResidentAI).GetMethod("GetBikeProbability", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + new[] + { + typeof (ushort), + typeof (CitizenInstance).MakeByRefType(), + typeof (Citizen.AgeGroup) + }, + null))); + } catch (Exception) { + Log.Error("Could not reverse-redirect ResidentAI::GetBikeProbability"); + detourFailed = true; + } + + Log.Info("Reverse-Redirection ResidentAI::GetCarProbability calls"); + try { + Detours.Add(new Detour(typeof(CustomResidentAI).GetMethod("GetCarProbability", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + new[] + { + typeof (ushort), + typeof (CitizenInstance).MakeByRefType(), + typeof (Citizen.AgeGroup) + }, + null), + typeof(ResidentAI).GetMethod("GetCarProbability", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + new[] + { + typeof (ushort), + typeof (CitizenInstance).MakeByRefType(), + typeof (Citizen.AgeGroup) + }, + null))); + } catch (Exception) { + Log.Error("Could not reverse-redirect ResidentAI::GetCarProbability"); + detourFailed = true; + } + + Log.Info("Reverse-Redirection TouristAI::GetTaxiProbability calls"); + try { + Detours.Add(new Detour( + typeof(CustomTouristAI).GetMethod("GetTaxiProbability", BindingFlags.NonPublic | BindingFlags.Instance), + typeof(TouristAI).GetMethod("GetTaxiProbability", BindingFlags.NonPublic | BindingFlags.Instance))); + } catch (Exception) { + Log.Error("Could not reverse-redirect TouristAI::GetTaxiProbability"); + detourFailed = true; + } + + Log.Info("Reverse-Redirection TouristAI::GetBikeProbability calls"); + try { + Detours.Add(new Detour( + typeof(CustomTouristAI).GetMethod("GetBikeProbability", BindingFlags.NonPublic | BindingFlags.Instance), + typeof(TouristAI).GetMethod("GetBikeProbability", BindingFlags.NonPublic | BindingFlags.Instance))); + } catch (Exception) { + Log.Error("Could not reverse-redirect TouristAI::GetBikeProbability"); + detourFailed = true; + } + + Log.Info("Reverse-Redirection TouristAI::GetCarProbability calls"); + try { + Detours.Add(new Detour( + typeof(CustomTouristAI).GetMethod("GetCarProbability", BindingFlags.NonPublic | BindingFlags.Instance), + typeof(TouristAI).GetMethod("GetCarProbability", BindingFlags.NonPublic | BindingFlags.Instance))); + } catch (Exception) { + Log.Error("Could not reverse-redirect TouristAI::GetCarProbability"); + detourFailed = true; + } + Log.Info("Reverse-Redirection HumanAI::ArriveAtDestination calls"); try { Detours.Add(new Detour(typeof(CustomHumanAI).GetMethod("ArriveAtDestination", @@ -345,7 +456,7 @@ public void initDetours() { }, null))); } catch (Exception) { - Log.Error("Could not reverse-redirect HumanAI::ArriveAtDestination"); + Log.Error("Could not reverse-redirect HumanAI::Spawn"); detourFailed = true; } @@ -785,7 +896,7 @@ public void initDetours() { Log.Error("Could not redirect VehicleManager::CreateVehicle calls"); detourFailed = true; } - + Log.Info("Redirecting TramBaseAI Calculate Segment Calls (2)"); try { Detours.Add(new Detour(typeof(TramBaseAI).GetMethod("CalculateSegmentPosition", @@ -951,7 +1062,7 @@ public void initDetours() { detourFailed = true; } - Log.Info("Redirecting HumanAI::ArriveAtTarget Calls"); + /*Log.Info("Redirecting HumanAI::ArriveAtTarget Calls"); try { Detours.Add(new Detour(typeof(HumanAI).GetMethod("ArriveAtTarget", BindingFlags.NonPublic | BindingFlags.Instance, @@ -965,7 +1076,7 @@ public void initDetours() { } catch (Exception) { Log.Error("Could not redirect HumanAI::ArriveAtTarget."); detourFailed = true; - } + }*/ Log.Info("Redirection ResidentAI::GetVehicleInfo calls"); try { @@ -1205,7 +1316,7 @@ public void initDetours() { detourFailed = true; } - + Log.Info("Redirection PathFind::CalculatePath calls for non-Traffic++"); try { Detours.Add(new Detour(typeof(PathFind).GetMethod("CalculatePath", @@ -1687,7 +1798,7 @@ public void initDetours() { Log.Error("Could not redirect RoadBaseAI::UpdateLanes"); detourFailed = true; } - + Log.Info("Redirection TrainAI::CheckNextLane calls"); try { Detours.Add(new Detour(typeof(TrainAI).GetMethod("CheckNextLane", @@ -1781,41 +1892,38 @@ internal static bool IsGameLoaded() { } public override void OnCreated(ILoading loading) { - //SelfDestruct.DestructOldInstances(this); + //SelfDestruct.DestructOldInstances(this); - base.OnCreated(loading); + base.OnCreated(loading); - ToolMode = TrafficManagerMode.None; + ToolMode = TrafficManagerMode.None; Detours = new List(); - DetourInited = false; - CustomPathManager = new CustomPathManager(); - } + DetourInited = false; + CustomPathManager = new CustomPathManager(); + } - public override void OnReleased() { - base.OnReleased(); + public override void OnReleased() { + base.OnReleased(); - if (ToolMode != TrafficManagerMode.None) { - ToolMode = TrafficManagerMode.None; - DestroyTool(); - } - } + if (ToolMode != TrafficManagerMode.None) { + ToolMode = TrafficManagerMode.None; + DestroyTool(); + } + } public override void OnLevelUnloading() { Log.Info("OnLevelUnloading"); base.OnLevelUnloading(); - Instance = this; if (IsPathManagerReplaced) { Singleton.instance.WaitForAllPaths(); } - revertDetours(); - gameLoaded = false; - Object.Destroy(BaseUI); - BaseUI = null; - Object.Destroy(TransportDemandUI); - TransportDemandUI = null; + /*Object.Destroy(BaseUI); + BaseUI = null; + Object.Destroy(TransportDemandUI); + TransportDemandUI = null;*/ - try { + try { TrafficPriorityManager.Instance.OnLevelUnloading(); CustomCarAI.OnLevelUnloading(); CustomRoadAI.OnLevelUnloading(); @@ -1834,18 +1942,20 @@ public override void OnLevelUnloading() { Log.Error("Exception unloading mod. " + e.Message); // ignored - prevents collision with other mods } + + revertDetours(); + gameLoaded = false; } public override void OnLevelLoaded(LoadMode mode) { SimulationManager.UpdateMode updateMode = SimulationManager.instance.m_metaData.m_updateMode; Log.Info($"OnLevelLoaded({mode}) called. updateMode={updateMode}"); - base.OnLevelLoaded(mode); + base.OnLevelLoaded(mode); - Log._Debug("OnLevelLoaded Returned from base, calling custom code."); - Instance = this; + Log._Debug("OnLevelLoaded Returned from base, calling custom code."); gameLoaded = false; - switch (updateMode) { + switch (updateMode) { case SimulationManager.UpdateMode.NewGameFromMap: case SimulationManager.UpdateMode.NewGameFromScenario: case SimulationManager.UpdateMode.LoadGame: @@ -1875,12 +1985,12 @@ public override void OnLevelLoaded(LoadMode mode) { default: Log.Info($"OnLevelLoaded: Unsupported game mode {mode}"); return; - } + } IsRainfallLoaded = CheckRainfallIsLoaded(); IsRushHourLoaded = CheckRushHourIsLoaded(); - if (! IsPathManagerReplaced) { + if (!IsPathManagerReplaced) { try { Log.Info("Pathfinder Compatible. Setting up CustomPathManager and SimManager."); var pathManagerInstance = typeof(Singleton).GetField("sInstance", BindingFlags.Static | BindingFlags.NonPublic); @@ -1924,16 +2034,18 @@ public override void OnLevelLoaded(LoadMode mode) { } Log.Info("Adding Controls to UI."); - BaseUI = ToolsModifierControl.toolController.gameObject.AddComponent(); - - // Init transport demand UI - var uiView = UIView.GetAView(); - TransportDemandUI = (UITransportDemand)uiView.AddUIComponent(typeof(UITransportDemand)); + if (BaseUI == null) { + Log._Debug("Adding UIBase instance."); + BaseUI = ToolsModifierControl.toolController.gameObject.AddComponent(); + } - initDetours(); + // Init transport demand UI + if (TransportDemandUI == null) { + var uiView = UIView.GetAView(); + TransportDemandUI = (UITransportDemand)uiView.AddUIComponent(typeof(UITransportDemand)); + } - Log.Info("Calling OnLevelLoaded."); - CustomPassengerCarAI.OnLevelLoaded(); + initDetours(); Log.Info("OnLevelLoaded complete."); } @@ -1976,34 +2088,34 @@ private bool Check3rdPartyModLoaded(string namespaceStr) { return thirPartyModLoaded; } - public void SetToolMode(TrafficManagerMode mode) { - if (mode == ToolMode) return; + public static void SetToolMode(TrafficManagerMode mode) { + if (mode == ToolMode) return; - ToolMode = mode; + ToolMode = mode; - if (mode != TrafficManagerMode.None) { - EnableTool(); - } else { + if (mode != TrafficManagerMode.None) { + EnableTool(); + } else { DisableTool(); - } - } + } + } - public void EnableTool() { - if (TrafficManagerTool == null) { - TrafficManagerTool = ToolsModifierControl.toolController.gameObject.GetComponent() ?? - ToolsModifierControl.toolController.gameObject.AddComponent(); - } + public static void EnableTool() { + if (TrafficManagerTool == null) { + TrafficManagerTool = ToolsModifierControl.toolController.gameObject.GetComponent() ?? + ToolsModifierControl.toolController.gameObject.AddComponent(); + } - ToolsModifierControl.toolController.CurrentTool = TrafficManagerTool; - ToolsModifierControl.SetTool(); - } + ToolsModifierControl.toolController.CurrentTool = TrafficManagerTool; + ToolsModifierControl.SetTool(); + } - public void DisableTool() { + public static void DisableTool() { ToolsModifierControl.toolController.CurrentTool = ToolsModifierControl.GetTool(); ToolsModifierControl.SetTool(); } - private void DestroyTool() { + private static void DestroyTool() { if (ToolsModifierControl.toolController != null) { ToolsModifierControl.toolController.CurrentTool = ToolsModifierControl.GetTool(); ToolsModifierControl.SetTool(); @@ -2014,6 +2126,6 @@ private void DestroyTool() { } } else Log.Warning("LoadingExtensions.DestroyTool: ToolsModifierControl.toolController is null!"); - } + } } } diff --git a/TLM/TLM/State/GlobalConfig.cs b/TLM/TLM/State/GlobalConfig.cs index 978a3782..8ff7a29b 100644 --- a/TLM/TLM/State/GlobalConfig.cs +++ b/TLM/TLM/State/GlobalConfig.cs @@ -14,7 +14,7 @@ namespace TrafficManager.State { public class GlobalConfig { public const string FILENAME = "TMPE_GlobalConfig.xml"; public const string BACKUP_FILENAME = FILENAME + ".bak"; - private static int LATEST_VERSION = 2; + private static int LATEST_VERSION = 3; #if DEBUG private static uint lastModificationCheckFrame = 0; #endif @@ -42,16 +42,16 @@ internal static void OnLevelUnloading() { } //public static GlobalConfig Instance() { -//#if DEBUG -// uint curDebugFrame = Singleton.instance.m_currentFrameIndex >> 10; -// if (lastModificationCheckFrame == 0) { -// lastModificationCheckFrame = curDebugFrame; -// } else if (lastModificationCheckFrame < curDebugFrame) { -// lastModificationCheckFrame = curDebugFrame; -// ReloadIfNewer(); -// } -//#endif - //return Instance; + //#if DEBUG + // uint curDebugFrame = Singleton.instance.m_currentFrameIndex >> 10; + // if (lastModificationCheckFrame == 0) { + // lastModificationCheckFrame = curDebugFrame; + // } else if (lastModificationCheckFrame < curDebugFrame) { + // lastModificationCheckFrame = curDebugFrame; + // ReloadIfNewer(); + // } + //#endif + //return Instance; //} private static DateTime ModifiedTime = DateTime.MinValue; @@ -82,7 +82,7 @@ internal static void OnLevelUnloading() { /// /// base lane changing cost factor on city streets /// - public float CityRoadLaneChangingBaseCost = 0.1f; + public float CityRoadLaneChangingBaseCost = 0.15f; /// /// lane changing cost base before junctions diff --git a/TLM/TLM/State/Options.cs b/TLM/TLM/State/Options.cs index e4ec7794..25fd6c64 100644 --- a/TLM/TLM/State/Options.cs +++ b/TLM/TLM/State/Options.cs @@ -122,8 +122,8 @@ public static bool MenuRebuildRequired { get { return menuRebuildRequired; } private set { menuRebuildRequired = value; - if (LoadingExtension.Instance != null && LoadingExtension.Instance.BaseUI != null) - LoadingExtension.Instance.BaseUI.Close(); + if (LoadingExtension.BaseUI != null) + LoadingExtension.BaseUI.Close(); } } diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index 8182e122..77f00467 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -18,7 +18,7 @@ full false bin\Debug\ - DEBUG + DEBUG;QUEUEDSTATS prompt 0 true @@ -33,6 +33,16 @@ 4 true + + bin\QueuedStats\ + QUEUEDSTATS + true + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + D:\Games\Steam\steamapps\common\Cities_Skylines\Cities_Data\Managed\Assembly-CSharp.dll diff --git a/TLM/TLM/ThreadingExtension.cs b/TLM/TLM/ThreadingExtension.cs index 5051b101..73503c5c 100644 --- a/TLM/TLM/ThreadingExtension.cs +++ b/TLM/TLM/ThreadingExtension.cs @@ -42,16 +42,16 @@ public override void OnAfterSimulationFrame() { public override void OnUpdate(float realTimeDelta, float simulationTimeDelta) { base.OnUpdate(realTimeDelta, simulationTimeDelta); #if !TAM - if (LoadingExtension.Instance == null || ToolsModifierControl.toolController == null || ToolsModifierControl.toolController == null || LoadingExtension.Instance.BaseUI == null) { + if (ToolsModifierControl.toolController == null || ToolsModifierControl.toolController == null || LoadingExtension.BaseUI == null) { return; } - if (ToolsModifierControl.toolController.CurrentTool != LoadingExtension.Instance.TrafficManagerTool && LoadingExtension.Instance.BaseUI.IsVisible()) { - LoadingExtension.Instance.BaseUI.Close(); + if (ToolsModifierControl.toolController.CurrentTool != LoadingExtension.TrafficManagerTool && LoadingExtension.BaseUI.IsVisible()) { + LoadingExtension.BaseUI.Close(); } if (Input.GetKeyDown(KeyCode.Escape)) { - LoadingExtension.Instance.BaseUI.Close(); + LoadingExtension.BaseUI.Close(); } #endif } diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index d9494023..f0649a1a 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -5,7 +5,7 @@ namespace TrafficManager { public class TrafficManagerMod : IUserMod { - public static readonly string Version = "1.8.3"; + public static readonly string Version = "1.8.4"; public static readonly uint GameVersion = 159507472u; public static readonly uint GameVersionA = 1u; diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs index 65c5d74a..40d23165 100644 --- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs +++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs @@ -22,10 +22,18 @@ enum MarkerSelectionMode { SelectTarget } + enum StayInLaneMode { + None, + Both, + Forward, + Backward + } + private Dictionary nodeGeometryUnsubscribers; private NodeLaneMarker selectedMarker = null; private NodeLaneMarker hoveredMarker = null; private Dictionary> currentNodeMarkers; + private StayInLaneMode stayInLaneMode = StayInLaneMode.None; //private bool initDone = false; class NodeLaneMarker { @@ -35,6 +43,8 @@ class NodeLaneMarker { internal Vector3 position; internal bool isSource; internal uint laneId; + internal int innerSimilarLaneIndex; + internal NetInfo.Direction finalDirection; internal float radius = 1f; internal Color color; internal List connectedMarkers = new List(); @@ -138,7 +148,12 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) { } } - if (Input.GetKey(KeyCode.Delete)) { + bool deleteAll = Input.GetKeyDown(KeyCode.Delete); + bool stayInLane = Input.GetKeyDown(KeyCode.S) && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) && Singleton.instance.m_nodes.m_buffer[SelectedNodeId].CountSegments() == 2; + if (stayInLane) + deleteAll = true; + + if (deleteAll) { // remove all connections at selected node List nodeMarkers = GetNodeMarkers(SelectedNodeId); @@ -152,6 +167,52 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) { } RefreshCurrentNodeMarkers(); } + + if (stayInLane) { + // "stay in lane" + switch (stayInLaneMode) { + case StayInLaneMode.None: + stayInLaneMode = StayInLaneMode.Both; + break; + case StayInLaneMode.Both: + stayInLaneMode = StayInLaneMode.Forward; + break; + case StayInLaneMode.Forward: + stayInLaneMode = StayInLaneMode.Backward; + break; + case StayInLaneMode.Backward: + stayInLaneMode = StayInLaneMode.None; + break; + } + + if (stayInLaneMode != StayInLaneMode.None) { + List nodeMarkers = GetNodeMarkers(SelectedNodeId); + if (nodeMarkers != null) { + selectedMarker = null; + foreach (NodeLaneMarker sourceLaneMarker in nodeMarkers) { + if (!sourceLaneMarker.isSource) + continue; + + if (stayInLaneMode == StayInLaneMode.Forward || stayInLaneMode == StayInLaneMode.Backward) { + if (sourceLaneMarker.finalDirection == NetInfo.Direction.Backward ^ stayInLaneMode == StayInLaneMode.Backward) { + continue; + } + } + + foreach (NodeLaneMarker targetLaneMarker in nodeMarkers) { + if (targetLaneMarker.isSource || targetLaneMarker.segmentId == sourceLaneMarker.segmentId) + continue; + + if (targetLaneMarker.innerSimilarLaneIndex == sourceLaneMarker.innerSimilarLaneIndex) { + Log._Debug($"Adding lane connection {sourceLaneMarker.laneId} -> {targetLaneMarker.laneId}"); + LaneConnectionManager.Instance.AddLaneConnection(sourceLaneMarker.laneId, targetLaneMarker.laneId, sourceLaneMarker.startNode); + } + } + } + } + RefreshCurrentNodeMarkers(); + } + } } if (GetMarkerSelectionMode() == MarkerSelectionMode.None && HoveredNodeId != 0) { @@ -178,6 +239,7 @@ public override void OnPrimaryClickOverlay() { #endif SelectedNodeId = 0; selectedMarker = null; + stayInLaneMode = StayInLaneMode.None; return; } @@ -191,6 +253,7 @@ public override void OnPrimaryClickOverlay() { if (markers != null) { SelectedNodeId = HoveredNodeId; selectedMarker = null; + stayInLaneMode = StayInLaneMode.None; currentNodeMarkers[SelectedNodeId] = markers; } @@ -204,11 +267,14 @@ public override void OnPrimaryClickOverlay() { // click on free spot. deselect node SelectedNodeId = 0; selectedMarker = null; + stayInLaneMode = StayInLaneMode.None; return; } } if (hoveredMarker != null) { + stayInLaneMode = StayInLaneMode.None; + #if DEBUGCONN Log._Debug($"TppLaneConnectorTool: hoveredMarker != null. selMode={GetMarkerSelectionMode()}"); #endif @@ -254,6 +320,7 @@ public override void OnSecondaryClickOverlay() { #if DEBUGCONN Log._Debug($"TppLaneConnectorTool: OnSecondaryClickOverlay: nothing to do"); #endif + stayInLaneMode = StayInLaneMode.None; break; case MarkerSelectionMode.SelectSource: // deselect node @@ -279,6 +346,7 @@ public override void OnActivate() { SelectedNodeId = 0; selectedMarker = null; hoveredMarker = null; + stayInLaneMode = StayInLaneMode.None; RefreshCurrentNodeMarkers(); } @@ -338,12 +406,13 @@ private List GetNodeMarkers(ushort nodeId) { NetInfo.Lane[] lanes = NetManager.instance.m_segments.m_buffer[segmentId].Info.m_lanes; uint laneId = NetManager.instance.m_segments.m_buffer[segmentId].m_lanes; for (byte laneIndex = 0; laneIndex < lanes.Length && laneId != 0; laneIndex++) { - if ((lanes[laneIndex].m_laneType & (NetInfo.LaneType.TransportVehicle | NetInfo.LaneType.Vehicle)) != NetInfo.LaneType.None && - (lanes[laneIndex].m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) != VehicleInfo.VehicleType.None) { + NetInfo.Lane laneInfo = lanes[laneIndex]; + if ((laneInfo.m_laneType & (NetInfo.LaneType.TransportVehicle | NetInfo.LaneType.Vehicle)) != NetInfo.LaneType.None && + (laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) != VehicleInfo.VehicleType.None) { Vector3? pos = null; bool isSource = false; - if (connManager.GetLaneEndPoint(segmentId, !isEndNode, laneIndex, laneId, lanes[laneIndex], out isSource, out pos)) { + if (connManager.GetLaneEndPoint(segmentId, !isEndNode, laneIndex, laneId, laneInfo, out isSource, out pos)) { nodeMarkers.Add(new NodeLaneMarker() { segmentId = segmentId, laneId = laneId, @@ -352,8 +421,10 @@ private List GetNodeMarkers(ushort nodeId) { position = (Vector3)pos + offset, color = colors[nodeMarkers.Count], isSource = isSource, - laneType = lanes[laneIndex].m_laneType, - vehicleType = lanes[laneIndex].m_vehicleType + laneType = laneInfo.m_laneType, + vehicleType = laneInfo.m_vehicleType, + innerSimilarLaneIndex = ((byte)(laneInfo.m_finalDirection & NetInfo.Direction.Forward) != 0) ? laneInfo.m_similarLaneIndex : laneInfo.m_similarLaneCount - laneInfo.m_similarLaneIndex - 1, + finalDirection = laneInfo.m_finalDirection }); } } @@ -400,7 +471,7 @@ private bool CheckSegmentsTurningAngle(ushort sourceSegmentId, ref NetSegment so NetInfo sourceSegmentInfo = netManager.m_segments.m_buffer[sourceSegmentId].Info; NetInfo targetSegmentInfo = netManager.m_segments.m_buffer[targetSegmentId].Info; - + float turningAngle = 0.01f - Mathf.Min(sourceSegmentInfo.m_maxTurnAngleCos, targetSegmentInfo.m_maxTurnAngleCos); if (turningAngle < 1f) { Vector3 sourceDirection; diff --git a/TLM/TLM/UI/UIBase.cs b/TLM/TLM/UI/UIBase.cs index ad3fd0cc..5b87c95e 100644 --- a/TLM/TLM/UI/UIBase.cs +++ b/TLM/TLM/UI/UIBase.cs @@ -8,7 +8,7 @@ namespace TrafficManager.UI { #if !TAM public class UIBase : UICustomControl { - + private UIMainMenuButton button; private bool _uiShown = false; @@ -23,6 +23,10 @@ public UIBase() { button = (UIMainMenuButton)uiView.AddUIComponent(typeof(UIMainMenuButton)); } + ~UIBase() { + UnityEngine.Object.Destroy(button); + } + public bool IsVisible() { return _uiShown; } @@ -35,41 +39,33 @@ public void ToggleMainMenu() { } public void Show() { - if (LoadingExtension.Instance != null) { - try { - ToolsModifierControl.mainToolbar.CloseEverything(); - } catch (Exception e) { - Log.Error("Error on Show(): " + e.ToString()); - } - GetMenu().Show(); - LoadingExtension.Instance.SetToolMode(TrafficManagerMode.Activated); - _uiShown = true; - button.UpdateSprites(); - } else { - Log._Debug("TM UI Show: LoadingExtension.Instance is null!"); + try { + ToolsModifierControl.mainToolbar.CloseEverything(); + } catch (Exception e) { + Log.Error("Error on Show(): " + e.ToString()); } + GetMenu().Show(); + LoadingExtension.SetToolMode(TrafficManagerMode.Activated); + _uiShown = true; + button.UpdateSprites(); } public void Close() { - if (LoadingExtension.Instance != null) { - var uiView = UIView.GetAView(); - var trafficManager = uiView.FindUIComponent("UITrafficManager"); - if (trafficManager != null) { - Log._Debug("Hiding TM UI"); - Destroy(trafficManager); - //trafficManager.Hide(); - } else { - Log._Debug("Hiding TM UI: null!"); - } - - UITrafficManager.deactivateButtons(); - TrafficManagerTool.SetToolMode(ToolMode.None); - LoadingExtension.Instance.SetToolMode(TrafficManagerMode.None); - _uiShown = false; - button.UpdateSprites(); + var uiView = UIView.GetAView(); + var trafficManager = uiView.FindUIComponent("UITrafficManager"); + if (trafficManager != null) { + Log._Debug("Hiding TM UI"); + Destroy(trafficManager); + //trafficManager.Hide(); } else { - Log._Debug("TM UI Close: LoadingExtension.Instance is null!"); + Log._Debug("Hiding TM UI: null!"); } + + UITrafficManager.deactivateButtons(); + TrafficManagerTool.SetToolMode(ToolMode.None); + LoadingExtension.SetToolMode(TrafficManagerMode.None); + _uiShown = false; + button.UpdateSprites(); } internal static UITrafficManager GetMenu() { diff --git a/TLM/TLM/UI/UIMainMenuButton.cs b/TLM/TLM/UI/UIMainMenuButton.cs index 5de1bc08..b8649320 100644 --- a/TLM/TLM/UI/UIMainMenuButton.cs +++ b/TLM/TLM/UI/UIMainMenuButton.cs @@ -50,7 +50,7 @@ public override void Start() { } protected override void OnClick(UIMouseEventParameter p) { - LoadingExtension.Instance.BaseUI.ToggleMainMenu(); + LoadingExtension.BaseUI.ToggleMainMenu(); UpdateSprites(); } @@ -67,7 +67,7 @@ protected override void OnPositionChanged() { } internal void UpdateSprites() { - if (! LoadingExtension.Instance.BaseUI.IsVisible()) { + if (! LoadingExtension.BaseUI.IsVisible()) { normalBgSprite = disabledBgSprite = focusedBgSprite = MAIN_MENU_BUTTON_BG_BASE; hoveredBgSprite = MAIN_MENU_BUTTON_BG_HOVERED; pressedBgSprite = MAIN_MENU_BUTTON_BG_ACTIVE; diff --git a/TLM/TLM/UI/UITrafficManager.cs b/TLM/TLM/UI/UITrafficManager.cs index 43db4125..96486426 100644 --- a/TLM/TLM/UI/UITrafficManager.cs +++ b/TLM/TLM/UI/UITrafficManager.cs @@ -18,7 +18,7 @@ namespace TrafficManager.UI { public class UITrafficManager : UIPanel { //private static UIState _uiState = UIState.None; -#if DEBUG +#if QUEUEDSTATS private static bool showPathFindStats = false; #endif @@ -43,19 +43,18 @@ public class UITrafficManager : UIPanel { private static UIButton _printDebugInfoButton = null; private static UIButton _noneToVehicleButton = null; private static UIButton _vehicleToNoneButton = null; - private static UIButton _togglePathFindStatsButton = null; private static UIButton _removeStuckEntitiesButton = null; #endif +#if QUEUEDSTATS + private static UIButton _togglePathFindStatsButton = null; +#endif + public static TrafficManagerTool TrafficLightTool; public static UILabel title; public override void Start() { - if (LoadingExtension.Instance == null) { - Log.Error("UITrafficManager.Start(): LoadingExtension is null."); - return; - } - TrafficLightTool = LoadingExtension.Instance.TrafficManagerTool; + TrafficLightTool = LoadingExtension.TrafficManagerTool; backgroundSprite = "GenericPanel"; color = new Color32(75, 75, 135, 255); @@ -157,9 +156,13 @@ public override void Start() { _vehicleToNoneButton = _createButton("Vehicle -> None", y, clickVehicleToNone); y += 40; height += 40; +#endif +#if QUEUEDSTATS _togglePathFindStatsButton = _createButton("Toggle PathFind stats", y, clickTogglePathFindStats); y += 40; height += 40; +#endif +#if DEBUG _removeStuckEntitiesButton = _createButton("Remove stuck entities", y, clickRemoveStuckEntities); y += 40; height += 40; @@ -263,10 +266,15 @@ private void clickNoneToVehicle(UIComponent component, UIMouseEventParameter eve } } } +#endif +#if QUEUEDSTATS private void clickTogglePathFindStats(UIComponent component, UIMouseEventParameter eventParam) { showPathFindStats = !showPathFindStats; } +#endif + +#if DEBUG private void clickRemoveStuckEntities(UIComponent component, UIMouseEventParameter eventParam) { TrafficManagerTool.SetToolMode(ToolMode.None); @@ -471,7 +479,7 @@ private void clickLaneConnector(UIComponent component, UIMouseEventParameter eve } public override void Update() { -#if DEBUG && QUEUEDSTATS +#if QUEUEDSTATS if (showPathFindStats && title != null) { title.text = CustomPathManager.TotalQueuedPathFinds.ToString(); #if EXTRAPF diff --git a/TLM/TLM/UI/UITransportDemand.cs b/TLM/TLM/UI/UITransportDemand.cs index 2d1783d0..0f9366b4 100644 --- a/TLM/TLM/UI/UITransportDemand.cs +++ b/TLM/TLM/UI/UITransportDemand.cs @@ -18,11 +18,6 @@ public class UITransportDemand : UIPanel { private static UILabel viewModeLabel; public override void Start() { - if (LoadingExtension.Instance == null) { - Log.Error("UITrafficManager.Start(): LoadingExtension is null."); - return; - } - var transportInfoViewPanel = GameObject.Find("(Library) PublicTransportInfoViewPanel").GetComponent(); if (transportInfoViewPanel != null) { Log._Debug($"Public transport info view panel found."); @@ -49,7 +44,6 @@ public override void Start() { switchViewModeButton = _createButton(Translation.GetString("Switch_view"), 3, 3, clickSwitchViewMode); } - private UIButton _createButton(string text, int x, int y, MouseEventHandler eventClick) { var button = AddUIComponent(); button.textScale = 0.8f;