diff --git a/README.md b/README.md index b48cdcb6..8eac7080 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,23 @@ A work-in-progress modification for **Cities: Skylines** to add additional traffic control # Changelog +1.7.5, 08/07/2016: +- Bugfix: AI: Cims were using pocket cars whenever possible +- Bugfix: AI: Path-finding failures led to much less vehicles spawning +- Bugfix: AI: Lane selection at junctions with custom lane connection was not always working properly (e.g. for Network Extensions roads with middle lane) +- Bugfix: While editing a timed traffic light it could happen that the traffic light was deleted + +1.7.4, 07/31/2016: +- AI: Switched from relative to absolute traffic density measurement +- AI: Tuned new parameters +- Bugfix: Activated/Disabled features were not loaded correctly +- Bugfix: AI: At specific junctions the lane changer did not work as intended +- Possible fix for OSX performance issues +- Code improvements +- Added French translations (thanks to @simon.royer007 for translating!) + 1.7.3, 07/29/2016: -- Added the possibility to enable/disable mod features (e.g. for performance reasons) +- Added the ability to enable/disable mod features (e.g. for performance reasons) - Bugfix: Vehicle type determination was incorrect (fixed u-turning trams/trains, stuck vehicles) - Bugfix: Clicking on a train/tram node with the lane connector tool led to an uncorrectable error (thanks to @noaccount for reporting this problem) - Further code improvements diff --git a/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs b/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs index 2cbe953b..c9e0d610 100644 --- a/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs +++ b/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs @@ -4,12 +4,18 @@ using System.Linq; using System.Text; using TrafficManager.Custom.PathFinding; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; using UnityEngine; namespace TrafficManager.Custom.AI { class CustomAmbulanceAI : CarAI { public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) { +#if DEBUG + //Log._Debug($"CustomAmbulanceAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + ExtVehicleType vehicleType = (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service; VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType = vehicleType; @@ -32,7 +38,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto endPosB = default(PathUnit.Position); } uint path; - if (Singleton.instance.CreatePath(vehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { + if (Singleton.instance.CreatePath(vehicleType, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomBusAI.cs b/TLM/TLM/Custom/AI/CustomBusAI.cs index a975f875..ceeb69d2 100644 --- a/TLM/TLM/Custom/AI/CustomBusAI.cs +++ b/TLM/TLM/Custom/AI/CustomBusAI.cs @@ -6,19 +6,23 @@ using System.Linq; using System.Text; using TrafficManager.Custom.PathFinding; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; using UnityEngine; namespace TrafficManager.Custom.AI { class CustomBusAI : CarAI { public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) { +#if DEBUG + //Log._Debug($"CustomBusAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + #if PATHRECALC VehicleState state = VehicleStateManager._GetVehicleState(vehicleID); bool recalcRequested = state.PathRecalculationRequested; state.PathRecalculationRequested = false; #endif - VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType = ExtVehicleType.Bus; - VehicleInfo info = this.m_info; bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0; PathUnit.Position startPosA; @@ -42,7 +46,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto #if PATHRECALC recalcRequested, #endif - ExtVehicleType.Bus, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { + ExtVehicleType.Bus, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomCarAI.cs b/TLM/TLM/Custom/AI/CustomCarAI.cs index cbd1fcbc..0f1bb1d4 100644 --- a/TLM/TLM/Custom/AI/CustomCarAI.cs +++ b/TLM/TLM/Custom/AI/CustomCarAI.cs @@ -6,12 +6,14 @@ using System.Collections.Generic; using ColossalFramework; using ColossalFramework.Math; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; using Random = UnityEngine.Random; using TrafficManager.Custom.PathFinding; using TrafficManager.State; +using TrafficManager.Manager; +using TrafficManager.Traffic; namespace TrafficManager.Custom.AI { public class CustomCarAI : CarAI { // correct would be to inherit from VehicleAI (in order to keep the correct references to `base`) @@ -31,38 +33,31 @@ internal static void OnLevelUnloading() { /// /// public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { -#if USEPATHWAITCOUNTER - VehicleState state = VehicleStateManager._GetVehicleState(vehicleId); -#endif - PathManager pathMan = Singleton.instance; #if DEBUG - if (!Options.disableSomething1) { + /*if (!Options.disableSomething1) { Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}) called. flags: {vehicleData.m_flags} pfFlags: {pathMan.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags}"); - } + }*/ #endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { PathManager pathManager = Singleton.instance; byte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags; - if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { #if USEPATHWAITCOUNTER + if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0) { + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = 0; // NON-STOCK CODE + } #endif + + if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { 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 -#if USEPATHWAITCOUNTER - || ((pathFindFlags & PathUnit.FLAG_CREATED) != 0 && state.PathWaitCounter == ushort.MaxValue) -#endif - ) { // NON-STOCK CODE -#if USEPATHWAITCOUNTER - state.PathWaitCounter = 0; // NON-STOCK CODE -#endif + } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) { // path == 0: non-stock code! vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton.instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; @@ -71,13 +66,11 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect } #if USEPATHWAITCOUNTER else { + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter+1); // NON-STOCK CODE } #endif } else { -#if USEPATHWAITCOUNTER - state.PathWaitCounter = 0; // NON-STOCK CODE -#endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } @@ -134,7 +127,7 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect int maxBlockCounter = (privateServiceIndex == -1) ? 150 : 100; if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0) { Singleton.instance.ReleaseVehicle(vehicleId); - } else if ((int)vehicleData.m_blockCounter == maxBlockCounter && Options.enableDespawning) { + } else if ((int)vehicleData.m_blockCounter >= maxBlockCounter && Options.enableDespawning) { Singleton.instance.ReleaseVehicle(vehicleId); } #if PATHRECALC @@ -146,6 +139,12 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect } public override bool TrySpawn(ushort vehicleID, ref Vehicle vehicleData) { + // NON-STOCK CODE START + if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { + VehicleStateManager.Instance().OnBeforeSpawnVehicle(vehicleID, ref vehicleData); + } + // NON-STOCK CODE END + if ((vehicleData.m_flags & Vehicle.Flags.Spawned) != (Vehicle.Flags)0) { return true; } @@ -156,12 +155,6 @@ public override bool TrySpawn(ushort vehicleID, ref Vehicle vehicleData) { vehicleData.Spawn(vehicleID); vehicleData.m_flags &= ~Vehicle.Flags.WaitingSpace; - // NON-STOCK CODE START - if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { - VehicleStateManager.Instance().OnVehicleSpawned(vehicleID, ref vehicleData); - } - // NON-STOCK CODE END - return true; } @@ -347,6 +340,10 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto bool recalcRequested = state.PathRecalculationRequested; state.PathRecalculationRequested = false; #endif +#if DEBUG + //Log._Debug($"CustomCarAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + VehicleInfo info = this.m_info; bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0; PathUnit.Position startPosA; @@ -374,12 +371,17 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto vehicleType = ExtVehicleType.RoadVehicle; } - bool res = Singleton.instance.CreatePath( + if (Singleton.instance.CreatePath( #if PATHRECALC recalcRequested, #endif - (ExtVehicleType)vehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false); - if (res) { + (ExtVehicleType)vehicleType, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { + +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs b/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs index a7dcbc9a..e4c193ea 100644 --- a/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs +++ b/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs @@ -4,8 +4,10 @@ using ColossalFramework; using UnityEngine; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.Custom.PathFinding; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.Custom.AI { public class CustomCargoTruckAI : CarAI { @@ -44,12 +46,14 @@ private static void RemoveOffers(ushort vehicleId, ref Vehicle data) { } public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) { +#if DEBUG + //Log._Debug($"CustomCargoTruckAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + if ((vehicleData.m_flags & (Vehicle.Flags.TransferToSource | Vehicle.Flags.GoingBack)) != 0) { return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget); } - VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType = ExtVehicleType.CargoTruck; - bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0; PathUnit.Position startPosA; PathUnit.Position startPosB; @@ -98,7 +102,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.CargoVehicle; VehicleInfo.VehicleType vehicleTypes = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship; uint path; - if (instance.CreatePath(ExtVehicleType.CargoVehicle, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, laneTypes, vehicleTypes, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { + if (instance.CreatePath(ExtVehicleType.CargoVehicle, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, laneTypes, vehicleTypes, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomCitizenAI.cs b/TLM/TLM/Custom/AI/CustomCitizenAI.cs index 329907a1..98bee08d 100644 --- a/TLM/TLM/Custom/AI/CustomCitizenAI.cs +++ b/TLM/TLM/Custom/AI/CustomCitizenAI.cs @@ -6,53 +6,101 @@ using System.Text; using TrafficManager.Custom.PathFinding; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; +using TrafficManager.Traffic; namespace TrafficManager.Custom.AI { class CustomCitizenAI : CitizenAI { - /*public static readonly int[] FREE_TRANSPORT_USAGE_PROBABILITY = { 80, 50, 5 }; - public static readonly int[] TRANSPORT_USAGE_PROBABILITY = { 40, 25, 10 };*/ + public static readonly int[] FREE_TRANSPORT_USAGE_PROBABILITY = { 90, 80, 50 }; + public static readonly int[] TRANSPORT_USAGE_PROBABILITY = { 50, 40, 30 }; + public static readonly int[] DAY_TAXI_USAGE_PROBABILITY = { 5, 10, 60 }; + public static readonly int[] NIGHT_TAXI_USAGE_PROBABILITY = { 10, 40, 70 }; // CitizenAI public bool CustomStartPathFind(ushort instanceID, ref CitizenInstance citizenData, Vector3 startPos, Vector3 endPos, VehicleInfo vehicleInfo) { - ExtVehicleType extVehicleType = ExtVehicleType.None; + SimulationManager simManager = Singleton.instance; + Citizen.Wealth wealthLevel = Singleton.instance.m_citizens.m_buffer[citizenData.m_citizen].WealthLevel; + bool useTaxi = false; + bool useCar = false; + bool useBike = false; + bool usePublicTransport = false; - NetInfo.LaneType laneType = NetInfo.LaneType.Pedestrian; - VehicleInfo.VehicleType vehicleType = VehicleInfo.VehicleType.None; bool randomParking = false; if (vehicleInfo != null) { if (vehicleInfo.m_class.m_subService == ItemClass.SubService.PublicTransportTaxi) { if ((citizenData.m_flags & CitizenInstance.Flags.CannotUseTaxi) == CitizenInstance.Flags.None && Singleton.instance.m_districts.m_buffer[0].m_productionData.m_finalTaxiCapacity != 0u) { - SimulationManager instance = Singleton.instance; - if (instance.m_isNightTime || instance.m_randomizer.Int32(2u) == 0) { - laneType |= (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle); - vehicleType |= vehicleInfo.m_vehicleType; - extVehicleType = ExtVehicleType.Taxi; // NON-STOCK CODE + if ((simManager.m_isNightTime && simManager.m_randomizer.Int32(100) < NIGHT_TAXI_USAGE_PROBABILITY[(int)wealthLevel]) || + (!simManager.m_isNightTime && simManager.m_randomizer.Int32(100) < DAY_TAXI_USAGE_PROBABILITY[(int)wealthLevel])) { + useTaxi = true; // NON-STOCK CODE } } } else { // NON-STOCK CODE START if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle) { - extVehicleType = ExtVehicleType.Bicycle; + useBike = true; } else if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Car) { - extVehicleType = ExtVehicleType.PassengerCar; + useCar = true; } // NON-STOCK CODE END - laneType |= NetInfo.LaneType.Vehicle; - vehicleType |= vehicleInfo.m_vehicleType; if (citizenData.m_targetBuilding != 0 && Singleton.instance.m_buildings.m_buffer[(int)citizenData.m_targetBuilding].Info.m_class.m_service > ItemClass.Service.Office) { randomParking = true; } } } + byte districtId = Singleton.instance.GetDistrict(startPos); + DistrictPolicies.Services servicePolicies = Singleton.instance.m_districts.m_buffer[(int)districtId].m_servicePolicies; + int transportUsageProb = (servicePolicies & DistrictPolicies.Services.FreeTransport) != DistrictPolicies.Services.None ? FREE_TRANSPORT_USAGE_PROBABILITY[(int)wealthLevel] : TRANSPORT_USAGE_PROBABILITY[(int)wealthLevel]; + + bool mayUseTransport = false; + if ((citizenData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) { // STOCK CODE + mayUseTransport = true; + if (useTaxi || simManager.m_randomizer.Int32(100) < transportUsageProb) { + usePublicTransport = true; + //mayUseTransport = true; + //Log._Debug($"CustomCitizenAI: citizen {instanceID} can use public transport"); + } + } + + ExtVehicleType extVehicleType = ExtVehicleType.None; + NetInfo.LaneType laneType = NetInfo.LaneType.Pedestrian; + VehicleInfo.VehicleType vehicleType = VehicleInfo.VehicleType.None; + + if (!usePublicTransport && !useCar && !useBike && !useTaxi && mayUseTransport) { + usePublicTransport = true; + } + + if (usePublicTransport) { + // cims using public transport do not use pocket cars + useCar = false; + + laneType |= NetInfo.LaneType.PublicTransport; + extVehicleType |= ExtVehicleType.PublicTransport; + } + + if (useTaxi) { + laneType |= (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle); + vehicleType |= vehicleInfo.m_vehicleType; + extVehicleType |= ExtVehicleType.Taxi; + } else if (useBike || useCar) { + laneType |= NetInfo.LaneType.Vehicle; + vehicleType |= vehicleInfo.m_vehicleType; + + if (useBike) + extVehicleType |= ExtVehicleType.Bicycle; + else + extVehicleType |= ExtVehicleType.PassengerCar; + } + PathUnit.Position vehiclePosition = default(PathUnit.Position); - ushort parkedVehicle = Singleton.instance.m_citizens.m_buffer[(int)((UIntPtr)citizenData.m_citizen)].m_parkedVehicle; - if (parkedVehicle != 0) { - Vector3 position = Singleton.instance.m_parkedVehicles.m_buffer[(int)parkedVehicle].m_position; - PathManager.FindPathPosition(position, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, false, false, 32f, out vehiclePosition); + if (useCar) { + ushort parkedVehicle = Singleton.instance.m_citizens.m_buffer[(int)((UIntPtr)citizenData.m_citizen)].m_parkedVehicle; + if (parkedVehicle != 0) { + Vector3 position = Singleton.instance.m_parkedVehicles.m_buffer[(int)parkedVehicle].m_position; + PathManager.FindPathPosition(position, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, false, false, 32f, out vehiclePosition); + } } bool allowUnderground = (citizenData.m_flags & (CitizenInstance.Flags.Underground | CitizenInstance.Flags.Transition)) != CitizenInstance.Flags.None; PathUnit.Position startPosA; @@ -60,32 +108,13 @@ public bool CustomStartPathFind(ushort instanceID, ref CitizenInstance citizenDa if (this.FindPathPosition(instanceID, ref citizenData, startPos, laneType, vehicleType, allowUnderground, out startPosA) && this.FindPathPosition(instanceID, ref citizenData, endPos, laneType, vehicleType, false, out endPosA)) { - // NON-STOCK CODE START // - /*Citizen.Wealth wealthLevel = Singleton.instance.m_citizens.m_buffer[citizenData.m_citizen].WealthLevel; - - byte districtId = Singleton.instance.GetDistrict(startPos); - DistrictPolicies.Services servicePolicies = Singleton.instance.m_districts.m_buffer[(int)districtId].m_servicePolicies; - int transportUsageProb = (servicePolicies & DistrictPolicies.Services.FreeTransport) != DistrictPolicies.Services.None ? FREE_TRANSPORT_USAGE_PROBABILITY[(int)wealthLevel] : TRANSPORT_USAGE_PROBABILITY[(int)wealthLevel];*/ - - //bool mayUseTransport = false; - if ((citizenData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) { // STOCK CODE - //if (vehicleInfo == null || (instanceID % 100)+1 <= transportUsageProb) { - laneType |= NetInfo.LaneType.PublicTransport; // STOCK CODE - //mayUseTransport = true; - //Log._Debug($"CustomCitizenAI: citizen {instanceID} can use public transport"); - //} - } - // NON-STOCK CODE END // PathUnit.Position position2 = default(PathUnit.Position); uint path; - bool res = false; - //Log._Debug($"CustomCitizenAI: citizen instance {instanceID}, id {citizenData.m_citizen}. {vehicleType} {extVehicleType} mayUseTransport={mayUseTransport} wealthLevel={wealthLevel}"); - if (extVehicleType == ExtVehicleType.None) - res = Singleton.instance.CreatePath(out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, position2, endPosA, position2, vehiclePosition, laneType, vehicleType, 20000f, false, false, false, false, randomParking); - else - res = Singleton.instance.CreatePath(false, (ExtVehicleType)extVehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, position2, endPosA, position2, vehiclePosition, laneType, vehicleType, 20000f, false, false, false, false, randomParking); +#if DEBUG + //Log._Debug($"CustomCitizenAI: citizen instance {instanceID}, id {citizenData.m_citizen}. vehicleType={vehicleType} laneType={laneType} extVehicleType={extVehicleType} usePublicTransport={usePublicTransport} useTaxi={useTaxi} useBike={useBike} useCar={useCar} wealthLevel={wealthLevel}"); +#endif // NON-STOCK CODE END // - if (res) { + if (Singleton.instance.CreatePath(false, (ExtVehicleType)extVehicleType, null, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, position2, endPosA, position2, vehiclePosition, laneType, vehicleType, 20000f, false, false, false, false, randomParking)) { if (citizenData.m_path != 0u) { Singleton.instance.ReleasePath(citizenData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomFireTruckAI.cs b/TLM/TLM/Custom/AI/CustomFireTruckAI.cs index 848ce8af..e0959ed7 100644 --- a/TLM/TLM/Custom/AI/CustomFireTruckAI.cs +++ b/TLM/TLM/Custom/AI/CustomFireTruckAI.cs @@ -6,12 +6,18 @@ using System.Linq; using System.Text; using TrafficManager.Custom.PathFinding; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; using UnityEngine; namespace TrafficManager.Custom.AI { class CustomFireTruckAI : CarAI { public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) { +#if DEBUG + //Log._Debug($"CustomFireTruckAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + #if PATHRECALC VehicleState state = VehicleStateManager._GetVehicleState(vehicleID); bool recalcRequested = state.PathRecalculationRequested; @@ -43,7 +49,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto #if PATHRECALC recalcRequested, #endif - vehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { + vehicleType, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomHumanAI.cs b/TLM/TLM/Custom/AI/CustomHumanAI.cs index 5e5a4135..e9e7f919 100644 --- a/TLM/TLM/Custom/AI/CustomHumanAI.cs +++ b/TLM/TLM/Custom/AI/CustomHumanAI.cs @@ -1,12 +1,13 @@ using ColossalFramework; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; +using TrafficManager.Manager; namespace TrafficManager.Custom.AI { class CustomHumanAI : CitizenAI { public bool CustomCheckTrafficLights(ushort node, ushort segment) { - var nodeSimulation = Options.timedLightsEnabled ? TrafficLightSimulation.GetNodeSimulation(node) : null; + var nodeSimulation = Options.timedLightsEnabled ? TrafficLightSimulationManager.Instance().GetNodeSimulation(node) : null; var instance = Singleton.instance; var currentFrameIndex = Singleton.instance.m_currentFrameIndex; @@ -16,7 +17,7 @@ public bool CustomCheckTrafficLights(ushort node, ushort segment) { // NON-STOCK CODE START // RoadBaseAI.TrafficLightState pedestrianLightState; - CustomSegmentLights lights = CustomTrafficLights.GetSegmentLights(node, segment); + CustomSegmentLights lights = CustomTrafficLightsManager.Instance().GetSegmentLights(node, segment); if (lights == null || nodeSimulation == null || !nodeSimulation.IsSimulationActive()) { RoadBaseAI.TrafficLightState vehicleLightState; diff --git a/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs b/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs index 87d2e1b0..235c04ec 100644 --- a/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs +++ b/TLM/TLM/Custom/AI/CustomPassengerCarAI.cs @@ -4,8 +4,10 @@ using ColossalFramework; using UnityEngine; using TrafficManager.Custom.PathFinding; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.State; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.Custom.AI { @@ -49,7 +51,9 @@ public static ushort GetDriverInstance(ushort vehicleID, ref Vehicle data) { } public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) { - VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType = ExtVehicleType.PassengerCar; +#if DEBUG + //Log._Debug($"CustomPassengerCarAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif #if PATHRECALC VehicleState state = VehicleStateManager._GetVehicleState(vehicleID); @@ -92,7 +96,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto #else false #endif - , ExtVehicleType.PassengerCar, out path, ref instance2.m_randomizer, instance2.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleType, 20000f, false, false, false, false, randomParking)) { + , ExtVehicleType.PassengerCar, vehicleID, out path, ref instance2.m_randomizer, instance2.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleType, 20000f, false, false, false, false, randomParking)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs b/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs index 1497f881..9ed18104 100644 --- a/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs +++ b/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs @@ -6,12 +6,18 @@ using System.Linq; using System.Text; using TrafficManager.Custom.PathFinding; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; using UnityEngine; namespace TrafficManager.Custom.AI { class CustomPoliceCarAI : CarAI { public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) { +#if DEBUG + //Log._Debug($"CustomPoliceCarAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + #if PATHRECALC VehicleState state = VehicleStateManager._GetVehicleState(vehicleID); bool recalcRequested = state.PathRecalculationRequested; @@ -44,7 +50,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto #if PATHRECALC recalcRequested, #endif - vehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { + vehicleType, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, 20000f, this.IsHeavyVehicle(), this.IgnoreBlocked(vehicleID, ref vehicleData), false, false)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomRoadAI.cs b/TLM/TLM/Custom/AI/CustomRoadAI.cs index 407bb3d5..5501e218 100644 --- a/TLM/TLM/Custom/AI/CustomRoadAI.cs +++ b/TLM/TLM/Custom/AI/CustomRoadAI.cs @@ -1,33 +1,34 @@ #define MARKCONGESTEDSEGMENTSx -#define ABSDENSITYx +#define ABSDENSITY +#define RELDENSITYx using System; using System.Collections.Generic; using ColossalFramework; using TrafficManager.TrafficLight; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; using ColossalFramework.Math; using System.Threading; using TrafficManager.UI; using TrafficManager.State; +using TrafficManager.Manager; namespace TrafficManager.Custom.AI { class CustomRoadAI : RoadBaseAI { - private static ushort[] nodeHousekeepingMask = { 3, 7, 15, 31, 63 }; - private static ushort[] segmentHousekeepingMask = { 15, 31, 63, 127, 255 }; - // TODO create accessor method for these arrays public static ushort[][] currentLaneTrafficBuffer; -#if ABSDENSITY - public static ushort[][] previousLaneTrafficBuffer; -#endif public static uint[][] currentLaneSpeeds; public static uint[][] currentLaneDensities; +#if ABSDENSITY + public static uint[][] maxLaneDensities; +#endif public static byte[][] laneMeanSpeeds; +#if RELDENSITY public static byte[][] laneMeanRelDensities; +#endif #if ABSDENSITY public static byte[][] laneMeanAbsDensities; #endif @@ -42,15 +43,36 @@ class CustomRoadAI : RoadBaseAI { //public static bool InStartupPhase = true; + internal static void DestroySegmentStats(ushort segmentId) { + if (!initDone) + return; + + currentLaneSpeeds[segmentId] = null; + currentLaneDensities[segmentId] = null; +#if ABSDENSITY + maxLaneDensities[segmentId] = null; +#endif + laneMeanSpeeds[segmentId] = null; +#if RELDENSITY + laneMeanRelDensities[segmentId] = null; +#endif +#if ABSDENSITY + laneMeanAbsDensities[segmentId] = null; +#endif + currentLaneTrafficBuffer[segmentId] = null; + } + public void CustomNodeSimulationStep(ushort nodeId, ref NetNode data) { if (simStartFrame == 0) simStartFrame = Singleton.instance.m_currentFrameIndex; if (Options.timedLightsEnabled) { try { - TrafficLightSimulation.SimulationStep(); + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); - var nodeSim = TrafficLightSimulation.GetNodeSimulation(nodeId); + tlsMan.SimulationStep(); + + var nodeSim = tlsMan.GetNodeSimulation(nodeId); if (nodeSim == null || !nodeSim.IsSimulationActive()) { OriginalSimulationStep(nodeId, ref data); } @@ -64,7 +86,24 @@ public void CustomNodeSimulationStep(ushort nodeId, ref NetNode data) { public void CustomSegmentSimulationStep(ushort segmentID, ref NetSegment data) { if (initDone) { try { - TrafficPriority.SegmentSimulationStep(segmentID); + uint curLaneId = data.m_lanes; + int numLanes = data.Info.m_lanes.Length; + uint laneIndex = 0; + + while (laneIndex < numLanes && curLaneId != 0u) { + Flags.applyLaneArrowFlags(curLaneId); + + laneIndex++; + curLaneId = Singleton.instance.m_lanes.m_buffer[curLaneId].m_nextLane; + } + + TrafficPriorityManager.Instance().SegmentSimulationStep(segmentID); + } catch (Exception e) { + Log.Error($"Error occured while housekeeping segment {segmentID}: " + e.ToString()); + } + + try { + VehicleStateManager.Instance().SimulationStep(); } catch (Exception e) { Log.Error($"Error occured while housekeeping segment {segmentID}: " + e.ToString()); } @@ -93,19 +132,20 @@ public void CustomSegmentSimulationStep(ushort segmentID, ref NetSegment data) { uint curLaneId = data.m_lanes; int numLanes = data.Info.m_lanes.Length; uint laneIndex = 0; - //bool resetDensity = false; uint maxDensity = 0u; uint densitySum = 0u; if (currentLaneTrafficBuffer[segmentID] == null || currentLaneTrafficBuffer[segmentID].Length < numLanes) { currentLaneTrafficBuffer[segmentID] = new ushort[numLanes]; #if ABSDENSITY - previousLaneTrafficBuffer[segmentID] = new ushort[numLanes]; + maxLaneDensities[segmentID] = new uint[numLanes]; #endif currentLaneSpeeds[segmentID] = new uint[numLanes]; currentLaneDensities[segmentID] = new uint[numLanes]; laneMeanSpeeds[segmentID] = new byte[numLanes]; +#if RELDENSITY laneMeanRelDensities[segmentID] = new byte[numLanes]; +#endif #if ABSDENSITY laneMeanAbsDensities[segmentID] = new byte[numLanes]; #endif @@ -120,8 +160,6 @@ public void CustomSegmentSimulationStep(ushort segmentID, ref NetSegment data) { laneIndex++; curLaneId = Singleton.instance.m_lanes.m_buffer[curLaneId].m_nextLane; } - /*if (maxDensity > 250) - resetDensity = true;*/ curLaneId = data.m_lanes; laneIndex = 0; @@ -131,10 +169,16 @@ public void CustomSegmentSimulationStep(ushort segmentID, ref NetSegment data) { #endif while (laneIndex < numLanes && curLaneId != 0u) { ushort currentBuf = currentLaneTrafficBuffer[segmentID][laneIndex]; + uint currentDensity = currentLaneDensities[segmentID][laneIndex]; #if ABSDENSITY - ushort previousBuf = previousLaneTrafficBuffer[segmentID][laneIndex]; + uint maxLaneDensity = maxLaneDensities[segmentID][laneIndex]; + if (currentDensity > maxLaneDensity) { + maxLaneDensities[segmentID][laneIndex] = currentDensity; + maxLaneDensity = currentDensity; + } else { + maxLaneDensities[segmentID][laneIndex] = (maxLaneDensity * 9) / 10; + } #endif - uint currentDensity = currentLaneDensities[segmentID][laneIndex]; //currentMeanDensity = (byte)Math.Min(100u, (uint)((currentDensities * 100u) / Math.Max(1u, maxDens))); // 0 .. 100 @@ -166,23 +210,23 @@ public void CustomSegmentSimulationStep(ushort segmentID, ref NetSegment data) { laneMeanSpeeds[segmentID][laneIndex] = (byte)Math.Max((int)previousMeanSpeed - 10, 0); #if ABSDENSITY - if (currentBuf > previousBuf) - laneMeanAbsDensities[segmentID][laneIndex] = (byte)Math.Min((int)laneMeanAbsDensities[segmentID][laneIndex] + 10, 100); - else if (currentBuf == 0 || currentBuf < previousBuf) - laneMeanAbsDensities[segmentID][laneIndex] = (byte)Math.Max((int)laneMeanAbsDensities[segmentID][laneIndex] - 10, 0); - previousLaneTrafficBuffer[segmentID][laneIndex] = currentBuf; + + if (maxLaneDensity > 0) + laneMeanAbsDensities[segmentID][laneIndex] = (byte)((Math.Min(currentDensity * 100 / maxLaneDensity, 100) * (uint)Options.someValue9 + laneMeanAbsDensities[segmentID][laneIndex]) / ((uint)Options.someValue9 + 1)); + else + laneMeanAbsDensities[segmentID][laneIndex] /= (byte)Options.someValue8; #endif +#if RELDENSITY if (densitySum > 0) laneMeanRelDensities[segmentID][laneIndex] = (byte)Math.Min(100u, (currentDensity * 100u) / densitySum); else laneMeanRelDensities[segmentID][laneIndex] = (byte)0; +#endif currentLaneTrafficBuffer[segmentID][laneIndex] = 0; currentLaneSpeeds[segmentID][laneIndex] = 0; - //if (resetDensity) { - currentLaneDensities[segmentID][laneIndex] /= 10u; - //} + currentLaneDensities[segmentID][laneIndex] = 0u; laneIndex++; curLaneId = Singleton.instance.m_lanes.m_buffer[curLaneId].m_nextLane; @@ -207,7 +251,7 @@ public void CustomSegmentSimulationStep(ushort segmentID, ref NetSegment data) { } public static void GetTrafficLightState(ushort vehicleId, ref Vehicle vehicleData, ushort nodeId, ushort fromSegmentId, byte fromLaneIndex, ushort toSegmentId, ref NetSegment segmentData, uint frame, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState) { - TrafficLightSimulation nodeSim = Options.timedLightsEnabled ? TrafficLightSimulation.GetNodeSimulation(nodeId) : null; + TrafficLightSimulation nodeSim = Options.timedLightsEnabled ? TrafficLightSimulationManager.Instance().GetNodeSimulation(nodeId) : null; if (nodeSim == null || !nodeSim.IsSimulationActive()) { RoadBaseAI.GetTrafficLightState(nodeId, ref segmentData, frame, out vehicleLightState, out pedestrianLightState); } else { @@ -216,7 +260,7 @@ public static void GetTrafficLightState(ushort vehicleId, ref Vehicle vehicleDat } public static void GetTrafficLightState(ushort vehicleId, ref Vehicle vehicleData, ushort nodeId, ushort fromSegmentId, byte fromLaneIndex, ushort toSegmentId, ref NetSegment segmentData, uint frame, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState, out bool vehicles, out bool pedestrians) { - TrafficLightSimulation nodeSim = Options.timedLightsEnabled ? TrafficLightSimulation.GetNodeSimulation(nodeId) : null; + TrafficLightSimulation nodeSim = Options.timedLightsEnabled ? TrafficLightSimulationManager.Instance().GetNodeSimulation(nodeId) : null; if (nodeSim == null || !nodeSim.IsSimulationActive()) { RoadBaseAI.GetTrafficLightState(nodeId, ref segmentData, frame, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians); } else { @@ -228,7 +272,7 @@ public static void GetTrafficLightState(ushort vehicleId, ref Vehicle vehicleDat private static void GetCustomTrafficLightState(ushort vehicleId, ref Vehicle vehicleData, ushort nodeId, ushort fromSegmentId, byte fromLaneIndex, ushort toSegmentId, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState, TrafficLightSimulation nodeSim = null) { if (nodeSim == null) { - nodeSim = TrafficLightSimulation.GetNodeSimulation(nodeId); + nodeSim = TrafficLightSimulationManager.Instance().GetNodeSimulation(nodeId); if (nodeSim == null) { Log.Error($"GetCustomTrafficLightState: node traffic light simulation not found at node {nodeId}! Vehicle {vehicleId} comes from segment {fromSegmentId} and goes to node {nodeId}"); vehicleLightState = TrafficLightState.Green; @@ -240,7 +284,7 @@ private static void GetCustomTrafficLightState(ushort vehicleId, ref Vehicle veh // get responsible traffic light //Log._Debug($"GetTrafficLightState: Getting custom light for vehicle {vehicleId} @ node {nodeId}, segment {fromSegmentId}, lane {fromLaneIndex}."); - CustomSegmentLights lights = CustomTrafficLights.GetSegmentLights(nodeId, fromSegmentId); + CustomSegmentLights lights = CustomTrafficLightsManager.Instance().GetSegmentLights(nodeId, fromSegmentId); CustomSegmentLight light = lights == null ? null : lights.GetCustomLight(fromLaneIndex); if (lights == null || light == null) { Log.Warning($"GetTrafficLightState: No custom light for vehicle {vehicleId} @ node {nodeId}, segment {fromSegmentId}, lane {fromLaneIndex} found. lights null? {lights == null} light null? {light == null}"); @@ -256,7 +300,7 @@ private static void GetCustomTrafficLightState(ushort vehicleId, ref Vehicle veh // get traffic light state from responsible traffic light if (toSegmentId == fromSegmentId) { - vehicleLightState = TrafficPriority.IsLeftHandDrive() ? light.GetLightRight() : light.GetLightLeft(); + vehicleLightState = TrafficPriorityManager.IsLeftHandDrive() ? light.GetLightRight() : light.GetLightLeft(); } else if (geometry.IsLeftSegment(toSegmentId, isStartNode)) { vehicleLightState = light.GetLightLeft(); } else if (geometry.IsRightSegment(toSegmentId, isStartNode)) { @@ -278,7 +322,7 @@ public static void CustomSetTrafficLightState(ushort nodeID, ref NetSegment segm public static void OriginalSetTrafficLightState(bool customCall, ushort nodeID, ref NetSegment segmentData, uint frame, RoadBaseAI.TrafficLightState vehicleLightState, RoadBaseAI.TrafficLightState pedestrianLightState, bool vehicles, bool pedestrians) { /// NON-STOCK CODE START /// - TrafficLightSimulation nodeSim = Options.timedLightsEnabled ? TrafficLightSimulation.GetNodeSimulation(nodeID) : null; + TrafficLightSimulation nodeSim = Options.timedLightsEnabled ? TrafficLightSimulationManager.Instance().GetNodeSimulation(nodeID) : null; if (nodeSim == null || !nodeSim.IsSimulationActive() || customCall) { /// NON-STOCK CODE END /// int num = (int)pedestrianLightState << 2 | (int)vehicleLightState; @@ -337,7 +381,7 @@ public void CustomUpdateLanes(ushort segmentID, ref NetSegment data, bool loadin } } - #region stock code +#region stock code public void OriginalUpdateLanes(ushort segmentID, ref NetSegment data, bool loading) { NetManager instance = Singleton.instance; bool flag = Singleton.instance.m_metaData.m_invertTraffic == SimulationMetaData.MetaBool.True; @@ -849,7 +893,7 @@ public void OriginalSimulationStep(ushort segmentID, ref NetSegment data) { } data.m_problems = problem; } - #endregion +#endregion internal static void OnLevelUnloading() { initDone = false; @@ -859,14 +903,17 @@ internal static void OnBeforeLoadData() { if (!initDone) { currentLaneTrafficBuffer = new ushort[NetManager.MAX_SEGMENT_COUNT][]; #if ABSDENSITY - previousLaneTrafficBuffer = new ushort[NetManager.MAX_SEGMENT_COUNT][]; + #endif currentLaneSpeeds = new uint[NetManager.MAX_SEGMENT_COUNT][]; currentLaneDensities = new uint[NetManager.MAX_SEGMENT_COUNT][]; laneMeanSpeeds = new byte[NetManager.MAX_SEGMENT_COUNT][]; +#if RELDENSITY laneMeanRelDensities = new byte[NetManager.MAX_SEGMENT_COUNT][]; +#endif #if ABSDENSITY laneMeanAbsDensities = new byte[NetManager.MAX_SEGMENT_COUNT][]; + maxLaneDensities = new uint[NetManager.MAX_SEGMENT_COUNT][]; #endif #if MARKCONGESTEDSEGMENTS segmentCongestion = new bool[NetManager.MAX_SEGMENT_COUNT]; @@ -878,12 +925,12 @@ internal static void OnBeforeLoadData() { internal static void resetTrafficStats() { for (ushort i = 0; i < NetManager.MAX_SEGMENT_COUNT; ++i) { - if (laneMeanRelDensities[i] != null) { - for (int k = 0; k < laneMeanRelDensities[i].Length; ++k) { + if (currentLaneTrafficBuffer[i] != null) { + for (int k = 0; k < currentLaneTrafficBuffer[i].Length; ++k) { laneMeanSpeeds[i][k] = 50; currentLaneTrafficBuffer[i][k] = 0; #if ABSDENSITY - previousLaneTrafficBuffer[i][k] = 0; + maxLaneDensities[i][k] = 0; #endif } } @@ -897,7 +944,7 @@ internal static void resetTrafficStats() { internal static void AddTraffic(ushort segmentId, byte laneIndex, ushort vehicleLength, ushort? speed) { if (!initDone) return; - if (laneMeanRelDensities[segmentId] == null || laneIndex >= laneMeanRelDensities[segmentId].Length) + if (currentLaneTrafficBuffer[segmentId] == null || laneIndex >= currentLaneTrafficBuffer[segmentId].Length) return; if (speed != null) { diff --git a/TLM/TLM/Custom/AI/CustomShipAI.cs b/TLM/TLM/Custom/AI/CustomShipAI.cs index 18b50db9..5c728184 100644 --- a/TLM/TLM/Custom/AI/CustomShipAI.cs +++ b/TLM/TLM/Custom/AI/CustomShipAI.cs @@ -4,12 +4,18 @@ using System.Linq; using System.Text; using TrafficManager.Custom.PathFinding; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; using UnityEngine; namespace TrafficManager.Custom.AI { class CustomShipAI : ShipAI { public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) { +#if DEBUG + //Log._Debug($"CustomShipAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + /// NON-STOCK CODE START /// ExtVehicleType vehicleType = VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType; if (vehicleType == ExtVehicleType.None) { @@ -38,8 +44,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto endPosB = default(PathUnit.Position); } uint path; - bool res = Singleton.instance.CreatePath((ExtVehicleType)vehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f); - if (res) { + if (Singleton.instance.CreatePath((ExtVehicleType)vehicleType, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomTaxiAI.cs b/TLM/TLM/Custom/AI/CustomTaxiAI.cs index b39bfcbb..19188798 100644 --- a/TLM/TLM/Custom/AI/CustomTaxiAI.cs +++ b/TLM/TLM/Custom/AI/CustomTaxiAI.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Text; using TrafficManager.Custom.PathFinding; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; using UnityEngine; @@ -36,7 +38,9 @@ public static ushort GetPassengerInstance(ushort vehicleID, ref Vehicle data) { } public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) { - VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType = ExtVehicleType.Taxi; +#if DEBUG + //Log._Debug($"CustomTaxiAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif #if PATHRECALC VehicleState state = VehicleStateManager._GetVehicleState(vehicleID); @@ -74,7 +78,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto #if PATHRECALC recalcRequested, #endif - ExtVehicleType.Taxi, out path, ref instance2.m_randomizer, instance2.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, laneType, vehicleType, 20000f)) { + ExtVehicleType.Taxi, vehicleID, out path, ref instance2.m_randomizer, instance2.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, laneType, vehicleType, 20000f)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomTrainAI.cs b/TLM/TLM/Custom/AI/CustomTrainAI.cs index d060651c..aa442fc3 100644 --- a/TLM/TLM/Custom/AI/CustomTrainAI.cs +++ b/TLM/TLM/Custom/AI/CustomTrainAI.cs @@ -8,42 +8,36 @@ using System.Text; using TrafficManager.Custom.PathFinding; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.Custom.AI { public class CustomTrainAI : TrainAI { // correct would be to inherit from VehicleAI (in order to keep the correct references to `base`) public void TrafficManagerSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { -#if USEPATHWAITCOUNTER - VehicleState state = VehicleStateManager._GetVehicleState(vehicleId); -#endif - if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { byte pathFindFlags = Singleton.instance.m_pathUnits.m_buffer[(int)((UIntPtr)vehicleData.m_path)].m_pathFindFlags; - if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { + #if USEPATHWAITCOUNTER + if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0) { + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = 0; // NON-STOCK CODE + } #endif + + if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { try { this.PathFindReady(vehicleId, ref vehicleData); } catch (Exception e) { Log.Warning($"TrainAI.PathFindReady({vehicleId}) for vehicle {vehicleData.Info?.m_class?.name} threw an exception: {e.ToString()}"); -//#if !DEBUG vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton.instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; vehicleData.Unspawn(vehicleId); -//#endif return; } - } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0 -#if USEPATHWAITCOUNTER - || ((pathFindFlags & PathUnit.FLAG_CREATED) != 0 && state.PathWaitCounter == ushort.MaxValue) -#endif - ) { -#if USEPATHWAITCOUNTER - state.PathWaitCounter = 0; // NON-STOCK CODE -#endif + } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) { vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton.instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; @@ -52,13 +46,11 @@ public void TrafficManagerSimulationStep(ushort vehicleId, ref Vehicle vehicleDa } #if USEPATHWAITCOUNTER else { - state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1); // NON-STOCK CODE + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); + state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter+1); // NON-STOCK CODE } #endif } else { -#if USEPATHWAITCOUNTER - state.PathWaitCounter = 0; // NON-STOCK CODE -#endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } @@ -149,12 +141,18 @@ public void TrafficManagerSimulationStep(ushort vehicleId, ref Vehicle vehicleDa } } } - if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0 || (vehicleData.m_blockCounter == 255 && Options.enableDespawning)) { + if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0 || (vehicleData.m_blockCounter == 255 /*&& Options.enableDespawning*/)) { Singleton.instance.ReleaseVehicle(vehicleId); } } public override bool TrySpawn(ushort vehicleID, ref Vehicle vehicleData) { + // NON-STOCK CODE START + if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { + VehicleStateManager.Instance().OnBeforeSpawnVehicle(vehicleID, ref vehicleData); + } + // NON-STOCK CODE END + if ((vehicleData.m_flags & Vehicle.Flags.Spawned) != (Vehicle.Flags)0) { return true; } @@ -173,12 +171,6 @@ public override bool TrySpawn(ushort vehicleID, ref Vehicle vehicleData) { vehicleData.m_flags &= ~Vehicle.Flags.WaitingSpace; CustomTrainAI.InitializePath(vehicleID, ref vehicleData); - // NON-STOCK CODE START - if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { - VehicleStateManager.Instance().OnVehicleSpawned(vehicleID, ref vehicleData); - } - // NON-STOCK CODE END - return true; } @@ -198,12 +190,16 @@ public void TmCalculateSegmentPositionPathFinder(ushort vehicleID, ref Vehicle v } } - public bool CustomStartPathFind(ushort vehicleId, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) { + public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) { +#if DEBUG + //Log._Debug($"CustomTrainAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif + /// NON-STOCK CODE START /// - ExtVehicleType vehicleType = VehicleStateManager.Instance()._GetVehicleState(vehicleId).VehicleType; + ExtVehicleType vehicleType = VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType; if (vehicleType == ExtVehicleType.None) { #if DEBUG - Log._Debug($"CustomTrainAI.CustomStartPathFind: Vehicle {vehicleId} does not have a valid vehicle type!"); + Log._Debug($"CustomTrainAI.CustomStartPathFind: Vehicle {vehicleID} does not have a valid vehicle type!"); #endif vehicleType = ExtVehicleType.RailVehicle; } else if (vehicleType == ExtVehicleType.CargoTrain) @@ -252,8 +248,12 @@ public bool CustomStartPathFind(ushort vehicleId, ref Vehicle vehicleData, Vecto endPosB = default(PathUnit.Position); } uint path; - bool res = Singleton.instance.CreatePath((ExtVehicleType)vehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, false, false, true, false); - if (res) { + if (Singleton.instance.CreatePath((ExtVehicleType)vehicleType, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, false, false, true, false)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomTramBaseAI.cs b/TLM/TLM/Custom/AI/CustomTramBaseAI.cs index 47c4b142..64de1f3d 100644 --- a/TLM/TLM/Custom/AI/CustomTramBaseAI.cs +++ b/TLM/TLM/Custom/AI/CustomTramBaseAI.cs @@ -8,8 +8,10 @@ using System.Text; using TrafficManager.Custom.PathFinding; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; +using TrafficManager.Manager; +using TrafficManager.Traffic; namespace TrafficManager.Custom.AI { class CustomTramBaseAI : TramBaseAI { @@ -20,10 +22,15 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { byte pathFindFlags = Singleton.instance.m_pathUnits.m_buffer[(int)((UIntPtr)vehicleData.m_path)].m_pathFindFlags; - if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { + #if USEPATHWAITCOUNTER + if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0) { + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = 0; // NON-STOCK CODE + } #endif + + if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { try { this.PathfindSuccess(vehicleId, ref vehicleData); this.PathFindReady(vehicleId, ref vehicleData); @@ -35,14 +42,7 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect this.PathfindFailure(vehicleId, ref vehicleData); return; } - } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0 -#if USEPATHWAITCOUNTER -|| ((pathFindFlags & PathUnit.FLAG_CREATED) != 0 && state.PathWaitCounter == ushort.MaxValue) -#endif -) { // NON-STOCK CODE -#if USEPATHWAITCOUNTER - state.PathWaitCounter = 0; // NON-STOCK CODE -#endif + } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) { vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton.instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; @@ -51,13 +51,11 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect } #if USEPATHWAITCOUNTER else { - state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1); // NON-STOCK CODE + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); + state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter+1); // NON-STOCK CODE } #endif } else { -#if USEPATHWAITCOUNTER - state.PathWaitCounter = 0; // NON-STOCK CODE -#endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } @@ -112,12 +110,18 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect break; } } - if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0 || (vehicleData.m_blockCounter == 255 && Options.enableDespawning)) { + if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0 || vehicleData.m_blockCounter == 255) { Singleton.instance.ReleaseVehicle(vehicleId); } } public override bool TrySpawn(ushort vehicleID, ref Vehicle vehicleData) { + // NON-STOCK CODE START + if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { + VehicleStateManager.Instance().OnBeforeSpawnVehicle(vehicleID, ref vehicleData); + } + // NON-STOCK CODE END + if ((vehicleData.m_flags & Vehicle.Flags.Spawned) != (Vehicle.Flags)0) { return true; } @@ -136,12 +140,6 @@ public override bool TrySpawn(ushort vehicleID, ref Vehicle vehicleData) { vehicleData.m_flags &= ~Vehicle.Flags.WaitingSpace; CustomTramBaseAI.InitializePath(vehicleID, ref vehicleData); - // NON-STOCK CODE START - if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { - VehicleStateManager.Instance().OnVehicleSpawned(vehicleID, ref vehicleData); - } - // NON-STOCK CODE END - return true; } @@ -150,7 +148,9 @@ private static void InitializePath(ushort vehicleID, ref Vehicle vehicleData) { } public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) { - VehicleStateManager.Instance()._GetVehicleState(vehicleID).VehicleType = ExtVehicleType.Tram; +#if DEBUG + //Log._Debug($"CustomTramBaseAI.CustomStartPathFind called for vehicle {vehicleID}"); +#endif VehicleInfo info = this.m_info; bool allowUnderground; @@ -178,7 +178,12 @@ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vecto endPosB = default(PathUnit.Position); } uint path; - if (Singleton.instance.CreatePath(ExtVehicleType.Tram, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, false, false, true, false)) { + if (Singleton.instance.CreatePath(ExtVehicleType.Tram, vehicleID, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle, info.m_vehicleType, 20000f, false, false, true, false)) { +#if USEPATHWAITCOUNTER + VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleID); + state.PathWaitCounter = 0; +#endif + if (vehicleData.m_path != 0u) { Singleton.instance.ReleasePath(vehicleData.m_path); } diff --git a/TLM/TLM/Custom/AI/CustomTransportLineAI.cs b/TLM/TLM/Custom/AI/CustomTransportLineAI.cs index 4b576bdd..c44be9e5 100644 --- a/TLM/TLM/Custom/AI/CustomTransportLineAI.cs +++ b/TLM/TLM/Custom/AI/CustomTransportLineAI.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using TrafficManager.Custom.PathFinding; +using TrafficManager.Geometry; using TrafficManager.Traffic; using UnityEngine; @@ -79,7 +80,7 @@ public static bool CustomStartPathFind(ushort segmentID, ref NetSegment data, It extVehicleType = ExtVehicleType.PassengerPlane; //Log._Debug($"Transport line. extVehicleType={extVehicleType}"); - if (Singleton.instance.CreatePath(extVehicleType, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, vehicleType, 20000f, false, true, true, skipQueue)) { + if (Singleton.instance.CreatePath(extVehicleType, null, out path, ref Singleton.instance.m_randomizer, Singleton.instance.m_currentBuildIndex, startPosA, startPosB, endPosA, endPosB, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, vehicleType, 20000f, false, true, true, skipQueue)) { if (startPosA.m_segment != 0 && startPosB.m_segment != 0) { NetNode[] expr_2F5_cp_0 = instance.m_nodes.m_buffer; ushort expr_2F5_cp_1 = data.m_startNode; diff --git a/TLM/TLM/Custom/AI/CustomVehicleAI.cs b/TLM/TLM/Custom/AI/CustomVehicleAI.cs index 5042097d..28b877f3 100644 --- a/TLM/TLM/Custom/AI/CustomVehicleAI.cs +++ b/TLM/TLM/Custom/AI/CustomVehicleAI.cs @@ -10,9 +10,11 @@ using System.Text; using TrafficManager.Custom.PathFinding; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.Custom.AI { class CustomVehicleAI : VehicleAI { @@ -342,6 +344,8 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, return false; } } else if (vehicleState != null && Options.prioritySignsEnabled) { + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + #if DEBUG //bool debug = destinationNodeId == 10864; //bool debug = destinationNodeId == 13531; @@ -353,7 +357,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, Log._Debug($"Vehicle {vehicleId} is arriving @ seg. {prevPos.m_segment} ({position.m_segment}, {nextPosition.m_segment}), node {targetNodeId} which is not a traffic light."); #endif - var prioritySegment = TrafficPriority.GetPrioritySegment(targetNodeId, prevPos.m_segment); + var prioritySegment = prioMan.GetPrioritySegment(targetNodeId, prevPos.m_segment); if (prioritySegment != null) { #if DEBUG if (debug) @@ -396,7 +400,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; - if (speed <= TrafficPriority.maxStopVelocity) { + if (speed <= TrafficPriorityManager.maxStopVelocity) { vehicleState.WaitTime++; float minStopWaitTime = UnityEngine.Random.Range(0f, 3f); @@ -404,7 +408,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, if (Options.simAccuracy >= 4) { vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } else { - hasIncomingCars = TrafficPriority.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); + hasIncomingCars = prioMan.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); #if DEBUG if (debug) Log._Debug($"hasIncomingCars: {hasIncomingCars}"); @@ -451,11 +455,11 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; - if (speed <= TrafficPriority.maxYieldVelocity || Options.simAccuracy <= 2) { + if (speed <= TrafficPriorityManager.maxYieldVelocity || Options.simAccuracy <= 2) { if (Options.simAccuracy >= 4) { vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } else { - hasIncomingCars = TrafficPriority.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); + hasIncomingCars = prioMan.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); #if DEBUG if (debug) Log._Debug($"Vehicle {vehicleId}: hasIncomingCars: {hasIncomingCars}"); @@ -479,7 +483,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, #endif // vehicle has not yet reached yield speed - maxSpeed = TrafficPriority.maxYieldVelocity; + maxSpeed = TrafficPriorityManager.maxYieldVelocity; return false; } } else { @@ -509,7 +513,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; - hasIncomingCars = TrafficPriority.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); + hasIncomingCars = prioMan.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); #if DEBUG if (debug) Log._Debug($"hasIncomingCars: {hasIncomingCars}"); @@ -526,7 +530,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, } return true; } - } else if (speed <= TrafficPriority.maxStopVelocity) { + } else if (speed <= TrafficPriorityManager.maxStopVelocity) { // vehicle is not moving. reset allowance to leave junction #if DEBUG if (debug) diff --git a/TLM/TLM/Custom/Manager/CustomNetManager.cs b/TLM/TLM/Custom/Manager/CustomNetManager.cs index e7d1e847..ef424cae 100644 --- a/TLM/TLM/Custom/Manager/CustomNetManager.cs +++ b/TLM/TLM/Custom/Manager/CustomNetManager.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; namespace TrafficManager.Custom.Manager { diff --git a/TLM/TLM/Custom/Manager/CustomVehicleManager.cs b/TLM/TLM/Custom/Manager/CustomVehicleManager.cs index af79fa2e..95ed0290 100644 --- a/TLM/TLM/Custom/Manager/CustomVehicleManager.cs +++ b/TLM/TLM/Custom/Manager/CustomVehicleManager.cs @@ -3,7 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using TrafficManager.Traffic; +using TrafficManager.Geometry; +using TrafficManager.Manager; using UnityEngine; namespace TrafficManager.Custom.Manager { diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs index 40782e30..37b7798d 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs @@ -1,5 +1,6 @@ #define DEBUGPFx #define DEBUGPF2x +#define DEBUGPF3x #define DEBUGMERGEx #define DEBUGLOCKSx #define DEBUGCOSTSx @@ -13,12 +14,14 @@ using ColossalFramework; using ColossalFramework.Math; using ColossalFramework.UI; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; using System.Collections.Generic; using TrafficManager.Custom.AI; using TrafficManager.TrafficLight; using TrafficManager.State; +using TrafficManager.Manager; +using TrafficManager.Traffic; namespace TrafficManager.Custom.PathFinding { public class CustomPathFind : PathFind { @@ -32,8 +35,6 @@ private struct BufferItem { public uint m_numSegmentsToJunction; } - public readonly static int SYNC_TIMEOUT = 10; - //Expose the private fields FieldInfo _fieldpathUnits; FieldInfo _fieldQueueFirst; @@ -100,12 +101,15 @@ private bool Terminated { private bool _randomParking; private bool _transportVehicle; private ExtVehicleType? _extVehicleType; + private ushort? _vehicleId; + private float _vehicleRand; private bool _extPublicTransport; //private static ushort laneChangeRandCounter = 0; #if DEBUG public uint _failedPathFinds = 0; public uint _succeededPathFinds = 0; #endif + public int pfId = 0; private Randomizer _pathRandomizer; private uint _pathFindIndex; private NetInfo.LaneType _laneTypes; @@ -118,17 +122,24 @@ private bool Terminated { private byte[] nextInnerLaneSimilarIndexes = new byte[16]; private byte[] laneIndexByOuterSimilarIndex = new byte[16]; private bool[] hasOutgoingConnections = new bool[16]; + private bool[] nextIsConnectedWithPrevious = new bool[16]; private ushort compatibleOuterSimilarIndexesMask = (ushort)0; private static readonly ushort[] POW2MASKS = new ushort[] { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; + private static readonly CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); public bool IsMasterPathFind = false; #if EXTRAPF public bool IsExtraPathFind = false; #endif - internal ExtVehicleType?[] pathUnitExtVehicleType = null; + private ExtVehicleType?[] pathUnitExtVehicleType = null; + private ushort?[] pathUnitVehicleIds = null; protected virtual void Awake() { +#if DEBUG + Log._Debug($"CustomPathFind.Awake called."); +#endif + var stockPathFindType = typeof(PathFind); const BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Instance; @@ -143,14 +154,23 @@ protected virtual void Awake() { _buffer = new BufferItem[65536]; // 2^16 _bufferLock = PathManager.instance.m_bufferLock; PathUnits = PathManager.instance.m_pathUnits; - QueueLock = new object(); +#if DEBUG + if (QueueLock == null) { + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is null. Creating."); + QueueLock = new object(); + } else { + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is NOT null."); + } +#endif _laneLocation = new uint[262144]; // 2^18 _laneTarget = new PathUnit.Position[262144]; // 2^18 _bufferMin = new int[1024]; // 2^10 _bufferMax = new int[1024]; // 2^10 - if (pathUnitExtVehicleType == null) + if (pathUnitExtVehicleType == null) { pathUnitExtVehicleType = new ExtVehicleType?[PathUnits.m_size]; + pathUnitVehicleIds = new ushort?[PathUnits.m_size]; + } m_pathfindProfiler = new ThreadProfiler(); CustomPathFindThread = new Thread(PathFindThread) { Name = "Pathfind" }; @@ -158,7 +178,7 @@ protected virtual void Awake() { CustomPathFindThread.Start(); if (!CustomPathFindThread.IsAlive) { //CODebugBase.Error(LogChannel.Core, "Path find thread failed to start!"); - Log.Error("Path find thread failed to start!"); + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) Path find thread failed to start!"); } } @@ -169,7 +189,7 @@ protected virtual void OnDestroy() { uint lockIter = 0; #endif try { - while (!Monitor.TryEnter(QueueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) {} + Monitor.Enter(QueueLock); Terminated = true; Monitor.PulseAll(QueueLock); } catch (Exception e) { @@ -179,29 +199,84 @@ protected virtual void OnDestroy() { } } - public bool CalculatePath(ExtVehicleType vehicleType, uint unit, bool skipQueue) { + public new bool CalculatePath(uint unit, bool skipQueue) { + return ExtCalculatePath(null, null, unit, skipQueue); + } + + public bool ExtCalculatePath(ExtVehicleType? vehicleType, ushort? vehicleId, uint unit, bool skipQueue) { if (Singleton.instance.AddPathReference(unit)) { try { - while (!Monitor.TryEnter(QueueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) { } + Monitor.Enter(QueueLock); +#if DEBUGPF3 + uint oldQueueLast = this.QueueLast; + uint oldQueueFirst = this.QueueFirst; + uint ppath = 0; +#endif + if (skipQueue) { +#if DEBUGPF3 + ppath |= 1; +#endif if (this.QueueLast == 0u) { this.QueueLast = unit; +#if DEBUGPF3 + ppath |= 2; +#endif } else { - this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_nextPathUnit = this.QueueFirst; + this.PathUnits.m_buffer[unit].m_nextPathUnit = this.QueueFirst; +#if DEBUGPF3 + ppath |= 4; +#endif } this.QueueFirst = unit; +#if DEBUGPF3 + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({vehicleType}, {vehicleId}, {unit}, {skipQueue}) skipQueue! ppath={ppath} QueueFirst={QueueFirst} QueueLast={QueueLast} oldQueueFirst={oldQueueFirst} oldQueueLast={oldQueueLast} unit.nextPathUnit={this.PathUnits.m_buffer[unit].m_nextPathUnit}"); +#endif } else { +#if DEBUGPF3 + ppath |= 8; +#endif if (this.QueueLast == 0u) { this.QueueFirst = unit; +#if DEBUGPF3 + ppath |= 16; +#endif } else { - this.PathUnits.m_buffer[(int)((UIntPtr)this.QueueLast)].m_nextPathUnit = unit; + this.PathUnits.m_buffer[this.QueueLast].m_nextPathUnit = unit; +#if DEBUGPF3 + ppath |= 32; +#endif } this.QueueLast = unit; +#if DEBUGPF3 + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({vehicleType}, {vehicleId}, {unit}, {skipQueue}) NOT skipQueue! ppath={ppath} QueueFirst={QueueFirst} QueueLast={QueueLast} oldQueueFirst={oldQueueFirst} oldQueueLast={oldQueueLast} queueLast.nextPathUnit={this.PathUnits.m_buffer[QueueLast].m_nextPathUnit}"); +#endif } this.PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_CREATED; - this.m_queuedPathFindCount++; + ++this.m_queuedPathFindCount; pathUnitExtVehicleType[unit] = vehicleType; + pathUnitVehicleIds[unit] = vehicleId; +#if DEBUGPF3 + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({vehicleType}, {vehicleId}, {unit}, {skipQueue}) finished. QueueFirst={QueueFirst} QueueLast={QueueLast} Calculating={Calculating}"); + List allUnits = new List(); + uint currentUnit = this.QueueFirst; + int i = 0; + while (currentUnit != 0 && currentUnit != QueueLast) { + allUnits.Add(currentUnit); + currentUnit = this.PathUnits.m_buffer[currentUnit].m_nextPathUnit; + + ++i; + if (i > 10000) { + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}): !!! CYCLE ???"); + break; + } + } + allUnits.Add(QueueLast); + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({vehicleType}, {vehicleId}, {unit}, {skipQueue}): allUnits={string.Join(", ", allUnits.Select(x => x.ToString()).ToArray())}"); +#endif Monitor.Pulse(this.QueueLock); + } catch (Exception e) { + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({vehicleType}, {vehicleId}, {unit}, {skipQueue}): Error: {e.ToString()}"); } finally { Monitor.Exit(this.QueueLock); } @@ -213,17 +288,19 @@ public bool CalculatePath(ExtVehicleType vehicleType, uint unit, bool skipQueue) // PathFind protected void PathFindImplementation(uint unit, ref PathUnit data) { NetManager instance = Singleton.instance; - this._laneTypes = (NetInfo.LaneType)this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_laneTypes; - this._vehicleTypes = (VehicleInfo.VehicleType)this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_vehicleTypes; - this._maxLength = this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_length; + this._laneTypes = (NetInfo.LaneType)this.PathUnits.m_buffer[unit].m_laneTypes; + this._vehicleTypes = (VehicleInfo.VehicleType)this.PathUnits.m_buffer[unit].m_vehicleTypes; + this._maxLength = this.PathUnits.m_buffer[unit].m_length; this._pathFindIndex = (this._pathFindIndex + 1u & 32767u); this._pathRandomizer = new Randomizer(unit); - this._isHeavyVehicle = ((this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags & 16) != 0); - this._ignoreBlocked = ((this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags & 32) != 0); - this._stablePath = ((this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags & 64) != 0); - this._randomParking = ((this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags & 128) != 0); + this._isHeavyVehicle = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 16) != 0); + this._ignoreBlocked = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 32) != 0); + this._stablePath = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 64) != 0); + this._randomParking = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 128) != 0); this._transportVehicle = ((byte)(this._laneTypes & NetInfo.LaneType.TransportVehicle) != 0); this._extVehicleType = pathUnitExtVehicleType[unit]; + this._vehicleId = pathUnitVehicleIds[unit]; + this._vehicleRand = this._vehicleId == null ? 0f : Math.Min(1f, (float)(_vehicleId % 101) * 0.01f); this._extPublicTransport = _extVehicleType != null && (_extVehicleType & ExtVehicleType.PublicTransport) != ExtVehicleType.None; #if DEBUGPF //Log._Debug($"CustomPathFind.PathFindImplementation: path unit {unit}, type {_extVehicleType}"); @@ -231,8 +308,8 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { if ((byte)(this._laneTypes & NetInfo.LaneType.Vehicle) != 0) { this._laneTypes |= NetInfo.LaneType.TransportVehicle; } - int num = (int)(this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_positionCount & 15); - int num2 = this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_positionCount >> 4; + int num = (int)(this.PathUnits.m_buffer[unit].m_positionCount & 15); + int num2 = this.PathUnits.m_buffer[unit].m_positionCount >> 4; BufferItem bufferItemStartA; if (data.m_position00.m_segment != 0 && num >= 1) { this._startLaneA = PathManager.GetLaneID(data.m_position00); @@ -300,20 +377,20 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { this._bufferMaxPos = -1; if (this._pathFindIndex == 0u) { uint maxUInt = 4294901760u; - for (int i = 0; i < 262144; i++) { + for (int i = 0; i < 262144; ++i) { this._laneLocation[i] = maxUInt; } } - for (int j = 0; j < 1024; j++) { + for (int j = 0; j < 1024; ++j) { this._bufferMin[j] = 0; this._bufferMax[j] = -1; } if (bufferItemEndA.m_position.m_segment != 0) { - this._bufferMax[0]++; + ++this._bufferMax[0]; this._buffer[++this._bufferMaxPos] = bufferItemEndA; } if (bufferItemEndB.m_position.m_segment != 0) { - this._bufferMax[0]++; + ++this._bufferMax[0]; this._buffer[++this._bufferMaxPos] = bufferItemEndB; } bool canFindPath = false; @@ -330,7 +407,7 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { int bufMin = this._bufferMin[this._bufferMinPos]; int bufMax = this._bufferMax[this._bufferMinPos]; if (bufMin > bufMax) { - this._bufferMinPos++; + ++this._bufferMinPos; } else { this._bufferMin[this._bufferMinPos] = bufMin + 1; BufferItem candidateItem = this._buffer[(this._bufferMinPos << 6) + bufMin]; @@ -367,35 +444,35 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { // explore the path if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0) { - ushort startNode = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_startNode; - this.ProcessItemMain(unit, candidateItem, startNode, ref instance.m_nodes.m_buffer[(int)startNode], 0, false); + ushort startNode = instance.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode; + this.ProcessItemMain(unit, candidateItem, startNode, ref instance.m_nodes.m_buffer[startNode], 0, false); } if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0) { - ushort endNode = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_endNode; - this.ProcessItemMain(unit, candidateItem, endNode, ref instance.m_nodes.m_buffer[(int)endNode], 255, false); + ushort endNode = instance.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode; + this.ProcessItemMain(unit, candidateItem, endNode, ref instance.m_nodes.m_buffer[endNode], 255, false); } // handle special nodes (e.g. bus stops) int num6 = 0; - ushort specialNodeId = instance.m_lanes.m_buffer[(int)((UIntPtr)candidateItem.m_laneID)].m_nodes; + ushort specialNodeId = instance.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes; if (specialNodeId != 0) { - ushort startNode2 = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_startNode; - ushort endNode2 = instance.m_segments.m_buffer[(int)candidateItem.m_position.m_segment].m_endNode; - bool flag2 = ((instance.m_nodes.m_buffer[(int)startNode2].m_flags | instance.m_nodes.m_buffer[(int)endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None; + ushort startNode2 = instance.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode; + ushort endNode2 = instance.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode; + bool flag2 = ((instance.m_nodes.m_buffer[startNode2].m_flags | instance.m_nodes.m_buffer[endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None; while (specialNodeId != 0) { NetInfo.Direction direction = NetInfo.Direction.None; - byte laneOffset = instance.m_nodes.m_buffer[(int)specialNodeId].m_laneOffset; + byte laneOffset = instance.m_nodes.m_buffer[specialNodeId].m_laneOffset; if (laneOffset <= candidateItem.m_position.m_offset) { direction |= NetInfo.Direction.Forward; } if (laneOffset >= candidateItem.m_position.m_offset) { direction |= NetInfo.Direction.Backward; } - if ((byte)(candidateItem.m_direction & direction) != 0 && (!flag2 || (instance.m_nodes.m_buffer[(int)specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) { - this.ProcessItemMain(unit, candidateItem, specialNodeId, ref instance.m_nodes.m_buffer[(int)specialNodeId], laneOffset, true); + if ((byte)(candidateItem.m_direction & direction) != 0 && (!flag2 || (instance.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) { + this.ProcessItemMain(unit, candidateItem, specialNodeId, ref instance.m_nodes.m_buffer[specialNodeId], laneOffset, true); } - specialNodeId = instance.m_nodes.m_buffer[(int)specialNodeId].m_nextLaneNode; + specialNodeId = instance.m_nodes.m_buffer[specialNodeId].m_nextLaneNode; if (++num6 == 32768) { Log.Warning("Special loop: Too many iterations"); break; @@ -422,16 +499,14 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { //Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}"); #endif pathUnitExtVehicleType[unit] = null; + pathUnitVehicleIds[unit] = null; - /*PathUnit[] expr_909_cp_0 = this._pathUnits.m_buffer; - UIntPtr expr_909_cp_1 = (UIntPtr)unit; - expr_909_cp_0[(int)expr_909_cp_1].m_pathFindFlags = (byte)(expr_909_cp_0[(int)expr_909_cp_1].m_pathFindFlags | 8);*/ return; } // we could calculate a valid path float totalPathLength = finalBufferItem.m_comparisonValue * this._maxLength; - this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_length = totalPathLength; + this.PathUnits.m_buffer[unit].m_length = totalPathLength; uint currentPathUnitId = unit; int currentItemPositionCount = 0; int sumOfPositionCounts = 0; @@ -443,36 +518,36 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { // the offsets differ: copy the found starting position and modify the offset to fit the desired offset PathUnit.Position position2 = currentPosition; position2.m_offset = startOffset; - this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].SetPosition(currentItemPositionCount++, position2); + this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2); // now we have: [desired starting position] } // add the found starting position to the path unit - this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].SetPosition(currentItemPositionCount++, currentPosition); - currentPosition = this._laneTarget[(int)((UIntPtr)finalBufferItem.m_laneID)]; // go to the next path position + this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition); + currentPosition = this._laneTarget[finalBufferItem.m_laneID]; // go to the next path position // now we have either [desired starting position, found starting position] or [found starting position], depending on if the found starting position matched the desired } // beginning with the starting position, going to the target position: assemble the path units - for (int k = 0; k < 262144; k++) { + for (int k = 0; k < 262144; ++k) { //pfCurrentState = 6; - this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].SetPosition(currentItemPositionCount++, currentPosition); // add the next path position to the current unit + this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition); // add the next path position to the current unit if ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) || (currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) { // we have reached the end position - this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_positionCount = (byte)currentItemPositionCount; + this.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount; sumOfPositionCounts += currentItemPositionCount; // add position count of last unit to sum if (sumOfPositionCounts != 0) { // for each path unit from start to target: calculate length (distance) to target - currentPathUnitId = this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_nextPathUnit; // (we do not need to calculate the length for the starting unit since this is done before; it's the total path length) - currentItemPositionCount = (int)this.PathUnits.m_buffer[(int)((UIntPtr)unit)].m_positionCount; + currentPathUnitId = this.PathUnits.m_buffer[unit].m_nextPathUnit; // (we do not need to calculate the length for the starting unit since this is done before; it's the total path length) + currentItemPositionCount = (int)this.PathUnits.m_buffer[unit].m_positionCount; int totalIter = 0; while (currentPathUnitId != 0u) { - this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_length = totalPathLength * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts; - currentItemPositionCount += (int)this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_positionCount; - currentPathUnitId = this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_nextPathUnit; + this.PathUnits.m_buffer[currentPathUnitId].m_length = totalPathLength * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts; + currentItemPositionCount += (int)this.PathUnits.m_buffer[currentPathUnitId].m_positionCount; + currentPathUnitId = this.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit; if (++totalIter >= 262144) { #if DEBUG Log.Error("THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: PathFindImplementation: Invalid list detected."); @@ -488,8 +563,10 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { PathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_READY; // Path found #if DEBUG ++_succeededPathFinds; - pathUnitExtVehicleType[unit] = null; #endif + pathUnitExtVehicleType[unit] = null; + pathUnitVehicleIds[unit] = null; + return; } @@ -498,26 +575,27 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { // the current path unit is full, we need a new one uint createdPathUnitId; try { - while (!Monitor.TryEnter(_bufferLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) { } + Monitor.Enter(_bufferLock); if (!this.PathUnits.CreateItem(out createdPathUnitId, ref this._pathRandomizer)) { // we failed to create a new path unit, thus the path-finding also failed - PathUnits.m_buffer[(int)((UIntPtr)unit)].m_pathFindFlags |= PathUnit.FLAG_FAILED; + PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED; #if DEBUG ++_failedPathFinds; //Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}"); #endif pathUnitExtVehicleType[unit] = null; + pathUnitVehicleIds[unit] = null; return; } - this.PathUnits.m_buffer[(int)((UIntPtr)createdPathUnitId)] = this.PathUnits.m_buffer[(int)currentPathUnitId]; - this.PathUnits.m_buffer[(int)((UIntPtr)createdPathUnitId)].m_referenceCount = 1; - this.PathUnits.m_buffer[(int)((UIntPtr)createdPathUnitId)].m_pathFindFlags = 4; - this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_nextPathUnit = createdPathUnitId; - this.PathUnits.m_buffer[(int)((UIntPtr)currentPathUnitId)].m_positionCount = (byte)currentItemPositionCount; + this.PathUnits.m_buffer[createdPathUnitId] = this.PathUnits.m_buffer[(int)currentPathUnitId]; + this.PathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1; + this.PathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY; + this.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId; + this.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount; sumOfPositionCounts += currentItemPositionCount; Singleton.instance.m_pathUnitCount = (int)(this.PathUnits.ItemCount() - 1u); } catch (Exception e) { - Log.Error("CustomPathFind.PathFindImplementation Error: " + e.ToString()); + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindImplementation Error: {e.ToString()}"); break; } finally { Monitor.Exit(this._bufferLock); @@ -533,13 +611,14 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) { CustomRoadAI.AddTraffic(currentPosition.m_segment, currentPosition.m_lane, (ushort)(this._isHeavyVehicle || _extVehicleType == ExtVehicleType.Bus ? 50 : 25), null); } // NON-STOCK CODE END - currentPosition = this._laneTarget[(int)((UIntPtr)laneID)]; + currentPosition = this._laneTarget[laneID]; } - PathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_FAILED; + PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED; #if DEBUG ++_failedPathFinds; #endif pathUnitExtVehicleType[unit] = null; + pathUnitVehicleIds[unit] = null; #if DEBUG //Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}"); #endif @@ -555,7 +634,8 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re #if DEBUGPF //bool debug = Options.disableSomething1 && item.m_position.m_segment == 1459 && nextNodeId == 19630; //bool debug = Options.disableSomething1 && (item.m_position.m_segment == 3833 || item.m_position.m_segment == 9649); - bool debug = Options.disableSomething1 && nextNodeId == 15458; + bool debug = Options.disableSomething1 && ((nextNodeId == 27237 && item.m_position.m_segment == 5699) || (nextNodeId == 5304 && item.m_position.m_segment == 9931) || (nextNodeId == 22427 && item.m_position.m_segment == 16166) || (nextNodeId == 18558 && item.m_position.m_segment == 2703)); + //bool debug = Options.disableSomething1 && ((nextNodeId == 27237 && item.m_position.m_segment == 5699) || (nextNodeId == 16068 && item.m_position.m_segment == 24398) || (nextNodeId == 12825 && item.m_position.m_segment == 17008)); #endif #if DEBUGPF /*if (m_queuedPathFindCount > 100 && Options.disableSomething1) @@ -570,8 +650,8 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re //bool debug = nextNodeId == 12732; #else bool debug = false; - LaneConnectionManager laneConnManager = LaneConnectionManager.Instance(); #endif + LaneConnectionManager laneConnManager = LaneConnectionManager.Instance(); #if DEBUGPF2 bool debug2 = debug; //Options.disableSomething1 && _extVehicleType == ExtVehicleType.Bicycle; @@ -586,14 +666,14 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re bool prevIsCenterPlatform = false; int similarLaneIndexFromInner = 0; // similar index, starting with 0 at leftmost lane NetInfo prevSegmentInfo = netManager.m_segments.m_buffer[(int)item.m_position.m_segment].Info; - int prevSimiliarLaneCount = 0; + byte prevSimilarLaneCount = 0; if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) { NetInfo.Lane prevLane = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane]; prevIsPedestrianLane = (prevLane.m_laneType == NetInfo.LaneType.Pedestrian); prevIsBicycleLane = (prevLane.m_laneType == NetInfo.LaneType.Vehicle && (prevLane.m_vehicleType & this._vehicleTypes) == VehicleInfo.VehicleType.Bicycle); //prevIsBusLane = (prevLane.m_laneType == NetInfo.LaneType.TransportVehicle && (prevLane.m_vehicleType & this._vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None); prevIsCenterPlatform = prevLane.m_centerPlatform; - prevSimiliarLaneCount = prevLane.m_similarLaneCount; + prevSimilarLaneCount = (byte)prevLane.m_similarLaneCount; if ((byte)(prevLane.m_finalDirection & NetInfo.Direction.Forward) != 0) { similarLaneIndexFromInner = prevLane.m_similarLaneIndex; } else { @@ -603,10 +683,17 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re int firstSimilarLaneIndexFromInner = similarLaneIndexFromInner; ushort prevSegmentId = item.m_position.m_segment; if (isMiddle) { - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 8; ++i) { ushort nextSegmentId = nextNode.GetSegment(i); if (nextSegmentId <= 0) continue; + +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + this.ProcessItemCosts(false, false, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromInner, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane); } } else if (prevIsPedestrianLane) { @@ -688,13 +775,19 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re } } else { //mCurrentState = 9; - for (int j = 0; j < 8; j++) { + for (int j = 0; j < 8; ++j) { ushort nextSegmentId = nextNode.GetSegment(j); if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) { #if DEBUGPF2 if (debug2) logBuf2.Add($"Exploring path! Segment {item.m_position.m_segment} @ node {nextNodeId}: going beauty1, seg. {nextSegmentId}, off {connectOffset}"); #endif +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + this.ProcessItemCosts(false, false, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromInner, connectOffset, false, true); } } @@ -760,8 +853,8 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re bool nextIsStartNodeOfPrevSegment = netManager.m_segments.m_buffer[(int)prevSegmentId].m_startNode == nextNodeId; //NetInfo.Direction normDirection = NetInfo.Direction.Backward;// TrafficPriority.IsLeftHandDrive() ? NetInfo.Direction.Forward : NetInfo.Direction.Backward; // direction to normalize indices to - bool isStrictLaneArrowPolicyEnabled = IsLaneArrowChangerEnabled() && - _extVehicleType != ExtVehicleType.Emergency && + bool isStrictLaneArrowPolicyEnabled = + (_extVehicleType != ExtVehicleType.Emergency || Options.disableSomething4) && (nextIsJunction || nextIsTransition) && //!Options.allRelaxed && !(Options.allRelaxed || (Options.relaxedBusses && _extVehicleType == ExtVehicleType.Bus)) && @@ -791,14 +884,14 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re int totalIncomingLanes = 0; // running number of next incoming lanes (number is updated at each segment iteration) int totalOutgoingLanes = 0; // running number of next outgoing lanes (number is updated at each segment iteration) - /*ushort[] incomingStraightSegments = null; // ids of incoming straight segments + ushort[] incomingStraightSegments = null; // ids of incoming straight segments ushort[] incomingRightSegments = null; // ids of incoming right segments ushort[] incomingLeftSegments = null; // ids of incoming left segments if (isStrictLaneArrowPolicyEnabled) { incomingStraightSegments = prevGeometry.GetIncomingStraightSegments(nextIsStartNodeOfPrevSegment); incomingLeftSegments = prevGeometry.GetIncomingLeftSegments(nextIsStartNodeOfPrevSegment); incomingRightSegments = prevGeometry.GetIncomingRightSegments(nextIsStartNodeOfPrevSegment); - }*/ + } // determine if we should explore the previous segment (for u-turns) bool explorePrevSegment = Flags.getUTurnAllowed(prevSegmentId, nextIsStartNodeOfPrevSegment) && @@ -815,11 +908,14 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re nextIsSimpleJunction = NodeGeometry.Get(nextNodeId).IsSimpleJunction; } + bool applyHighwayRules = Options.highwayRules && nextIsSimpleJunction && nextAreOnlyOneWayHighways && prevIsOutgoingOneWay && prevIsHighway; + bool applyHighwayRulesAtJunction = applyHighwayRules && nextIsRealJunction; + ushort nextSegmentId; if (explorePrevSegment) { nextSegmentId = prevSegmentId; } else { - if (TrafficPriority.IsLeftHandDrive()) { + if (TrafficPriorityManager.IsLeftHandDrive()) { nextSegmentId = netManager.m_segments.m_buffer[prevSegmentId].GetLeftSegment(nextNodeId); } else { nextSegmentId = netManager.m_segments.m_buffer[prevSegmentId].GetRightSegment(nextNodeId); @@ -839,7 +935,7 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer: Custom part started for vehicle type {_extVehicleType}"); #endif // NON-STOCK CODE END // - for (int k = 0; k < 8; k++) { + for (int k = 0; k < 8; ++k) { #if DEBUGPF if (debug) logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer: Segment Iteration {k}. nextSegmentId={nextSegmentId}"); @@ -868,6 +964,12 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re logBuf.Add($"Exploring path from {nextSegmentId} to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer: strict lane arrow policy disabled. ({nextIsJunction} || {nextIsTransition}) && !({Options.allRelaxed} || ({Options.relaxedBusses} && {_extVehicleType == ExtVehicleType.Bus})) && {(this._vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None}"); #endif +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + // NON-STOCK CODE END // if (ProcessItemCosts(true, true, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromInner, connectOffset, true, nextIsBeautificationNode)) { mayTurnAround = true; @@ -885,6 +987,13 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re #endif _vehicleTypes &= ~VehicleInfo.VehicleType.Car; + +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + if (ProcessItemCosts(false, false, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromInner, connectOffset, true, nextIsBeautificationNode)) { mayTurnAround = true; } @@ -904,23 +1013,30 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re SegmentGeometry nextGeometry = SegmentGeometry.Get(nextSegmentId); bool nextIsHighway = nextGeometry.IsHighway(); - bool applyHighwayRules = Options.highwayRules && nextIsSimpleJunction && nextAreOnlyOneWayHighways && prevIsOutgoingOneWay && prevIsHighway && nextIsRealJunction; - bool applyHighwayRulesAtSegment = applyHighwayRules; bool nextIsStartNodeOfNextSegment = netManager.m_segments.m_buffer[nextSegmentId].m_startNode == nextNodeId; if (nextSegmentId != prevSegmentId) { - isIncomingStraight = prevGeometry.IsIncomingStraightSegment(nextSegmentId, nextIsStartNodeOfPrevSegment); + for (int j = 0; j < incomingStraightSegments.Length; ++j) { + if (incomingStraightSegments[j] == nextSegmentId) + isIncomingStraight = true; + } + if (! isIncomingStraight) { - isIncomingRight = prevGeometry.IsIncomingRightSegment(nextSegmentId, nextIsStartNodeOfPrevSegment); + for (int j = 0; j < incomingRightSegments.Length; ++j) { + if (incomingRightSegments[j] == nextSegmentId) + isIncomingRight = true; + } + if (! isIncomingRight) { - isIncomingLeft = prevGeometry.IsIncomingLeftSegment(nextSegmentId, nextIsStartNodeOfPrevSegment); + for (int j = 0; j < incomingLeftSegments.Length; ++j) { + if (incomingLeftSegments[j] == nextSegmentId) + isIncomingLeft = true; + } + if (! isIncomingLeft) isValid = false; } } - /*incomingStraightSegments = prevGeometry.GetIncomingStraightSegments(nextIsStartNodeOfPrevSegment); - incomingLeftSegments = prevGeometry.GetIncomingLeftSegments(nextIsStartNodeOfPrevSegment); - incomingRightSegments = prevGeometry.GetIncomingRightSegments(nextIsStartNodeOfPrevSegment);*/ } else { isIncomingTurn = true; } @@ -928,14 +1044,8 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re // we need outgoing lanes too! if (!isValid) { // recalculate geometry if segment is unknown - /*if (!SegmentGeometry.Get(nextSegmentId).IsOutgoingOneWay(nextIsStartNodeOfNextSegment)) { -#if DEBUG - Log._Debug($"(PFWARN) Segment {nextSegmentId} is neither incoming left, right or straight segment @ {nextNodeId}, going to segment {prevSegmentId}"); -#endif - //prevGeometry.VerifyConnectedSegment(nextSegmentId); - }*/ - if (!applyHighwayRulesAtSegment) { + if (!applyHighwayRulesAtJunction) { #if DEBUGPF couldFindCustomPath = true; // not of interest #endif @@ -956,7 +1066,7 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re uint curLaneId = netManager.m_segments.m_buffer[nextSegmentId].m_lanes; byte laneIndex = 0; compatibleOuterSimilarIndexesMask = 0; // holds a bitmask that indicates which elements in `laneIndexByOuterSimilarIndex` are valid for the current run - bool hasLaneConnections = false; // true if lanes are connected by the lane connection tool + bool hasLaneConnections = false; // true if any lanes are connected by the lane connection tool while (laneIndex < nextSegmentInfo.m_lanes.Length && curLaneId != 0u) { @@ -1011,7 +1121,7 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re bool hasLeftArrow = ((NetLane.Flags)netManager.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Left) == NetLane.Flags.Left; bool hasRightArrow = ((NetLane.Flags)netManager.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Right) == NetLane.Flags.Right; bool hasForwardArrow = ((NetLane.Flags)netManager.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.Forward) != NetLane.Flags.None || ((NetLane.Flags)netManager.m_lanes.m_buffer[curLaneId].m_flags & NetLane.Flags.LeftForwardRight) == NetLane.Flags.None; - #if DEBUGPF +#if DEBUGPF if (debug) { if (hasLeftArrow) { logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex} has LEFT arrow. isIncomingRight? {isIncomingRight}, isIncomingLeft? {isIncomingLeft}, isIncomingStraight? {isIncomingStraight}"); @@ -1025,37 +1135,40 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex} has FORWARD arrow. isIncomingRight? {isIncomingRight}, isIncomingLeft? {isIncomingLeft}, isIncomingStraight? {isIncomingStraight}"); } } - #endif +#endif /*bool isValidIncomingRight = isIncomingRight && hasLeftArrow; bool isValidIncomingLeft = isIncomingLeft && hasRightArrow; bool isValidIncomingStraight = isIncomingStraight && hasForwardArrow; bool isValidIncomingTurn = isIncomingTurn && ((TrafficPriority.IsLeftHandDrive() && hasRightArrow) || (!TrafficPriority.IsLeftHandDrive() && hasLeftArrow));*/ - #if DEBUGPF +#if DEBUGPF /*if (debug) logBuf.Add($"Segment {nextSegmentId}, lane {curLaneId}, {laneIndex}. isValidIncomingRight? {isValidIncomingRight}, isValidIncomingLeft? {isValidIncomingLeft}, isValidIncomingStraight? {isValidIncomingStraight} isValidIncomingTurn? {isValidIncomingTurn}");*/ - #endif +#endif // add valid next lanes - if ((!nextHasOutgoingConnections || nextIsConnectedWithPrev) && - (nextHasOutgoingConnections || - applyHighwayRulesAtSegment || + if ( + (nextHasOutgoingConnections && nextIsConnectedWithPrev) || // lanes are connected + applyHighwayRules || // highway rules enabled (isIncomingRight && hasLeftArrow) || // valid incoming right (isIncomingLeft && hasRightArrow) || // valid incoming left (isIncomingStraight && hasForwardArrow) || // valid incoming straight - (isIncomingTurn && ((TrafficPriority.IsLeftHandDrive() && hasRightArrow) || (!TrafficPriority.IsLeftHandDrive() && hasLeftArrow))))) { // valid incoming turn + (isIncomingTurn && ((TrafficPriorityManager.IsLeftHandDrive() && hasRightArrow) || (!TrafficPriorityManager.IsLeftHandDrive() && hasLeftArrow))) // valid turning lane + ) { // valid incoming turn + laneIndexes[curLaneI] = laneIndex; laneIds[curLaneI] = curLaneId; nextOuterLaneSimilarIndexes[curLaneI] = nextOuterSimilarLaneIndex; nextInnerLaneSimilarIndexes[curLaneI] = nextInnerSimilarLaneIndex; laneIndexByOuterSimilarIndex[nextOuterSimilarLaneIndex] = curLaneI; hasOutgoingConnections[curLaneI] = nextHasOutgoingConnections; + nextIsConnectedWithPrevious[curLaneI] = nextIsConnectedWithPrev; compatibleOuterSimilarIndexesMask |= POW2MASKS[nextOuterSimilarLaneIndex]; - #if DEBUGPF +#if DEBUGPF if (debug) logBuf.Add($"Adding lane #{curLaneI} (id {curLaneId}, idx {laneIndex}), outer sim. idx: {nextOuterSimilarLaneIndex}, inner sim. idx.: {nextInnerSimilarLaneIndex}"); - #endif +#endif curLaneI++; } //} @@ -1064,8 +1177,8 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re } } - curLaneId = netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_nextLane; - laneIndex++; + curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane; + ++laneIndex; } // foreach lane if (curLaneI > 0) { @@ -1073,7 +1186,11 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re byte nextLaneIndex = 0; uint nextLaneId = 0u; short nextLaneI = -1; - short nextCompatibleLaneCount = curLaneI; + byte nextCompatibleLaneCount = curLaneI; + + // enable highway rules only at junctions or at simple lane merging/splitting points + short laneDiff = (short)((short)nextCompatibleLaneCount - (short)prevSimilarLaneCount); + bool applyHighwayRulesAtSegment = applyHighwayRules && (nextIsRealJunction || Math.Abs(laneDiff) == 1); #if DEBUGPF if (debug) { @@ -1083,77 +1200,172 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re #endif // mix of incoming/outgoing lanes on the right side of prev. segment is not allowed in highway mode - if (totalIncomingLanes > 0 && totalOutgoingLanes > 0) { + /*if (totalIncomingLanes > 0 && totalOutgoingLanes > 0) { // TODO should never happen since `nextIsSimpleJunction` == true #if DEBUGPF if (debug) logBuf.Add($"{totalIncomingLanes} incoming lanes and {totalOutgoingLanes} outgoing lanes found. Disabling highway rules."); #endif applyHighwayRulesAtSegment = false; - } + }*/ if (!hasLaneConnections && applyHighwayRulesAtSegment) { // apply highway rules - int numLanesSeen = Math.Max(totalIncomingLanes, totalOutgoingLanes); // number of lanes that were processed in earlier segment iterations (either all incoming or all outgoing) #if DEBUGPF if (debug) - logBuf.Add($"Applying highway rules. {numLanesSeen} lanes found ({totalIncomingLanes} incoming, {totalOutgoingLanes} outgoing)."); + logBuf.Add($"Applying highway rules. lanes found: {totalIncomingLanes} incoming, {totalOutgoingLanes} outgoing."); #endif - int nextInnerSimilarIndex; - /*int prevLeftSimilarIndex = TrafficPriority.IsLeftHandDrive() ? prevOuterSimilarLaneIndex : prevInnerSimilarLaneIndex; - int prevRightSimilarIndex = TrafficPriority.IsLeftHandDrive() ? prevInnerSimilarLaneIndex : prevOuterSimilarLaneIndex; - byte[] laneLeftSimilarLaneIndexes = TrafficPriority.IsLeftHandDrive() ? nextOuterLaneSimilarIndexes : nextInnerLaneSimilarIndexes; - byte[] laneRightSimilarLaneIndexes = TrafficPriority.IsLeftHandDrive() ? nextInnerLaneSimilarIndexes : nextOuterLaneSimilarIndexes;*/ - if (totalOutgoingLanes > 0) { - nextInnerSimilarIndex = prevInnerSimilarLaneIndex + numLanesSeen; // lane splitting + + if (applyHighwayRulesAtJunction) { + // we reached a highway junction where more than two segments are connected to each other + + int numLanesSeen = Math.Max(totalIncomingLanes, totalOutgoingLanes); // number of lanes that were processed in earlier segment iterations (either all incoming or all outgoing) + int nextInnerSimilarIndex; + + if (totalOutgoingLanes > 0) { + // lane splitting at junction + nextInnerSimilarIndex = prevInnerSimilarLaneIndex + numLanesSeen; #if DEBUGPF - if (debug) - logBuf.Add($"Performing lane split. nextInnerSimilarIndex={nextInnerSimilarIndex} = prevInnerSimilarLaneIndex({prevInnerSimilarLaneIndex}) + numLanesSeen({numLanesSeen})"); + if (debug) + logBuf.Add($"Performing lane split. nextInnerSimilarIndex={nextInnerSimilarIndex} = prevInnerSimilarLaneIndex({prevInnerSimilarLaneIndex}) + numLanesSeen({numLanesSeen})"); #endif - } else { - nextInnerSimilarIndex = prevInnerSimilarLaneIndex - numLanesSeen; // lane merging + } else { + // lane merging at junction + nextInnerSimilarIndex = prevInnerSimilarLaneIndex - numLanesSeen; + } #if DEBUGPF if (debug) logBuf.Add($"Performing lane merge. nextInnerSimilarIndex={nextInnerSimilarIndex} = prevInnerSimilarLaneIndex({prevInnerSimilarLaneIndex}) - numLanesSeen({numLanesSeen})"); #endif - } - if (nextInnerSimilarIndex >= 0 && nextInnerSimilarIndex < nextCompatibleLaneCount) { - // enough lanes available - nextLaneI = FindValue(ref nextInnerLaneSimilarIndexes, nextInnerSimilarIndex, nextCompatibleLaneCount);// Convert.ToInt32(indexByInnerSimilarLaneIndex[nextInnerSimilarIndex]) - 1; + if (nextInnerSimilarIndex >= 0 && nextInnerSimilarIndex < nextCompatibleLaneCount) { + // enough lanes available + nextLaneI = FindValue(ref nextInnerLaneSimilarIndexes, nextInnerSimilarIndex, nextCompatibleLaneCount);// Convert.ToInt32(indexByInnerSimilarLaneIndex[nextInnerSimilarIndex]) - 1; #if DEBUGPF if (debug) logBuf.Add($"Next lane within bounds. nextLaneI={nextLaneI}"); #endif - } else { - // Highway lanes "failed". Too few lanes at prevSegment or nextSegment. - if (nextInnerSimilarIndex < 0) { - // lane merging failed (too many incoming lanes) - if (totalIncomingLanes >= prevSimiliarLaneCount) { - // there have already been explored more incoming lanes than outgoing lanes on the previous segment. Allow the current segment to also join the big merging party. What a fun! - nextLaneI = FindValue(ref nextOuterLaneSimilarIndexes, prevOuterSimilarLaneIndex, nextCompatibleLaneCount); - } } else { - if (totalOutgoingLanes >= nextCompatibleLaneCount) { - // there have already been explored more outgoing lanes than incoming lanes on the previous segment. Also allow vehicles to go to the current segment. - nextLaneI = FindValue(ref nextOuterLaneSimilarIndexes, 0, nextCompatibleLaneCount); + // Highway lanes "failed". Too few lanes at prevSegment or nextSegment. + if (nextInnerSimilarIndex < 0) { + // lane merging failed (too many incoming lanes) + if (totalIncomingLanes >= prevSimilarLaneCount) { + // there have already been explored more incoming lanes than outgoing lanes on the previous segment. Allow the current segment to also join the big merging party. What a fun! + nextLaneI = FindValue(ref nextOuterLaneSimilarIndexes, prevOuterSimilarLaneIndex, nextCompatibleLaneCount); + } + } else { + if (totalOutgoingLanes >= nextCompatibleLaneCount) { + // there have already been explored more outgoing lanes than incoming lanes on the previous segment. Also allow vehicles to go to the current segment. + nextLaneI = FindValue(ref nextOuterLaneSimilarIndexes, 0, nextCompatibleLaneCount); + } } - } - // If nextLaneI is still -1 here, then highways rules really cannot handle this situation (that's ok). + // If nextLaneI is still -1 here, then highways rules really cannot handle this situation (that's ok). #if DEBUGPF if (debug) logBuf.Add($"Next lane out of bounds. nextLaneI={nextLaneI}, isIncomingLeft={isIncomingLeft}, prevOuterSimilarLaneIndex={prevOuterSimilarLaneIndex}, prevInnerSimilarLaneIndex={prevInnerSimilarLaneIndex}"); #endif - } + } - if (nextLaneI < 0 || nextLaneI >= nextCompatibleLaneCount) { + if (nextLaneI < 0 || nextLaneI >= nextCompatibleLaneCount) { +#if DEBUGPF + if (debug) + Log.Error($"(PFERR) Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer, {prevInnerSimilarLaneIndex} from inner: Highway lane selector cannot find suitable lane! isIncomingLeft={isIncomingLeft} isIncomingRight={isIncomingRight} totalIncomingLanes={totalIncomingLanes}"); + couldFindCustomPath = true; // not of interest for us +#endif + goto nextIter; // no path to this lane + } + } else { #if DEBUGPF if (debug) - Log.Error($"(PFERR) Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane id {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer, {prevInnerSimilarLaneIndex} from inner: Highway lane selector cannot find suitable lane! isIncomingLeft={isIncomingLeft} isIncomingRight={isIncomingRight} totalIncomingLanes={totalIncomingLanes}"); - couldFindCustomPath = true; // not of interest for us + logBuf.Add($"Simple lane splitting/merging point @ highway rules! laneDiff={laneDiff}"); +#endif + + // simple lane splitting/merging point. the number of lanes is guaranteed to differ by 1 + short minNextOuterSimilarIndex = -1; + short maxNextOuterSimilarIndex = -1; + + if (laneDiff == 1) { + // simple lane merge + if (prevOuterSimilarLaneIndex == 0) { + // merge outer lane + minNextOuterSimilarIndex = 0; + maxNextOuterSimilarIndex = 1; + +#if DEBUGPF + if (debug) + logBuf.Add($"Simple lane splitting/merging point @ highway rules! Lane merge: Merging outer lane: {minNextOuterSimilarIndex} - {maxNextOuterSimilarIndex}"); +#endif + } else { + // other lanes stay + 1 + minNextOuterSimilarIndex = maxNextOuterSimilarIndex = (short)(prevOuterSimilarLaneIndex + 1); + +#if DEBUGPF + if (debug) + logBuf.Add($"Simple lane splitting/merging point @ highway rules! Lane merge: Other lanes stay + 1: {minNextOuterSimilarIndex} - {maxNextOuterSimilarIndex}"); +#endif + } + } else { // diff == -1 + // simple lane split + if (prevOuterSimilarLaneIndex <= 1) { + // split outer lane + minNextOuterSimilarIndex = maxNextOuterSimilarIndex = 0; + +#if DEBUGPF + if (debug) + logBuf.Add($"Simple lane splitting/merging point @ highway rules! Lane split: Outer lane splits: {minNextOuterSimilarIndex} - {maxNextOuterSimilarIndex}"); +#endif + } else { + // other lanes stay - 1 + minNextOuterSimilarIndex = maxNextOuterSimilarIndex = (short)(prevOuterSimilarLaneIndex - 1); +#if DEBUGPF + if (debug) + logBuf.Add($"Simple lane splitting/merging point @ highway rules! Lane split: Other lanes stay - 1: {minNextOuterSimilarIndex} - {maxNextOuterSimilarIndex}"); +#endif + } + } + + + // explore lanes + for (short nextOuterSimilarIndex = minNextOuterSimilarIndex; nextOuterSimilarIndex <= maxNextOuterSimilarIndex; ++nextOuterSimilarIndex) { +#if DEBUGPF + if (debug) + logBuf.Add($"current outer similar index = {nextOuterSimilarIndex}, min. {minNextOuterSimilarIndex} max. {maxNextOuterSimilarIndex}"); +#endif + nextLaneI = FindCompatibleLane(ref laneIndexByOuterSimilarIndex, compatibleOuterSimilarIndexesMask, nextOuterSimilarIndex); + +#if DEBUGPF + if (debug) + logBuf.Add($"(*) nextLaneI = {nextLaneI}"); +#endif + if (nextLaneI < 0) { + continue; + } + + // go to matched lane + nextLaneIndex = laneIndexes[nextLaneI]; + nextLaneId = laneIds[nextLaneI]; + +#if DEBUGPF + if (debug) + logBuf.Add($"Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane idx {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer. There are {curLaneI} candidate lanes. We choose lane {nextLaneI} (index {nextLaneIndex}, {nextOuterSimilarIndex} compatible from outer). lhd: {TrafficPriorityManager.IsLeftHandDrive()}, ped: {pedestrianAllowed}, magical flag4: {mayTurnAround}"); +#endif + +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + + if (ProcessItemCosts(true, false, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromInner, connectOffset, true, nextIsBeautificationNode, nextLaneIndex, nextLaneId, out foundForced)) { + mayTurnAround = true; + } +#if DEBUGPF + couldFindCustomPath = true; #endif - goto nextIter; // no path to this lane + } + + goto nextIter; } } else if (nextCompatibleLaneCount == 1) { nextLaneI = 0; @@ -1163,19 +1375,41 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re #endif } else { // city rules, multiple lanes: lane matching - byte prevSimilarLaneCount = (byte)prevLane.m_similarLaneCount; - #if DEBUGPF if (debug) - logBuf.Add($"Multiple target lanes found. prevSimilarLaneCount={prevSimilarLaneCount}"); + logBuf.Add($"Multiple target lanes ({nextCompatibleLaneCount}) found. prevSimilarLaneCount={prevSimilarLaneCount}"); #endif + // min/max compatible outer similar lane indices short minNextOuterSimilarIndex = -1; short maxNextOuterSimilarIndex = -1; if (nextIsRealJunction) { - // at junctions: try to match distinct lanes (1-to-1, n-to-1) - minNextOuterSimilarIndex = prevOuterSimilarLaneIndex; - maxNextOuterSimilarIndex = prevOuterSimilarLaneIndex; + // at junctions: try to match distinct lanes + + /*if (prevOuterSimilarLaneIndex >= nextCompatibleLaneCount-1) { + // split inner lane + minNextOuterSimilarIndex = maxNextOuterSimilarIndex = (short)((short)nextCompatibleLaneCount - 1); +#if DEBUGPF + if (debug) + logBuf.Add($"City rules: Splitting inner lane. minNextOuterSimilarLaneIndex={minNextOuterSimilarIndex} maxNextOuterSimilarLaneIndex={maxNextOuterSimilarIndex}"); +#endif + } else */if (nextCompatibleLaneCount > prevSimilarLaneCount && prevOuterSimilarLaneIndex == prevSimilarLaneCount-1) { + // merge inner lanes + minNextOuterSimilarIndex = prevOuterSimilarLaneIndex; + maxNextOuterSimilarIndex = (short)((short)nextCompatibleLaneCount - 1); +#if DEBUGPF + if (debug) + logBuf.Add($"City rules: Merging inner lanes. minNextOuterSimilarLaneIndex={minNextOuterSimilarIndex} maxNextOuterSimilarLaneIndex={maxNextOuterSimilarIndex}"); +#endif + } else { + // 1-to-n (lane splitting is done by FindCompatibleLane), 1-to-1 (direct lane matching) + minNextOuterSimilarIndex = prevOuterSimilarLaneIndex; + maxNextOuterSimilarIndex = prevOuterSimilarLaneIndex; +#if DEBUGPF + if (debug) + logBuf.Add($"City rules: 1-to-1/1-to-n. minNextOuterSimilarLaneIndex={minNextOuterSimilarIndex} maxNextOuterSimilarLaneIndex={maxNextOuterSimilarIndex}"); +#endif + } bool mayChangeLanes = isIncomingStraight && Flags.getStraightLaneChangingAllowed(nextSegmentId, nextIsStartNodeOfPrevSegment); #if DEBUGPF @@ -1185,14 +1419,14 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re if (!mayChangeLanes) { bool nextHasBusLane = nextGeometry.HasBusLane(); if (nextHasBusLane && !prevHasBusLane) { + // allow vehicles on the bus lane AND on the next lane to merge on this lane maxNextOuterSimilarIndex = (short)Math.Min(nextCompatibleLaneCount - 1, maxNextOuterSimilarIndex + 1); } else if (!nextHasBusLane && prevHasBusLane) { + // allow vehicles to enter the bus lane minNextOuterSimilarIndex = (short)Math.Max(0, minNextOuterSimilarIndex - 1); } - } - - // vehicles may change lanes when going straight? - if (mayChangeLanes) { + } else { + // vehicles may change lanes when going straight minNextOuterSimilarIndex = (short)Math.Max(0, minNextOuterSimilarIndex - 1); maxNextOuterSimilarIndex = (short)Math.Min(nextCompatibleLaneCount - 1, maxNextOuterSimilarIndex + 1); #if DEBUGPF @@ -1202,7 +1436,7 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re } #if DEBUGPF if (debug) - logBuf.Add($"Next is junction. minNextOuterSimilarIndex={minNextOuterSimilarIndex}, maxNextOuterSimilarIndex={maxNextOuterSimilarIndex}"); + logBuf.Add($"Next is junction with city rules. minNextOuterSimilarIndex={minNextOuterSimilarIndex}, maxNextOuterSimilarIndex={maxNextOuterSimilarIndex}"); #endif } else { // lane merging/splitting @@ -1214,20 +1448,30 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re } // find best matching lane(s) - for (short nextOuterSimilarIndex = minNextOuterSimilarIndex; nextOuterSimilarIndex <= maxNextOuterSimilarIndex; ++nextOuterSimilarIndex) { + for (short nextOuterSimilarIndex = hasLaneConnections ? (short)0 : minNextOuterSimilarIndex; nextOuterSimilarIndex < (hasLaneConnections ? (short)nextCompatibleLaneCount : (short)(minNextOuterSimilarIndex+1)); ++nextOuterSimilarIndex) { + nextLaneI = FindCompatibleLane(ref laneIndexByOuterSimilarIndex, compatibleOuterSimilarIndexesMask, nextOuterSimilarIndex); + + if (nextLaneI < 0) { + continue; + } + + if (nextOuterSimilarIndex < minNextOuterSimilarIndex || nextOuterSimilarIndex > maxNextOuterSimilarIndex) { + if (!hasOutgoingConnections[nextLaneI] || !nextIsConnectedWithPrevious[nextLaneI]) + continue; // disregard lane since it is not connected to previous lane + } else { + if (hasOutgoingConnections[nextLaneI] && !nextIsConnectedWithPrevious[nextLaneI]) + continue; // disregard lane since it is not connected to previous lane but has outgoing connections + } + #if DEBUGPF if (debug) logBuf.Add($"current outer similar index = {nextOuterSimilarIndex}, min. {minNextOuterSimilarIndex} max. {maxNextOuterSimilarIndex}"); #endif - nextLaneI = FindCompatibleLane(ref laneIndexByOuterSimilarIndex, compatibleOuterSimilarIndexesMask, nextOuterSimilarIndex); #if DEBUGPF if (debug) logBuf.Add($"(*) nextLaneI = {nextLaneI}"); #endif - if (nextLaneI < 0) { - continue; - } // go to matched lane nextLaneIndex = laneIndexes[nextLaneI]; @@ -1235,8 +1479,14 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re #if DEBUGPF if (debug) - logBuf.Add($"Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane idx {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer. There are {curLaneI} candidate lanes. We choose lane {nextLaneI} (index {nextLaneIndex}, {nextOuterSimilarIndex} compatible from outer). lhd: {TrafficPriority.IsLeftHandDrive()}, ped: {pedestrianAllowed}, magical flag4: {mayTurnAround}"); + logBuf.Add($"Exploring path from {nextSegmentId} ({nextDir}) to {item.m_position.m_segment}, lane idx {item.m_position.m_lane}, {prevOuterSimilarLaneIndex} from outer. There are {curLaneI} candidate lanes. We choose lane {nextLaneI} (index {nextLaneIndex}, {nextOuterSimilarIndex} compatible from outer). lhd: {TrafficPriorityManager.IsLeftHandDrive()}, ped: {pedestrianAllowed}, magical flag4: {mayTurnAround}"); +#endif +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } #endif + if (ProcessItemCosts(true, false, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromInner, connectOffset, true, nextIsBeautificationNode, nextLaneIndex, nextLaneId, out foundForced)) { mayTurnAround = true; } @@ -1295,6 +1545,12 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re } } +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + if (ProcessItemCosts(true, true, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref similarLaneIndexFromInner, connectOffset, true, nextIsBeautificationNode, nextLaneIndex, nextLaneId, out foundForced)) { mayTurnAround = true; } @@ -1323,6 +1579,12 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re } else { // we were coming from a beautfication node; visiting a park building or a pedestrian/bicycle pathway +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + // stock code: if (this.ProcessItemCosts(false, false, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromInner, connectOffset, true, nextIsBeautificationNode)) { mayTurnAround = true; @@ -1344,11 +1606,11 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re }*/ //} - nextIter: + nextIter: if (nextSegmentId == prevSegmentId) similarLaneIndexFromInner = firstSimilarLaneIndexFromInner; // u-turning does not "consume" a lane - if (TrafficPriority.IsLeftHandDrive()) { + if (TrafficPriorityManager.IsLeftHandDrive()) { nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetLeftSegment(nextNodeId); } else { nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId); @@ -1374,6 +1636,13 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re #endif // vehicles may turn around if the street is blocked nextSegmentId = item.m_position.m_segment; + +#if DEBUGPF + if (debug) { + FlushMainLog(logBuf, unitId); + } +#endif + this.ProcessItemCosts(false, false, debug, item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref similarLaneIndexFromInner, connectOffset, true, false); } @@ -1401,7 +1670,7 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re // transport lines, cargo lines, etc. bool targetDisabled = (nextNode.m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None; - ushort nextSegment = netManager.m_lanes.m_buffer[(int)((UIntPtr)nextNode.m_lane)].m_segment; + ushort nextSegment = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment; if (nextSegment != 0 && nextSegment != item.m_position.m_segment) { #if DEBUGPF if (debug) @@ -1417,11 +1686,10 @@ private void ProcessItemMain(uint unitId, BufferItem item, ushort nextNodeId, re #if DEBUGPF if (debug) { - foreach (String toLog in logBuf) { - Log._Debug($"Pathfinder ({this._pathFindIndex}) for unit {unitId}: " + toLog); - } + FlushMainLog(logBuf, unitId); } #endif + #if DEBUGPF2 if (debug2) { foreach (String toLog in logBuf2) { @@ -1447,7 +1715,7 @@ private static short FindValue(ref byte[] values, int value, short length) { } /// - /// 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. + /// 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. /// /// array to be queried /// a bitmask holding all valid indices of `values` @@ -1461,7 +1729,7 @@ private static short FindCompatibleLane(ref byte[] values, ushort validMask, sho nextLaneI = values[i]; if (n <= 0) - break; + break; // found exact index --n; } return nextLaneI; @@ -1470,12 +1738,12 @@ private static short FindCompatibleLane(ref byte[] values, ushort validMask, sho /// /// Calculates minimum and maximum outer similar lane indices for lane merging/splitting. /// - /// previous outer similar lane index + /// previous similar lane index /// number of found compatible lanes at the next segment /// number of similar lanes at the previous segment - /// output: minimum outer similar lane index - /// ouput: maximum outer similar lane index - private void HandleLaneMergesAndSplits(ref BufferItem item, ushort nextSegmentId, short prevOuterSimilarLaneIndex, short nextCompatibleLaneCount, short prevSimilarLaneCount, out short minNextOuterSimilarLaneIndex, out short maxNextOuterSimilarLaneIndex) { + /// output: minimum outer similar lane index + /// ouput: maximum outer similar lane index + private void HandleLaneMergesAndSplits(ref BufferItem item, ushort nextSegmentId, short prevSimilarLaneIndex, short nextCompatibleLaneCount, short prevSimilarLaneCount, out short minNextSimilarLaneIndex, out short maxNextSimilarLaneIndex) { #if DEBUGMERGE uint cp = 0; #endif @@ -1497,25 +1765,25 @@ private void HandleLaneMergesAndSplits(ref BufferItem item, ushort nextSegmentId #if DEBUGMERGE cp |= 512u; #endif - minNextOuterSimilarLaneIndex = 0; - maxNextOuterSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 - } else if (prevOuterSimilarLaneIndex == 0) { + minNextSimilarLaneIndex = 0; + maxNextSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 + } else if (prevSimilarLaneIndex == 0) { #if DEBUGMERGE cp |= 1024u; #endif - minNextOuterSimilarLaneIndex = 0; - maxNextOuterSimilarLaneIndex = a; - } else if (prevOuterSimilarLaneIndex == prevSimilarLaneCount - 1) { + minNextSimilarLaneIndex = 0; + maxNextSimilarLaneIndex = a; + } else if (prevSimilarLaneIndex == prevSimilarLaneCount - 1) { #if DEBUGMERGE cp |= 2048u; #endif - minNextOuterSimilarLaneIndex = (short)(prevOuterSimilarLaneIndex + a); - maxNextOuterSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 + minNextSimilarLaneIndex = (short)(prevSimilarLaneIndex + a); + maxNextSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 } else { #if DEBUGMERGE cp |= 4096u; #endif - minNextOuterSimilarLaneIndex = maxNextOuterSimilarLaneIndex = (short)(prevOuterSimilarLaneIndex + a); + minNextSimilarLaneIndex = maxNextSimilarLaneIndex = (short)(prevSimilarLaneIndex + a); } } else { #if DEBUGMERGE @@ -1528,37 +1796,37 @@ private void HandleLaneMergesAndSplits(ref BufferItem item, ushort nextSegmentId #if DEBUGMERGE cp |= 8192u; #endif - minNextOuterSimilarLaneIndex = 0; - maxNextOuterSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 - } else if (prevOuterSimilarLaneIndex == 0) { + minNextSimilarLaneIndex = 0; + maxNextSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 + } else if (prevSimilarLaneIndex == 0) { #if DEBUGMERGE cp |= 16384u; #endif - minNextOuterSimilarLaneIndex = 0; - maxNextOuterSimilarLaneIndex = b; - } else if (prevOuterSimilarLaneIndex == prevSimilarLaneCount - 1) { + minNextSimilarLaneIndex = 0; + maxNextSimilarLaneIndex = b; + } else if (prevSimilarLaneIndex == prevSimilarLaneCount - 1) { #if DEBUGMERGE cp |= 32768u; #endif - minNextOuterSimilarLaneIndex = (short)(prevOuterSimilarLaneIndex + a); - maxNextOuterSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 + minNextSimilarLaneIndex = (short)(prevSimilarLaneIndex + a); + maxNextSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); // always >=0 } else if (_pathRandomizer.Int32(0, 1) == 0) { #if DEBUGMERGE cp |= 65536u; #endif - minNextOuterSimilarLaneIndex = maxNextOuterSimilarLaneIndex = (short)(prevOuterSimilarLaneIndex + a); + minNextSimilarLaneIndex = maxNextSimilarLaneIndex = (short)(prevSimilarLaneIndex + a); } else { #if DEBUGMERGE cp |= 131072u; #endif - minNextOuterSimilarLaneIndex = maxNextOuterSimilarLaneIndex = (short)(prevOuterSimilarLaneIndex + b); + minNextSimilarLaneIndex = maxNextSimilarLaneIndex = (short)(prevSimilarLaneIndex + b); } } } else if (prevSimilarLaneCount == nextCompatibleLaneCount) { #if DEBUGMERGE cp |= 2u; #endif - minNextOuterSimilarLaneIndex = maxNextOuterSimilarLaneIndex = prevOuterSimilarLaneIndex; + minNextSimilarLaneIndex = maxNextSimilarLaneIndex = prevSimilarLaneIndex; } else { #if DEBUGMERGE cp |= 4u; @@ -1571,7 +1839,7 @@ private void HandleLaneMergesAndSplits(ref BufferItem item, ushort nextSegmentId #endif // split outer lanes short a = (short)((byte)(prevSimilarLaneCount - nextCompatibleLaneCount) >> 1); // prevSimilarLaneCount - nextCompatibleLaneCount is always > 0 - minNextOuterSimilarLaneIndex = maxNextOuterSimilarLaneIndex = (short)(prevOuterSimilarLaneIndex - a); // a is always <= prevSimilarLaneCount + minNextSimilarLaneIndex = maxNextSimilarLaneIndex = (short)(prevSimilarLaneIndex - a); // a is always <= prevSimilarLaneCount } else { #if DEBUGMERGE cp |= 256u; @@ -1579,35 +1847,35 @@ private void HandleLaneMergesAndSplits(ref BufferItem item, ushort nextSegmentId // split outer lanes, criss-cross inner lanes short a = (short)((byte)(prevSimilarLaneCount - nextCompatibleLaneCount - 1) >> 1); // prevSimilarLaneCount - nextCompatibleLaneCount - 1 is always >= 0 - minNextOuterSimilarLaneIndex = (a - 1 >= prevOuterSimilarLaneIndex) ? (short)0 : (short)(prevOuterSimilarLaneIndex - a - 1); - maxNextOuterSimilarLaneIndex = (a >= prevOuterSimilarLaneIndex) ? (short)0 : (short)(prevOuterSimilarLaneIndex - a); + minNextSimilarLaneIndex = (a - 1 >= prevSimilarLaneIndex) ? (short)0 : (short)(prevSimilarLaneIndex - a - 1); + maxNextSimilarLaneIndex = (a >= prevSimilarLaneIndex) ? (short)0 : (short)(prevSimilarLaneIndex - a); #if DEBUGPF - if (minNextOuterSimilarLaneIndex > maxNextOuterSimilarLaneIndex) { - Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: HandleLaneMergesAndSplits: item: lane {item.m_position.m_lane} @ seg. {item.m_position.m_segment}, nextSegmentId={nextSegmentId} prevSimilarLaneCount={prevSimilarLaneCount} nextCompatibleLaneCount={nextCompatibleLaneCount} a={a} prevOuterSimilarLaneIndex={prevOuterSimilarLaneIndex} minNextOuterSimilarLaneIndex={minNextOuterSimilarLaneIndex} maxNextOuterSimilarLaneIndex={maxNextOuterSimilarLaneIndex}"); + if (minNextSimilarLaneIndex > maxNextSimilarLaneIndex) { + Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: HandleLaneMergesAndSplits: item: lane {item.m_position.m_lane} @ seg. {item.m_position.m_segment}, nextSegmentId={nextSegmentId} prevSimilarLaneCount={prevSimilarLaneCount} nextCompatibleLaneCount={nextCompatibleLaneCount} a={a} prevSimilarLaneIndex={prevSimilarLaneIndex} minNextSimilarLaneIndex={minNextSimilarLaneIndex} maxNextSimilarLaneIndex={maxNextSimilarLaneIndex}"); } #endif } } - if (minNextOuterSimilarLaneIndex > nextCompatibleLaneCount - 1) { + if (minNextSimilarLaneIndex > nextCompatibleLaneCount - 1) { #if DEBUGMERGE cp |= 8u; #endif - minNextOuterSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); + minNextSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); } - if (maxNextOuterSimilarLaneIndex > nextCompatibleLaneCount - 1) { + if (maxNextSimilarLaneIndex > nextCompatibleLaneCount - 1) { #if DEBUGMERGE cp |= 16u; #endif - maxNextOuterSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); + maxNextSimilarLaneIndex = (short)(nextCompatibleLaneCount - 1); } - if (minNextOuterSimilarLaneIndex > maxNextOuterSimilarLaneIndex) { + if (minNextSimilarLaneIndex > maxNextSimilarLaneIndex) { #if DEBUGPF && DEBUGMERGE Log.Error($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Erroneous calculation in HandleMergeAndSplits detected! cp={cp}"); #endif - minNextOuterSimilarLaneIndex = maxNextOuterSimilarLaneIndex; + minNextSimilarLaneIndex = maxNextSimilarLaneIndex; } } @@ -1646,7 +1914,7 @@ private void ProcessItemPublicTransport(BufferItem item, ushort targetNodeId, bo float offsetLength = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * 0.003921569f * averageLength; float methodDistance = item.m_methodDistance + offsetLength; float comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * this._maxLength); - Vector3 b = instance.m_lanes.m_buffer[(int)((UIntPtr)item.m_laneID)].CalculatePosition((float)connectOffset * 0.003921569f); + Vector3 b = instance.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * 0.003921569f); uint laneIndex = 0; #if DEBUG int wIter = 0; @@ -1663,7 +1931,7 @@ private void ProcessItemPublicTransport(BufferItem item, ushort targetNodeId, bo if (nextLane == curLaneId) { NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[laneIndex]; if (nextLaneInfo.CheckType(this._laneTypes, this._vehicleTypes)) { - Vector3 a = instance.m_lanes.m_buffer[(int)((UIntPtr)nextLane)].CalculatePosition((float)offset * 0.003921569f); + Vector3 a = instance.m_lanes.m_buffer[nextLane].CalculatePosition((float)offset * 0.003921569f); float distance = Vector3.Distance(a, b); BufferItem nextItem; // NON-STOCK CODE START // @@ -1711,8 +1979,8 @@ private void ProcessItemPublicTransport(BufferItem item, ushort targetNodeId, bo } return; } - curLaneId = instance.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_nextLane; - laneIndex++; + curLaneId = instance.m_lanes.m_buffer[curLaneId].m_nextLane; + ++laneIndex; } } #endregion @@ -1729,6 +1997,14 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: ProcessItemSub item {item.m_position.m_segment} {item.m_position.m_lane}, targetNodeId {targetNodeId}");*/ #endif +#if DEBUGPF + List logBuf = null; + if (debug) { + logBuf = new List(); + logBuf.Add($"ProcessItemCosts called. Exploring path from {nextSegmentId} to {item.m_position.m_segment} lane id {item.m_position.m_lane} @ {targetNodeId}. forceLaneIndex={forceLaneIndex}, forceLaneId={forceLaneId}"); + } +#endif + //bool emergencyLaneSelection = (Options.disableSomething3 && _extVehicleType == ExtVehicleType.Emergency); LaneConnectionManager laneConnManager = Options.laneConnectorEnabled ? LaneConnectionManager.Instance() : null; @@ -1736,6 +2012,14 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow foundForced = false; bool blocked = false; if ((nextSegment.m_flags & (NetSegment.Flags.PathFailed | NetSegment.Flags.Flooded)) != NetSegment.Flags.None) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Segment is PathFailed or Flooded: {nextSegment.m_flags}"); +#endif +#if DEBUGPF + if (debug) + FlushCostLog(logBuf); +#endif return blocked; } NetManager netManager = Singleton.instance; @@ -1758,8 +2042,16 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow } else { vector2 = nextSegment.m_startDirection; } - float sqrDistance = vector.x * vector2.x + vector.z * vector2.z; - if (sqrDistance >= turningAngle) { + float sqrLen = vector.x * vector2.x + vector.z * vector2.z; + if (sqrLen >= turningAngle) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: turningAngle < 1f! sqrLen={sqrLen} >= turningAngle{turningAngle}!"); +#endif +#if DEBUGPF + if (debug) + FlushCostLog(logBuf); +#endif return blocked; } } @@ -1772,6 +2064,11 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow !_stablePath && enableVehicle; +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: useAdvancedAI={useAdvancedAI}, isStockLaneChangerUsed={Options.isStockLaneChangerUsed()}, _extVehicleType={_extVehicleType}, allowCustomLaneChanging={allowCustomLaneChanging}, nonBus={((ExtVehicleType)_extVehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) != ExtVehicleType.None}, _stablePath={_stablePath}, enableVehicle={enableVehicle}"); +#endif + float prevMaxSpeed = 1f; float prevLaneSpeed = 1f; NetInfo.LaneType prevLaneType = NetInfo.LaneType.None; @@ -1782,7 +2079,7 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow prevIsHighway = ((RoadBaseAI)prevSegmentInfo.m_netAI).m_highwayRules; int prevOuterSimilarLaneIndex = -1; int prevNumLanes = 1; - //float prevSpeed = 1f; + float prevSpeed = 1f; float prevDensity = 0.1f; bool isMiddle = connectOffset != 0 && connectOffset != 255; NetInfo.Lane prevLaneInfo = null; @@ -1801,10 +2098,11 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow prevOuterSimilarLaneIndex = prevLaneInfo.m_similarLaneIndex; } if (useAdvancedAI) { - //prevSpeed = CustomRoadAI.laneMeanSpeeds[item.m_position.m_segment] != null && item.m_position.m_lane < CustomRoadAI.laneMeanSpeeds[item.m_position.m_segment].Length ? CustomRoadAI.laneMeanSpeeds[item.m_position.m_segment][item.m_position.m_lane] : 1f; - prevDensity = CustomRoadAI.laneMeanRelDensities[item.m_position.m_segment] != null && item.m_position.m_lane < CustomRoadAI.laneMeanRelDensities[item.m_position.m_segment].Length ? CustomRoadAI.laneMeanRelDensities[item.m_position.m_segment][item.m_position.m_lane] : 0.1f; + prevSpeed = CustomRoadAI.laneMeanSpeeds[item.m_position.m_segment] != null && item.m_position.m_lane < CustomRoadAI.laneMeanSpeeds[item.m_position.m_segment].Length ? CustomRoadAI.laneMeanSpeeds[item.m_position.m_segment][item.m_position.m_lane] : 1f; + prevDensity = CustomRoadAI.laneMeanAbsDensities[item.m_position.m_segment] != null && item.m_position.m_lane < CustomRoadAI.laneMeanAbsDensities[item.m_position.m_segment].Length ? CustomRoadAI.laneMeanAbsDensities[item.m_position.m_segment][item.m_position.m_lane] : 0.1f; + //prevDensity = CustomRoadAI.laneMeanRelDensities[item.m_position.m_segment] != null && item.m_position.m_lane < CustomRoadAI.laneMeanRelDensities[item.m_position.m_segment].Length ? CustomRoadAI.laneMeanRelDensities[item.m_position.m_segment][item.m_position.m_lane] : 0.1f; - //prevSpeed = (float)Math.Max(0.1f, Math.Round(prevSpeed * 0.1f) / 10f); // 0.01, 0.1, 0.2, ... , 1 + prevSpeed = (float)Math.Max(0.1f, Math.Round(prevSpeed * 0.1f) / 10f); // 0.01, 0.1, 0.2, ... , 1 prevDensity = (float)Math.Max(0.1f, Math.Round(prevDensity * 0.1f) / 10f); // 0.1, 0.2, ..., 1 } // NON-STOCK CODE END // @@ -1816,6 +2114,11 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow (prevLaneType & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.Parking)) == NetInfo.LaneType.None; // NON-STOCK CODE } +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: useAdvancedAI(2)={useAdvancedAI}, prevCar={(prevVehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None}, prevPedParking={(prevLaneType & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.Parking)) == NetInfo.LaneType.None}"); +#endif + float prevCost = netManager.m_segments.m_buffer[(int)item.m_position.m_segment].m_averageLength; if (!useAdvancedAI && !this._stablePath) { // NON-STOCK CODE // CO randomization. Only randomizes over segments, not over lanes. @@ -1832,14 +2135,26 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow if (this._isHeavyVehicle && (netManager.m_segments.m_buffer[(int)item.m_position.m_segment].m_flags & NetSegment.Flags.HeavyBan) != NetSegment.Flags.None) { // heavy vehicle ban +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: applying heavy vehicle ban on deactivated advanced AI"); +#endif prevCost *= 10f; } else if (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & _vehicleTypes) == VehicleInfo.VehicleType.Car && (netManager.m_segments.m_buffer[(int)item.m_position.m_segment].m_flags & NetSegment.Flags.CarBan) != NetSegment.Flags.None) { // car ban: used by "Old Town" policy +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: applying old town policy on deactivated advanced AI"); +#endif prevCost *= 5f; } if (this._transportVehicle && prevLaneType == NetInfo.LaneType.TransportVehicle) { // public transport/emergency vehicles should stay on their designated lanes, if possible +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: applying public transport cost modifier on deactivated advaned AI"); +#endif prevCost *= 0.5f; // non-stock value } } @@ -1857,8 +2172,8 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & _vehicleTypes) == VehicleInfo.VehicleType.Car && (netManager.m_segments.m_buffer[(int)item.m_position.m_segment].m_flags & NetSegment.Flags.CarBan) != NetSegment.Flags.None)) { #if DEBUGPF - if (Options.disableSomething1 && debug) { - Log._Debug($"Vehicle {_extVehicleType} should not use lane {item.m_position.m_lane} @ seg. {item.m_position.m_segment}, null? {prevLaneInfo == null}"); + if (debug) { + logBuf.Add($"ProcessItemCosts: Vehicle {_extVehicleType} should not use lane {item.m_position.m_lane} @ seg. {item.m_position.m_segment}, null? {prevLaneInfo == null}"); } #endif avoidLane = true; @@ -1867,8 +2182,8 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow // check for vehicle restrictions if (!CanUseLane(debug, item.m_position.m_segment, prevSegmentInfo, item.m_position.m_lane, prevLaneInfo)) { #if DEBUGPF - if (Options.disableSomething1 && debug) { - Log._Debug($"Vehicle {_extVehicleType} must not use lane {item.m_position.m_lane} @ seg. {item.m_position.m_segment}, null? {prevLaneInfo == null}"); + if (debug) { + logBuf.Add($"ProcessItemCosts: Vehicle {_extVehicleType} must not use lane {item.m_position.m_lane} @ seg. {item.m_position.m_segment}, null? {prevLaneInfo == null}"); } #endif strictlyAvoidLane = true; @@ -1876,11 +2191,20 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow if (!useAdvancedAI) { // apply vehicle restrictions when not using Advanced AI - if (strictlyAvoidLane) + if (strictlyAvoidLane) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: applying strict lane avoidance on deactivated advaned AI"); +#endif prevCost *= 50f; + } // add costs for u-turns if (!isMiddle && nextSegmentId == item.m_position.m_segment && (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: applying u-turn cost factor on deactivated advaned AI"); +#endif prevCost *= Options.someValue4; } } @@ -1891,9 +2215,9 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow float prevOffsetCost = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * 0.003921569f * prevCost; float prevMethodDist = item.m_methodDistance + prevOffsetCost; float prevComparisonPlusOffsetCostOverSpeed = item.m_comparisonValue + prevOffsetCost / (prevLaneSpeed * this._maxLength); - Vector3 prevLanePosition = netManager.m_lanes.m_buffer[(int)((UIntPtr)item.m_laneID)].CalculatePosition((float)connectOffset * 0.003921569f); + Vector3 prevLanePosition = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * 0.003921569f); int newLaneIndexFromInner = laneIndexFromInner; - bool transitionNode = (netManager.m_nodes.m_buffer[(int)targetNodeId].m_flags & NetNode.Flags.Transition) != NetNode.Flags.None; + bool transitionNode = (netManager.m_nodes.m_buffer[targetNodeId].m_flags & NetNode.Flags.Transition) != NetNode.Flags.None; NetInfo.LaneType allowedLaneTypes = this._laneTypes; VehicleInfo.VehicleType allowedVehicleTypes = this._vehicleTypes; @@ -1916,44 +2240,66 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow bool prevIsJunction = (netManager.m_nodes.m_buffer[sourceNodeId].m_flags & NetNode.Flags.Junction) != NetNode.Flags.None; bool nextIsHighway = SegmentGeometry.Get(nextSegmentId).IsHighway(); bool nextIsRealJunction = SegmentGeometry.Get(item.m_position.m_segment).CountOtherSegments(nextIsStartNodeOfPrevSegment) > 1; - // determines if a vehicles wants to change lanes here (randomized). If true, costs for changing to an adjacent lane are not added to the result - //bool wantToChangeLane = false; - /*if (useAdvancedAI && Options.laneChangingRandomization != 5) { - laneChangeRandCounter = (ushort)((laneChangeRandCounter + 1) % Options.getLaneChangingRandomizationTargetValue()); - wantToChangeLane = (laneChangeRandCounter == 0); - }*/ // NON-STOCK CODE END // uint laneIndex = forceLaneIndex != null ? (uint)forceLaneIndex : 0u; // NON-STOCK CODE, forcedLaneIndex is not null if the next node is a (real) junction uint curLaneId = (uint)(forceLaneId != null ? forceLaneId : nextSegment.m_lanes); // NON-STOCK CODE, forceLaneId is not null if the next node is a (real) junction while (laneIndex < nextNumLanes && curLaneId != 0u) { // NON-STOCK CODE START // - if (forceLaneIndex != null && laneIndex != forceLaneIndex) + if (forceLaneIndex != null && laneIndex != forceLaneIndex) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: forceLaneIndex break! laneIndex={laneIndex}"); +#endif break; + } // NON-STOCK CODE END // NetInfo.Lane nextLane = nextSegmentInfo.m_lanes[laneIndex]; #if DEBUGCOSTS bool costDebug = debug; //bool costDebug = Options.disableSomething1 && (nextSegmentId == 9649 || nextSegmentId == 1043); - List logBuf = null; if (costDebug) { - logBuf = new List(); - logBuf.Add($"Path from {nextSegmentId} (idx {laneIndex}, id {curLaneId}) to {item.m_position.m_segment} (lane {prevOuterSimilarLaneIndex} from outer, idx {item.m_position.m_lane}): costDebug=TRUE, explore? {nextLane.CheckType(allowedLaneTypes, allowedVehicleTypes)} && {(nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)} && {(byte)(nextLane.m_finalDirection & nextFinalDir) != 0 && CanLanesConnect(curLaneId, item.m_laneID)}"); + logBuf.Add($"Path from {nextSegmentId} (idx {laneIndex}, id {curLaneId}) to {item.m_position.m_segment} (lane {prevOuterSimilarLaneIndex} from outer, idx {item.m_position.m_lane}): costDebug=TRUE, explore? {nextLane.CheckType(allowedLaneTypes, allowedVehicleTypes)} && {(nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)} && {(byte)(nextLane.m_finalDirection & nextFinalDir) != 0}"); } #endif if (forceLaneId == null && Options.laneConnectorEnabled) { - if (laneConnManager.HasConnections(curLaneId, nextIsStartNodeOfNextSegment) && !laneConnManager.AreLanesConnected(curLaneId, item.m_laneID, nextIsStartNodeOfNextSegment)) - goto IL_8F5; +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: forceLaneId == null. Checking if next lane idx {laneIndex}, id {curLaneId} has outgoing connections (startNode={nextIsStartNodeOfNextSegment})"); +#endif + + if (laneConnManager.HasConnections(curLaneId, nextIsStartNodeOfNextSegment) && !laneConnManager.AreLanesConnected(curLaneId, item.m_laneID, nextIsStartNodeOfNextSegment)) { +#if DEBUGPF + if (debug) { + logBuf.Add($"Source lane {curLaneId} is NOT connected with target lane {item.m_laneID}, but source lane has outgoing connections! Skipping lane"); + } +#endif + goto CONTINUE_LANE_LOOP; + } + +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Check for outgoing connections passed!"); +#endif } if ((byte)(nextLane.m_finalDirection & nextFinalDir) != 0) { // lane direction is compatible +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Lane direction check passed: {nextLane.m_finalDirection}"); +#endif if ((nextLane.CheckType(allowedLaneTypes, allowedVehicleTypes)/* || (emergencyLaneSelection && nextLane.m_vehicleType == VehicleInfo.VehicleType.None)*/) && (nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)) { // vehicle types match and no u-turn to the previous lane +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: vehicle type check passed: {nextLane.CheckType(allowedLaneTypes, allowedVehicleTypes)} && {(nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)}"); +#endif + // NON-STOCK CODE START // float nextMaxSpeed = /*emergencyLaneSelection ? 5f : */GetLaneSpeedLimit(nextSegmentId, laneIndex, curLaneId, nextLane); bool addCustomTrafficCosts = useAdvancedAI && @@ -1962,10 +2308,15 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow ((nextLane.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None/* || emergencyLaneSelection*/) && (nextLane.m_laneType & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.Parking)) == NetInfo.LaneType.None; - if (nextIsRealJunction && addCustomTrafficCosts) { + /*if (nextIsRealJunction && addCustomTrafficCosts) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: reducing maxSpeed in front of junctions"); +#endif + // max. speed is reduced in front of junctions nextMaxSpeed *= 0.25f; - } + }*/ float customDeltaCost = 0f; float nextSpeed = 1f; @@ -1973,7 +2324,8 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow if (addCustomTrafficCosts && !strictlyAvoidLane) { // calculate current lane density & speed nextSpeed = CustomRoadAI.laneMeanSpeeds[nextSegmentId] != null && laneIndex < CustomRoadAI.laneMeanSpeeds[nextSegmentId].Length ? CustomRoadAI.laneMeanSpeeds[nextSegmentId][laneIndex] : 1f; - nextDensity = CustomRoadAI.laneMeanRelDensities[nextSegmentId] != null && laneIndex < CustomRoadAI.laneMeanRelDensities[nextSegmentId].Length ? CustomRoadAI.laneMeanRelDensities[nextSegmentId][laneIndex] : 0.1f; + nextDensity = CustomRoadAI.laneMeanAbsDensities[nextSegmentId] != null && laneIndex < CustomRoadAI.laneMeanAbsDensities[nextSegmentId].Length ? CustomRoadAI.laneMeanAbsDensities[nextSegmentId][laneIndex] : 0.1f; + //nextDensity = CustomRoadAI.laneMeanRelDensities[nextSegmentId] != null && laneIndex < CustomRoadAI.laneMeanRelDensities[nextSegmentId].Length ? CustomRoadAI.laneMeanRelDensities[nextSegmentId][laneIndex] : 0.1f; nextSpeed = (float)Math.Max(0.1f, Math.Round(nextSpeed * 0.1f) / 10f); // 0.1, 0.2, ..., 1 nextDensity = (float)Math.Max(0.1f, Math.Round(nextDensity * 0.1f) / 10f); // 0.1, 0.2, ..., 1 @@ -1982,15 +2334,15 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow Vector3 a; if ((byte)(nextDir & NetInfo.Direction.Forward) != 0) { - a = netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_bezier.d; + a = netManager.m_lanes.m_buffer[curLaneId].m_bezier.d; } else { - a = netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_bezier.a; + a = netManager.m_lanes.m_buffer[curLaneId].m_bezier.a; } float transitionCost = Vector3.Distance(a, prevLanePosition); // This gives the distance of the previous to next lane endpoints. #if DEBUGCOSTS if (costDebug) - logBuf.Add($"ProcessItemCosts: costs from {nextSegmentId} (off {(byte)(((nextDir & NetInfo.Direction.Forward) == 0) ? 0 : 255)}) to {item.m_position.m_segment} (off {item.m_position.m_offset}), connectOffset={connectOffset}: distanceOnBezier={distanceOnBezier}"); + logBuf.Add($"ProcessItemCosts: costs from {nextSegmentId} (off {(byte)(((nextDir & NetInfo.Direction.Forward) == 0) ? 0 : 255)}) to {item.m_position.m_segment} (off {item.m_position.m_offset}), connectOffset={connectOffset}: transitionCost={transitionCost}"); #endif if (transitionNode) { @@ -2014,6 +2366,11 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow nextItem.m_methodDistance = prevMethodDist + transitionCost; } +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: checking if methodDistance is in range: {nextLane.m_laneType != NetInfo.LaneType.Pedestrian} || {nextItem.m_methodDistance < 1000f} ({nextItem.m_methodDistance})"); +#endif + if (nextLane.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < 1000f) { // NON-STOCK CODE START // @@ -2025,16 +2382,26 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow customDeltaCost = transitionCost + prevOffsetCost; // distanceOnBezier now holds the costs for driving on the segment + costs for changing the segment if (strictlyAvoidLane) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: applying strict lane avoidance on ACTIVATED advanced AI"); +#endif + // apply vehicle restrictions customDeltaCost *= 50f; } else if (avoidLane && (_extVehicleType == null || (_extVehicleType & (ExtVehicleType.CargoTruck | ExtVehicleType.PassengerCar)) != ExtVehicleType.None)) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: applying lane avoidance on ACTIVATED advanced AI"); +#endif + // apply vanilla game restriction policies customDeltaCost *= 3f; } #if DEBUGCOSTS if (costDebug) { - logBuf.Add($"Path from {nextSegmentId} (idx {laneIndex}, id {curLaneId}) to {item.m_position.m_segment} (lane {prevOuterSimilarLaneIndex} from outer, idx {item.m_position.m_lane}): useAdvancedAI={useAdvancedAI}, addCustomTrafficCosts={addCustomTrafficCosts}, distanceOnBezier={distanceOnBezier} avoidLane={avoidLane} strictlyAvoidLane={strictlyAvoidLane}"); + logBuf.Add($"ProcessItemCosts: Path from {nextSegmentId} (idx {laneIndex}, id {curLaneId}) to {item.m_position.m_segment} (lane {prevOuterSimilarLaneIndex} from outer, idx {item.m_position.m_lane}): useAdvancedAI={useAdvancedAI}, addCustomTrafficCosts={addCustomTrafficCosts}, transitionCost={transitionCost} avoidLane={avoidLane} strictlyAvoidLane={strictlyAvoidLane}"); } #endif } @@ -2043,17 +2410,24 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow nextItem.m_direction = nextDir; if (curLaneId == this._startLaneA) { if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this._startOffsetA) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this._startOffsetA)) { - curLaneId = netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_nextLane; - goto IL_90F; +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Current lane is start lane A. goto next lane"); +#endif + goto CONTINUE_LANE_LOOP; } float nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this._startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLane); // NON-STOCK CODE float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this._startOffsetA)) * 0.003921569f; nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * this._maxLength); } + if (curLaneId == this._startLaneB) { if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this._startOffsetB) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this._startOffsetB)) { - curLaneId = netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_nextLane; - goto IL_90F; +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Current lane is start lane B. goto next lane"); +#endif + goto CONTINUE_LANE_LOOP; } float nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this._startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLane); // NON-STOCK CODE float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this._startOffsetB)) * 0.003921569f; @@ -2062,23 +2436,40 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow if (!this._ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None && (byte)(nextLane.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) { // NON-STOCK CODE START // - if (addCustomTrafficCosts) + if (addCustomTrafficCosts) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Applying blocked road cost factor on ACTIVATED advanced AI"); +#endif customDeltaCost *= 10f; - else + } else { // NON-STOCK CODE END // +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Applying blocked road cost factor on disabled advanced AI"); +#endif + nextItem.m_comparisonValue += 0.1f; + } blocked = true; } + + if ((byte)(nextLane.m_laneType & prevLaneType) != 0 && nextLane.m_vehicleType == prevVehicleType) { +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Applying transport vehicle costs"); +#endif + // NON-STOCK CODE START // if (Options.isStockLaneChangerUsed()) { // NON-STOCK CODE END // //if (! ignoreLaneArrows) { // this is CO's way of matching lanes between segments - int firstTarget = (int)netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_firstTarget; - int lastTarget = (int)netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_lastTarget; + int firstTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_firstTarget; + int lastTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_lastTarget; if (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) { nextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this._maxLength); } @@ -2092,7 +2483,7 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow // NON-STOCK CODE START // } else { - if (addCustomTrafficCosts && _extVehicleType != ExtVehicleType.Emergency) { + if (addCustomTrafficCosts && (_extVehicleType != ExtVehicleType.Emergency || Options.disableSomething4)) { bool isBus = _extVehicleType != null && ((_extVehicleType & ExtVehicleType.Bus) != ExtVehicleType.None); bool isPublicTransport = _extVehicleType != null && ((_extVehicleType & ExtVehicleType.RoadPublicTransport) != ExtVehicleType.None); bool nextIsTransportLane = nextLane.m_laneType == NetInfo.LaneType.TransportVehicle; @@ -2116,6 +2507,11 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow if (addCustomTrafficCosts) { // Advanced AI cost calculation +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Calculating advanced AI costs"); +#endif + int nextOuterSimilarLaneIndex; if ((byte)(nextLane.m_direction & NetInfo.Direction.Forward) != 0) { nextOuterSimilarLaneIndex = nextLane.m_similarLaneCount - nextLane.m_similarLaneIndex - 1; @@ -2124,18 +2520,24 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow } float relLaneDist = nextOuterSimilarLaneIndex - prevOuterSimilarLaneIndex; // relative lane distance - //bool isPreferredLaneChangingDir = relLaneDist > 0 ^ TrafficPriority.IsLeftHandDrive(); // RH traffic: prefer lane changes to the outer, LH traffic: prefer lane changes to the left + bool isPreferredLaneChangingDir = relLaneDist == 1; + float laneDist = !isMiddle && nextSegmentId == item.m_position.m_segment ? Options.someValue4 : (float)Math.Abs(relLaneDist); // absolute lane distance. U-turns are treated as changing lanes, too. if (forceLaneIndex == null && prevIsHighway && nextIsHighway && laneDist > 1) { // disable lane changing by more than one on highways - goto IL_8F5; + goto CONTINUE_LANE_LOOP; } if (strictlyAvoidLane) { // we assume maximum traffic density and minimum speed on strictly-to-be-avoided lanes nextSpeed = 0; - nextDensity = 1; + prevDensity = 1; + +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: Strict lane avoidance: nextSpeed = 0, prevDensity = 1"); +#endif } float metric = 1f; // resulting cost multiplicator @@ -2157,13 +2559,12 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow if (Options.disableSomething2) divMetric = nextMaxSpeed; else - divMetric = Options.pathCostMultiplicator2 * nextSpeed * nextMaxSpeed; // 0 .. nextMaxSpeed + divMetric = nextSpeed * nextMaxSpeed; // 0 .. nextMaxSpeed // calculate density metric - if (Options.disableSomething3) - multMetric = 1f; - else - multMetric = 1f + Options.pathCostMultiplicator * nextDensity; // 1 .. pathCostMultiplicator + if (prevSpeed <= Options.someValue13) + prevDensity = 1f; + multMetric = Options.someValue12 + Options.pathCostMultiplicator2 * prevDensity; // calculate density/speed metric metric = Math.Max(0.01f, multMetric) / Math.Max(0.1f, divMetric); @@ -2171,33 +2572,34 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow metricBeforeLanes = metric; #endif - // multiply with lane distance if distance > 1 or if vehicle does not like to change lanes float laneMetric = 1f; - float laneChangeCostBase = (nextIsHighway ? Options.someValue : Options.someValue5) * (_isHeavyVehicle ? Options.someValue2 : 1f) + prevDensity; - /*float laneChangeCostBase = 1f; - if (nextIsHighway) { - // highways - laneChangeCostBase = _isHeavyVehicle ? Options.someValue : Options.someValue2; // heavy vehicles have higher lane changing costs assigned - } else { - // city roads - laneChangeCostBase = Options.someValue5; - }*/ - - //if (! isPreferredLaneChangingDir) - //laneChangeCostBase *= 1.25f; // costs for changing lanes not along the "favorite" direction of the map traffic system - if ((!isMiddle && nextSegmentId == item.m_position.m_segment) || // u-turns - (_extVehicleType != ExtVehicleType.Emergency && // emergency vehicles may do everything - forceLaneIndex == null // no lane changing at junctions + if (laneDist > 0 && // lane would be changed + ((_extVehicleType != ExtVehicleType.Emergency || Options.disableSomething4) && // emergency vehicles may do everything + !nextIsRealJunction // no lane changing at junctions )) { + // multiply with lane distance if distance > 1 or if vehicle does not like to change lanes + + float laneChangeCostBase = 1f + // base + (nextIsHighway ? Options.someValue : Options.someValue5) * // changing lanes on highways is more expensive than on city streets + (_isHeavyVehicle ? Options.someValue2 : 1f) * // changing lanes is more expensive for heavy vehicles + (laneDist > 1 ? Options.someValue11 : 1f) * // changing more than one lane at a time is expensive + (2f - _vehicleRand); // fast vehicles (= high _vehicleRand) tend to change lanes more often + + if (isPreferredLaneChangingDir && prevIsHighway && nextIsHighway) { + float outerLaneCostFactor = (1f + Options.pathCostMultiplicator * (_vehicleRand - 0.5f)); // [1 - 0.5 * pathCostMultiplicator; 1 + 0.5 * pathCostMultiplicator], depending on _vehicleRand + laneChangeCostBase = Math.Max(1f, laneChangeCostBase * outerLaneCostFactor); // slower vehicles tend to stay on the outer lane; faster vehicles on inner lanes + } + // we use the power operator here to express that lane changing one-by-one is preferred over changing multiple lanes at once laneMetric = (float)Math.Pow(laneChangeCostBase, laneDist); metric *= laneMetric; } // avoid lane changing before junctions: multiply with inverted distance to next junction - if (forceLaneIndex == null && - _extVehicleType != ExtVehicleType.Emergency && + if ((!prevIsHighway || !nextIsHighway) && + !nextIsRealJunction && + (_extVehicleType != ExtVehicleType.Emergency || Options.disableSomething4) && laneDist > 0 && nextItem.m_numSegmentsToJunction < 3) { float junctionMetric = (float)Math.Pow(Options.someValue3, Math.Max(0f, 3f - nextItem.m_numSegmentsToJunction)); @@ -2240,22 +2642,14 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow if (costDebug) { logBuf.Add($"Path from {nextSegmentId} (lane {nextOuterSimilarLaneIndex} from outer, idx {laneIndex}) to {item.m_position.m_segment} (lane {prevOuterSimilarLaneIndex} from outer, idx {item.m_position.m_lane}."); //logBuf.Add($"distanceOverMeanMaxSpeed = {distanceOverMeanMaxSpeed} oldDistanceOverMaxSpeed = {oldDistanceOverMaxSpeed}, prevMaxSpeed={prevMaxSpeed}, nextMaxSpeed={nextMaxSpeed}, prevSpeed={prevSpeed}, nextSpeed={nextSpeed}"); - logBuf.Add($"distanceOverMeanMaxSpeed = {distanceOverMeanMaxSpeed} oldDistanceOverMaxSpeed = {oldDistanceOverMaxSpeed}, prevMaxSpeed={prevMaxSpeed}, nextMaxSpeed={nextMaxSpeed}, nextSpeed={nextSpeed} nextDensity={nextDensity}"); + logBuf.Add($"deltaCostOverMeanMaxSpeed = {deltaCostOverMeanMaxSpeed} oldTransitionDistanceOverMaxSpeed = {oldTransitionDistanceOverMaxSpeed}, prevMaxSpeed={prevMaxSpeed}, nextMaxSpeed={nextMaxSpeed}, nextSpeed={nextSpeed} nextDensity={nextDensity}"); } #endif nextItem.m_comparisonValue += deltaCostOverMeanMaxSpeed; #if DEBUGCOSTS if (costDebug) { - logBuf.Add($"Total cost = {distanceOverMeanMaxSpeed}, comparison value = {nextItem.m_comparisonValue}"); - } -#endif -#if DEBUGCOSTS - if (costDebug) { - foreach (String toLog in logBuf) { - Log._Debug($"Pathfinder ({this._pathFindIndex}): " + toLog); - } - logBuf.Clear(); + logBuf.Add($"Total cost = {deltaCostOverMeanMaxSpeed}, comparison value = {nextItem.m_comparisonValue}"); } #endif @@ -2263,7 +2657,7 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow // comparison value got too big. Do not add the lane to the buffer #if DEBUGPF if (debug) - Log._Debug($"Pathfinder ({this._pathFindIndex}): comparisonValue is >1, NaN or Infinity: {nextItem.m_comparisonValue}. seg. {nextSegmentId}, lane {laneIndex}, off {nextItem.m_position.m_offset} -> {item.m_position.m_segment}, lane {item.m_position.m_lane}, off {item.m_position.m_offset}."); + logBuf.Add($"Pathfinder ({this._pathFindIndex}): comparisonValue is >1, NaN or Infinity: {nextItem.m_comparisonValue}. seg. {nextSegmentId}, lane {laneIndex}, off {nextItem.m_position.m_offset} -> {item.m_position.m_segment}, lane {item.m_position.m_lane}, off {item.m_position.m_offset}."); #endif #if DEBUG //Log.Error($"THREAD #{Thread.CurrentThread.ManagedThreadId}, PF {this._pathFindIndex}: Comparison value > 1, NaN or infinity! distanceOverMeanMaxSpeed={distanceOverMeanMaxSpeed}, nextSpeed={nextSpeed}, prevSpeed={prevSpeed}"); @@ -2272,7 +2666,15 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow } #if DEBUGPF if (debug) { - //Log.Message($">> PF {this._pathFindIndex} -- seg {item2.m_position.m_segment}, lane {item2.m_position.m_lane} (idx {item2.m_laneID}), off {item2.m_position.m_offset}, cost {item2.m_comparisonValue}, totalCost {totalCost} = traffic={trafficCost}, junction={junctionCost}, lane={laneChangeCost}"); + //logBuf.Add($">> PF {this._pathFindIndex} -- seg {item2.m_position.m_segment}, lane {item2.m_position.m_lane} (idx {item2.m_laneID}), off {item2.m_position.m_offset}, cost {item2.m_comparisonValue}, totalCost {totalCost} = traffic={trafficCost}, junction={junctionCost}, lane={laneChangeCost}"); + } +#endif + +#if DEBUGPF + if (debug) { + logBuf.Add($"addItem={addItem}"); + + FlushCostLog(logBuf); } #endif } @@ -2288,7 +2690,7 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow nextItem.m_laneID = curLaneId; #if DEBUGPF if (debug) - Log._Debug($">> PF {this._pathFindIndex} -- Adding item: seg {nextItem.m_position.m_segment}, lane {nextItem.m_position.m_lane} (idx {nextItem.m_laneID}), off {nextItem.m_position.m_offset} -> seg {item.m_position.m_segment}, lane {item.m_position.m_lane} (idx {item.m_laneID}), off {item.m_position.m_offset}, cost {nextItem.m_comparisonValue}, previous cost {item.m_comparisonValue}, methodDist {nextItem.m_methodDistance}"); + logBuf.Add($">> PF {this._pathFindIndex} -- Adding item: seg {nextItem.m_position.m_segment}, lane {nextItem.m_position.m_lane} (idx {nextItem.m_laneID}), off {nextItem.m_position.m_offset} -> seg {item.m_position.m_segment}, lane {item.m_position.m_lane} (idx {item.m_laneID}), off {item.m_position.m_offset}, cost {nextItem.m_comparisonValue}, previous cost {item.m_comparisonValue}, methodDist {nextItem.m_methodDistance}"); #endif this.AddBufferItem(nextItem, item.m_position); @@ -2296,38 +2698,59 @@ private bool ProcessItemCosts(bool allowCustomLaneChanging, bool ignoreLaneArrow } else { #if DEBUGPF if (debug) - Log._Debug($">> PF {this._pathFindIndex} -- NOT adding item"); + logBuf.Add($">> PF {this._pathFindIndex} -- NOT adding item"); #endif } // NON-STOCK CODE END // } } - goto IL_8F5; + goto CONTINUE_LANE_LOOP; } + if ((byte)(nextLane.m_laneType & prevLaneType) != 0 && (nextLane.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) { - newLaneIndexFromInner++; - goto IL_8F5; + ++newLaneIndexFromInner; } - goto IL_8F5; - IL_90F: - laneIndex++; + + CONTINUE_LANE_LOOP: + + curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane; + ++laneIndex; continue; - IL_8F5: -#if DEBUGCOSTS - if (costDebug) { - foreach (String toLog in logBuf) { - Log._Debug($"Pathfinder ({this._pathFindIndex}): " + toLog); - } - logBuf.Clear(); - } -#endif - curLaneId = netManager.m_lanes.m_buffer[(int)((UIntPtr)curLaneId)].m_nextLane; - goto IL_90F; } // foreach lane laneIndexFromInner = newLaneIndexFromInner; + +#if DEBUGPF + if (debug) + logBuf.Add($"ProcessItemCosts: method finished at end."); + + if (debug) + FlushCostLog(logBuf); +#endif return blocked; } +#if DEBUGPF + private void FlushCostLog(List logBuf) { + if (logBuf == null) + return; + + foreach (String toLog in logBuf) { + Log._Debug($"Pathfinder ({this._pathFindIndex}) *COSTS*: " + toLog); + } + logBuf.Clear(); + } + + private void FlushMainLog(List logBuf, uint unitId) { + if (logBuf == null) + return; + + foreach (String toLog in logBuf) { + Log._Debug($"Pathfinder ({this._pathFindIndex}) for unit {unitId} *MAIN*: " + toLog); + } + logBuf.Clear(); + } +#endif + #region stock code // 4 private void ProcessItemPedBicycle(BufferItem item, ushort targetNodeId, ushort nextSegmentId, ref NetSegment nextSegment, byte connectOffset, byte laneSwitchOffset, int laneIndex, uint lane) { @@ -2335,7 +2758,7 @@ private void ProcessItemPedBicycle(BufferItem item, ushort targetNodeId, ushort return; } // NON-STOCK CODE START - CustomSegmentLights lights = CustomTrafficLights.GetSegmentLights(targetNodeId, nextSegmentId); + CustomSegmentLights lights = customTrafficLightsManager.GetSegmentLights(targetNodeId, nextSegmentId); if (lights != null) { if (lights.InvalidPedestrianLight) { return; @@ -2348,10 +2771,10 @@ private void ProcessItemPedBicycle(BufferItem item, ushort targetNodeId, ushort int num = nextSegmentInfo.m_lanes.Length; float distance; byte offset; - Vector3 b = netManager.m_lanes.m_buffer[(int)((UIntPtr)item.m_laneID)].CalculatePosition((float)laneSwitchOffset * 0.003921569f); + Vector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)laneSwitchOffset * 0.003921569f); if (nextSegmentId == item.m_position.m_segment) { // next segment is previous segment - Vector3 a = netManager.m_lanes.m_buffer[(int)((UIntPtr)lane)].CalculatePosition((float)connectOffset * 0.003921569f); + Vector3 a = netManager.m_lanes.m_buffer[lane].CalculatePosition((float)connectOffset * 0.003921569f); distance = Vector3.Distance(a, b); offset = connectOffset; } else { @@ -2359,9 +2782,9 @@ private void ProcessItemPedBicycle(BufferItem item, ushort targetNodeId, ushort NetInfo.Direction direction = (targetNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward; Vector3 a; if ((byte)(direction & NetInfo.Direction.Forward) != 0) { - a = netManager.m_lanes.m_buffer[(int)((UIntPtr)lane)].m_bezier.d; + a = netManager.m_lanes.m_buffer[lane].m_bezier.d; } else { - a = netManager.m_lanes.m_buffer[(int)((UIntPtr)lane)].m_bezier.a; + a = netManager.m_lanes.m_buffer[lane].m_bezier.a; } distance = Vector3.Distance(a, b); offset = (byte)(((direction & NetInfo.Direction.Forward) == 0) ? 0 : 255); @@ -2382,13 +2805,13 @@ private void ProcessItemPedBicycle(BufferItem item, ushort targetNodeId, ushort } float prevCost = netManager.m_segments.m_buffer[(int)item.m_position.m_segment].m_averageLength; // NON-STOCK CODE START - if (_extVehicleType == ExtVehicleType.Bicycle) { + //if (_extVehicleType == ExtVehicleType.Bicycle) { if ((vehicleType & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None) { prevCost *= 0.95f; } else if ((netManager.m_segments.m_buffer[(int)item.m_position.m_segment].m_flags & NetSegment.Flags.BikeBan) != NetSegment.Flags.None) { prevCost *= 5f; } - } + //} ushort sourceNodeId = (targetNodeId == netManager.m_segments.m_buffer[item.m_position.m_segment].m_startNode) ? netManager.m_segments.m_buffer[item.m_position.m_segment].m_endNode : netManager.m_segments.m_buffer[item.m_position.m_segment].m_startNode; // no lane changing directly in front of a junction bool prevIsJunction = (netManager.m_nodes.m_buffer[sourceNodeId].m_flags & NetNode.Flags.Junction) != NetNode.Flags.None; // NON-STOCK CODE END @@ -2504,7 +2927,7 @@ private void AddBufferItem(BufferItem item, PathUnit.Position target) { } while (_bufferMax[comparisonBufferPos] == 63) { - comparisonBufferPos++; + ++comparisonBufferPos; if (comparisonBufferPos == 1024) { return; } @@ -2516,8 +2939,8 @@ private void AddBufferItem(BufferItem item, PathUnit.Position target) { bufferIndex = (comparisonBufferPos << 6 | ++_bufferMax[comparisonBufferPos]); _buffer[bufferIndex] = item; - _laneLocation[(int)((UIntPtr)item.m_laneID)] = (_pathFindIndex << 16 | (uint)bufferIndex); - _laneTarget[(int)((UIntPtr)item.m_laneID)] = target; + _laneLocation[item.m_laneID] = (_pathFindIndex << 16 | (uint)bufferIndex); + _laneTarget[item.m_laneID] = target; } private void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType type) { NetManager instance = Singleton.instance; @@ -2538,7 +2961,7 @@ private void PathFindThread() { while (true) { //Log.Message($"Pathfind Thread #{Thread.CurrentThread.ManagedThreadId} iteration!"); try { - while (!Monitor.TryEnter(QueueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) { } + Monitor.Enter(QueueLock); while (QueueFirst == 0u && !Terminated) { #if DEBUGPF @@ -2553,6 +2976,10 @@ private void PathFindThread() { #endif //} } + +#if DEBUGPF3 + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread iteration START. QueueFirst={QueueFirst} QueueLast={QueueLast}"); +#endif #if DEBUGPF /*if (m_queuedPathFindCount > 100 && Options.disableSomething1) Log._Debug($"Pathfind Thread #{Thread.CurrentThread.ManagedThreadId} is continuing now!");*/ @@ -2561,21 +2988,28 @@ private void PathFindThread() { break; } Calculating = QueueFirst; - QueueFirst = PathUnits.m_buffer[(int)((UIntPtr)Calculating)].m_nextPathUnit; +#if DEBUGPF3 + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread iteration. Setting Calculating=QueueFirst=Calculating. Setting QueueFirst={PathUnits.m_buffer[Calculating].m_nextPathUnit}"); +#endif + QueueFirst = PathUnits.m_buffer[Calculating].m_nextPathUnit; if (QueueFirst == 0u) { QueueLast = 0u; m_queuedPathFindCount = 0; } else { - m_queuedPathFindCount--; + --m_queuedPathFindCount; } #if DEBUGPF /*if (m_queuedPathFindCount > 100 && Options.disableSomething1) Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PathFindThread: Starting pathfinder. Remaining queued pathfinders: {m_queuedPathFindCount}"); */ #endif - PathUnits.m_buffer[(int)((UIntPtr)Calculating)].m_nextPathUnit = 0u; - PathUnits.m_buffer[(int)((UIntPtr)Calculating)].m_pathFindFlags = (byte)((PathUnits.m_buffer[(int)((UIntPtr)Calculating)].m_pathFindFlags & -2) | 2); + PathUnits.m_buffer[Calculating].m_nextPathUnit = 0u; + PathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)((PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CREATED) | PathUnit.FLAG_CALCULATING); + +#if DEBUGPF3 + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread iteration END. QueueFirst={QueueFirst} QueueLast={QueueLast} Calculating={Calculating} flags={PathUnits.m_buffer[Calculating].m_pathFindFlags}"); +#endif } catch (Exception e) { - Log.Error("CustomPathFind.PathFindThread Error (1): " + e.ToString()); + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (1): {e.ToString()}"); } finally { Monitor.Exit(QueueLock); } @@ -2588,17 +3022,18 @@ private void PathFindThread() { /*if (m_queuedPathFindCount > 100 && Options.disableSomething1) Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Calling PathFindImplementation now. Calculating={Calculating}");*/ #endif - PathFindImplementation(Calculating, ref PathUnits.m_buffer[(int)((UIntPtr)Calculating)]); + PathFindImplementation(Calculating, ref PathUnits.m_buffer[Calculating]); } catch (Exception ex) { - Log.Error($"THREAD #{Thread.CurrentThread.ManagedThreadId} Path find error: " + ex.ToString()); + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (2): {ex.ToString()}"); //UIView.ForwardException(ex); #if DEBUG ++_failedPathFinds; #endif pathUnitExtVehicleType[Calculating] = null; + pathUnitVehicleIds[Calculating] = null; - PathUnits.m_buffer[(int)Calculating].m_pathFindFlags |= PathUnit.FLAG_FAILED; + PathUnits.m_buffer[Calculating].m_pathFindFlags |= PathUnit.FLAG_FAILED; } finally { #if DEBUGPF m_pathfindProfiler.EndStep(); @@ -2614,13 +3049,33 @@ private void PathFindThread() { #endif try { - while (!Monitor.TryEnter(QueueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) { } - PathUnits.m_buffer[(int)((UIntPtr)Calculating)].m_pathFindFlags = (byte)(PathUnits.m_buffer[(int)((UIntPtr)Calculating)].m_pathFindFlags & -3); + Monitor.Enter(QueueLock); + +#if DEBUGPF3 + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread calculation finished for unit {Calculating}. flags={PathUnits.m_buffer[Calculating].m_pathFindFlags}"); + + List allUnits = new List(); + uint currentUnit = this.QueueFirst; + int i = 0; + while (currentUnit != 0 && currentUnit != QueueLast) { + allUnits.Add(currentUnit); + currentUnit = this.PathUnits.m_buffer[currentUnit].m_nextPathUnit; + ++i; + if (i > 10000) { + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}): !!! CYCLE ???"); + break; + } + } + allUnits.Add(QueueLast); + Log._Debug($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}): allUnits={string.Join(", ", allUnits.Select(x => x.ToString()).ToArray())}"); +#endif + + PathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)(PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CALCULATING); Singleton.instance.ReleasePath(Calculating); Calculating = 0u; Monitor.Pulse(QueueLock); } catch (Exception e) { - Log.Error("CustomPathFind.PathFindThread Error (3): " + e.ToString()); + Log.Error($"(PF #{_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (3): {e.ToString()}"); } finally { Monitor.Exit(QueueLock); } @@ -2670,13 +3125,5 @@ protected virtual bool CanUseLane(bool debug, ushort segmentId, NetInfo segmentI protected virtual float GetLaneSpeedLimit(ushort segmentId, uint laneIndex, uint laneId, NetInfo.Lane lane) { return Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance().GetLockFreeGameSpeedLimit(segmentId, (uint)laneIndex, laneId, lane) : lane.m_speedLimit; } - - /// - /// Determines if the Traffic Manager: PE lane arrow changer is enabled - /// - /// - protected virtual bool IsLaneArrowChangerEnabled() { - return true; - } } } diff --git a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs index 6cdad9a8..a2d47956 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs @@ -1,5 +1,6 @@ -#define QUEUEDSTATSx +#define QUEUEDSTATS #define EXTRAPFx +#define DEBUGPF3x using System; using System.Linq; @@ -9,8 +10,9 @@ using ColossalFramework.Math; using JetBrains.Annotations; using UnityEngine; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.State; +using TrafficManager.Traffic; // ReSharper disable InconsistentNaming @@ -70,6 +72,7 @@ public void UpdateWithPathManagerValues(PathManager stockPathManager) { for (var i = 0; i < numCustomPathFinds; i++) { _replacementPathFinds[i] = gameObject.AddComponent(); + _replacementPathFinds[i].pfId = i; if (i == 0) { _replacementPathFinds[i].IsMasterPathFind = true; } @@ -87,6 +90,10 @@ public void UpdateWithPathManagerValues(PathManager stockPathManager) { fieldInfo?.SetValue(this, _replacementPathFinds); for (var i = 0; i < numOfStockPathFinds; i++) { +#if DEBUG + Log._Debug($"PF {i}: {stockPathFinds[i].m_queuedPathFindCount} queued path-finds"); +#endif + //stockPathFinds[i].WaitForAllPaths(); // would cause deadlock since we have a lock on m_bufferLock Destroy(stockPathFinds[i]); } } finally { @@ -96,37 +103,76 @@ public void UpdateWithPathManagerValues(PathManager stockPathManager) { InitDone = true; } - public bool CreatePath(ExtVehicleType vehicleType, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPos, PathUnit.Position endPos, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { + public new void ReleasePath(uint unit) { +#if DEBUGPF3 + Log.Warning($"CustomPathManager.ReleasePath({unit}) called."); +#endif + + if (this.m_pathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags == 0) { + return; + } + Monitor.Enter(m_bufferLock); + try { + int num = 0; + while (unit != 0u) { + if (this.m_pathUnits.m_buffer[(int)((UIntPtr)unit)].m_referenceCount > 1) { + --this.m_pathUnits.m_buffer[unit].m_referenceCount; + break; + } + + /*if (this.m_pathUnits.m_buffer[unit].m_pathFindFlags == PathUnit.FLAG_CREATED) { + Log.Error($"Will release path unit {unit} which is CREATED!"); + }*/ + + uint nextPathUnit = this.m_pathUnits.m_buffer[(int)((UIntPtr)unit)].m_nextPathUnit; + this.m_pathUnits.m_buffer[(int)((UIntPtr)unit)].m_simulationFlags = 0; + this.m_pathUnits.m_buffer[(int)((UIntPtr)unit)].m_pathFindFlags = 0; + this.m_pathUnits.m_buffer[(int)((UIntPtr)unit)].m_nextPathUnit = 0u; + this.m_pathUnits.m_buffer[(int)((UIntPtr)unit)].m_referenceCount = 0; + this.m_pathUnits.ReleaseItem(unit); + unit = nextPathUnit; + if (++num >= 262144) { + CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); + break; + } + } + this.m_pathUnitCount = (int)(this.m_pathUnits.ItemCount() - 1u); + } finally { + Monitor.Exit(this.m_bufferLock); + } + } + + public bool CreatePath(ExtVehicleType vehicleType, ushort? vehicleId, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPos, PathUnit.Position endPos, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { PathUnit.Position position = default(PathUnit.Position); - return this.CreatePath(false, vehicleType, out unit, ref randomizer, buildIndex, startPos, position, endPos, position, position, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); + return this.CreatePath(false, vehicleType, vehicleId, out unit, ref randomizer, buildIndex, startPos, position, endPos, position, position, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); } - public bool CreatePath(ExtVehicleType vehicleType, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { + public bool CreatePath(ExtVehicleType vehicleType, ushort? vehicleId, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { PathUnit.Position def = default(PathUnit.Position); - return this.CreatePath(false, vehicleType, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); + return this.CreatePath(false, vehicleType, vehicleId, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); } - public bool CreatePath(ExtVehicleType vehicleType, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength, bool isHeavyVehicle, bool ignoreBlocked, bool stablePath, bool skipQueue) { + public bool CreatePath(ExtVehicleType vehicleType, ushort? vehicleId, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength, bool isHeavyVehicle, bool ignoreBlocked, bool stablePath, bool skipQueue) { PathUnit.Position def = default(PathUnit.Position); - return this.CreatePath(false, vehicleType, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, isHeavyVehicle, ignoreBlocked, stablePath, skipQueue, false); + return this.CreatePath(false, vehicleType, vehicleId, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, isHeavyVehicle, ignoreBlocked, stablePath, skipQueue, false); } - public bool CreatePath(bool recalc, ExtVehicleType vehicleType, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPos, PathUnit.Position endPos, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { + public bool CreatePath(bool recalc, ExtVehicleType vehicleType, ushort? vehicleId, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPos, PathUnit.Position endPos, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { PathUnit.Position position = default(PathUnit.Position); - return this.CreatePath(recalc, vehicleType, out unit, ref randomizer, buildIndex, startPos,position, endPos,position,position, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); + return this.CreatePath(recalc, vehicleType, vehicleId, out unit, ref randomizer, buildIndex, startPos,position, endPos,position,position, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); } - public bool CreatePath(bool recalc, ExtVehicleType vehicleType, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { + public bool CreatePath(bool recalc, ExtVehicleType vehicleType, ushort? vehicleId, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength) { PathUnit.Position def = default(PathUnit.Position); - return this.CreatePath(recalc, vehicleType, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); + return this.CreatePath(recalc, vehicleType, vehicleId, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, false, false, false, false, false); } - public bool CreatePath(bool recalc, ExtVehicleType vehicleType, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength, bool isHeavyVehicle, bool ignoreBlocked, bool stablePath, bool skipQueue) { + public bool CreatePath(bool recalc, ExtVehicleType vehicleType, ushort? vehicleId, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength, bool isHeavyVehicle, bool ignoreBlocked, bool stablePath, bool skipQueue) { PathUnit.Position def = default(PathUnit.Position); - return this.CreatePath(recalc, vehicleType, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, isHeavyVehicle, ignoreBlocked, stablePath, skipQueue, false); + return this.CreatePath(recalc, vehicleType, vehicleId, out unit, ref randomizer, buildIndex, startPosA, startPosB, endPosA, endPosB, def, laneTypes, vehicleTypes, maxLength, isHeavyVehicle, ignoreBlocked, stablePath, skipQueue, false); } - public bool CreatePath(bool recalc, ExtVehicleType vehicleType, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, PathUnit.Position vehiclePosition, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength, bool isHeavyVehicle, bool ignoreBlocked, bool stablePath, bool skipQueue, bool randomParking) { + public bool CreatePath(bool recalc, ExtVehicleType vehicleType, ushort? vehicleId, out uint unit, ref Randomizer randomizer, uint buildIndex, PathUnit.Position startPosA, PathUnit.Position startPosB, PathUnit.Position endPosA, PathUnit.Position endPosB, PathUnit.Position vehiclePosition, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, float maxLength, bool isHeavyVehicle, bool ignoreBlocked, bool stablePath, bool skipQueue, bool randomParking) { uint num; try { Monitor.Enter(this.m_bufferLock); @@ -174,7 +220,7 @@ public bool CreatePath(bool recalc, ExtVehicleType vehicleType, out uint unit, r ExtraQueuedPathFinds = 0; #endif #endif - for (int i = 0; i < _replacementPathFinds.Length; i++) { + for (int i = 0; i < _replacementPathFinds.Length; ++i) { CustomPathFind pathFindCandidate = _replacementPathFinds[i]; #if QUEUEDSTATS TotalQueuedPathFinds += (uint)pathFindCandidate.m_queuedPathFindCount; @@ -192,7 +238,7 @@ public bool CreatePath(bool recalc, ExtVehicleType vehicleType, out uint unit, r pathFind = pathFindCandidate; } } - if (pathFind != null && pathFind.CalculatePath(vehicleType, unit, skipQueue)) { + if (pathFind != null && pathFind.ExtCalculatePath(vehicleType, vehicleId, unit, skipQueue)) { return true; } this.ReleasePath(unit); diff --git a/TLM/TLM/Traffic/NodeGeometry.cs b/TLM/TLM/Geometry/NodeGeometry.cs similarity index 93% rename from TLM/TLM/Traffic/NodeGeometry.cs rename to TLM/TLM/Geometry/NodeGeometry.cs index d41ae5a1..5f34a79a 100644 --- a/TLM/TLM/Traffic/NodeGeometry.cs +++ b/TLM/TLM/Geometry/NodeGeometry.cs @@ -6,11 +6,13 @@ using System.Linq; using System.Text; using System.Threading; +using TrafficManager.Manager; using TrafficManager.State; +using TrafficManager.Traffic; using TrafficManager.TrafficLight; using TrafficManager.Util; -namespace TrafficManager.Traffic { +namespace TrafficManager.Geometry { public class NodeGeometry : IObservable { private static NodeGeometry[] nodeGeometries; @@ -139,17 +141,21 @@ internal void Recalculate() { Flags.applyNodeTrafficLightFlag(NodeId); + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + // check if node is valid if (!IsValid()) { for (int i = 0; i < 8; ++i) { SegmentEndGeometries[i] = null; } - TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, false, true); + tlsMan.RemoveNodeFromSimulation(NodeId, false, true); Flags.setNodeTrafficLight(NodeId, false); } else { NetManager netManager = Singleton.instance; + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + bool hasTrafficLight = (netManager.m_nodes.m_buffer[NodeId].m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None; - var nodeSim = TrafficLightSimulation.GetNodeSimulation(NodeId); + var nodeSim = tlsMan.GetNodeSimulation(NodeId); if (nodeSim == null) { byte numSegmentsWithSigns = 0; for (var s = 0; s < 8; s++) { @@ -161,7 +167,7 @@ internal void Recalculate() { Log._Debug($"NodeGeometry.Recalculate: Housekeeping segment {segmentId}"); #endif - SegmentEnd prioritySegment = TrafficPriority.GetPrioritySegment(NodeId, segmentId); + SegmentEnd prioritySegment = prioMan.GetPrioritySegment(NodeId, segmentId); if (prioritySegment == null) { continue; } @@ -180,7 +186,7 @@ internal void Recalculate() { if (numSegmentsWithSigns > 0) { // add priority segments for newly created segments - numSegmentsWithSigns += TrafficPriority.AddPriorityNode(NodeId); + numSegmentsWithSigns += prioMan.AddPriorityNode(NodeId); } } diff --git a/TLM/TLM/Traffic/SegmentEndGeometry.cs b/TLM/TLM/Geometry/SegmentEndGeometry.cs similarity index 99% rename from TLM/TLM/Traffic/SegmentEndGeometry.cs rename to TLM/TLM/Geometry/SegmentEndGeometry.cs index ac9d8456..f2215746 100644 --- a/TLM/TLM/Traffic/SegmentEndGeometry.cs +++ b/TLM/TLM/Geometry/SegmentEndGeometry.cs @@ -6,7 +6,7 @@ using TrafficManager.State; using UnityEngine; -namespace TrafficManager.Traffic { +namespace TrafficManager.Geometry { public class SegmentEndGeometry { public ushort SegmentId { get; private set; @@ -252,15 +252,15 @@ public ushort[] GetIncomingSegments() { } for (int k = 0; k < 7; ++k) { - if (IncomingRightSegments[k] == 0) + if (IncomingStraightSegments[k] == 0) break; - ret[i++] = IncomingRightSegments[k]; + ret[i++] = IncomingStraightSegments[k]; } for (int k = 0; k < 7; ++k) { - if (IncomingStraightSegments[k] == 0) + if (IncomingRightSegments[k] == 0) break; - ret[i++] = IncomingStraightSegments[k]; + ret[i++] = IncomingRightSegments[k]; } return ret; @@ -322,7 +322,7 @@ internal void Recalculate(bool propagate) { bool hasOtherSegments = false; for (var s = 0; s < 8; s++) { - var otherSegmentId = netManager.m_nodes.m_buffer[nodeId].GetSegment(s); + ushort otherSegmentId = netManager.m_nodes.m_buffer[nodeId].GetSegment(s); if (otherSegmentId == 0 || otherSegmentId == SegmentId) continue; /*ItemClass otherConnectionClass = Singleton.instance.m_segments.m_buffer[otherSegmentId].Info.GetConnectionClass(); diff --git a/TLM/TLM/Traffic/SegmentGeometry.cs b/TLM/TLM/Geometry/SegmentGeometry.cs similarity index 98% rename from TLM/TLM/Traffic/SegmentGeometry.cs rename to TLM/TLM/Geometry/SegmentGeometry.cs index b438c01e..4b33def4 100644 --- a/TLM/TLM/Traffic/SegmentGeometry.cs +++ b/TLM/TLM/Geometry/SegmentGeometry.cs @@ -8,12 +8,14 @@ using System.Threading; using TrafficManager.Custom.AI; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using TrafficManager.Util; using UnityEngine; +using TrafficManager.Traffic; +using TrafficManager.Manager; -namespace TrafficManager.Traffic { +namespace TrafficManager.Geometry { /// /// Manages segment geometry data (e.g. if a segment is one-way or not, which incoming/outgoing segments are connected at the start or end node) of one specific segment. /// Directional data (left, right, straight) is always given relatively to the managed segment. @@ -860,18 +862,18 @@ public bool IsOutgoing(bool startNode) { /// other segment /// defines if the segment should be checked at the start node (true) or end node (false) /// relative direction of the other segment relatively to the managed segment at the given node - public Direction GetDirection(ushort otherSegmentId, bool startNode) { + public ArrowDirection GetDirection(ushort otherSegmentId, bool startNode) { if (!IsValid(otherSegmentId)) - return Direction.Forward; + return ArrowDirection.Forward; if (otherSegmentId == SegmentId) - return Direction.Turn; + return ArrowDirection.Turn; else if (IsRightSegment(otherSegmentId, startNode)) - return Direction.Right; + return ArrowDirection.Right; else if (IsLeftSegment(otherSegmentId, startNode)) - return Direction.Left; + return ArrowDirection.Left; else - return Direction.Forward; + return ArrowDirection.Forward; } /// @@ -930,7 +932,7 @@ internal static bool calculateIsOutgoingOneWay(ushort segmentId, ushort nodeId) if (instance.m_segments.m_buffer[segmentId].m_startNode == nodeId) dir = NetInfo.Direction.Backward; var dir2 = ((instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir); - var dir3 = TrafficPriority.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; + var dir3 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; var laneId = instance.m_segments.m_buffer[segmentId].m_lanes; var laneIndex = 0; @@ -1023,7 +1025,7 @@ internal static void calculateOneWayAtNode(ushort segmentId, ushort nodeId, out if (instance.m_segments.m_buffer[segmentId].m_startNode == nodeId) dir = NetInfo.Direction.Backward; var dir2 = ((instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir); - var dir3 = TrafficPriority.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; + var dir3 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; var hasForward = false; var hasBackward = false; diff --git a/TLM/TLM/LoadingExtension.cs b/TLM/TLM/LoadingExtension.cs index 0b8203a2..fa617215 100644 --- a/TLM/TLM/LoadingExtension.cs +++ b/TLM/TLM/LoadingExtension.cs @@ -3,7 +3,7 @@ using ColossalFramework; using ICities; using TrafficManager.Custom.AI; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using TrafficManager.UI; using UnityEngine; @@ -16,6 +16,7 @@ using TrafficManager.Util; using TrafficManager.Custom.Manager; using System.Linq; +using TrafficManager.Manager; namespace TrafficManager { public class LoadingExtension : LoadingExtensionBase { @@ -60,7 +61,8 @@ public LoadingExtension() { public void revertDetours() { if (DetourInited) { Log.Info("Revert detours"); - foreach (Detour d in Detours.Reverse()) { + Detours.Reverse(); + foreach (Detour d in Detours) { RedirectionHelper.RevertRedirect(d.OriginalMethod, d.Redirect); } DetourInited = false; @@ -575,6 +577,39 @@ public void initDetours() { } if (IsPathManagerCompatible) { + Log.Info("Redirection PathFind::CalculatePath calls for non-Traffic++"); + try { + Detours.Add(new Detour(typeof(PathFind).GetMethod("CalculatePath", + BindingFlags.Public | BindingFlags.Instance, + null, + new[] + { + typeof (uint), + typeof (bool) + }, + null), + typeof(CustomPathFind).GetMethod("CalculatePath"))); + } catch (Exception) { + Log.Error("Could not redirect PathFind::CalculatePath"); + detourFailed = true; + } + + Log.Info("Redirection PathManager::ReleasePath calls for non-Traffic++"); + try { + Detours.Add(new Detour(typeof(PathManager).GetMethod("ReleasePath", + BindingFlags.Public | BindingFlags.Instance, + null, + new[] + { + typeof (uint) + }, + null), + typeof(CustomPathManager).GetMethod("ReleasePath"))); + } catch (Exception) { + Log.Error("Could not redirect PathManager::ReleasePath"); + detourFailed = true; + } + Log.Info("Redirection CarAI Calculate Segment Position calls for non-Traffic++"); try { Detours.Add(new Detour(typeof(CarAI).GetMethod("CalculateSegmentPosition", @@ -1057,6 +1092,9 @@ public override void OnLevelUnloading() { Log.Info("OnLevelUnloading"); base.OnLevelUnloading(); Instance = this; + if (IsPathManagerReplaced) { + Singleton.instance.WaitForAllPaths(); + } revertDetours(); gameLoaded = false; @@ -1064,11 +1102,11 @@ public override void OnLevelUnloading() { UI = null; try { - TrafficPriority.OnLevelUnloading(); + TrafficPriorityManager.Instance().OnLevelUnloading(); CustomCarAI.OnLevelUnloading(); CustomRoadAI.OnLevelUnloading(); - CustomTrafficLights.OnLevelUnloading(); - TrafficLightSimulation.OnLevelUnloading(); + CustomTrafficLightsManager.Instance().OnLevelUnloading(); + TrafficLightSimulationManager.Instance().OnLevelUnloading(); VehicleRestrictionsManager.Instance().OnLevelUnloading(); Flags.OnLevelUnloading(); Translation.OnLevelUnloading(); @@ -1115,7 +1153,6 @@ public override void OnLevelLoaded(LoadMode mode) { return; } - TrafficPriority.OnLevelLoading(); #if !TAM determinePathManagerCompatible(); IsRainfallLoaded = CheckRainfallIsLoaded(); diff --git a/TLM/TLM/TrafficLight/CustomTrafficLights.cs b/TLM/TLM/Manager/CustomTrafficLightsManager.cs similarity index 64% rename from TLM/TLM/TrafficLight/CustomTrafficLights.cs rename to TLM/TLM/Manager/CustomTrafficLightsManager.cs index 9098e2d2..aa6292d8 100644 --- a/TLM/TLM/TrafficLight/CustomTrafficLights.cs +++ b/TLM/TLM/Manager/CustomTrafficLightsManager.cs @@ -1,17 +1,35 @@ using System; using System.Collections.Generic; using ColossalFramework; -using TrafficManager.Traffic; +using TrafficManager.Geometry; +using TrafficManager.Util; +using TrafficManager.TrafficLight; -namespace TrafficManager.TrafficLight { - class CustomTrafficLights { +namespace TrafficManager.Manager { + /// + /// Manages the states of all custom traffic lights on the map + /// + public class CustomTrafficLightsManager : ICustomManager { + private static CustomTrafficLightsManager instance = null; + + public static CustomTrafficLightsManager Instance() { + if (instance == null) + instance = new CustomTrafficLightsManager(); + return instance; + } /// - /// Manual light by segment id + /// custom traffic lights by segment id /// - private static CustomSegment[] CustomSegments = new CustomSegment[NetManager.MAX_SEGMENT_COUNT]; + private CustomSegment[] CustomSegments = new CustomSegment[NetManager.MAX_SEGMENT_COUNT]; - internal static void AddLiveSegmentLights(ushort nodeId, ushort segmentId) { + /// + /// Adds custom traffic lights at the specified node and segment. + /// Light states (red, yellow, green) are taken from the "live" state, that is the traffic light's light state right before the custom light takes control. + /// + /// + /// + internal void AddLiveSegmentLights(ushort nodeId, ushort segmentId) { #if TRACE Singleton.instance.Start("CustomTrafficLights.AddLiveSegmentLights"); #endif @@ -22,8 +40,6 @@ internal static void AddLiveSegmentLights(ushort nodeId, ushort segmentId) { return; } - //Log.Message($"Adding live segment light: {segmentId} @ {nodeId}"); - var currentFrameIndex = Singleton.instance.m_currentFrameIndex; RoadBaseAI.TrafficLightState vehicleLightState; @@ -44,7 +60,14 @@ internal static void AddLiveSegmentLights(ushort nodeId, ushort segmentId) { #endif } - public static void AddSegmentLights(ushort nodeId, ushort segmentId, RoadBaseAI.TrafficLightState light=RoadBaseAI.TrafficLightState.Red) { + /// + /// Adds custom traffic lights at the specified node and segment. + /// Light stats are set to the given light state, or to "Red" if no light state is given. + /// + /// + /// + /// (optional) light state to set + public void AddSegmentLights(ushort nodeId, ushort segmentId, RoadBaseAI.TrafficLightState lightState=RoadBaseAI.TrafficLightState.Red) { #if TRACE Singleton.instance.Start("CustomTrafficLights.AddSegmentLights"); #endif @@ -67,15 +90,15 @@ public static void AddSegmentLights(ushort nodeId, ushort segmentId, RoadBaseAI. #endif if (customSegment.Node1 == 0) { - customSegment.Node1Lights = new CustomSegmentLights(nodeId, segmentId, light); + customSegment.Node1Lights = new CustomSegmentLights(nodeId, segmentId, lightState); customSegment.Node1 = nodeId; } else { - customSegment.Node2Lights = new CustomSegmentLights(nodeId, segmentId, light); + customSegment.Node2Lights = new CustomSegmentLights(nodeId, segmentId, lightState); customSegment.Node2 = nodeId; } } else { customSegment = new CustomSegment(); - customSegment.Node1Lights = new CustomSegmentLights(nodeId, segmentId, light); + customSegment.Node1Lights = new CustomSegmentLights(nodeId, segmentId, lightState); customSegment.Node1 = nodeId; CustomSegments[segmentId] = customSegment; } @@ -84,14 +107,23 @@ public static void AddSegmentLights(ushort nodeId, ushort segmentId, RoadBaseAI. #endif } - public static void RemoveSegmentLights(ushort segmentId) { + /// + /// Removes all custom traffic lights at both ends of the given segment. + /// + /// + public void RemoveSegmentLights(ushort segmentId) { #if DEBUG Log.Warning($"Removing all segment lights from segment {segmentId}"); #endif CustomSegments[segmentId] = null; } - public static void RemoveSegmentLight(ushort nodeId, ushort segmentId) { + /// + /// Removes the custom traffic light at the given segment end. + /// + /// + /// + public void RemoveSegmentLight(ushort nodeId, ushort segmentId) { #if TRACE Singleton.instance.Start("CustomTrafficLights.RemoveSegmentLight"); #endif @@ -121,7 +153,12 @@ public static void RemoveSegmentLight(ushort nodeId, ushort segmentId) { #endif } - private static void Cleanup(ushort segmentId) { + /// + /// Performs a housekeeping operation for all custom traffic lights at the given segment. + /// It is checked wheter the segment and connected nodes are still valid. If not, corresponding custom traffic lights are removed. + /// + /// + private void Cleanup(ushort segmentId) { #if TRACE Singleton.instance.Start("CustomTrafficLights.Cleanup"); #endif @@ -152,7 +189,13 @@ private static void Cleanup(ushort segmentId) { #endif } - public static bool IsSegmentLight(ushort nodeId, ushort segmentId) { + /// + /// Checks if a custom traffic light is present at the given segment end. + /// + /// + /// + /// + public bool IsSegmentLight(ushort nodeId, ushort segmentId) { #if TRACE Singleton.instance.Start("CustomTrafficLights.IsSegmentLight"); #endif @@ -177,7 +220,13 @@ public static bool IsSegmentLight(ushort nodeId, ushort segmentId) { return false; } - public static CustomSegmentLights GetOrLiveSegmentLights(ushort nodeId, ushort segmentId) { + /// + /// Retrieves the custom traffic light at the given segment end. If none exists, a new custom traffic light is created and returned. + /// + /// + /// + /// existing or new custom traffic light at segment end + public CustomSegmentLights GetOrLiveSegmentLights(ushort nodeId, ushort segmentId) { #if TRACE Singleton.instance.Start("CustomTrafficLights.GetOrLiveSegmentLights"); #endif @@ -191,7 +240,13 @@ public static CustomSegmentLights GetOrLiveSegmentLights(ushort nodeId, ushort s return ret; } - public static CustomSegmentLights GetSegmentLights(ushort nodeId, ushort segmentId) { + /// + /// Retrieves the custom traffic light at the given segment end. + /// + /// + /// + /// existing custom traffic light at segment end, null if none exists + public CustomSegmentLights GetSegmentLights(ushort nodeId, ushort segmentId) { #if TRACE Singleton.instance.Start("CustomTrafficLights.GetSegmentLights"); #endif @@ -224,7 +279,7 @@ public static CustomSegmentLights GetSegmentLights(ushort nodeId, ushort segment return null; } - internal static void OnLevelUnloading() { + public void OnLevelUnloading() { CustomSegments = new CustomSegment[NetManager.MAX_SEGMENT_COUNT]; } } diff --git a/TLM/TLM/Traffic/LaneConnectionManager.cs b/TLM/TLM/Manager/LaneConnectionManager.cs similarity index 97% rename from TLM/TLM/Traffic/LaneConnectionManager.cs rename to TLM/TLM/Manager/LaneConnectionManager.cs index af22a1de..d7fa282d 100644 --- a/TLM/TLM/Traffic/LaneConnectionManager.cs +++ b/TLM/TLM/Manager/LaneConnectionManager.cs @@ -6,11 +6,13 @@ using System.Linq; using System.Text; using System.Threading; +using TrafficManager.Geometry; +using TrafficManager.Traffic; using TrafficManager.State; using TrafficManager.Util; using UnityEngine; -namespace TrafficManager.Traffic { +namespace TrafficManager.Manager { public class LaneConnectionManager : IObserver { private static LaneConnectionManager instance = null; @@ -416,7 +418,7 @@ private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) { foreach (ushort connectedSegmentId in connectedSegmentIds) { if (connectedSegmentId == 0) continue; - Direction dir = segmentGeo.GetDirection(connectedSegmentId, startNode); + ArrowDirection dir = segmentGeo.GetDirection(connectedSegmentId, startNode); #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}. dir={dir}"); @@ -424,18 +426,18 @@ private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) { // check if arrow has already been set for this direction switch (dir) { - case Direction.Turn: + case ArrowDirection.Turn: default: continue; - case Direction.Forward: + case ArrowDirection.Forward: if ((arrows & Flags.LaneArrows.Forward) != Flags.LaneArrows.None) continue; break; - case Direction.Left: + case ArrowDirection.Left: if ((arrows & Flags.LaneArrows.Left) != Flags.LaneArrows.None) continue; break; - case Direction.Right: + case ArrowDirection.Right: if ((arrows & Flags.LaneArrows.Right) != Flags.LaneArrows.None) continue; break; @@ -468,16 +470,16 @@ private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) { #endif if (addArrow) { switch (dir) { - case Direction.Turn: + case ArrowDirection.Turn: default: continue; - case Direction.Forward: + case ArrowDirection.Forward: arrows |= Flags.LaneArrows.Forward; break; - case Direction.Left: + case ArrowDirection.Left: arrows |= Flags.LaneArrows.Left; break; - case Direction.Right: + case ArrowDirection.Right: arrows |= Flags.LaneArrows.Right; break; } diff --git a/TLM/TLM/Traffic/SpeedLimitManager.cs b/TLM/TLM/Manager/SpeedLimitManager.cs similarity index 99% rename from TLM/TLM/Traffic/SpeedLimitManager.cs rename to TLM/TLM/Manager/SpeedLimitManager.cs index 78a4a589..67783969 100644 --- a/TLM/TLM/Traffic/SpeedLimitManager.cs +++ b/TLM/TLM/Manager/SpeedLimitManager.cs @@ -6,10 +6,9 @@ using TrafficManager.State; using UnityEngine; -namespace TrafficManager.Traffic { +namespace TrafficManager.Manager { public class SpeedLimitManager { private static SpeedLimitManager instance = null; - private static readonly float MAX_SPEED = 6f; // 300 km/h public static SpeedLimitManager Instance() { if (instance == null) @@ -17,6 +16,8 @@ public static SpeedLimitManager Instance() { return instance; } + private static readonly float MAX_SPEED = 6f; // 300 km/h + static SpeedLimitManager() { Instance(); } diff --git a/TLM/TLM/Manager/TrafficLightSimulationManager.cs b/TLM/TLM/Manager/TrafficLightSimulationManager.cs new file mode 100644 index 00000000..0de6afb6 --- /dev/null +++ b/TLM/TLM/Manager/TrafficLightSimulationManager.cs @@ -0,0 +1,135 @@ +using System; +using ColossalFramework; +using TrafficManager.Geometry; +using System.Collections.Generic; +using TrafficManager.State; +using TrafficManager.Custom.AI; +using System.Linq; +using TrafficManager.Util; +using TrafficManager.TrafficLight; + +namespace TrafficManager.Manager { + public class TrafficLightSimulationManager : ICustomManager { + private static TrafficLightSimulationManager instance = null; + + public static TrafficLightSimulationManager Instance() { + if (instance == null) + instance = new TrafficLightSimulationManager(); + return instance; + } + + /// + /// For each node id: traffic light simulation assigned to the node + /// + //public TrafficLightSimulation[] TrafficLightSimulations = new TrafficLightSimulation[NetManager.MAX_NODE_COUNT]; + public Dictionary TrafficLightSimulations = new Dictionary(); + + private TrafficLightSimulationManager() { + + } + + public void SimulationStep() { +#if TRACE + Singleton.instance.Start("TrafficLightSimulation.SimulationStep"); +#endif + try { + foreach (KeyValuePair e in TrafficLightSimulations) { + try { + var nodeSim = e.Value; + var nodeId = e.Key; + if (nodeSim.IsTimedLightActive()) { + Flags.applyNodeTrafficLightFlag(nodeId); + nodeSim.TimedLight.SimulationStep(); + } + } catch (Exception ex) { + Log.Warning($"Error occured while simulating traffic light @ node {e.Key}: {ex.ToString()}"); + } + } + } catch (Exception ex) { + // TODO the dictionary was modified (probably a segment connected to a traffic light was changed/removed). rework this + Log.Warning($"Error occured while iterating over traffic light simulations: {ex.ToString()}"); + } +#if TRACE + Singleton.instance.Stop("TrafficLightSimulation.SimulationStep"); +#endif + } + + /// + /// Adds a traffic light simulation to the node with the given id + /// + /// + public TrafficLightSimulation AddNodeToSimulation(ushort nodeId) { + if (TrafficLightSimulations.ContainsKey(nodeId)) { + return TrafficLightSimulations[nodeId]; + } + TrafficLightSimulations.Add(nodeId, new TrafficLightSimulation(nodeId)); + return TrafficLightSimulations[nodeId]; + } + + /// + /// Destroys the traffic light and removes it + /// + /// + /// + public void RemoveNodeFromSimulation(ushort nodeId, bool destroyGroup, bool removeTrafficLight) { + if (!TrafficLightSimulations.ContainsKey(nodeId)) + return; + + TrafficLightSimulation sim = TrafficLightSimulations[nodeId]; + + if (sim.TimedLight != null) { + // remove/destroy other timed traffic lights in group + List oldNodeGroup = new List(sim.TimedLight.NodeGroup); + foreach (var timedNodeId in oldNodeGroup) { + var otherNodeSim = GetNodeSimulation(timedNodeId); + if (otherNodeSim == null) { + continue; + } + + if (destroyGroup || timedNodeId == nodeId) { + //Log._Debug($"Slave: Removing simulation @ node {timedNodeId}"); + otherNodeSim.DestroyTimedTrafficLight(); + otherNodeSim.DestroyManualTrafficLight(); + otherNodeSim.NodeGeoUnsubscriber?.Dispose(); + TrafficLightSimulations.Remove(timedNodeId); + if (removeTrafficLight) + Flags.setNodeTrafficLight(timedNodeId, false); + } else { + otherNodeSim.TimedLight.RemoveNodeFromGroup(nodeId); + } + } + } + + //Flags.setNodeTrafficLight(nodeId, false); + sim.DestroyTimedTrafficLight(); + sim.DestroyManualTrafficLight(); + sim.NodeGeoUnsubscriber?.Dispose(); + TrafficLightSimulations.Remove(nodeId); + if (removeTrafficLight) + Flags.setNodeTrafficLight(nodeId, false); + } + + public TrafficLightSimulation GetNodeSimulation(ushort nodeId) { +#if TRACE + Singleton.instance.Start("TrafficLightSimulation.GetNodeSimulation"); +#endif + + TrafficLightSimulation ret = null; + if (TrafficLightSimulations.ContainsKey(nodeId)) { + ret = TrafficLightSimulations[nodeId]; + } + +#if TRACE + Singleton.instance.Stop("TrafficLightSimulation.GetNodeSimulation"); +#endif + return ret; + } + + public void OnLevelUnloading() { + TrafficLightSimulations.Clear(); + /*for (ushort nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) { + TrafficLightSimulations[nodeId] = null; + }*/ + } + } +} diff --git a/TLM/TLM/Traffic/TrafficPriority.cs b/TLM/TLM/Manager/TrafficPriorityManager.cs similarity index 84% rename from TLM/TLM/Traffic/TrafficPriority.cs rename to TLM/TLM/Manager/TrafficPriorityManager.cs index 31578905..26598b2b 100644 --- a/TLM/TLM/Traffic/TrafficPriority.cs +++ b/TLM/TLM/Manager/TrafficPriorityManager.cs @@ -7,27 +7,33 @@ using UnityEngine; using TrafficManager.State; using System.Threading; +using TrafficManager.Util; +using TrafficManager.Traffic; +using TrafficManager.Geometry; + +namespace TrafficManager.Manager { + public class TrafficPriorityManager : ICustomManager { + private static TrafficPriorityManager instance = null; + + public static TrafficPriorityManager Instance() { + if (instance == null) + instance = new TrafficPriorityManager(); + return instance; + } -namespace TrafficManager.Traffic { - class TrafficPriority { - public static float maxStopVelocity = 0.1f; - public static float maxYieldVelocity = 0.3f; - - /// - /// Dictionary of segments that are connected to roads with timed traffic lights or priority signs. Index: segment id - /// - public static TrafficSegment[] TrafficSegments = null; + public readonly static float maxStopVelocity = 0.1f; + public readonly static float maxYieldVelocity = 0.3f; /// - /// Determines if vehicles should be cleared + /// List of segments that are connected to roads with timed traffic lights or priority signs. Index: segment id /// - private static bool ClearTrafficRequested = false; + public TrafficSegment[] TrafficSegments = null; - static TrafficPriority() { + private TrafficPriorityManager() { TrafficSegments = new TrafficSegment[Singleton.instance.m_segments.m_size]; } - public static SegmentEnd AddPrioritySegment(ushort nodeId, ushort segmentId, SegmentEnd.PriorityType type) { + public SegmentEnd AddPrioritySegment(ushort nodeId, ushort segmentId, SegmentEnd.PriorityType type) { #if TRACE Singleton.instance.Start("TrafficPriority.AddPrioritySegment"); #endif @@ -98,7 +104,7 @@ public static SegmentEnd AddPrioritySegment(ushort nodeId, ushort segmentId, Seg return ret; } - public static void RemovePrioritySegments(ushort nodeId) { // priorityNodes: OK + public void RemovePrioritySegments(ushort nodeId) { // priorityNodes: OK if (nodeId <= 0) return; @@ -111,7 +117,7 @@ public static void RemovePrioritySegments(ushort nodeId) { // priorityNodes: OK } } - public static List GetPrioritySegments(ushort nodeId) { + public List GetPrioritySegments(ushort nodeId) { #if TRACE Singleton.instance.Start("TrafficPriority.GetPrioritySegments"); #endif @@ -140,7 +146,7 @@ public static List GetPrioritySegments(ushort nodeId) { return ret; } - public static bool IsPrioritySegment(ushort nodeId, ushort segmentId) { + public bool IsPrioritySegment(ushort nodeId, ushort segmentId) { #if TRACE Singleton.instance.Start("TrafficPriority.IsPrioritySegment"); #endif @@ -157,7 +163,7 @@ public static bool IsPrioritySegment(ushort nodeId, ushort segmentId) { NetManager netManager = Singleton.instance; if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) { RemovePrioritySegment(nodeId, segmentId); - CustomTrafficLights.RemoveSegmentLights(segmentId); + CustomTrafficLightsManager.Instance().RemoveSegmentLights(segmentId); #if TRACE Singleton.instance.Stop("TrafficPriority.IsPrioritySegment"); #endif @@ -178,7 +184,7 @@ public static bool IsPrioritySegment(ushort nodeId, ushort segmentId) { return false; } - public static bool IsPriorityNode(ushort nodeId) { + public bool IsPriorityNode(ushort nodeId) { #if TRACE Singleton.instance.Start("TrafficPriority.IsPriorityNode"); #endif @@ -210,7 +216,7 @@ public static bool IsPriorityNode(ushort nodeId) { return false; } - public static SegmentEnd GetPrioritySegment(ushort nodeId, ushort segmentId) { + public SegmentEnd GetPrioritySegment(ushort nodeId, ushort segmentId) { #if TRACE Singleton.instance.Start("TrafficPriority.GetPrioritySegment"); #endif @@ -245,7 +251,7 @@ public static SegmentEnd GetPrioritySegment(ushort nodeId, ushort segmentId) { return ret; } - internal static void RemovePrioritySegment(ushort nodeId, ushort segmentId) { + internal void RemovePrioritySegment(ushort nodeId, ushort segmentId) { if (nodeId <= 0 || segmentId <= 0 || TrafficSegments[segmentId] == null) return; var prioritySegment = TrafficSegments[segmentId]; @@ -271,35 +277,12 @@ internal static void RemovePrioritySegment(ushort nodeId, ushort segmentId) { TrafficSegments[segmentId] = null; } - internal static void ClearTraffic() { - try { - Monitor.Enter(Singleton.instance); - - for (ushort i = 0; i < Singleton.instance.m_vehicles.m_size; ++i) { - if ( - (Singleton.instance.m_vehicles.m_buffer[i].m_flags & Vehicle.Flags.Created) != 0 /*&& - Singleton.instance.m_vehicles.m_buffer[i].Info.m_vehicleType == VehicleInfo.VehicleType.Car*/) - Singleton.instance.ReleaseVehicle(i); - } - - CustomRoadAI.resetTrafficStats(); - } catch (Exception ex) { - Log.Error($"Error occured while trying to clear traffic: {ex.ToString()}"); - } finally { - Monitor.Exit(Singleton.instance); - } - } - - internal static void RequestClearTraffic() { - ClearTrafficRequested = true; - } - /// /// Adds/Sets a node as a priority node /// /// /// number of priority segments added - internal static byte AddPriorityNode(ushort nodeId) { + internal byte AddPriorityNode(ushort nodeId) { if (nodeId <= 0) return 0; @@ -309,18 +292,18 @@ internal static byte AddPriorityNode(ushort nodeId) { if (segmentId == 0) continue; - if (TrafficPriority.IsPrioritySegment(nodeId, segmentId)) + if (IsPrioritySegment(nodeId, segmentId)) continue; /*if (SegmentGeometry.Get(segmentId).IsOutgoingOneWay(nodeId)) continue;*/ // we need this for pedestrian traffic lights - TrafficPriority.AddPrioritySegment(nodeId, segmentId, SegmentEnd.PriorityType.None); + AddPrioritySegment(nodeId, segmentId, SegmentEnd.PriorityType.None); ++ret; } return ret; } - public static bool HasIncomingVehiclesWithHigherPriority(ushort targetVehicleId, ref Vehicle targetVehicleData, ref PathUnit.Position curPos, ref PathUnit.Position nextPos) { + public bool HasIncomingVehiclesWithHigherPriority(ushort targetVehicleId, ref Vehicle targetVehicleData, ref PathUnit.Position curPos, ref PathUnit.Position nextPos) { #if TRACE Singleton.instance.Start("TrafficPriority.HasIncomingVehiclesWithHigherPriority"); #endif @@ -566,7 +549,7 @@ public static bool HasIncomingVehiclesWithHigherPriority(ushort targetVehicleId, return false; } - private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort targetCarId, ref Vehicle targetVehicleData, ref PathUnit.Position targetCurPos, ref PathUnit.Position targetNextPos, bool targetIsOnMainRoad, ushort incomingCarId, ref PathUnit.Position incomingCurPos, ref PathUnit.Position incomingNextPos, bool incomingIsOnMainRoad, SegmentEnd targetEnd, SegmentEnd incomingEnd) { + private bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort targetCarId, ref Vehicle targetVehicleData, ref PathUnit.Position targetCurPos, ref PathUnit.Position targetNextPos, bool targetIsOnMainRoad, ushort incomingCarId, ref PathUnit.Position incomingCurPos, ref PathUnit.Position incomingNextPos, bool incomingIsOnMainRoad, SegmentEnd targetEnd, SegmentEnd incomingEnd) { #if TRACE Singleton.instance.Start("TrafficPriority.HasVehiclePriority"); #endif @@ -637,10 +620,8 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort //var targetVehPos = targetVehState.GetCurrentPosition(); - bool incomingIsOnEmergency = false;// (incomingVehState.VehicleType & ExtVehicleType.Emergency) != ExtVehicleType.None; - // check if target is on main road and incoming is on low-priority road - if (targetIsOnMainRoad && !incomingIsOnMainRoad && !incomingIsOnEmergency) { + if (targetIsOnMainRoad && !incomingIsOnMainRoad) { #if DEBUG if (debug) { Log._Debug($" HasVehiclePriority: Target is on main road and incoming is not. Target HAS PRIORITY."); @@ -665,9 +646,9 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort SegmentGeometry targetGeometry = SegmentGeometry.Get(targetCurPos.m_segment); SegmentGeometry incomingGeometry = SegmentGeometry.Get(incomingCurPos.m_segment); bool isTargetStartNode = targetGeometry.StartNodeId() == nodeId; - Direction targetToDir = targetGeometry.GetDirection(targetNextPos.m_segment, isTargetStartNode); - Direction incomingFromRelDir = targetGeometry.GetDirection(incomingCurPos.m_segment, isTargetStartNode); - Direction incomingToDir = incomingGeometry.GetDirection(incomingNextPos.m_segment, incomingGeometry.StartNodeId() == nodeId); + ArrowDirection targetToDir = targetGeometry.GetDirection(targetNextPos.m_segment, isTargetStartNode); + ArrowDirection incomingFromRelDir = targetGeometry.GetDirection(incomingCurPos.m_segment, isTargetStartNode); + ArrowDirection incomingToDir = incomingGeometry.GetDirection(incomingNextPos.m_segment, incomingGeometry.StartNodeId() == nodeId); #if DEBUG if (debug) { Log._Debug($" HasVehiclePriority: targetToDir: {targetToDir.ToString()}, incomingRelDir: {incomingFromRelDir.ToString()}, incomingToDir: {incomingToDir.ToString()}"); @@ -712,7 +693,7 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort } #endif switch (targetToDir) { - case Direction.Left: + case ArrowDirection.Left: laneOrderCorrect = IsLaneOrderConflictFree(debug, targetNextPos.m_segment, transitNodeId, targetNextPos.m_lane, incomingNextPos.m_lane); // stay left #if DEBUG if (debug) { @@ -720,11 +701,11 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort } #endif break; - case Direction.Forward: + case ArrowDirection.Forward: default: switch (incomingFromRelDir) { - case Direction.Left: - case Direction.Forward: + case ArrowDirection.Left: + case ArrowDirection.Forward: laneOrderCorrect = IsLaneOrderConflictFree(debug, targetNextPos.m_segment, transitNodeId, incomingNextPos.m_lane, targetNextPos.m_lane); // stay right #if DEBUG if (debug) { @@ -732,7 +713,7 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort } #endif break; - case Direction.Right: + case ArrowDirection.Right: laneOrderCorrect = IsLaneOrderConflictFree(debug, targetNextPos.m_segment, transitNodeId, targetNextPos.m_lane, incomingNextPos.m_lane); // stay left #if DEBUG if (debug) { @@ -740,7 +721,7 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort } #endif break; - case Direction.Turn: + case ArrowDirection.Turn: default: laneOrderCorrect = true; #if DEBUG @@ -751,7 +732,7 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort break; } break; - case Direction.Right: + case ArrowDirection.Right: laneOrderCorrect = IsLaneOrderConflictFree(debug, targetNextPos.m_segment, transitNodeId, incomingNextPos.m_lane, targetNextPos.m_lane); // stay right #if DEBUG if (debug) { @@ -781,7 +762,7 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort return true; } - bool incomingCrossingStreet = incomingToDir == Direction.Forward || incomingToDir == Direction.Left; + bool incomingCrossingStreet = incomingToDir == ArrowDirection.Forward || incomingToDir == ArrowDirection.Left; #if DEBUG if (debug) { @@ -791,9 +772,9 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort bool ret; switch (targetToDir) { - case Direction.Right: + case ArrowDirection.Right: // target: BOTTOM->RIGHT - if (((!targetIsOnMainRoad && incomingIsOnMainRoad) || incomingIsOnEmergency) && sameTargets && !laneOrderCorrect) { + if ((!targetIsOnMainRoad && incomingIsOnMainRoad) && sameTargets && !laneOrderCorrect) { #if DEBUG if (debug) { Log._Debug($" HasVehiclePriority: Target is going RIGHT and is on low-priority road turning right. the other vehicle is on a priority road. target MUST WAIT."); @@ -807,7 +788,7 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort #if DEBUG if (debug) { - Log._Debug($" HasVehiclePriority: Target is going RIGHT without conflict (targetIsOnMainRoad={targetIsOnMainRoad}, incomingIsOnMainRoad={incomingIsOnMainRoad}, incomingIsOnEmergency={incomingIsOnEmergency}, sameTargets={sameTargets}, laneOrderCorrect={laneOrderCorrect}). target HAS PRIORITY."); + Log._Debug($" HasVehiclePriority: Target is going RIGHT without conflict (targetIsOnMainRoad={targetIsOnMainRoad}, incomingIsOnMainRoad={incomingIsOnMainRoad}, sameTargets={sameTargets}, laneOrderCorrect={laneOrderCorrect}). target HAS PRIORITY."); } #endif @@ -815,33 +796,33 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort Singleton.instance.Stop("TrafficPriority.HasVehiclePriority"); #endif return true; - case Direction.Forward: + case ArrowDirection.Forward: default: // target: BOTTOM->TOP switch (incomingFromRelDir) { - case Direction.Right: - ret = !incomingIsOnMainRoad && !incomingIsOnEmergency && !incomingCrossingStreet; + case ArrowDirection.Right: + ret = !incomingIsOnMainRoad && !incomingCrossingStreet; #if DEBUG if (debug) { - Log._Debug($" HasVehiclePriority: Target is going FORWARD, incoming is coming from RIGHT. incomingIsOnMainRoad={incomingIsOnMainRoad}, incomingIsOnEmergency={incomingIsOnEmergency}, incomingCrossingStreet={incomingCrossingStreet}, result={ret}"); + Log._Debug($" HasVehiclePriority: Target is going FORWARD, incoming is coming from RIGHT. incomingIsOnMainRoad={incomingIsOnMainRoad}, incomingCrossingStreet={incomingCrossingStreet}, result={ret}"); } #endif #if TRACE Singleton.instance.Stop("TrafficPriority.HasVehiclePriority"); #endif return ret; - case Direction.Left: - ret = (targetIsOnMainRoad && !incomingIsOnEmergency) || !incomingCrossingStreet; + case ArrowDirection.Left: + ret = true;// ; targetIsOnMainRoad || !incomingCrossingStreet; #if DEBUG if (debug) { - Log._Debug($" HasVehiclePriority: Target is going FORWARD, incoming is coming from LEFT. targetIsOnMainRoad={targetIsOnMainRoad}, incomingIsOnEmergency={incomingIsOnEmergency}, incomingCrossingStreet={incomingCrossingStreet}, result={ret}"); + Log._Debug($" HasVehiclePriority: Target is going FORWARD, incoming is coming from LEFT. targetIsOnMainRoad={targetIsOnMainRoad}, incomingCrossingStreet={incomingCrossingStreet}, result={ret}"); } #endif #if TRACE Singleton.instance.Stop("TrafficPriority.HasVehiclePriority"); #endif return ret; - case Direction.Forward: + case ArrowDirection.Forward: default: #if DEBUG if (debug) { @@ -853,10 +834,10 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort #endif return true; } - case Direction.Left: + case ArrowDirection.Left: // target: BOTTOM->LEFT switch (incomingFromRelDir) { - case Direction.Right: + case ArrowDirection.Right: ret = !incomingCrossingStreet; #if DEBUG if (debug) { @@ -867,24 +848,24 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort Singleton.instance.Stop("TrafficPriority.HasVehiclePriority"); #endif return ret; - case Direction.Left: - if (targetIsOnMainRoad && incomingIsOnMainRoad && !incomingIsOnEmergency) { // bent priority road + case ArrowDirection.Left: + if (targetIsOnMainRoad && incomingIsOnMainRoad) { // bent priority road ret = true; } else { ret = !incomingCrossingStreet; } #if DEBUG if (debug) { - Log._Debug($" HasVehiclePriority: Target is going LEFT, incoming is coming from LEFT. targetIsOnMainRoad={targetIsOnMainRoad}, incomingIsOnMainRoad={incomingIsOnMainRoad}, incomingIsOnEmergency={incomingIsOnEmergency}, incomingCrossingStreet={incomingCrossingStreet}. result={ret}"); + Log._Debug($" HasVehiclePriority: Target is going LEFT, incoming is coming from LEFT. targetIsOnMainRoad={targetIsOnMainRoad}, incomingIsOnMainRoad={incomingIsOnMainRoad}, incomingCrossingStreet={incomingCrossingStreet}. result={ret}"); } #endif #if TRACE Singleton.instance.Stop("TrafficPriority.HasVehiclePriority"); #endif return ret; - case Direction.Forward: + case ArrowDirection.Forward: default: - ret = incomingToDir == Direction.Left || incomingToDir == Direction.Turn; + ret = incomingToDir == ArrowDirection.Left || incomingToDir == ArrowDirection.Turn; #if DEBUG if (debug) { Log._Debug($" HasVehiclePriority: Target is going LEFT, incoming is coming from FORWARD. incomingToDir={incomingToDir}. result={ret}"); @@ -911,29 +892,15 @@ private static bool HasVehiclePriority(bool debug, ushort transitNodeId, ushort return false; } - private static Direction InvertLeftRight(Direction dir) { - if (dir == Direction.Left) - dir = Direction.Right; - else if (dir == Direction.Right) - dir = Direction.Left; + private static ArrowDirection InvertLeftRight(ArrowDirection dir) { + if (dir == ArrowDirection.Left) + dir = ArrowDirection.Right; + else if (dir == ArrowDirection.Right) + dir = ArrowDirection.Left; return dir; } - internal static void OnLevelLoading() { - try { - //TrafficPriority.fixJunctions(); // TODO maybe remove this - } catch (Exception e) { - Log.Error($"OnLevelLoading: {e.ToString()}"); - } - } - - internal static void OnLevelUnloading() { - TrafficLightSimulation.TrafficLightSimulations.Clear(); - for (int i = 0; i < TrafficSegments.Length; ++i) - TrafficSegments[i] = null; - } - - public static bool IsLaneOrderConflictFree(bool debug, ushort segmentId, ushort nodeId, byte leftLaneIndex, byte rightLaneIndex) { // TODO I think this is incorrect. See TrafficLightTool._guiLaneChangeWindow + public bool IsLaneOrderConflictFree(bool debug, ushort segmentId, ushort nodeId, byte leftLaneIndex, byte rightLaneIndex) { // TODO I think this is incorrect. See TrafficLightTool._guiLaneChangeWindow #if TRACE Singleton.instance.Start("TrafficPriority.IsLaneOrderConflictFree"); #endif @@ -951,7 +918,7 @@ public static bool IsLaneOrderConflictFree(bool debug, ushort segmentId, ushort NetInfo.Direction dir = nodeId == netManager.m_segments.m_buffer[segmentId].m_startNode ? NetInfo.Direction.Backward : NetInfo.Direction.Forward; NetInfo.Direction dir2 = ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir); - NetInfo.Direction dir3 = TrafficPriority.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; + NetInfo.Direction dir3 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; NetInfo.Lane leftLane = segmentInfo.m_lanes[leftLaneIndex]; NetInfo.Lane rightLane = segmentInfo.m_lanes[rightLaneIndex]; @@ -986,14 +953,10 @@ public static bool IsLeftHandDrive() { //private static ushort nextValidityCheckedSegment = 0; - public static void SegmentSimulationStep(ushort segmentId) { + public void SegmentSimulationStep(ushort segmentId) { #if TRACE Singleton.instance.Start("TrafficPriority.SegmentSimulationStep"); #endif - if (ClearTrafficRequested) { - TrafficPriority.ClearTraffic(); - ClearTrafficRequested = false; - } // simulate segment-ends TrafficSegment trafficSegment = TrafficSegments[segmentId]; @@ -1009,5 +972,11 @@ public static void SegmentSimulationStep(ushort segmentId) { Singleton.instance.Stop("TrafficPriority.SegmentSimulationStep"); #endif } + + public void OnLevelUnloading() { + TrafficLightSimulationManager.Instance().TrafficLightSimulations.Clear(); + for (int i = 0; i < TrafficSegments.Length; ++i) + TrafficSegments[i] = null; + } } } diff --git a/TLM/TLM/Traffic/VehicleRestrictionsManager.cs b/TLM/TLM/Manager/VehicleRestrictionsManager.cs similarity index 98% rename from TLM/TLM/Traffic/VehicleRestrictionsManager.cs rename to TLM/TLM/Manager/VehicleRestrictionsManager.cs index e9fe8c63..f4fe2076 100644 --- a/TLM/TLM/Traffic/VehicleRestrictionsManager.cs +++ b/TLM/TLM/Manager/VehicleRestrictionsManager.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using TrafficManager.Geometry; using TrafficManager.State; +using TrafficManager.Traffic; -namespace TrafficManager.Traffic { - class VehicleRestrictionsManager { +namespace TrafficManager.Manager { + public class VehicleRestrictionsManager { private static VehicleRestrictionsManager instance = null; public static VehicleRestrictionsManager Instance() { @@ -88,7 +90,7 @@ internal Dictionary GetAllowedVehicleTypesAsDict(ushort se var dir = NetInfo.Direction.Forward; var dir2 = ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir); - var dir3 = TrafficPriority.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; + var dir3 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; NetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info; uint curLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes; diff --git a/TLM/TLM/Traffic/VehicleStateManager.cs b/TLM/TLM/Manager/VehicleStateManager.cs similarity index 84% rename from TLM/TLM/Traffic/VehicleStateManager.cs rename to TLM/TLM/Manager/VehicleStateManager.cs index 17ab4a5a..a5f7e993 100644 --- a/TLM/TLM/Traffic/VehicleStateManager.cs +++ b/TLM/TLM/Manager/VehicleStateManager.cs @@ -1,4 +1,6 @@ -using ColossalFramework; +#define USEPATHWAITCOUNTERx + +using ColossalFramework; using System; using System.Collections.Generic; using System.Linq; @@ -6,9 +8,10 @@ using System.Threading; using TrafficManager.Custom.AI; using TrafficManager.State; +using TrafficManager.Traffic; using UnityEngine; -namespace TrafficManager.Traffic { +namespace TrafficManager.Manager { public class VehicleStateManager { private static VehicleStateManager instance = null; @@ -18,6 +21,11 @@ public static VehicleStateManager Instance() { return instance; } + /// + /// Determines if vehicles should be cleared + /// + private static bool ClearTrafficRequested = false; + static VehicleStateManager() { Instance(); } @@ -128,6 +136,9 @@ internal void OnReleaseVehicle(ushort vehicleId) { #endif VehicleState state = _GetVehicleState(vehicleId); state.VehicleType = ExtVehicleType.None; +#if USEPATHWAITCOUNTER + state.PathWaitCounter = 0; +#endif state.Valid = false; //VehicleStates[vehicleId].Reset(); #if TRACE @@ -135,12 +146,12 @@ internal void OnReleaseVehicle(ushort vehicleId) { #endif } - internal void OnVehicleSpawned(ushort vehicleId, ref Vehicle vehicleData) { + internal void OnBeforeSpawnVehicle(ushort vehicleId, ref Vehicle vehicleData) { //Log._Debug($"VehicleStateManager: OnPathFindReady({vehicleId})"); #if TRACE Singleton.instance.Start("VehicleStateManager.OnPathFindReady"); #endif - VehicleStates[vehicleId].OnVehicleSpawned(ref vehicleData); + VehicleStates[vehicleId].OnBeforeSpawnVehicle(ref vehicleData); #if TRACE Singleton.instance.Stop("VehicleStateManager.OnPathFindReady"); #endif @@ -157,7 +168,7 @@ internal void InitAllVehicles() { try { DetermineVehicleType(vehicleId, ref Singleton.instance.m_vehicles.m_buffer[vehicleId]); - OnVehicleSpawned(vehicleId, ref Singleton.instance.m_vehicles.m_buffer[vehicleId]); + OnBeforeSpawnVehicle(vehicleId, ref Singleton.instance.m_vehicles.m_buffer[vehicleId]); } catch (Exception e) { Log.Error("VehicleStateManager: InitAllVehicles Error: " + e.ToString()); } @@ -239,5 +250,36 @@ internal void InitAllVehicles() { #endif return null; } + + internal void SimulationStep() { + try { + if (ClearTrafficRequested) { + ClearTraffic(); + ClearTrafficRequested = false; + } + } finally { } + } + + internal void ClearTraffic() { + try { + Monitor.Enter(Singleton.instance); + + for (ushort i = 0; i < Singleton.instance.m_vehicles.m_size; ++i) { + if ( + (Singleton.instance.m_vehicles.m_buffer[i].m_flags & Vehicle.Flags.Created) != 0) + Singleton.instance.ReleaseVehicle(i); + } + + CustomRoadAI.resetTrafficStats(); + } catch (Exception ex) { + Log.Error($"Error occured while trying to clear traffic: {ex.ToString()}"); + } finally { + Monitor.Exit(Singleton.instance); + } + } + + internal void RequestClearTraffic() { + ClearTrafficRequested = true; + } } } diff --git a/TLM/TLM/Resources/lang_fr.txt b/TLM/TLM/Resources/lang_fr.txt index 1d6b653a..74a2377d 100644 --- a/TLM/TLM/Resources/lang_fr.txt +++ b/TLM/TLM/Resources/lang_fr.txt @@ -113,15 +113,15 @@ Select_a_timed_traffic_light_program Sélectionner un programme de chronométrag The_chosen_traffic_light_program_is_incompatible_to_this_junction Le modèle de chronométrage choisi est incompatible avec cette jonction Advanced_AI_cannot_be_activated L'IA avancée ne peut pas être activée The_Advanced_Vehicle_AI_cannot_be_activated L'IA avancée de véhicule ne peut être activée car vous utilisez déjà un autre mod qui modifie modifie le comportement des véhicules (par exemple Improved AI ou Traffic++). -Enable_dynamic_path_calculation Enable dynamic path calculation -Lane_Arrow_Changer_Disabled_Connection The lane arrow changer for this lane is disabled because you have created lane connections by hand. -Lane_connector Lane connector -Connected_lanes Connected lanes -Use_alternative_view_mode Use alternative view mode -Road_type Road type -Default_speed_limit Default speed limit -Unit_system Unit system -Metric Metric -Imperial Imperial -Use_more_CPU_cores_for_route_calculation_if_available Use more CPU cores for route calculation (if available) -Activated_features Activated features \ No newline at end of file +Enable_dynamic_path_calculation Activer le calcul dynamique des chemins +Lane_Arrow_Changer_Disabled_Connection Le modificateur des directions de voies est désactivé pour cette voie car vous avez manuellement créé des connections de voies. +Lane_connector Connecteur de voies +Connected_lanes Voies connectées +Use_alternative_view_mode Utiliser le mode de vue alternatif +Road_type Type de route +Default_speed_limit Limite de vitesse par défaut +Unit_system Système d'unités +Metric Système métrique +Imperial Système impérial +Use_more_CPU_cores_for_route_calculation_if_available Utiliser plus de cœurs du CPU pour le calcul de trajets (si disponible) +Activated_features caractéristiques activées \ No newline at end of file diff --git a/TLM/TLM/State/Flags.cs b/TLM/TLM/State/Flags.cs index adc6fa2c..927aee5d 100644 --- a/TLM/TLM/State/Flags.cs +++ b/TLM/TLM/State/Flags.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Text; using System.Threading; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; namespace TrafficManager.State { @@ -83,9 +85,11 @@ public static bool IsInitDone() { } public static void resetTrafficLights(bool all) { + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + for (ushort i = 0; i < Singleton.instance.m_nodes.m_size; ++i) { nodeTrafficLightFlag[i] = null; - if (! all && TrafficPriority.IsPriorityNode(i)) + if (! all && prioMan.IsPriorityNode(i)) continue; Singleton.instance.UpdateNodeFlags(i); } @@ -579,7 +583,7 @@ internal static bool mayHaveLaneArrows(uint laneId, bool? startNode=null) { var dir = NetInfo.Direction.Forward; var dir2 = ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir); - var dir3 = TrafficPriority.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; + var dir3 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; NetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info; uint curLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes; diff --git a/TLM/TLM/State/Options.cs b/TLM/TLM/State/Options.cs index c46e91ab..8035cdb7 100644 --- a/TLM/TLM/State/Options.cs +++ b/TLM/TLM/State/Options.cs @@ -6,11 +6,12 @@ using ColossalFramework; using ColossalFramework.UI; using ICities; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.State; using TrafficManager.UI; using ColossalFramework.Plugins; using ColossalFramework.Globalization; +using TrafficManager.Manager; namespace TrafficManager.State { @@ -68,6 +69,10 @@ public class Options : MonoBehaviour { private static UITextField someValue7Field = null; private static UITextField someValue8Field = null; private static UITextField someValue9Field = null; + private static UITextField someValue10Field = null; + private static UITextField someValue11Field = null; + private static UITextField someValue12Field = null; + private static UITextField someValue13Field = null; #endif private static UIHelperBase mainGroup = null; @@ -76,7 +81,7 @@ public class Options : MonoBehaviour { private static UIHelperBase maintenanceGroup = null; private static UIHelperBase featureGroup = null; - public static int simAccuracy = 1; + public static int simAccuracy = 0; //public static int laneChangingRandomization = 2; public static int recklessDrivers = 3; public static bool relaxedBusses = true; @@ -94,27 +99,35 @@ public class Options : MonoBehaviour { public static bool advancedAI = false; private static bool dynamicPathRecalculation = false; public static bool highwayRules = false; +#if DEBUG + public static bool showLanes = true; +#else public static bool showLanes = false; +#endif public static bool strongerRoadConditionEffects = false; public static bool enableDespawning = true; public static bool preferOuterLane = false; //public static byte publicTransportUsage = 1; - public static float pathCostMultiplicator = 0.75f; // debug value - public static float pathCostMultiplicator2 = 1f; // debug value + public static float pathCostMultiplicator = 0f; // debug value + public static float pathCostMultiplicator2 = 0.25f; // debug value public static bool disableSomething1 = false; // debug switch public static bool disableSomething2 = false; // debug switch public static bool disableSomething3 = false; // debug switch public static bool disableSomething4 = false; // debug switch public static bool disableSomething5 = false; // debug switch - public static float someValue = 3f; // debug value + public static float someValue = 0.65f; // debug value public static float someValue2 = 1.25f; // debug value public static float someValue3 = 2f; // debug value public static float someValue4 = 5f; // debug value - public static float someValue5 = 2f; // debug value + public static float someValue5 = 0.5f; // debug value public static float someValue6 = 1.5f; // debug value public static float someValue7 = 0.75f; // debug value - public static float someValue8 = 3f; // debug value - public static float someValue9 = 0.8f; // debug value + public static float someValue8 = 2; // debug value + public static float someValue9 = 1; // debug value + public static float someValue10 = 0.5f; // debug value + public static float someValue11 = 2.5f; // debug value + public static float someValue12 = 0.75f; // debug value + public static float someValue13 = 0.5f; // debug value public static bool prioritySignsEnabled = true; public static bool timedLightsEnabled = true; @@ -126,7 +139,7 @@ public static bool MenuRebuildRequired { get { return menuRebuildRequired; } private set { menuRebuildRequired = value; - if (LoadingExtension.Instance.UI != null) + if (LoadingExtension.Instance != null && LoadingExtension.Instance.UI != null) LoadingExtension.Instance.UI.Close(); } } @@ -196,6 +209,10 @@ public static void makeSettings(UIHelperBase helper) { someValue7Field = maintenanceGroup.AddTextfield("Some value #7", String.Format("{0:0.##}", someValue7), onSomeValue7Changed) as UITextField; someValue8Field = maintenanceGroup.AddTextfield("Some value #8", String.Format("{0:0.##}", someValue8), onSomeValue8Changed) as UITextField; someValue9Field = maintenanceGroup.AddTextfield("Some value #9", String.Format("{0:0.##}", someValue9), onSomeValue9Changed) as UITextField; + someValue10Field = maintenanceGroup.AddTextfield("Some value #10", String.Format("{0:0.##}", someValue10), onSomeValue10Changed) as UITextField; + someValue11Field = maintenanceGroup.AddTextfield("Some value #11", String.Format("{0:0.##}", someValue11), onSomeValue11Changed) as UITextField; + someValue12Field = maintenanceGroup.AddTextfield("Some value #12", String.Format("{0:0.##}", someValue12), onSomeValue12Changed) as UITextField; + someValue13Field = maintenanceGroup.AddTextfield("Some value #13", String.Format("{0:0.##}", someValue13), onSomeValue13Changed) as UITextField; #endif } @@ -668,6 +685,58 @@ private static void onSomeValue9Changed(string newSomeValueStr) { } } + private static void onSomeValue10Changed(string newSomeValueStr) { + if (!checkGameLoaded()) + return; + + try { + float newSomeValue = Single.Parse(newSomeValueStr); + someValue10 = newSomeValue; + } catch (Exception e) { + Log.Warning($"An invalid value was inserted: '{newSomeValueStr}'. Error: {e.ToString()}"); + //UIView.library.ShowModal("ExceptionPanel").SetMessage("Invalid value", "An invalid value was inserted.", false); + } + } + + private static void onSomeValue11Changed(string newSomeValueStr) { + if (!checkGameLoaded()) + return; + + try { + float newSomeValue = Single.Parse(newSomeValueStr); + someValue11 = newSomeValue; + } catch (Exception e) { + Log.Warning($"An invalid value was inserted: '{newSomeValueStr}'. Error: {e.ToString()}"); + //UIView.library.ShowModal("ExceptionPanel").SetMessage("Invalid value", "An invalid value was inserted.", false); + } + } + + private static void onSomeValue12Changed(string newSomeValueStr) { + if (!checkGameLoaded()) + return; + + try { + float newSomeValue = Single.Parse(newSomeValueStr); + someValue12 = newSomeValue; + } catch (Exception e) { + Log.Warning($"An invalid value was inserted: '{newSomeValueStr}'. Error: {e.ToString()}"); + //UIView.library.ShowModal("ExceptionPanel").SetMessage("Invalid value", "An invalid value was inserted.", false); + } + } + + private static void onSomeValue13Changed(string newSomeValueStr) { + if (!checkGameLoaded()) + return; + + try { + float newSomeValue = Single.Parse(newSomeValueStr); + someValue13 = newSomeValue; + } catch (Exception e) { + Log.Warning($"An invalid value was inserted: '{newSomeValueStr}'. Error: {e.ToString()}"); + //UIView.library.ShowModal("ExceptionPanel").SetMessage("Invalid value", "An invalid value was inserted.", false); + } + } + private static void onClickForgetToggledLights() { if (!checkGameLoaded()) return; diff --git a/TLM/TLM/State/SerializableDataExtension.cs b/TLM/TLM/State/SerializableDataExtension.cs index fa701ee5..1fd13063 100644 --- a/TLM/TLM/State/SerializableDataExtension.cs +++ b/TLM/TLM/State/SerializableDataExtension.cs @@ -6,7 +6,7 @@ using System.Threading; using ColossalFramework; using ICities; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; using Random = UnityEngine.Random; @@ -14,6 +14,8 @@ using TrafficManager.State; using TrafficManager.Custom.AI; using TrafficManager.UI; +using TrafficManager.Manager; +using TrafficManager.Traffic; namespace TrafficManager.State { public class SerializableDataExtension : SerializableDataExtensionBase { @@ -193,6 +195,8 @@ private static void LoadDataState() { return; } + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + // load priority segments if (_configuration.PrioritySegments != null) { Log.Info($"Loading {_configuration.PrioritySegments.Count()} priority segments"); @@ -226,18 +230,18 @@ private static void LoadDataState() { #endif continue; } - if (TrafficPriority.IsPrioritySegment((ushort)segment[0], (ushort)segment[1])) { + if (prioMan.IsPrioritySegment((ushort)segment[0], (ushort)segment[1])) { #if DEBUG if (debug) Log._Debug($"Loading priority segment: segment {segment[1]} @ node {segment[0]} is already a priority segment"); #endif - TrafficPriority.GetPrioritySegment((ushort)segment[0], (ushort)segment[1]).Type = (SegmentEnd.PriorityType)segment[2]; + prioMan.GetPrioritySegment((ushort)segment[0], (ushort)segment[1]).Type = (SegmentEnd.PriorityType)segment[2]; continue; } #if DEBUG Log._Debug($"Adding Priority Segment of type: {segment[2].ToString()} to segment {segment[1]} @ node {segment[0]}"); #endif - TrafficPriority.AddPrioritySegment((ushort)segment[0], (ushort)segment[1], (SegmentEnd.PriorityType)segment[2]); + prioMan.AddPrioritySegment((ushort)segment[0], (ushort)segment[1], (SegmentEnd.PriorityType)segment[2]); } catch (Exception e) { // ignore, as it's probably corrupt save data. it'll be culled on next save Log.Warning("Error loading data from Priority segments: " + e.ToString()); @@ -273,6 +277,7 @@ private static void LoadDataState() { var timedStepSegmentCount = 0; NetManager netManager = Singleton.instance; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); if (_configuration.TimedLights != null) { Log.Info($"Loading {_configuration.TimedLights.Count()} timed traffic lights (new method)"); @@ -285,7 +290,7 @@ private static void LoadDataState() { Log._Debug($"Adding Timed Node at node {cnfTimedLights.nodeId}"); - TrafficLightSimulation sim = TrafficLightSimulation.AddNodeToSimulation(cnfTimedLights.nodeId); + TrafficLightSimulation sim = tlsMan.AddNodeToSimulation(cnfTimedLights.nodeId); sim.SetupTimedTrafficLight(cnfTimedLights.nodeGroup); var timedNode = sim.TimedLight; @@ -349,7 +354,7 @@ private static void LoadDataState() { nodeGroup.Add(_configuration.TimedNodeGroups[i][j]); } - TrafficLightSimulation sim = TrafficLightSimulation.AddNodeToSimulation(nodeid); + TrafficLightSimulation sim = tlsMan.AddNodeToSimulation(nodeid); sim.SetupTimedTrafficLight(nodeGroup); var timedNode = sim.TimedLight; @@ -585,7 +590,9 @@ public override void OnSaveData() { Log.Info("Saving Mod Data."); var configuration = new Configuration(); - if (TrafficPriority.TrafficSegments != null) { + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + + if (prioMan.TrafficSegments != null) { for (ushort i = 0; i < Singleton.instance.m_segments.m_size; i++) { try { SavePrioritySegment(i, configuration); @@ -601,6 +608,8 @@ public override void OnSaveData() { } } + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + for (ushort i = 0; i < Singleton.instance.m_nodes.m_size; i++) { /*if (TrafficLightSimulation.LightSimulationByNodeId != null) { SaveTrafficLightSimulation(i, configuration); @@ -610,7 +619,7 @@ public override void OnSaveData() { SaveManualTrafficLight(i, configuration); }*/ - TrafficLightSimulation sim = TrafficLightSimulation.GetNodeSimulation(i); + TrafficLightSimulation sim = tlsMan.GetNodeSimulation(i); if (sim != null && sim.IsTimedLight()) { try { SaveTimedTrafficLight(i, configuration); @@ -768,7 +777,7 @@ private static void SaveNodeLights(int i, Configuration configuration) { private static void SaveTimedTrafficLight(ushort i, Configuration configuration) { try { - TrafficLightSimulation sim = TrafficLightSimulation.GetNodeSimulation(i); + TrafficLightSimulation sim = TrafficLightSimulationManager.Instance().GetNodeSimulation(i); if (sim == null || !sim.IsTimedLight()) return; @@ -810,7 +819,7 @@ private static void SaveTimedTrafficLight(ushort i, Configuration configuration) Log._Debug($"Saving pedestrian light @ seg. {e.Key}, step {j}: {cnfSegLights.pedestrianLightState} {cnfSegLights.manualPedestrianMode}"); - foreach (KeyValuePair e2 in segLights.CustomLights) { + foreach (KeyValuePair e2 in segLights.CustomLights) { Log._Debug($"Saving timed light step {j}, segment {e.Key}, vehicleType {e2.Key} at node {i}."); CustomSegmentLight segLight = e2.Value; @@ -843,26 +852,28 @@ private void SaveLaneAllowedVehicleTypes(Configuration.LaneVehicleTypes laneVehi private static void SavePrioritySegment(ushort segmentId, Configuration configuration) { try { - if (TrafficPriority.TrafficSegments[segmentId] == null) { + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + + if (prioMan.TrafficSegments[segmentId] == null) { return; } - if (TrafficPriority.TrafficSegments[segmentId].Node1 != 0 && TrafficPriority.TrafficSegments[segmentId].Instance1.Type != SegmentEnd.PriorityType.None) { - Log.Info($"Saving Priority Segment of type: {TrafficPriority.TrafficSegments[segmentId].Instance1.Type} @ node {TrafficPriority.TrafficSegments[segmentId].Node1}, seg. {segmentId}"); + if (prioMan.TrafficSegments[segmentId].Node1 != 0 && prioMan.TrafficSegments[segmentId].Instance1.Type != SegmentEnd.PriorityType.None) { + Log.Info($"Saving Priority Segment of type: {prioMan.TrafficSegments[segmentId].Instance1.Type} @ node {prioMan.TrafficSegments[segmentId].Node1}, seg. {segmentId}"); configuration.PrioritySegments.Add(new[] { - TrafficPriority.TrafficSegments[segmentId].Node1, segmentId, - (int) TrafficPriority.TrafficSegments[segmentId].Instance1.Type + prioMan.TrafficSegments[segmentId].Node1, segmentId, + (int) prioMan.TrafficSegments[segmentId].Instance1.Type }); } - if (TrafficPriority.TrafficSegments[segmentId].Node2 == 0 || TrafficPriority.TrafficSegments[segmentId].Instance2.Type == SegmentEnd.PriorityType.None) + if (prioMan.TrafficSegments[segmentId].Node2 == 0 || prioMan.TrafficSegments[segmentId].Instance2.Type == SegmentEnd.PriorityType.None) return; - Log.Info($"Saving Priority Segment of type: {TrafficPriority.TrafficSegments[segmentId].Instance2.Type} @ node {TrafficPriority.TrafficSegments[segmentId].Node2}, seg. {segmentId}"); + Log.Info($"Saving Priority Segment of type: {prioMan.TrafficSegments[segmentId].Instance2.Type} @ node {prioMan.TrafficSegments[segmentId].Node2}, seg. {segmentId}"); configuration.PrioritySegments.Add(new[] { - TrafficPriority.TrafficSegments[segmentId].Node2, segmentId, - (int) TrafficPriority.TrafficSegments[segmentId].Instance2.Type + prioMan.TrafficSegments[segmentId].Node2, segmentId, + (int) prioMan.TrafficSegments[segmentId].Instance2.Type }); } catch (Exception e) { Log.Error($"Error adding Priority Segments to Save: {e.ToString()}"); diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index 2e2b9454..53f44960 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -72,12 +72,13 @@ + - - - - - + + + + + @@ -88,7 +89,7 @@ - + @@ -101,8 +102,8 @@ - - + + @@ -118,8 +119,8 @@ - - + + @@ -127,7 +128,7 @@ - + diff --git a/TLM/TLM/Traffic/Direction.cs b/TLM/TLM/Traffic/ArrowDirection.cs similarity index 84% rename from TLM/TLM/Traffic/Direction.cs rename to TLM/TLM/Traffic/ArrowDirection.cs index b6b7e827..d9c0baed 100644 --- a/TLM/TLM/Traffic/Direction.cs +++ b/TLM/TLM/Traffic/ArrowDirection.cs @@ -4,7 +4,7 @@ using System.Text; namespace TrafficManager.Traffic { - public enum Direction { + public enum ArrowDirection { Left, Forward, Right, diff --git a/TLM/TLM/Traffic/SegmentEnd.cs b/TLM/TLM/Traffic/SegmentEnd.cs index ad720a58..ee9e2fbe 100644 --- a/TLM/TLM/Traffic/SegmentEnd.cs +++ b/TLM/TLM/Traffic/SegmentEnd.cs @@ -6,13 +6,14 @@ using System.Collections.Generic; using System.Linq; using ColossalFramework; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using TrafficManager.Custom.AI; using TrafficManager.Util; using System.Threading; using TrafficManager.State; using TrafficManager.UI; +using TrafficManager.Manager; /// /// A segment end describes a directional traffic segment connected to a controlled node @@ -176,7 +177,7 @@ public Dictionary GetVehicleMetricGoingToSegment(bool includeStopp return; } - if (!includeStopped && vehState.GetLastFrameVelocity().magnitude < TrafficPriority.maxStopVelocity) { + if (!includeStopped && vehState.GetLastFrameVelocity().magnitude < TrafficPriorityManager.maxStopVelocity) { #if DEBUGMETRIC if (debug) Log._Debug($" GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: too slow"); @@ -271,7 +272,7 @@ private void UnregisterAllVehicles() { public void OnUpdate(SegmentGeometry geometry) { if (!geometry.IsValid()) { - TrafficPriority.RemovePrioritySegment(NodeId, SegmentId); + TrafficPriorityManager.Instance().RemovePrioritySegment(NodeId, SegmentId); return; } @@ -292,8 +293,8 @@ public void OnUpdate(NodeGeometry geometry) { } internal void Housekeeping() { - if (TrafficManagerTool.GetToolMode() != ToolMode.AddPrioritySigns && TrafficLightSimulation.GetNodeSimulation(NodeId) == null && Type == PriorityType.None) - TrafficPriority.RemovePrioritySegments(NodeId); + if (TrafficManagerTool.GetToolMode() != ToolMode.AddPrioritySigns && TrafficLightSimulationManager.Instance().GetNodeSimulation(NodeId) == null && Type == PriorityType.None) + TrafficPriorityManager.Instance().RemovePrioritySegments(NodeId); } } } diff --git a/TLM/TLM/Traffic/TrafficSegment.cs b/TLM/TLM/Traffic/TrafficSegment.cs index 2a56adb5..3c310cad 100644 --- a/TLM/TLM/Traffic/TrafficSegment.cs +++ b/TLM/TLM/Traffic/TrafficSegment.cs @@ -1,17 +1,16 @@ -namespace TrafficManager.Traffic -{ +namespace TrafficManager.Traffic { /// /// A traffic segment (essentially a road) connects two nodes (Node1, Node2). One traffic segment /// can act as two different priority segments, one for each node. /// - class TrafficSegment - { - public ushort Node1 = 0; - public ushort Node2 = 0; + /// + public class TrafficSegment { // TODO remove this class + public ushort Node1 = 0; + public ushort Node2 = 0; - public int Segment = 0; + public int Segment = 0; - public SegmentEnd Instance1; - public SegmentEnd Instance2; - } + public SegmentEnd Instance1; + public SegmentEnd Instance2; + } } diff --git a/TLM/TLM/Traffic/VehicleJunctionTransitState.cs b/TLM/TLM/Traffic/VehicleJunctionTransitState.cs index d658decf..28e9eeae 100644 --- a/TLM/TLM/Traffic/VehicleJunctionTransitState.cs +++ b/TLM/TLM/Traffic/VehicleJunctionTransitState.cs @@ -1,4 +1,4 @@ -namespace TrafficManager { +namespace TrafficManager.Traffic { public enum VehicleJunctionTransitState { None, Enter, diff --git a/TLM/TLM/Traffic/VehicleState.cs b/TLM/TLM/Traffic/VehicleState.cs index 289c2f15..c3cacc85 100644 --- a/TLM/TLM/Traffic/VehicleState.cs +++ b/TLM/TLM/Traffic/VehicleState.cs @@ -9,6 +9,8 @@ using UnityEngine; using System.Collections.Generic; using TrafficManager.TrafficLight; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.Traffic { public class VehicleState { @@ -77,7 +79,17 @@ public ushort NextVehicleIdOnSegment { } #if USEPATHWAITCOUNTER - public ushort PathWaitCounter { get; internal set; } = 0; + public ushort PathWaitCounter { + get { return pathWaitCounter; } + internal set { + pathWaitCounter = value; + if (pathWaitCounter >= 32767) { + Log.Warning($"Path wait counter hit maximum for vehicle {VehicleId}. Check path {Singleton.instance.m_vehicles.m_buffer[VehicleId].m_path}!"); + } + } + } + + private ushort pathWaitCounter = 0; #endif public VehicleState(ushort vehicleId) { @@ -337,7 +349,7 @@ internal void UpdatePosition(ref Vehicle vehicleData, ref PathUnit.Position curP LastPositionUpdate = Singleton.instance.m_currentFrameIndex; - SegmentEnd end = TrafficPriority.GetPrioritySegment(GetTransitNodeId(ref curPos, ref nextPos), curPos.m_segment); + SegmentEnd end = TrafficPriorityManager.Instance().GetPrioritySegment(GetTransitNodeId(ref curPos, ref nextPos), curPos.m_segment); if (CurrentSegmentEnd != end) { if (CurrentSegmentEnd != null) { @@ -366,13 +378,13 @@ internal void UpdatePosition(ref Vehicle vehicleData) { internal bool CheckValidity(ref Vehicle vehicleData, bool skipCached=false) { #if DEBUG - bool debug = skipCached; - byte pfFlags = Singleton.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags; + //bool debug = skipCached; + //byte pfFlags = Singleton.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags; #endif #if DEBUG - if (debug) - Log._Debug($"VehicleState.CheckValidity({VehicleId}) called. created: {(vehicleData.m_flags & Vehicle.Flags.Created) != 0} ({vehicleData.m_flags}) handled: {(vehicleData.Info.m_vehicleType & HANDLED_VEHICLE_TYPES) != VehicleInfo.VehicleType.None} ({vehicleData.Info.m_vehicleType}) has path unit: {vehicleData.m_path != 0} path ready: {(pfFlags & PathUnit.FLAG_READY) != 0} ({pfFlags})"); + //if (debug) + //Log._Debug($"VehicleState.CheckValidity({VehicleId}) called. created: {(vehicleData.m_flags & Vehicle.Flags.Created) != 0} ({vehicleData.m_flags}) handled: {(vehicleData.Info.m_vehicleType & HANDLED_VEHICLE_TYPES) != VehicleInfo.VehicleType.None} ({vehicleData.Info.m_vehicleType}) has path unit: {vehicleData.m_path != 0} path ready: {(pfFlags & PathUnit.FLAG_READY) != 0} ({pfFlags})"); #endif if (!skipCached && !Valid) @@ -407,9 +419,9 @@ internal static ushort GetTransitNodeId(ref PathUnit.Position curPos, ref PathUn return transitNodeId; } - internal void OnVehicleSpawned(ref Vehicle vehicleData) { + internal void OnBeforeSpawnVehicle(ref Vehicle vehicleData) { #if DEBUG - Log._Debug($"VehicleState.OnPathFindReady called for vehicle {VehicleId} ({VehicleType}"); + //Log._Debug($"VehicleState.OnPathFindReady called for vehicle {VehicleId} ({VehicleType}"); #endif Reset(); @@ -440,14 +452,14 @@ private void ApplyVehicleTypeToTrailers() { VehicleStateManager vehStateManager = VehicleStateManager.Instance(); #if DEBUG - Log._Debug($"Applying VehicleType to trailes of vehicle {VehicleId} to {VehicleType}."); + //Log._Debug($"Applying VehicleType to trailes of vehicle {VehicleId} to {VehicleType}."); #endif // apply vehicle type to all leading/trailing vehicles ushort otherVehicleId = vehManager.m_vehicles.m_buffer[VehicleId].m_leadingVehicle; while (otherVehicleId != 0) { #if DEBUG - Log._Debug($" Setting VehicleType of leader {otherVehicleId} to {VehicleType}."); + //Log._Debug($" Setting VehicleType of leader {otherVehicleId} to {VehicleType}."); #endif VehicleState otherState = vehStateManager._GetVehicleState(otherVehicleId); otherState.Valid = true; @@ -458,7 +470,7 @@ private void ApplyVehicleTypeToTrailers() { otherVehicleId = vehManager.m_vehicles.m_buffer[VehicleId].m_trailingVehicle; while (otherVehicleId != 0) { #if DEBUG - Log._Debug($" Setting VehicleType of trailer {otherVehicleId} to {VehicleType}."); + //Log._Debug($" Setting VehicleType of trailer {otherVehicleId} to {VehicleType}."); #endif VehicleState otherState = vehStateManager._GetVehicleState(otherVehicleId); otherState.Valid = true; diff --git a/TLM/TLM/TrafficLight/CustomSegment.cs b/TLM/TLM/TrafficLight/CustomSegment.cs index ddcbf854..5a74ec0e 100644 --- a/TLM/TLM/TrafficLight/CustomSegment.cs +++ b/TLM/TLM/TrafficLight/CustomSegment.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using TrafficManager.Traffic; +using TrafficManager.Geometry; namespace TrafficManager.TrafficLight { diff --git a/TLM/TLM/TrafficLight/CustomSegmentLight.cs b/TLM/TLM/TrafficLight/CustomSegmentLight.cs index c556b699..5ae7c526 100644 --- a/TLM/TLM/TrafficLight/CustomSegmentLight.cs +++ b/TLM/TLM/TrafficLight/CustomSegmentLight.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; using ColossalFramework; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; using TrafficManager.Custom.AI; namespace TrafficManager.TrafficLight { + /// + /// Represents the traffic light (left, forward, right) at a specific segment end + /// public class CustomSegmentLight : ICloneable { public enum Mode { Simple = 1, // <^> diff --git a/TLM/TLM/TrafficLight/CustomSegmentLights.cs b/TLM/TLM/TrafficLight/CustomSegmentLights.cs index c8492832..cf3df36b 100644 --- a/TLM/TLM/TrafficLight/CustomSegmentLights.cs +++ b/TLM/TLM/TrafficLight/CustomSegmentLights.cs @@ -4,12 +4,17 @@ using System; using System.Collections.Generic; using ColossalFramework; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using UnityEngine; using TrafficManager.Custom.AI; using System.Linq; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.TrafficLight { + /// + /// Represents the set of custom traffic lights located at a node + /// public class CustomSegmentLights : ICloneable { private ushort nodeId; private ushort segmentId; diff --git a/TLM/TLM/TrafficLight/TimedTrafficLights.cs b/TLM/TLM/TrafficLight/TimedTrafficLights.cs index 30a914d8..a9ca019b 100644 --- a/TLM/TLM/TrafficLight/TimedTrafficLights.cs +++ b/TLM/TLM/TrafficLight/TimedTrafficLights.cs @@ -4,8 +4,10 @@ using System.Collections.Generic; using ColossalFramework; using TrafficManager.Custom.AI; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using System.Linq; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.TrafficLight { public class TimedTrafficLights { @@ -59,6 +61,8 @@ public void Start() { /*if (!housekeeping()) return;*/ + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + for (int s = 0; s < 8; ++s) { ushort segmentId = Singleton.instance.m_nodes.m_buffer[NodeId].GetSegment(s); if (segmentId == 0) @@ -73,7 +77,7 @@ public void Start() { } } - CustomTrafficLights.GetOrLiveSegmentLights(NodeId, segmentId).InvalidPedestrianLight = needsAlwaysGreenPedestrian; + customTrafficLightsManager.GetOrLiveSegmentLights(NodeId, segmentId).InvalidPedestrianLight = needsAlwaysGreenPedestrian; } CurrentStep = 0; @@ -86,7 +90,7 @@ public void Start() { internal void RemoveNodeFromGroup(ushort otherNodeId) { NodeGroup.Remove(otherNodeId); if (NodeGroup.Count <= 0) { - TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, true, false); + TrafficLightSimulationManager.Instance().RemoveNodeFromSimulation(NodeId, true, false); return; } masterNodeId = NodeGroup[0]; @@ -231,17 +235,6 @@ public void SimulationStep() { return; }*/ - if (!Steps[CurrentStep].isValid()) { -#if DEBUGTTL - Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}) is not valid."); -#endif - TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, false, false); -#if TRACE - Singleton.instance.Stop("TimedTrafficLights.SimulationStep"); -#endif - return; - } - #if DEBUGTTL Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (1)"); #endif @@ -279,7 +272,9 @@ public void SimulationStep() { #endif // change step - int oldCurrentStep = CurrentStep; + CurrentStep = (CurrentStep + 1) % NumSteps(); + Steps[CurrentStep].Start(); + /*int oldCurrentStep = CurrentStep; while (true) { SkipStep(false); @@ -298,7 +293,7 @@ public void SimulationStep() { if (flow >= wait) { break; } - } + }*/ Steps[CurrentStep].SetLights(); #if TRACE Singleton.instance.Stop("TimedTrafficLights.SimulationStep"); @@ -306,9 +301,11 @@ public void SimulationStep() { } public void SetLights() { + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + // set lights foreach (ushort slaveNodeId in NodeGroup) { - TrafficLightSimulation slaveSim = TrafficLightSimulation.GetNodeSimulation(slaveNodeId); + TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId); if (slaveSim == null || !slaveSim.IsTimedLight()) { //TrafficLightSimulation.RemoveNodeFromSimulation(slaveNodeId, false); // we iterate over NodeGroup!! continue; @@ -321,9 +318,11 @@ public void SkipStep(bool setLights=true) { if (!isMasterNode()) return; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + var newCurrentStep = (CurrentStep + 1) % NumSteps(); foreach (ushort slaveNodeId in NodeGroup) { - TrafficLightSimulation slaveSim = TrafficLightSimulation.GetNodeSimulation(slaveNodeId); + TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId); if (slaveSim == null || !slaveSim.IsTimedLight()) { continue; } @@ -342,7 +341,7 @@ public long CheckNextChange(ushort segmentId, ExtVehicleType vehicleType, int li var numFrames = Steps[CurrentStep].MaxTimeRemaining(); RoadBaseAI.TrafficLightState currentState; - CustomSegmentLights segmentLights = CustomTrafficLights.GetSegmentLights(NodeId, segmentId); + CustomSegmentLights segmentLights = CustomTrafficLightsManager.Instance().GetSegmentLights(NodeId, segmentId); if (segmentLights == null) { Log._Debug($"CheckNextChange: No segment lights at node {NodeId}, segment {segmentId}"); return 99; @@ -406,6 +405,9 @@ internal void handleNewSegments() { return; } + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + for (int s = 0; s < 8; ++s) { ushort segmentId = Singleton.instance.m_nodes.m_buffer[NodeId].GetSegment(s); if (segmentId <= 0) @@ -421,7 +423,7 @@ internal void handleNewSegments() { foreach (KeyValuePair e in Steps[0].segmentLights) { var fromSegmentId = e.Key; - if (!TrafficPriority.IsPrioritySegment(NodeId, fromSegmentId)) { + if (!prioMan.IsPrioritySegment(NodeId, fromSegmentId)) { Log._Debug($"Identified old segment {fromSegmentId} @ {NodeId}"); invalidSegmentIds.Add(fromSegmentId); } @@ -432,7 +434,7 @@ internal void handleNewSegments() { if (invalidSegmentIds.Count > 0) { var oldSegmentId = invalidSegmentIds[0]; - TrafficPriority.RemovePrioritySegment(NodeId, oldSegmentId); + prioMan.RemovePrioritySegment(NodeId, oldSegmentId); Log._Debug($"Replacing old segment {oldSegmentId} @ {NodeId} with new segment {segmentId}"); // replace the old segment with the newly created one @@ -452,11 +454,11 @@ internal void handleNewSegments() { Steps[i].segmentLights.Add(segmentId, customLights); Steps[i].calcMaxSegmentLength(); Log._Debug($"Getting live segment lights of new segment {segmentId} @ {NodeId} and applying mode @ step {i}"); - CustomSegmentLights liveSegLights = CustomTrafficLights.GetSegmentLights(NodeId, segmentId); + CustomSegmentLights liveSegLights = customTrafficLightsManager.GetSegmentLights(NodeId, segmentId); if (liveSegLights == null) { Log.Error($"No live segment lights for seg. {segmentId} @ node {NodeId} found!"); - CustomTrafficLights.AddSegmentLights(NodeId, segmentId); - liveSegLights = CustomTrafficLights.GetSegmentLights(NodeId, segmentId); + customTrafficLightsManager.AddSegmentLights(NodeId, segmentId); + liveSegLights = customTrafficLightsManager.GetSegmentLights(NodeId, segmentId); } foreach (KeyValuePair e in customLights.CustomLights) { @@ -482,7 +484,7 @@ internal void handleNewSegments() { } internal TimedTrafficLights MasterLights() { - TrafficLightSimulation masterSim = TrafficLightSimulation.GetNodeSimulation(masterNodeId); + TrafficLightSimulation masterSim = TrafficLightSimulationManager.Instance().GetNodeSimulation(masterNodeId); if (masterSim == null || !masterSim.IsTimedLight()) return null; return masterSim.TimedLight; @@ -508,12 +510,14 @@ internal void ChangeLightMode(ushort segmentId, ExtVehicleType vehicleType, Cust } internal void Join(TimedTrafficLights otherTimedLight) { + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + if (NumSteps() < otherTimedLight.NumSteps()) { // increase the number of steps at our timed lights for (int i = NumSteps(); i < otherTimedLight.NumSteps(); ++i) { TimedTrafficLightsStep otherStep = otherTimedLight.GetStep(i); foreach (ushort slaveNodeId in NodeGroup) { - TrafficLightSimulation ourSim = TrafficLightSimulation.GetNodeSimulation(slaveNodeId); + TrafficLightSimulation ourSim = tlsMan.GetNodeSimulation(slaveNodeId); if (ourSim == null || !ourSim.IsTimedLight()) continue; TimedTrafficLights ourTimedLight = ourSim.TimedLight; @@ -525,7 +529,7 @@ internal void Join(TimedTrafficLights otherTimedLight) { for (int i = otherTimedLight.NumSteps(); i < NumSteps(); ++i) { TimedTrafficLightsStep ourStep = GetStep(i); foreach (ushort slaveNodeId in otherTimedLight.NodeGroup) { - TrafficLightSimulation theirSim = TrafficLightSimulation.GetNodeSimulation(slaveNodeId); + TrafficLightSimulation theirSim = tlsMan.GetNodeSimulation(slaveNodeId); if (theirSim == null || !theirSim.IsTimedLight()) continue; TimedTrafficLights theirTimedLight = theirSim.TimedLight; @@ -546,7 +550,7 @@ internal void Join(TimedTrafficLights otherTimedLight) { float[] waitFlowBalances = new float[NumSteps()]; foreach (ushort timedNodeId in newNodeGroup) { - TrafficLightSimulation timedSim = TrafficLightSimulation.GetNodeSimulation(timedNodeId); + TrafficLightSimulation timedSim = tlsMan.GetNodeSimulation(timedNodeId); if (timedSim == null || !timedSim.IsTimedLight()) continue; TimedTrafficLights timedLight = timedSim.TimedLight; @@ -571,7 +575,7 @@ internal void Join(TimedTrafficLights otherTimedLight) { // apply means & reset foreach (ushort timedNodeId in newNodeGroup) { - TrafficLightSimulation timedSim = TrafficLightSimulation.GetNodeSimulation(timedNodeId); + TrafficLightSimulation timedSim = tlsMan.GetNodeSimulation(timedNodeId); if (timedSim == null || !timedSim.IsTimedLight()) continue; TimedTrafficLights timedLight = timedSim.TimedLight; @@ -607,18 +611,17 @@ private void DestroySegmentEnds() { private void SetupSegmentEnd(ushort segmentId) { if (segmentId <= 0) return; - //SegmentGeometry.Get(segmentId).Recalculate(true, true); - if (!TrafficPriority.IsPrioritySegment(NodeId, segmentId)) - TrafficPriority.AddPrioritySegment(NodeId, segmentId, SegmentEnd.PriorityType.None); - /*if (!CustomTrafficLights.IsSegmentLight(nodeId, segmentId)) - CustomTrafficLights.AddSegmentLights(nodeId, segmentId);*/ + + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + + if (!prioMan.IsPrioritySegment(NodeId, segmentId)) + prioMan.AddPrioritySegment(NodeId, segmentId, SegmentEnd.PriorityType.None); } private void DestroySegmentEnd(ushort segmentId) { if (segmentId <= 0) return; - TrafficPriority.RemovePrioritySegment(NodeId, segmentId); - //CustomTrafficLights.RemoveSegmentLights(segmentId); + TrafficPriorityManager.Instance().RemovePrioritySegment(NodeId, segmentId); } } } diff --git a/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs b/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs index 9515d721..a12c14fa 100644 --- a/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs +++ b/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs @@ -1,11 +1,14 @@ +#define DEBUGTTLx #define DEBUGMETRICx using System; using System.Collections.Generic; using ColossalFramework; using TrafficManager.TrafficLight; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.Custom.AI; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.TrafficLight { public class TimedTrafficLightsStep : ICloneable { @@ -49,8 +52,6 @@ public class TimedTrafficLightsStep : ICloneable { /// float maxSegmentLength = 0f; - private bool invalid = false; // TODO rework - public float waitFlowBalance = 1f; public TimedTrafficLightsStep(TimedTrafficLights timedNode, int minTime, int maxTime, float waitFlowBalance, bool makeRed=false) { @@ -89,16 +90,17 @@ internal void calcMaxSegmentLength() { } } - public bool isValid() { - return !invalid; - } - /// /// Checks if the green-to-red (=yellow) phase is finished /// /// internal bool isEndTransitionDone() { - return endTransitionStart != null && getCurrentFrame() > endTransitionStart && StepDone(false); + bool isStepDone = StepDone(false); + bool ret = endTransitionStart != null && getCurrentFrame() > endTransitionStart && isStepDone; +#if DEBUGTTL + Log._Debug($"TimedTrafficLightsStep.isEndTransitionDone() called for master NodeId={timedNode.NodeId}. CurrentStep={timedNode.CurrentStep} getCurrentFrame()={getCurrentFrame()} endTransitionStart={endTransitionStart} isStepDone={isStepDone} ret={ret}"); +#endif + return ret; } /// @@ -111,7 +113,12 @@ internal bool isInEndTransition() { if (masterLights != null) return masterLights.Steps[timedNode.CurrentStep].isInEndTransition(); } - return endTransitionStart != null && getCurrentFrame() <= endTransitionStart && StepDone(false); + bool isStepDone = StepDone(false); + bool ret = endTransitionStart != null && getCurrentFrame() <= endTransitionStart && isStepDone; +#if DEBUGTTL + Log._Debug($"TimedTrafficLightsStep.isInEndTransition() called for master NodeId={timedNode.NodeId}. CurrentStep={timedNode.CurrentStep} getCurrentFrame()={getCurrentFrame()} endTransitionStart={endTransitionStart} isStepDone={isStepDone} ret={ret}"); +#endif + return ret; } internal bool isInStartTransition() { @@ -120,7 +127,14 @@ internal bool isInStartTransition() { if (masterLights != null) return timedNode.MasterLights().Steps[timedNode.CurrentStep].isInStartTransition(); } - return getCurrentFrame() == startFrame && !StepDone(false); + + bool isStepDone = StepDone(false); + bool ret = getCurrentFrame() == startFrame && !isStepDone; +#if DEBUGTTL + Log._Debug($"TimedTrafficLightsStep.isInStartTransition() called for master NodeId={timedNode.NodeId}. CurrentStep={timedNode.CurrentStep} getCurrentFrame()={getCurrentFrame()} startFrame={startFrame} isStepDone={isStepDone} ret={ret}"); +#endif + + return ret; } public RoadBaseAI.TrafficLightState GetLight(ushort segmentId, ExtVehicleType vehicleType, int lightType) { @@ -170,7 +184,9 @@ public void SetLights(bool noTransition) { Singleton.instance.Start("TimedTrafficLightsStep.SetLights"); #endif try { - bool atEndTransition = !noTransition && isInEndTransition(); // = yellow + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + + bool atEndTransition = !noTransition && (isInEndTransition() || isEndTransitionDone()); // = yellow bool atStartTransition = !noTransition && !atEndTransition && isInStartTransition(); // = red + yellow #if DEBUG @@ -225,10 +241,18 @@ public void SetLights(bool noTransition) { } #endif +#if DEBUG + //Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition}) called for NodeId={timedNode.NodeId}. atStartTransition={atStartTransition} atEndTransition={atEndTransition}"); +#endif + foreach (KeyValuePair e in segmentLights) { var segmentId = e.Key; var curStepSegmentLights = e.Value; +#if DEBUG + //Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition}) -> segmentId={segmentId} @ NodeId={timedNode.NodeId}"); +#endif + #if DEBUG if (!previousStep.segmentLights.ContainsKey(segmentId)) { Log.Error($"TimedTrafficLightsStep: previousStep does not contain lights for segment {segmentId}!"); @@ -252,7 +276,7 @@ public void SetLights(bool noTransition) { //segLightState.makeRedOrGreen(); // TODO temporary fix - var liveSegmentLights = CustomTrafficLights.GetOrLiveSegmentLights(timedNode.NodeId, segmentId); + var liveSegmentLights = customTrafficLightsManager.GetOrLiveSegmentLights(timedNode.NodeId, segmentId); if (liveSegmentLights == null) { continue; } @@ -276,6 +300,10 @@ public void SetLights(bool noTransition) { #endif foreach (ExtVehicleType vehicleType in curStepSegmentLights.VehicleTypes) { +#if DEBUG + //Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition}) -> segmentId={segmentId} @ NodeId={timedNode.NodeId} for vehicle {vehicleType}"); +#endif + CustomSegmentLight liveSegmentLight = liveSegmentLights.GetCustomLight(vehicleType); if (liveSegmentLight == null) { #if DEBUG @@ -317,6 +345,10 @@ public void SetLights(bool noTransition) { liveSegmentLight.LightLeft = calcLightState(prevStepSegmentLight.LightLeft, curStepSegmentLight.LightLeft, nextStepSegmentLight.LightLeft, atStartTransition, atEndTransition); liveSegmentLight.LightRight = calcLightState(prevStepSegmentLight.LightRight, curStepSegmentLight.LightRight, nextStepSegmentLight.LightRight, atStartTransition, atEndTransition); +#if DEBUG + //Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition}) -> *SETTING* LightLeft={liveSegmentLight.LightLeft} LightMain={liveSegmentLight.LightMain} LightRight={liveSegmentLight.LightRight} for segmentId={segmentId} @ NodeId={timedNode.NodeId} for vehicle {vehicleType}"); +#endif + //Log._Debug($"Step @ {timedNode.NodeId}: Segment {segmentId} for vehicle type {vehicleType}: L: {liveSegmentLight.LightLeft.ToString()} F: {liveSegmentLight.LightMain.ToString()} R: {liveSegmentLight.LightRight.ToString()}"); } @@ -328,7 +360,7 @@ public void SetLights(bool noTransition) { } } catch (Exception e) { Log.Error($"Exception in TimedTrafficStep.SetLights: {e.ToString()}"); - invalid = true; + //invalid = true; } #if TRACE Singleton.instance.Stop("TimedTrafficLightsStep.SetLights"); @@ -340,7 +372,7 @@ public void SetLights(bool noTransition) { /// /// internal void addSegment(ushort segmentId, bool makeRed) { - segmentLights.Add(segmentId, (CustomSegmentLights)CustomTrafficLights.GetOrLiveSegmentLights(timedNode.NodeId, segmentId).Clone()); + segmentLights.Add(segmentId, (CustomSegmentLights)CustomTrafficLightsManager.Instance().GetOrLiveSegmentLights(timedNode.NodeId, segmentId).Clone()); if (makeRed) segmentLights[segmentId].MakeRed(); else @@ -368,7 +400,7 @@ public void UpdateLights() { var segLights = e.Value; //if (segment == 0) continue; - var liveSegLights = CustomTrafficLights.GetSegmentLights(timedNode.NodeId, segmentId); + var liveSegLights = CustomTrafficLightsManager.Instance().GetSegmentLights(timedNode.NodeId, segmentId); if (liveSegLights == null) continue; @@ -421,8 +453,10 @@ public bool StepDone(bool updateValues) { #if DEBUG //Log.Message("step finished @ " + nodeId); #endif - stepDone = true; - endTransitionStart = getCurrentFrame(); + if (!stepDone && updateValues) { + stepDone = true; + endTransitionStart = getCurrentFrame(); + } #if TRACE Singleton.instance.Stop("TimedTrafficLightsStep.StepDone"); #endif @@ -432,23 +466,14 @@ public bool StepDone(bool updateValues) { if (getCurrentFrame() >= startFrame + minTime) { if (!timedNode.isMasterNode()) { TimedTrafficLights masterLights = timedNode.MasterLights(); - - if (masterLights == null) { - invalid = true; - stepDone = true; - endTransitionStart = getCurrentFrame(); -#if TRACE - Singleton.instance.Stop("TimedTrafficLightsStep.StepDone"); -#endif - return true; - } bool done = masterLights.Steps[masterLights.CurrentStep].StepDone(updateValues); #if DEBUG //Log.Message("step finished (1) @ " + nodeId); #endif - stepDone = done; - if (stepDone) + if (!stepDone && done && updateValues) { + stepDone = done; endTransitionStart = getCurrentFrame(); + } #if TRACE Singleton.instance.Stop("TimedTrafficLightsStep.StepDone"); #endif @@ -457,21 +482,32 @@ public bool StepDone(bool updateValues) { // we are the master node float wait, flow; uint curFrame = getCurrentFrame(); + //Log._Debug($"TTL @ {timedNode.NodeId}: curFrame={curFrame} lastFlowWaitCalc={lastFlowWaitCalc}"); if (lastFlowWaitCalc < curFrame) { + //Log._Debug($"TTL @ {timedNode.NodeId}: lastFlowWaitCalc.instance.Stop("TimedTrafficLightsStep.StepDone"); #endif return stepDone; } else { - lastFlowWaitCalc = curFrame; + if (updateValues) { + lastFlowWaitCalc = curFrame; + //Log._Debug($"TTL @ {timedNode.NodeId}: updated lastFlowWaitCalc=curFrame={curFrame}"); + } } } else { flow = minFlow; wait = maxWait; + //Log._Debug($"TTL @ {timedNode.NodeId}: lastFlowWaitCalc>=curFrame wait={maxWait} flow={minFlow}"); } + float newFlow = minFlow; float newWait = maxWait; @@ -496,17 +532,21 @@ public bool StepDone(bool updateValues) { #else bool done = newWait > 0 && newFlow < newWait; #endif + + //Log._Debug($"TTL @ {timedNode.NodeId}: newWait={newWait} newFlow={newFlow} updateValues={updateValues} stepDone={stepDone} done={done}"); + if (updateValues) { minFlow = newFlow; maxWait = newWait; + //Log._Debug($"TTL @ {timedNode.NodeId}: updated minFlow=newFlow={minFlow} maxWait=newWait={maxWait}"); } #if DEBUG //Log.Message("step finished (2) @ " + nodeId); #endif - if (updateValues) + if (updateValues && !stepDone && done) { stepDone = done; - if (stepDone) endTransitionStart = getCurrentFrame(); + } #if TRACE Singleton.instance.Stop("TimedTrafficLightsStep.StepDone"); #endif @@ -547,17 +587,15 @@ public bool calcWaitFlow(out float wait, out float flow) { uint curMeanFlow = 0; uint curMeanWait = 0; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + // we are the master node. calculate traffic data foreach (ushort timedNodeId in timedNode.NodeGroup) { - TrafficLightSimulation sim = TrafficLightSimulation.GetNodeSimulation(timedNodeId); + TrafficLightSimulation sim = tlsMan.GetNodeSimulation(timedNodeId); if (sim == null || !sim.IsTimedLight()) continue; TimedTrafficLights slaveTimedNode = sim.TimedLight; - if (slaveTimedNode.NumSteps() <= timedNode.CurrentStep) { - for (int i = 0; i < slaveTimedNode.NumSteps(); ++i) - slaveTimedNode.GetStep(i).invalid = true; - continue; - } TimedTrafficLightsStep slaveStep = slaveTimedNode.Steps[timedNode.CurrentStep]; //List segmentIdsToDelete = new List(); @@ -568,7 +606,7 @@ public bool calcWaitFlow(out float wait, out float flow) { var segLights = e.Value; // one of the traffic lights at this segment is green: count minimum traffic flowing through - SegmentEnd fromSeg = TrafficPriority.GetPrioritySegment(timedNodeId, fromSegmentId); + SegmentEnd fromSeg = prioMan.GetPrioritySegment(timedNodeId, fromSegmentId); if (fromSeg == null) { #if DEBUGMETRIC if (debug) @@ -616,11 +654,11 @@ public bool calcWaitFlow(out float wait, out float flow) { continue; // build directions from toSegment to fromSegment - Dictionary directions = new Dictionary(); + Dictionary directions = new Dictionary(); foreach (KeyValuePair f in allCarsToSegmentMetric) { var toSegmentId = f.Key; SegmentGeometry geometry = SegmentGeometry.Get(fromSegmentId); - Direction dir = geometry.GetDirection(toSegmentId, timedNodeId == geometry.StartNodeId()); + ArrowDirection dir = geometry.GetDirection(toSegmentId, timedNodeId == geometry.StartNodeId()); directions[toSegmentId] = dir; #if DEBUGMETRIC if (debug) @@ -641,16 +679,16 @@ public bool calcWaitFlow(out float wait, out float flow) { bool addToFlow = false; switch (directions[toSegmentId]) { - case Direction.Turn: - addToFlow = TrafficPriority.IsLeftHandDrive() ? segLight.isRightGreen() : segLight.isLeftGreen(); + case ArrowDirection.Turn: + addToFlow = TrafficPriorityManager.IsLeftHandDrive() ? segLight.isRightGreen() : segLight.isLeftGreen(); break; - case Direction.Left: + case ArrowDirection.Left: addToFlow = segLight.isLeftGreen(); break; - case Direction.Right: + case ArrowDirection.Right: addToFlow = segLight.isRightGreen(); break; - case Direction.Forward: + case ArrowDirection.Forward: default: addToFlow = segLight.isForwardGreen(); break; @@ -676,16 +714,6 @@ public bool calcWaitFlow(out float wait, out float flow) { /*foreach (int segmentId in segmentIdsToDelete) { slaveStep.segmentLightStates.Remove(segmentId); }*/ - - if (slaveStep.segmentLights.Count <= 0) { - invalid = true; - flow = 0f; - wait = 0f; -#if TRACE - Singleton.instance.Stop("TimedTrafficLightsStep.calcWaitFlow"); -#endif - return false; - } } #if DEBUGMETRIC diff --git a/TLM/TLM/TrafficLight/TrafficLightSimulation.cs b/TLM/TLM/TrafficLight/TrafficLightSimulation.cs index ef599771..273ee0c7 100644 --- a/TLM/TLM/TrafficLight/TrafficLightSimulation.cs +++ b/TLM/TLM/TrafficLight/TrafficLightSimulation.cs @@ -1,20 +1,15 @@ using System; using ColossalFramework; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using System.Collections.Generic; using TrafficManager.State; using TrafficManager.Custom.AI; using System.Linq; using TrafficManager.Util; +using TrafficManager.Manager; namespace TrafficManager.TrafficLight { public class TrafficLightSimulation : IObserver { - /// - /// For each node id: traffic light simulation assigned to the node - /// - //public static TrafficLightSimulation[] TrafficLightSimulations = new TrafficLightSimulation[NetManager.MAX_NODE_COUNT]; - public static Dictionary TrafficLightSimulations = new Dictionary(); - /// /// Timed traffic light by node id /// @@ -28,17 +23,19 @@ public ushort NodeId { private bool manualTrafficLights = false; - private IDisposable nodeGeoUnsubscriber = null; + internal IDisposable NodeGeoUnsubscriber { + get; private set; + } = null; public TrafficLightSimulation(ushort nodeId) { Log._Debug($"TrafficLightSimulation: Constructor called @ node {nodeId}"); Flags.setNodeTrafficLight(nodeId, true); this.NodeId = nodeId; - nodeGeoUnsubscriber = NodeGeometry.Get(nodeId).Subscribe(this); + NodeGeoUnsubscriber = NodeGeometry.Get(nodeId).Subscribe(this); } ~TrafficLightSimulation() { - nodeGeoUnsubscriber?.Dispose(); + NodeGeoUnsubscriber?.Dispose(); } public void SetupManualTrafficLight() { @@ -94,110 +91,6 @@ public bool IsSimulationActive() { return IsManualLight() || IsTimedLightActive(); } - public static void SimulationStep() { -#if TRACE - Singleton.instance.Start("TrafficLightSimulation.SimulationStep"); -#endif - try { - foreach (KeyValuePair e in TrafficLightSimulations) { - try { - var nodeSim = e.Value; - var nodeId = e.Key; - if (nodeSim.IsTimedLightActive()) { - Flags.applyNodeTrafficLightFlag(nodeId); - nodeSim.TimedLight.SimulationStep(); - } - } catch (Exception ex) { - Log.Warning($"Error occured while simulating traffic light @ node {e.Key}: {ex.ToString()}"); - } - } - } catch (Exception ex) { - // TODO the dictionary was modified (probably a segment connected to a traffic light was changed/removed). rework this - Log.Warning($"Error occured while iterating over traffic light simulations: {ex.ToString()}"); - } -#if TRACE - Singleton.instance.Stop("TrafficLightSimulation.SimulationStep"); -#endif - } - - /// - /// Adds a traffic light simulation to the node with the given id - /// - /// - public static TrafficLightSimulation AddNodeToSimulation(ushort nodeId) { - if (TrafficLightSimulations.ContainsKey(nodeId)) { - return TrafficLightSimulations[nodeId]; - } - TrafficLightSimulations.Add(nodeId, new TrafficLightSimulation(nodeId)); - return TrafficLightSimulations[nodeId]; - } - - /// - /// Destroys the traffic light and removes it - /// - /// - /// - public static void RemoveNodeFromSimulation(ushort nodeId, bool destroyGroup, bool removeTrafficLight) { - if (!TrafficLightSimulations.ContainsKey(nodeId)) - return; - - TrafficLightSimulation sim = TrafficLightSimulation.TrafficLightSimulations[nodeId]; - - if (sim.TimedLight != null) { - // remove/destroy other timed traffic lights in group - List oldNodeGroup = new List(sim.TimedLight.NodeGroup); - foreach (var timedNodeId in oldNodeGroup) { - var otherNodeSim = GetNodeSimulation(timedNodeId); - if (otherNodeSim == null) { - continue; - } - - if (destroyGroup || timedNodeId == nodeId) { - //Log._Debug($"Slave: Removing simulation @ node {timedNodeId}"); - otherNodeSim.DestroyTimedTrafficLight(); - otherNodeSim.DestroyManualTrafficLight(); - otherNodeSim.nodeGeoUnsubscriber.Dispose(); - TrafficLightSimulations.Remove(timedNodeId); - if (removeTrafficLight) - Flags.setNodeTrafficLight(timedNodeId, false); - } else { - otherNodeSim.TimedLight.RemoveNodeFromGroup(nodeId); - } - } - } - - //Flags.setNodeTrafficLight(nodeId, false); - sim.DestroyTimedTrafficLight(); - sim.DestroyManualTrafficLight(); - sim.nodeGeoUnsubscriber?.Dispose(); - TrafficLightSimulations.Remove(nodeId); - if (removeTrafficLight) - Flags.setNodeTrafficLight(nodeId, false); - } - - public static TrafficLightSimulation GetNodeSimulation(ushort nodeId) { -#if TRACE - Singleton.instance.Start("TrafficLightSimulation.GetNodeSimulation"); -#endif - - TrafficLightSimulation ret = null; - if (TrafficLightSimulations.ContainsKey(nodeId)) { - ret = TrafficLightSimulations[nodeId]; - } - -#if TRACE - Singleton.instance.Stop("TrafficLightSimulation.GetNodeSimulation"); -#endif - return ret; - } - - internal static void OnLevelUnloading() { - TrafficLightSimulations.Clear(); - /*for (ushort nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) { - TrafficLightSimulations[nodeId] = null; - }*/ - } - public void OnUpdate(NodeGeometry nodeGeometry) { #if DEBUG Log._Debug($"TrafficLightSimulation: OnUpdate @ node {NodeId} ({nodeGeometry.NodeId})"); @@ -205,7 +98,7 @@ public void OnUpdate(NodeGeometry nodeGeometry) { if (!Flags.mayHaveTrafficLight(NodeId)) { Log.Warning($"Housekeeping: Node {NodeId} has traffic light simulation but must not have a traffic light!"); - TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, false, true); + TrafficLightSimulationManager.Instance().RemoveNodeFromSimulation(NodeId, false, true); } if (!IsManualLight() && !IsTimedLight()) @@ -213,10 +106,12 @@ public void OnUpdate(NodeGeometry nodeGeometry) { if (!nodeGeometry.IsValid()) { // node has become invalid. Remove manual/timed traffic light and destroy custom lights - RemoveNodeFromSimulation(NodeId, false, false); + TrafficLightSimulationManager.Instance().RemoveNodeFromSimulation(NodeId, false, false); return; } + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + for (var s = 0; s < 8; s++) { var segmentId = Singleton.instance.m_nodes.m_buffer[NodeId].GetSegment(s); @@ -227,12 +122,12 @@ public void OnUpdate(NodeGeometry nodeGeometry) { #endif // add custom lights - if (!TrafficLight.CustomTrafficLights.IsSegmentLight(NodeId, segmentId)) { - TrafficLight.CustomTrafficLights.AddSegmentLights(NodeId, segmentId); + if (!customTrafficLightsManager.IsSegmentLight(NodeId, segmentId)) { + customTrafficLightsManager.AddSegmentLights(NodeId, segmentId); } // housekeep timed light - TrafficLight.CustomTrafficLights.GetSegmentLights(NodeId, segmentId).housekeeping(true); + customTrafficLightsManager.GetSegmentLights(NodeId, segmentId).housekeeping(true); } // ensure there is a physical traffic light @@ -253,25 +148,29 @@ internal void housekeeping() { } private void setupLiveSegments() { + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + for (var s = 0; s < 8; s++) { var segmentId = Singleton.instance.m_nodes.m_buffer[NodeId].GetSegment(s); if (segmentId == 0) continue; //SegmentGeometry.Get(segmentId)?.Recalculate(true, true); - if (!TrafficLight.CustomTrafficLights.IsSegmentLight(NodeId, segmentId)) { - TrafficLight.CustomTrafficLights.AddSegmentLights(NodeId, segmentId); + if (!customTrafficLightsManager.IsSegmentLight(NodeId, segmentId)) { + customTrafficLightsManager.AddSegmentLights(NodeId, segmentId); } } } private void destroyLiveSegments() { + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + for (var s = 0; s < 8; s++) { var segmentId = Singleton.instance.m_nodes.m_buffer[NodeId].GetSegment(s); if (segmentId == 0) continue; - if (TrafficLight.CustomTrafficLights.IsSegmentLight(NodeId, segmentId)) { - TrafficLight.CustomTrafficLights.RemoveSegmentLight(NodeId, segmentId); + if (customTrafficLightsManager.IsSegmentLight(NodeId, segmentId)) { + customTrafficLightsManager.RemoveSegmentLight(NodeId, segmentId); } } } diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index 99402b4d..a4519efe 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.7.3"; + public static readonly string Version = "1.7.5"; public static readonly uint GameVersion = 155313168u; public static readonly uint GameVersionA = 1u; diff --git a/TLM/TLM/UI/SubTools/LaneArrowTool.cs b/TLM/TLM/UI/SubTools/LaneArrowTool.cs index 4e8aa7b5..56e283f1 100644 --- a/TLM/TLM/UI/SubTools/LaneArrowTool.cs +++ b/TLM/TLM/UI/SubTools/LaneArrowTool.cs @@ -6,7 +6,7 @@ using System.Text; using TrafficManager.Custom.AI; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs index 6f25f42c..2414d38b 100644 --- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs +++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs @@ -8,10 +8,11 @@ using System.Text; using TrafficManager.Custom.AI; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using TrafficManager.Util; using UnityEngine; +using TrafficManager.Manager; namespace TrafficManager.UI.SubTools { public class LaneConnectorTool : SubTool { diff --git a/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs index c2d94a11..0fe6318f 100644 --- a/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs @@ -6,9 +6,11 @@ using System.Text; using TrafficManager.Custom.AI; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; +using TrafficManager.Manager; +using TrafficManager.Traffic; namespace TrafficManager.UI.SubTools { public class ManualTrafficLightsTool : SubTool { @@ -22,16 +24,19 @@ public ManualTrafficLightsTool(TrafficManagerTool mainTool) : base(mainTool) { public override void OnPrimaryClickOverlay() { if (SelectedNodeId != 0) return; - TrafficLightSimulation sim = TrafficLightSimulation.GetNodeSimulation(HoveredNodeId); + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + + TrafficLightSimulation sim = tlsMan.GetNodeSimulation(HoveredNodeId); if (sim == null || !sim.IsTimedLight()) { if ((Singleton.instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None) { - TrafficPriority.RemovePrioritySegments(HoveredNodeId); + prioMan.RemovePrioritySegments(HoveredNodeId); Flags.setNodeTrafficLight(HoveredNodeId, true); } SelectedNodeId = HoveredNodeId; - sim = TrafficLightSimulation.AddNodeToSimulation(SelectedNodeId); + sim = tlsMan.AddNodeToSimulation(SelectedNodeId); sim.SetupManualTrafficLight(); /*for (var s = 0; s < 8; s++) { @@ -49,7 +54,10 @@ public override void OnToolGUI(Event e) { var hoveredSegment = false; if (SelectedNodeId != 0) { - var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(SelectedNodeId); + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + + var nodeSimulation = tlsMan.GetNodeSimulation(SelectedNodeId); nodeSimulation.housekeeping(); /*if (Singleton.instance.m_nodes.m_buffer[SelectedNode].CountSegments() == 2) { @@ -61,10 +69,10 @@ public override void OnToolGUI(Event e) { var segmentId = Singleton.instance.m_nodes.m_buffer[SelectedNodeId].GetSegment(i); if (segmentId == 0 || nodeSimulation == null || - !CustomTrafficLights.IsSegmentLight(SelectedNodeId, segmentId)) continue; + !customTrafficLightsManager.IsSegmentLight(SelectedNodeId, segmentId)) continue; var position = CalculateNodePositionForSegment(Singleton.instance.m_nodes.m_buffer[SelectedNodeId], ref Singleton.instance.m_segments.m_buffer[segmentId]); - var segmentLights = CustomTrafficLights.GetSegmentLights(SelectedNodeId, segmentId); + var segmentLights = customTrafficLightsManager.GetSegmentLights(SelectedNodeId, segmentId); var screenPos = Camera.main.WorldToScreenPoint(position); screenPos.y = Screen.height - screenPos.y; @@ -651,14 +659,15 @@ private void RenderManualSelectionOverlay(RenderManager.CameraInfo cameraInfo) { } private void RenderManualNodeOverlays(RenderManager.CameraInfo cameraInfo) { - var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(SelectedNodeId); + var nodeSimulation = TrafficLightSimulationManager.Instance().GetNodeSimulation(SelectedNodeId); + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); for (var i = 0; i < 8; i++) { var colorGray = new Color(0.25f, 0.25f, 0.25f, 0.25f); ushort segmentId = Singleton.instance.m_nodes.m_buffer[SelectedNodeId].GetSegment(i); if (segmentId == 0 || - (nodeSimulation != null && CustomTrafficLights.IsSegmentLight(SelectedNodeId, segmentId))) + (nodeSimulation != null && customTrafficLightsManager.IsSegmentLight(SelectedNodeId, segmentId))) continue; var position = CalculateNodePositionForSegment(Singleton.instance.m_nodes.m_buffer[SelectedNodeId], segmentId); @@ -670,12 +679,14 @@ private void RenderManualNodeOverlays(RenderManager.CameraInfo cameraInfo) { public override void Cleanup() { if (SelectedNodeId == 0) return; - var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(SelectedNodeId); + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + + var nodeSimulation = tlsMan.GetNodeSimulation(SelectedNodeId); if (nodeSimulation == null || !nodeSimulation.IsManualLight()) return; nodeSimulation.DestroyManualTrafficLight(); - TrafficLightSimulation.RemoveNodeFromSimulation(SelectedNodeId, true, false); + tlsMan.RemoveNodeFromSimulation(SelectedNodeId, true, false); } } } diff --git a/TLM/TLM/UI/SubTools/PrioritySignsTool.cs b/TLM/TLM/UI/SubTools/PrioritySignsTool.cs index 9e70c517..e52f390e 100644 --- a/TLM/TLM/UI/SubTools/PrioritySignsTool.cs +++ b/TLM/TLM/UI/SubTools/PrioritySignsTool.cs @@ -6,9 +6,11 @@ using System.Text; using TrafficManager.Custom.AI; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.UI.SubTools { public class PrioritySignsTool : SubTool { @@ -32,7 +34,7 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) { } // no highlight for existing priority node in sign mode - if (TrafficPriority.IsPriorityNode(HoveredNodeId)) + if (TrafficPriorityManager.Instance().IsPriorityNode(HoveredNodeId)) return; if (HoveredNodeId == 0) return; @@ -56,7 +58,7 @@ private void RefreshCurrentPrioritySegmentIds() { if ((Singleton.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) continue; - var trafficSegment = TrafficPriority.TrafficSegments[segmentId]; + var trafficSegment = TrafficPriorityManager.Instance().TrafficSegments[segmentId]; if (trafficSegment == null) continue; @@ -73,28 +75,31 @@ public void ShowGUI(bool viewOnly) { if (viewOnly && !Options.prioritySignsOverlay) return; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + bool clicked = !viewOnly ? MainTool.CheckClicked() : false; var hoveredSegment = false; //Log.Message("_guiPrioritySigns called. num of prio segments: " + TrafficPriority.PrioritySegments.Count); HashSet nodeIdsWithSigns = new HashSet(); foreach (ushort segmentId in currentPrioritySegmentIds) { - var trafficSegment = TrafficPriority.TrafficSegments[segmentId]; + var trafficSegment = prioMan.TrafficSegments[segmentId]; if (trafficSegment == null) continue; SegmentGeometry geometry = SegmentGeometry.Get(segmentId); List prioritySegments = new List(); - if (TrafficLightSimulation.GetNodeSimulation(trafficSegment.Node1) == null) { - SegmentEnd tmpSeg1 = TrafficPriority.GetPrioritySegment(trafficSegment.Node1, segmentId); + if (tlsMan.GetNodeSimulation(trafficSegment.Node1) == null) { + SegmentEnd tmpSeg1 = prioMan.GetPrioritySegment(trafficSegment.Node1, segmentId); bool startNode = geometry.StartNodeId() == trafficSegment.Node1; if (tmpSeg1 != null && !geometry.IsOutgoingOneWay(startNode)) { prioritySegments.Add(tmpSeg1); nodeIdsWithSigns.Add(trafficSegment.Node1); } } - if (TrafficLightSimulation.GetNodeSimulation(trafficSegment.Node2) == null) { - SegmentEnd tmpSeg2 = TrafficPriority.GetPrioritySegment(trafficSegment.Node2, segmentId); + if (tlsMan.GetNodeSimulation(trafficSegment.Node2) == null) { + SegmentEnd tmpSeg2 = prioMan.GetPrioritySegment(trafficSegment.Node2, segmentId); bool startNode = geometry.StartNodeId() == trafficSegment.Node2; if (tmpSeg2 != null && !geometry.IsOutgoingOneWay(startNode)) { prioritySegments.Add(tmpSeg2); @@ -194,7 +199,7 @@ public void ShowGUI(bool viewOnly) { } if (setUndefinedSignsToMainRoad) { - foreach (var otherPrioritySegment in TrafficPriority.GetPrioritySegments(nodeId)) { + foreach (var otherPrioritySegment in prioMan.GetPrioritySegments(nodeId)) { if (otherPrioritySegment.SegmentId == prioritySegment.SegmentId) continue; if (otherPrioritySegment.Type == SegmentEnd.PriorityType.None) @@ -246,7 +251,7 @@ public void ShowGUI(bool viewOnly) { // determine if we may add new priority signs to this node bool ok = false; - TrafficLightSimulation nodeSim = TrafficLightSimulation.GetNodeSimulation(HoveredNodeId); + TrafficLightSimulation nodeSim = tlsMan.GetNodeSimulation(HoveredNodeId); if ((Singleton.instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None) { // no traffic light set ok = true; @@ -261,14 +266,14 @@ public void ShowGUI(bool viewOnly) { //Log._Debug("_guiPrioritySigns: hovered+clicked @ nodeId=" + HoveredNodeId + "/" + hoveredExistingNodeId); if (delete) { - TrafficPriority.RemovePrioritySegments(hoveredExistingNodeId); + prioMan.RemovePrioritySegments(hoveredExistingNodeId); RefreshCurrentPrioritySegmentIds(); } else if (ok) { - if (!TrafficPriority.IsPriorityNode(HoveredNodeId)) { + if (!prioMan.IsPriorityNode(HoveredNodeId)) { //Log._Debug("_guiPrioritySigns: adding prio segments @ nodeId=" + HoveredNodeId); - TrafficLightSimulation.RemoveNodeFromSimulation(HoveredNodeId, false, true); + tlsMan.RemoveNodeFromSimulation(HoveredNodeId, false, true); Flags.setNodeTrafficLight(HoveredNodeId, false); // TODO refactor! - TrafficPriority.AddPriorityNode(HoveredNodeId); + prioMan.AddPriorityNode(HoveredNodeId); RefreshCurrentPrioritySegmentIds(); } } else if (nodeSim != null && nodeSim.IsTimedLight()) { @@ -282,14 +287,16 @@ public void ShowGUI(bool viewOnly) { } private static int GetNumberOfMainRoads(ushort nodeId, ref NetNode node) { + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + var numMainRoads = 0; for (var s = 0; s < 8; s++) { var segmentId2 = node.GetSegment(s); if (segmentId2 == 0 || - !TrafficPriority.IsPrioritySegment(nodeId, segmentId2)) + !prioMan.IsPrioritySegment(nodeId, segmentId2)) continue; - var prioritySegment2 = TrafficPriority.GetPrioritySegment(nodeId, + var prioritySegment2 = prioMan.GetPrioritySegment(nodeId, segmentId2); if (prioritySegment2.Type == SegmentEnd.PriorityType.Main) { @@ -300,7 +307,9 @@ private static int GetNumberOfMainRoads(ushort nodeId, ref NetNode node) { } public override void Cleanup() { - foreach (TrafficSegment trafficSegment in TrafficPriority.TrafficSegments) { + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + + foreach (TrafficSegment trafficSegment in prioMan.TrafficSegments) { try { trafficSegment?.Instance1?.Housekeeping(); trafficSegment?.Instance2?.Housekeeping(); diff --git a/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs b/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs index 8ea26deb..cca4f52c 100644 --- a/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs +++ b/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs @@ -6,9 +6,10 @@ using System.Text; using TrafficManager.Custom.AI; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; +using TrafficManager.Manager; namespace TrafficManager.UI.SubTools { public class SpeedLimitsTool : SubTool { diff --git a/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs index dbaad7cc..8fba9fe3 100644 --- a/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs @@ -6,9 +6,11 @@ using System.Text; using TrafficManager.Custom.AI; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; +using TrafficManager.Manager; +using TrafficManager.Traffic; namespace TrafficManager.UI.SubTools { public class TimedTrafficLightsTool : SubTool { @@ -44,12 +46,14 @@ public override void Initialize() { } private void RefreshCurrentTimedNodeIds() { + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + currentTimedNodeIds.Clear(); for (ushort nodeId = 1; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) { if ((Singleton.instance.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Created) == NetNode.Flags.None) continue; - TrafficLightSimulation lightSim = TrafficLightSimulation.GetNodeSimulation(nodeId); + TrafficLightSimulation lightSim = tlsMan.GetNodeSimulation(nodeId); if (lightSim != null && lightSim.IsTimedLight()) { currentTimedNodeIds.Add(nodeId); } @@ -57,11 +61,13 @@ private void RefreshCurrentTimedNodeIds() { } public override void OnActivate() { + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + RefreshCurrentTimedNodeIds(); nodeSelectionLocked = false; foreach (ushort nodeId in currentTimedNodeIds) { - TrafficLightSimulation lightSim = TrafficLightSimulation.GetNodeSimulation(nodeId); + TrafficLightSimulation lightSim = tlsMan.GetNodeSimulation(nodeId); if (lightSim != null) { lightSim.housekeeping(); } @@ -72,6 +78,8 @@ public override void OnPrimaryClickOverlay() { if (HoveredNodeId <= 0 || nodeSelectionLocked) return; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + switch (TrafficManagerTool.GetToolMode()) { case ToolMode.TimedLightsSelectNode: case ToolMode.TimedLightsShowLights: @@ -80,7 +88,7 @@ public override void OnPrimaryClickOverlay() { ClearSelectedNodes(); } - TrafficLightSimulation timedSim = TrafficLightSimulation.GetNodeSimulation(HoveredNodeId); + TrafficLightSimulation timedSim = tlsMan.GetNodeSimulation(HoveredNodeId); if (timedSim == null || !timedSim.IsTimedLight()) { if (IsNodeSelected(HoveredNodeId)) { RemoveSelectedNode(HoveredNodeId); @@ -113,7 +121,7 @@ public override void OnPrimaryClickOverlay() { //bool mayEnterBlocked = Options.mayEnterBlockedJunctions; TimedTrafficLights existingTimedLight = null; foreach (var nodeId in SelectedNodeIndexes) { - var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(nodeId); + var nodeSimulation = tlsMan.GetNodeSimulation(nodeId); if (nodeSimulation == null || !nodeSimulation.IsTimedLight()) continue; TimedTrafficLights timedNode = nodeSimulation.TimedLight; @@ -124,14 +132,14 @@ public override void OnPrimaryClickOverlay() { existingTimedLight = timedNode; } - var timedSim2 = TrafficLightSimulation.GetNodeSimulation(HoveredNodeId); + var timedSim2 = tlsMan.GetNodeSimulation(HoveredNodeId); /*if (timedSim2 != null) timedSim2.housekeeping();*/ TimedTrafficLights timedLight2 = null; if (timedSim2 == null || !timedSim2.IsTimedLight()) { var nodeGroup = new List(); nodeGroup.Add(HoveredNodeId); - timedSim2 = TrafficLightSimulation.AddNodeToSimulation(HoveredNodeId); + timedSim2 = tlsMan.AddNodeToSimulation(HoveredNodeId); timedSim2.SetupTimedTrafficLight(nodeGroup); timedLight2 = timedSim2.TimedLight; //timedLight.vehiclesMayEnterBlockedJunctions = mayEnterBlocked; @@ -153,7 +161,7 @@ public override void OnPrimaryClickOverlay() { } if (SelectedNodeIndexes.Contains(HoveredNodeId)) { - TrafficLightSimulation.RemoveNodeFromSimulation(HoveredNodeId, false, false); + tlsMan.RemoveNodeFromSimulation(HoveredNodeId, false, false); RefreshCurrentTimedNodeIds(); } RemoveSelectedNode(HoveredNodeId); @@ -224,6 +232,8 @@ private void _guiTimedControlPanel(int num) { var layoutGreen = new GUIStyle { normal = { textColor = new Color(0f, 1f, 0f) } }; var layoutYellow = new GUIStyle { normal = { textColor = new Color(1f, 1f, 0f) } }; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + if (TrafficManagerTool.GetToolMode() == ToolMode.TimedLightsAddNode || TrafficManagerTool.GetToolMode() == ToolMode.TimedLightsRemoveNode) { GUILayout.Label(Translation.GetString("Select_junction")); if (GUILayout.Button(Translation.GetString("Cancel"))) { @@ -232,7 +242,7 @@ private void _guiTimedControlPanel(int num) { return; } - var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(SelectedNodeIndexes[0]); + var nodeSimulation = tlsMan.GetNodeSimulation(SelectedNodeIndexes[0]); var timedNodeMain = nodeSimulation.TimedLight; if (nodeSimulation == null || timedNodeMain == null) { @@ -277,7 +287,7 @@ private void _guiTimedControlPanel(int num) { GUILayout.Space(5); GUILayout.EndVertical(); if (GUILayout.Button(Translation.GetString("Skip"), GUILayout.Width(80))) { - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.SkipStep(); } } @@ -296,7 +306,7 @@ private void _guiTimedControlPanel(int num) { if (i > 0) { if (GUILayout.Button(Translation.GetString("up"), GUILayout.Width(48))) { - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.MoveStep(i, i - 1); _timedViewedStep = i - 1; } @@ -307,7 +317,7 @@ private void _guiTimedControlPanel(int num) { if (i < timedNodeMain.NumSteps() - 1) { if (GUILayout.Button(Translation.GetString("down"), GUILayout.Width(48))) { - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.MoveStep(i, i + 1); _timedViewedStep = i + 1; } @@ -322,7 +332,7 @@ private void _guiTimedControlPanel(int num) { _timedPanelAdd = false; _timedViewedStep = i; - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.GetStep(i).SetLights(true); } } @@ -338,7 +348,7 @@ private void _guiTimedControlPanel(int num) { _stepMaxValueStr = _stepMaxValue.ToString(); nodeSelectionLocked = true; - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.GetStep(i).SetLights(true); } } @@ -347,7 +357,7 @@ private void _guiTimedControlPanel(int num) { _timedPanelAdd = false; _timedViewedStep = -1; - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.RemoveStep(i); } } @@ -370,7 +380,7 @@ private void _guiTimedControlPanel(int num) { _stepMaxValue = oldStepMaxValue; if (GUILayout.Button(Translation.GetString("Save"), GUILayout.Width(70))) { - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { if (_stepMinValue < 0) _stepMinValue = 0; @@ -420,7 +430,7 @@ private void _guiTimedControlPanel(int num) { _stepMaxValue = oldStepMaxValue; if (GUILayout.Button(Translation.GetString("Add"), GUILayout.Width(70))) { - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { if (_stepMinValue < 0) _stepMinValue = 0; if (_stepMaxValue <= 0) @@ -465,13 +475,13 @@ private void _guiTimedControlPanel(int num) { } if (GUILayout.Button(Translation.GetString("Stop"))) { - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.Stop(); } } bool isInTestMode = false; - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { if (sim.TimedLight.IsInTestMode()) { isInTestMode = true; break; @@ -481,13 +491,13 @@ private void _guiTimedControlPanel(int num) { var curStep = timedNodeMain.CurrentStep; _waitFlowBalance = timedNodeMain.GetStep(curStep).waitFlowBalance; makeFlowPolicyDisplay(isInTestMode); - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.GetStep(curStep).waitFlowBalance = _waitFlowBalance; } //var mayEnterIfBlocked = GUILayout.Toggle(timedNodeMain.vehiclesMayEnterBlockedJunctions, Translation.GetString("Vehicles_may_enter_blocked_junctions"), new GUILayoutOption[] { }); var testMode = GUILayout.Toggle(timedNodeMain.IsInTestMode(), Translation.GetString("Enable_test_mode_(stay_in_current_step)"), new GUILayoutOption[] { }); - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { sim.TimedLight.SetTestMode(testMode); //sim.TimedLight.vehiclesMayEnterBlockedJunctions = mayEnterIfBlocked; } @@ -497,7 +507,7 @@ private void _guiTimedControlPanel(int num) { _timedPanelAdd = false; nodeSelectionLocked = false; - foreach (var sim in SelectedNodeIndexes.Select(TrafficLightSimulation.GetNodeSimulation)) { + foreach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) { #if DEBUG // Log._Debug("Starting traffic light @ " + sim.TimedLight.NodeId); #endif @@ -602,6 +612,10 @@ private void _guiTimedTrafficLightsNode() { } private void _guiTimedTrafficLights() { + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + CustomTrafficLightsManager customTrafficLightsManager = CustomTrafficLightsManager.Instance(); + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + _cursorInSecondaryPanel = false; GUILayout.Window(253, _windowRect, _guiTimedControlPanel, Translation.GetString("Timed_traffic_lights_manager")); @@ -611,7 +625,7 @@ private void _guiTimedTrafficLights() { var hoveredSegment = false; foreach (var nodeId in SelectedNodeIndexes) { - var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(nodeId); + var nodeSimulation = tlsMan.GetNodeSimulation(nodeId); //nodeSimulation.housekeeping(); if (nodeSimulation == null || !nodeSimulation.IsTimedLight()) continue; @@ -634,12 +648,12 @@ private void _guiTimedTrafficLights() { if (srcSegmentId == 0) continue; - if (nodeSimulation == null || !CustomTrafficLights.IsSegmentLight(nodeId, srcSegmentId)) { + if (nodeSimulation == null || !customTrafficLightsManager.IsSegmentLight(nodeId, srcSegmentId)) { Log._Debug($"segment {srcSegmentId} @ {nodeId} is not a custom segment light. {nodeSimulation==null}"); continue; } - CustomSegmentLights liveSegmentLights = CustomTrafficLights.GetSegmentLights(nodeId, srcSegmentId); + CustomSegmentLights liveSegmentLights = customTrafficLightsManager.GetSegmentLights(nodeId, srcSegmentId); if (!nodeSimulation.IsTimedLightActive()) { liveSegmentLights.MakeRedOrGreen(); } @@ -800,7 +814,7 @@ private void _guiTimedTrafficLights() { #if DEBUG if (timedActive /*&& _timedShowNumbers*/) { - var prioSeg = TrafficPriority.GetPrioritySegment(nodeId, srcSegmentId); + var prioSeg = prioMan.GetPrioritySegment(nodeId, srcSegmentId); var counterSize = 20f * zoom; var yOffset = counterSize + 77f * zoom - modeHeight * 2; @@ -1376,6 +1390,8 @@ private void _guiTimedTrafficLights() { } private void _guiTimedTrafficLightsNodeWindow(int num) { + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + if (SelectedNodeIndexes.Count < 1) { GUILayout.Label(Translation.GetString("Select_nodes")); } else { @@ -1390,8 +1406,8 @@ private void _guiTimedTrafficLightsNodeWindow(int num) { _waitFlowBalance = 0.8f; foreach (var selectedNodeIndex in SelectedNodeIndexes) { - TrafficLightSimulation.AddNodeToSimulation(selectedNodeIndex); - var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(selectedNodeIndex); + tlsMan.AddNodeToSimulation(selectedNodeIndex); + var nodeSimulation = tlsMan.GetNodeSimulation(selectedNodeIndex); nodeSimulation.SetupTimedTrafficLight(SelectedNodeIndexes); RefreshCurrentTimedNodeIds(); @@ -1432,8 +1448,10 @@ private string getWaitFlowBalanceInfo() { private void DisableTimed() { if (SelectedNodeIndexes.Count <= 0) return; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + foreach (var selectedNodeIndex in SelectedNodeIndexes) { - TrafficLightSimulation.RemoveNodeFromSimulation(selectedNodeIndex, true, false); + tlsMan.RemoveNodeFromSimulation(selectedNodeIndex, true, false); } RefreshCurrentTimedNodeIds(); } @@ -1570,6 +1588,8 @@ public override void ShowGUIOverlay() { TrafficManagerTool.GetToolMode() != ToolMode.TimedLightsShowLights) return; + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance(); + for (ushort nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) { #if DEBUG bool debug = nodeId == 21361; @@ -1582,7 +1602,7 @@ public override void ShowGUIOverlay() { continue; } - TrafficLightSimulation lightSim = TrafficLightSimulation.GetNodeSimulation(nodeId); + TrafficLightSimulation lightSim = tlsMan.GetNodeSimulation(nodeId); if (lightSim != null && lightSim.IsTimedLight()) { TimedTrafficLights timedNode = lightSim.TimedLight; if (timedNode == null) { @@ -1590,7 +1610,7 @@ public override void ShowGUIOverlay() { if (debug) Log._Debug($"TimedTrafficLightsTool.ShowGUIOverlay: Node {nodeId} does not have an instance of TimedTrafficLights. Removing node from simulation"); #endif - TrafficLightSimulation.RemoveNodeFromSimulation(nodeId, true, false); + tlsMan.RemoveNodeFromSimulation(nodeId, true, false); RefreshCurrentTimedNodeIds(); break; } diff --git a/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs index 7e537e81..a9b6229b 100644 --- a/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs @@ -5,9 +5,10 @@ using System.Linq; using System.Text; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; +using TrafficManager.Manager; namespace TrafficManager.UI.SubTools { public class ToggleTrafficLightsTool : SubTool { @@ -22,13 +23,13 @@ public override void OnPrimaryClickOverlay() { if ((Singleton.instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.Junction) == NetNode.Flags.None) return; - TrafficLightSimulation sim = TrafficLightSimulation.GetNodeSimulation(HoveredNodeId); + TrafficLightSimulation sim = TrafficLightSimulationManager.Instance().GetNodeSimulation(HoveredNodeId); if (sim != null && sim.IsTimedLight()) { MainTool.ShowTooltip(Translation.GetString("NODE_IS_TIMED_LIGHT"), Singleton.instance.m_nodes.m_buffer[HoveredNodeId].m_position); return; } - TrafficPriority.RemovePrioritySegments(HoveredNodeId); + TrafficPriorityManager.Instance().RemovePrioritySegments(HoveredNodeId); Flags.setNodeTrafficLight(HoveredNodeId, (Singleton.instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None); } diff --git a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs index 9cfe5f92..6eefd9b6 100644 --- a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs +++ b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs @@ -5,9 +5,11 @@ using System.Linq; using System.Text; using TrafficManager.State; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.UI.SubTools { public class VehicleRestrictionsTool : SubTool { diff --git a/TLM/TLM/UI/TrafficLightToolTextureResources.cs b/TLM/TLM/UI/TrafficLightToolTextureResources.cs index 543476f2..e38a6f55 100644 --- a/TLM/TLM/UI/TrafficLightToolTextureResources.cs +++ b/TLM/TLM/UI/TrafficLightToolTextureResources.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using TrafficManager.Geometry; +using TrafficManager.Manager; using TrafficManager.Traffic; using TrafficManager.UI; using UnityEngine; diff --git a/TLM/TLM/UI/TrafficManagerTool.cs b/TLM/TLM/UI/TrafficManagerTool.cs index c028fa39..93c236de 100644 --- a/TLM/TLM/UI/TrafficManagerTool.cs +++ b/TLM/TLM/UI/TrafficManagerTool.cs @@ -1,6 +1,6 @@ #define MARKCONGESTEDSEGMENTSx #define USEPATHWAITCOUNTERx -#define ABSDENSITYx +#define ABSDENSITY using System; using System.Collections.Generic; @@ -10,12 +10,14 @@ using ColossalFramework.UI; using JetBrains.Annotations; using TrafficManager.Custom.AI; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.UI; using UnityEngine; using TrafficManager.State; using TrafficManager.TrafficLight; using TrafficManager.UI.SubTools; +using TrafficManager.Traffic; +using TrafficManager.Manager; namespace TrafficManager.UI { [UsedImplicitly] @@ -471,12 +473,12 @@ private void _guiLanes(ushort segmentId, ref NetSegment segment, ref NetInfo seg labelStr += ", in start-up phase"; else*/ labelStr += ", avg. speed: " + (CustomRoadAI.laneMeanSpeeds[segmentId] != null && i < CustomRoadAI.laneMeanSpeeds[segmentId].Length ? ""+CustomRoadAI.laneMeanSpeeds[segmentId][i] : "?") + " %"; - labelStr += ", rel. density: " + (CustomRoadAI.laneMeanRelDensities[segmentId] != null && i < CustomRoadAI.laneMeanRelDensities[segmentId].Length ? "" + CustomRoadAI.laneMeanRelDensities[segmentId][i] : "?") + " %"; -#if ABSDENSITY - labelStr += ", abs. density: " + (CustomRoadAI.laneMeanAbsDensities[segmentId] != null && i < CustomRoadAI.laneMeanAbsDensities[segmentId].Length ? "" + CustomRoadAI.laneMeanAbsDensities[segmentId][i] : "?") + " %"; -#endif + //labelStr += ", rel. dens.: " + (CustomRoadAI.laneMeanRelDensities[segmentId] != null && i < CustomRoadAI.laneMeanRelDensities[segmentId].Length ? "" + CustomRoadAI.laneMeanRelDensities[segmentId][i] : "?") + " %"; #if DEBUG - labelStr += " (" + (CustomRoadAI.currentLaneDensities[segmentId] != null && i < CustomRoadAI.currentLaneDensities[segmentId].Length ? "" + CustomRoadAI.currentLaneDensities[segmentId][i] : "?") + "/" + totalDensity + ")"; + labelStr += " (" + (CustomRoadAI.currentLaneDensities[segmentId] != null && i < CustomRoadAI.currentLaneDensities[segmentId].Length ? "" + CustomRoadAI.currentLaneDensities[segmentId][i] : "?") + "/" + (CustomRoadAI.maxLaneDensities[segmentId] != null && i < CustomRoadAI.maxLaneDensities[segmentId].Length ? "" + CustomRoadAI.maxLaneDensities[segmentId][i] : "?") + "/" + totalDensity + ")"; +#endif +#if ABSDENSITY + labelStr += ", abs. dens.: " + (CustomRoadAI.laneMeanAbsDensities[segmentId] != null && i < CustomRoadAI.laneMeanAbsDensities[segmentId].Length ? "" + CustomRoadAI.laneMeanAbsDensities[segmentId][i] : "?") + " %"; #endif labelStr += "\n"; @@ -494,6 +496,7 @@ private void _guiLanes(ushort segmentId, ref NetSegment segment, ref NetInfo seg /// private void _guiSegments() { GUIStyle _counterStyle = new GUIStyle(); + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); Array16 segments = Singleton.instance.m_segments; for (int i = 1; i < segments.m_size; ++i) { if (segments.m_buffer[i].m_flags == NetSegment.Flags.None) // segment is unused @@ -527,8 +530,8 @@ private void _guiSegments() { labelStr += ", flags: " + segments.m_buffer[i].m_flags.ToString() + ", condition: " + segments.m_buffer[i].m_condition; #endif #if DEBUG - SegmentEnd startEnd = TrafficPriority.GetPrioritySegment(segments.m_buffer[i].m_startNode, (ushort)i); - SegmentEnd endEnd = TrafficPriority.GetPrioritySegment(segments.m_buffer[i].m_endNode, (ushort)i); + SegmentEnd startEnd = prioMan.GetPrioritySegment(segments.m_buffer[i].m_startNode, (ushort)i); + SegmentEnd endEnd = prioMan.GetPrioritySegment(segments.m_buffer[i].m_endNode, (ushort)i); labelStr += "\nstart? " + (startEnd != null) + " veh.: " + startEnd?.GetRegisteredVehicleCount() + ", end? " + (endEnd != null) + " veh.: " + endEnd?.GetRegisteredVehicleCount(); #endif labelStr += "\nTraffic: " + segments.m_buffer[i].m_trafficDensity + " %"; @@ -675,15 +678,15 @@ private void _guiVehicles() { else timeToTransitNode = Single.PositiveInfinity; }*/ - String labelStr = "V #" + i + " is a " + (vState.Valid ? "valid" : "invalid") + " " + vState.VehicleType + " @ ~" + vehSpeed + " km/h"; + String labelStr = "V #" + i + " is a " + (vState.Valid ? "valid" : "invalid") + " " + vState.VehicleType + " @ ~" + vehSpeed + " km/h (" + vState.JunctionTransitState + ")"; +#if USEPATHWAITCOUNTER + labelStr += ", pwc: " + vState.PathWaitCounter + ", seg. " + vState.CurrentSegmentEnd?.SegmentId; +#endif //String labelStr = "Veh. " + i + " @ " + String.Format("{0:0.##}", vehSpeed) + "/" + (vState != null ? vState.CurrentMaxSpeed.ToString() : "-") + " (" + (vState != null ? vState.VehicleType.ToString() : "-") + ", valid? " + (vState != null ? vState.Valid.ToString() : "-") + ")" + ", len: " + (vState != null ? vState.TotalLength.ToString() : "-") + ", state: " + (vState != null ? vState.JunctionTransitState.ToString() : "-"); #if PATHRECALC labelStr += ", recalc: " + (vState != null ? vState.LastPathRecalculation.ToString() : "-"); #endif //labelStr += "\npos: " + curPos?.m_segment + "(" + curPos?.m_lane + ")->" + nextPos?.m_segment + "(" + nextPos?.m_lane + ")" /* + ", dist: " + distanceToTransitNode + ", time: " + timeToTransitNode*/ + ", last update: " + vState?.LastPositionUpdate; -#if USEPATHWAITCOUNTER - labelStr += ", wait: " + vState?.PathWaitCounter; -#endif Vector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr)); Rect labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y - dim.y - 50f, dim.x, dim.y); @@ -789,7 +792,7 @@ internal static int GetSegmentNumVehicleLanes(ushort segmentId, ushort? nodeId, if (nodeId != null) { dir = (Singleton.instance.m_segments.m_buffer[segmentId].m_startNode == nodeId) ? NetInfo.Direction.Backward : NetInfo.Direction.Forward; dir2 = ((Singleton.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection((NetInfo.Direction)dir); - dir3 = TrafficPriority.IsLeftHandDrive() ? NetInfo.InvertDirection((NetInfo.Direction)dir2) : dir2; + dir3 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection((NetInfo.Direction)dir2) : dir2; } var numLanes = 0; @@ -860,14 +863,16 @@ public static Texture2D MakeTex(int width, int height, Color col) { } private static int GetNumberOfMainRoads(ushort nodeId, ref NetNode node) { + TrafficPriorityManager prioMan = TrafficPriorityManager.Instance(); + var numMainRoads = 0; for (var s = 0; s < 8; s++) { var segmentId2 = node.GetSegment(s); if (segmentId2 == 0 || - !TrafficPriority.IsPrioritySegment(nodeId, segmentId2)) + !prioMan.IsPrioritySegment(nodeId, segmentId2)) continue; - var prioritySegment2 = TrafficPriority.GetPrioritySegment(nodeId, + var prioritySegment2 = prioMan.GetPrioritySegment(nodeId, segmentId2); if (prioritySegment2.Type == SegmentEnd.PriorityType.Main) { diff --git a/TLM/TLM/UI/UITrafficManager.cs b/TLM/TLM/UI/UITrafficManager.cs index 08066b63..03be84aa 100644 --- a/TLM/TLM/UI/UITrafficManager.cs +++ b/TLM/TLM/UI/UITrafficManager.cs @@ -1,16 +1,17 @@ -#define QUEUEDSTATSx +#define QUEUEDSTATS #define EXTRAPFx using System; using System.Linq; using ColossalFramework; using ColossalFramework.UI; -using TrafficManager.Traffic; +using TrafficManager.Geometry; using TrafficManager.TrafficLight; using UnityEngine; using TrafficManager.State; using TrafficManager.Custom.PathFinding; using System.Collections.Generic; +using TrafficManager.Manager; namespace TrafficManager.UI { #if !TAM @@ -385,7 +386,7 @@ public static void deactivateButtons() { private void clickClearTraffic(UIComponent component, UIMouseEventParameter eventParam) { TrafficManagerTool.SetToolMode(ToolMode.None); - TrafficPriority.RequestClearTraffic(); + VehicleStateManager.Instance().RequestClearTraffic(); } private static void ClickToggleDespawn(UIComponent component, UIMouseEventParameter eventParam) { diff --git a/TLM/TLM/Util/ICustomManager.cs b/TLM/TLM/Util/ICustomManager.cs index 48f6b36e..4c98ea71 100644 --- a/TLM/TLM/Util/ICustomManager.cs +++ b/TLM/TLM/Util/ICustomManager.cs @@ -5,7 +5,6 @@ namespace TrafficManager.Util { public interface ICustomManager { - void OnLevelLoaded(); - void OnLevelUnloaded(); + void OnLevelUnloading(); } } diff --git a/TLM/TLM/Util/SegmentTraverser.cs b/TLM/TLM/Util/SegmentTraverser.cs index 0caa7e17..bd933f09 100644 --- a/TLM/TLM/Util/SegmentTraverser.cs +++ b/TLM/TLM/Util/SegmentTraverser.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; using TrafficManager.Custom.AI; -using TrafficManager.Traffic; +using TrafficManager.Geometry; namespace TrafficManager.Util { public class SegmentTraverser {