diff --git a/README.md b/README.md index 3119b654..5dadbaa3 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,16 @@ A work-in-progress modification for **Cities: Skylines** to add additional traff User manual: http://www.viathinksoft.de/tmpe # Changelog +1.8.14, 01/07/2017 +- Bugfix: Wait/flow ratio at timed traffic lights is sometimes not correctly calculated +- Bugfix: A deadlock situation can arise at junctions with priority signs such that no vehicle enters the junction +- Bugfix: When adding a junction to a timed traffic light, sometimes light states given by user input are not correctly stored +- Bugfix: Joining two timed traffic lights sets the minimum time to "1" for steps with zero minimum time assigned +- Bugfix: Modifications of timed traffic light states are sometimes not visible while editting the light (but they are applied nonetheless) +- Bugfix: Button background is not always correctly changed after clicking on a button within the main menu +- Tram lanes can now be customized by using the lane connector tool +- Minor performance optimizations for priority sign simulation + 1.8.13, 01/05/2017 - Bugfix: Timed traffic ligt data can become corrupt when upgrading a road segment next to a traffic light, leading to faulty UI behavior (thanks to @Brain for reporting this issue) - Bugfix: The position of the main menu button resets after switching to the free camera mode (thanks to @Impact and @gravage for reporting this issue) diff --git a/TLM/TLM/Custom/AI/CustomVehicleAI.cs b/TLM/TLM/Custom/AI/CustomVehicleAI.cs index dae943a6..f5a04f9d 100644 --- a/TLM/TLM/Custom/AI/CustomVehicleAI.cs +++ b/TLM/TLM/Custom/AI/CustomVehicleAI.cs @@ -373,7 +373,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, #endif var currentFrameIndex2 = Singleton.instance.m_currentFrameIndex; var frame = currentFrameIndex2 >> 4; - float speed = lastFrameData.m_velocity.magnitude; + float sqrSpeed = lastFrameData.m_velocity.sqrMagnitude; if (vehicleState.JunctionTransitState == VehicleJunctionTransitState.None) { #if DEBUG @@ -389,7 +389,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, case SegmentEnd.PriorityType.Stop: #if DEBUG if (debug) - Log._Debug($"Vehicle {vehicleId}: STOP sign. waittime={vehicleState.WaitTime}, vel={speed}"); + Log._Debug($"Vehicle {vehicleId}: STOP sign. waittime={vehicleState.WaitTime}, sqrSpeed={sqrSpeed}"); #endif if (Options.simAccuracy <= 2 || (Options.simAccuracy >= 3 && vehicleState.WaitTime < MaxPriorityWaitTime)) { @@ -399,7 +399,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; - if (speed <= TrafficPriorityManager.MAX_STOP_VELOCITY) { + if (sqrSpeed <= TrafficPriorityManager.MAX_SQR_STOP_VELOCITY) { vehicleState.WaitTime++; float minStopWaitTime = UnityEngine.Random.Range(0f, 3f); @@ -454,7 +454,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; - if (speed <= TrafficPriorityManager.MAX_YÃŒELD_VELOCITY || Options.simAccuracy <= 2) { + if (sqrSpeed <= TrafficPriorityManager.MAX_SQR_YÃŒELD_VELOCITY || Options.simAccuracy <= 2) { if (Options.simAccuracy >= 4) { vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } else { @@ -478,7 +478,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, } else { #if DEBUG if (debug) - Log._Debug($"Vehicle {vehicleId}: Vehicle has not yet reached yield speed (reduce {speed} by {vehicleState.ReduceSpeedByValueToYield})"); + Log._Debug($"Vehicle {vehicleId}: Vehicle has not yet reached yield speed (reduce {sqrSpeed} by {vehicleState.ReduceSqrSpeedByValueToYield})"); #endif // vehicle has not yet reached yield speed @@ -529,7 +529,7 @@ internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, } return true; } - } else if (speed <= TrafficPriorityManager.MAX_STOP_VELOCITY) { + } else if (sqrSpeed <= TrafficPriorityManager.MAX_SQR_STOP_VELOCITY) { // vehicle is not moving. reset allowance to leave junction #if DEBUG if (debug) diff --git a/TLM/TLM/Geometry/NodeGeometry.cs b/TLM/TLM/Geometry/NodeGeometry.cs index 96f19fe9..d60d4660 100644 --- a/TLM/TLM/Geometry/NodeGeometry.cs +++ b/TLM/TLM/Geometry/NodeGeometry.cs @@ -27,6 +27,10 @@ public SegmentEndGeometry[] SegmentEndGeometries { get; private set; } = new SegmentEndGeometry[8]; + public byte NumSegmentEnds { get; private set; } = 0; + + public bool NeedsRecalculation { get; private set; } = false; // TODO actually use this flag + /// /// Holds a list of observers which are being notified as soon as the managed node's geometry is updated (but not neccessarily modified) /// @@ -88,8 +92,14 @@ internal void AddSegment(ushort segmentId, bool startNode, bool propagate) { } } - if (propagate) - RecalculateSegments(segmentId); + if (propagate) { + NeedsRecalculation = true; + try { + RecalculateSegments(segmentId); + } finally { + NeedsRecalculation = false; + } + } Recalculate(); } @@ -110,13 +120,20 @@ internal void RemoveSegment(ushort segmentId, bool propagate) { } } - if (propagate) - RecalculateSegments(segmentId); + if (propagate) { + NeedsRecalculation = true; + try { + RecalculateSegments(segmentId); + } finally { + NeedsRecalculation = false; + } + } Recalculate(); } private void Cleanup() { IsSimpleJunction = false; + NumSegmentEnds = 0; } internal void RecalculateSegments(ushort? ignoreSegmentId= null) { @@ -159,6 +176,7 @@ internal void Recalculate() { for (int i = 0; i < 8; ++i) { if (SegmentEndGeometries[i] == null) continue; + ++NumSegmentEnds; #if DEBUGGEO if (GlobalConfig.Instance.DebugSwitches[5]) Log._Debug($"NodeGeometry.Recalculate: Iterating over segment end {SegmentEndGeometries[i].SegmentId} @ node {NodeId}"); diff --git a/TLM/TLM/Geometry/SegmentGeometry.cs b/TLM/TLM/Geometry/SegmentGeometry.cs index d01caa76..f152946d 100644 --- a/TLM/TLM/Geometry/SegmentGeometry.cs +++ b/TLM/TLM/Geometry/SegmentGeometry.cs @@ -976,7 +976,7 @@ public bool AreHighwayRulesEnabled(bool startNode) { /// segment to check /// node the given segment shall be checked at /// true, if the given segment is an outgoing one-way road at the given node, false otherwise - internal static bool calculateIsOutgoingOneWay(ushort segmentId, ushort nodeId) { + internal static bool calculateIsOutgoingOneWay(ushort segmentId, ushort nodeId) { // TODO move to SegmentEnd if (!IsValid(segmentId)) return false; @@ -984,17 +984,13 @@ internal static bool calculateIsOutgoingOneWay(ushort segmentId, ushort nodeId) var info = instance.m_segments.m_buffer[segmentId].Info; - var dir = NetInfo.Direction.Forward; - 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 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection(dir2) : dir2; + NetInfo.Direction dir = NetUtil.GetSegmentEndDirection(segmentId, ref instance.m_segments.m_buffer[segmentId], instance.m_segments.m_buffer[segmentId].m_startNode == nodeId); var laneId = instance.m_segments.m_buffer[segmentId].m_lanes; var laneIndex = 0; while (laneIndex < info.m_lanes.Length && laneId != 0u) { if (info.m_lanes[laneIndex].m_laneType != NetInfo.LaneType.Pedestrian && - (info.m_lanes[laneIndex].m_direction == dir3)) { + ((info.m_lanes[laneIndex].m_direction & dir) != NetInfo.Direction.None)) { return false; } diff --git a/TLM/TLM/Manager/CustomSegmentLightsManager.cs b/TLM/TLM/Manager/CustomSegmentLightsManager.cs index 8e7f92af..2c2edf2f 100644 --- a/TLM/TLM/Manager/CustomSegmentLightsManager.cs +++ b/TLM/TLM/Manager/CustomSegmentLightsManager.cs @@ -64,7 +64,7 @@ public CustomSegmentLights AddLiveSegmentLights(ushort segmentId, bool startNode /// (optional) light state to set public CustomSegmentLights AddSegmentLights(ushort segmentId, bool startNode, RoadBaseAI.TrafficLightState lightState=RoadBaseAI.TrafficLightState.Red) { #if DEBUG - Log.Warning($"CustomTrafficLights.AddSegmentLights: Adding segment light: {segmentId} @ startNode={startNode}"); + Log._Debug($"CustomTrafficLights.AddSegmentLights: Adding segment light: {segmentId} @ startNode={startNode}"); #endif CustomSegment customSegment = CustomSegments[segmentId]; if (customSegment == null) { diff --git a/TLM/TLM/Manager/TrafficPriorityManager.cs b/TLM/TLM/Manager/TrafficPriorityManager.cs index 4b1b8e51..ec6cf635 100644 --- a/TLM/TLM/Manager/TrafficPriorityManager.cs +++ b/TLM/TLM/Manager/TrafficPriorityManager.cs @@ -18,7 +18,8 @@ static TrafficPriorityManager() { Instance = new TrafficPriorityManager(); } - public const float MAX_STOP_VELOCITY = 0.1f; + public const float MAX_SQR_STOP_VELOCITY = 0.01f; + public const float MAX_SQR_YÌELD_VELOCITY = 0.09f; public const float MAX_YÌELD_VELOCITY = 0.3f; /// @@ -404,9 +405,10 @@ public bool HasIncomingVehiclesWithHigherPriority(ushort targetVehicleId, ref Ve bool conflicting = false; incomingState.ProcessCurrentAndNextPathPositionAndOtherVehicleCurrentAndNextPathPosition(ref vehManager.m_vehicles.m_buffer[incomingVehicleId], ref curPos, ref nextPos, ref targetVehicleData, delegate (ref Vehicle incomingVehicleData, ref PathUnit.Position incomingCurPos, ref PathUnit.Position incomingNextPos, ref Vehicle targetVehData, ref PathUnit.Position targetCurPos, ref PathUnit.Position targetNextPos) { + bool incomingStateChangedRecently = incomingState.IsJunctionTransitStateNew(); if ( incomingState.JunctionTransitState == VehicleJunctionTransitState.Enter || - (incomingState.JunctionTransitState == VehicleJunctionTransitState.Leave && (incomingState.LastStateUpdate >> VehicleState.STATE_UPDATE_SHIFT) >= (frame >> VehicleState.STATE_UPDATE_SHIFT))) { + (incomingState.JunctionTransitState == VehicleJunctionTransitState.Leave && incomingStateChangedRecently)) { // incoming vehicle is (1) entering the junction or (2) leaving but last state update ocurred very recently. Vector3 incomingPos = incomingVehicleData.GetLastFramePosition(); Vector3 incomingVel = incomingVehicleData.GetLastFrameVelocity(); @@ -460,19 +462,30 @@ public bool HasIncomingVehiclesWithHigherPriority(ushort targetVehicleId, ref Ve Log._Debug($"HasIncomingVehicles: Target is stopped."); #endif } - } else { + } else if (incomingState.JunctionTransitState == VehicleJunctionTransitState.Leave) { #if DEBUG if (debug) - Log._Debug($"HasIncomingVehicles: Incoming {incomingVehicleId} is LEAVING but state update occurred recently."); + Log._Debug($"HasIncomingVehicles: Incoming {incomingVehicleId} is LEAVING but state update did not occur recently."); +#endif + + float incomingSqrSpeed = incomingVehicleData.GetLastFrameVelocity().sqrMagnitude; + if (incomingSqrSpeed <= MAX_SQR_STOP_VELOCITY) { +#if DEBUG + if (debug) + Log._Debug($"HasIncomingVehicles: Incoming {incomingVehicleId} is LEAVING but not moving. -> BLOCKED"); #endif + incomingState.JunctionTransitState = VehicleJunctionTransitState.Blocked; + incomingStateChangedRecently = true; + } } - if (incomingState.JunctionTransitState == VehicleJunctionTransitState.Blocked && - (incomingState.LastStateUpdate >> VehicleState.STATE_UPDATE_SHIFT) < (frame >> VehicleState.STATE_UPDATE_SHIFT) + if (!incomingStateChangedRecently && + (incomingState.JunctionTransitState == VehicleJunctionTransitState.Blocked || + (incomingState.JunctionTransitState == VehicleJunctionTransitState.Stop && targetVehicleId < incomingVehicleId)) ) { #if DEBUG if (debug) - Log._Debug($"HasIncomingVehicles: Incoming {incomingVehicleId} is BLOCKED and has waited a bit. *IGNORING*"); + Log._Debug($"HasIncomingVehicles: Incoming {incomingVehicleId} is BLOCKED and has waited a bit or is STOP and targetVehicleId {targetVehicleId} < incomingVehicleId {incomingVehicleId}. *IGNORING*"); #endif // incoming vehicle waits because the junction is blocked and we waited a little. Allow target vehicle to enter the junciton. diff --git a/TLM/TLM/State/GlobalConfig.cs b/TLM/TLM/State/GlobalConfig.cs index aa966394..1fe43c38 100644 --- a/TLM/TLM/State/GlobalConfig.cs +++ b/TLM/TLM/State/GlobalConfig.cs @@ -74,6 +74,7 @@ internal static void OnLevelUnloading() { #if DEBUG public ushort PathFindDebugNodeId = 0; + public ushort TTLDebugNodeId = 0; #endif /// diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index 7ec23324..b9a3006e 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -98,6 +98,9 @@ + + + @@ -105,6 +108,7 @@ + @@ -122,8 +126,8 @@ - - + + diff --git a/TLM/TLM/Traffic/ExtCitizenInstance.cs b/TLM/TLM/Traffic/ExtCitizenInstance.cs index a6154fc3..3338bd41 100644 --- a/TLM/TLM/Traffic/ExtCitizenInstance.cs +++ b/TLM/TLM/Traffic/ExtCitizenInstance.cs @@ -141,9 +141,9 @@ public ExtPathMode PathMode { } internal set { #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[7]) { + /*if (GlobalConfig.Instance.DebugSwitches[7]) { Log.Warning($"Changing PathMode for citizen instance {InstanceId}: {pathMode} -> {value}"); - } + }*/ #endif pathMode = value; } @@ -207,9 +207,9 @@ public uint GetCitizenId() { internal void Reset() { #if DEBUG - if (GlobalConfig.Instance.DebugSwitches[7]) { + /*if (GlobalConfig.Instance.DebugSwitches[7]) { Log.Warning($"Resetting PathMode for citizen instance {InstanceId}"); - } + }*/ #endif //Flags = ExtFlags.None; PathMode = ExtPathMode.None; diff --git a/TLM/TLM/Traffic/SegmentEnd.cs b/TLM/TLM/Traffic/SegmentEnd.cs index 48a1f403..7ee85e25 100644 --- a/TLM/TLM/Traffic/SegmentEnd.cs +++ b/TLM/TLM/Traffic/SegmentEnd.cs @@ -167,7 +167,7 @@ public Dictionary GetVehicleMetricGoingToSegment(bool includeStopp return; } - if (!includeStopped && vehState.GetLastFrameVelocity().magnitude < TrafficPriorityManager.MAX_STOP_VELOCITY) { + if (!includeStopped && vehState.GetLastFrameVelocity().sqrMagnitude < TrafficPriorityManager.MAX_SQR_STOP_VELOCITY) { #if DEBUGMETRIC2 if (debug) Log._Debug($" GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: too slow"); diff --git a/TLM/TLM/Traffic/VehicleState.cs b/TLM/TLM/Traffic/VehicleState.cs index 4a97295d..a4acd43b 100644 --- a/TLM/TLM/Traffic/VehicleState.cs +++ b/TLM/TLM/Traffic/VehicleState.cs @@ -45,7 +45,7 @@ public float TotalLength { private ushort VehicleId; public int WaitTime = 0; - public float ReduceSpeedByValueToYield; + public float ReduceSqrSpeedByValueToYield; private bool valid = false; public bool Valid { @@ -493,7 +493,7 @@ internal void OnVehicleSpawned(ref Vehicle vehicleData) { // enforce updating trailers (they are not always present at path-finding time) ApplyVehicleTypeToTrailers(); - ReduceSpeedByValueToYield = UnityEngine.Random.Range(16f, 28f); + ReduceSqrSpeedByValueToYield = UnityEngine.Random.Range(256f, 784f); try { TotalLength = Singleton.instance.m_vehicles.m_buffer[VehicleId].CalculateTotalLength(VehicleId); } catch (Exception) { @@ -538,5 +538,14 @@ private void ApplyVehicleTypeToTrailers() { otherVehicleId = vehManager.m_vehicles.m_buffer[otherVehicleId].m_trailingVehicle; } } + + /// + /// Determines if the junction transit state has been recently modified + /// + /// + internal bool IsJunctionTransitStateNew() { + uint frame = Singleton.instance.m_currentFrameIndex; + return (LastStateUpdate >> VehicleState.STATE_UPDATE_SHIFT) >= (frame >> VehicleState.STATE_UPDATE_SHIFT); + } } } diff --git a/TLM/TLM/TrafficLight/CustomSegmentLight.cs b/TLM/TLM/TrafficLight/CustomSegmentLight.cs index aab37a50..b895f327 100644 --- a/TLM/TLM/TrafficLight/CustomSegmentLight.cs +++ b/TLM/TLM/TrafficLight/CustomSegmentLight.cs @@ -39,20 +39,7 @@ public Mode CurrentMode { return; currentMode = value; - switch (currentMode) { - case Mode.Simple: - leftLight = LightMain; - rightLight = LightMain; - break; - case Mode.SingleLeft: - rightLight = LightMain; - break; - case Mode.SingleRight: - leftLight = LightMain; - break; - } - - lights.OnChange(); + EnsureModeLights(); } } internal Mode currentMode = Mode.Simple; @@ -95,6 +82,38 @@ public RoadBaseAI.TrafficLightState LightRight { CustomSegmentLights lights; + private void EnsureModeLights() { + bool changed = false; + + switch (currentMode) { + case Mode.Simple: + if (leftLight != LightMain) { + leftLight = LightMain; + changed = true; + } + if (rightLight != LightMain) { + rightLight = LightMain; + changed = true; + } + break; + case Mode.SingleLeft: + if (rightLight != LightMain) { + rightLight = LightMain; + changed = true; + } + break; + case Mode.SingleRight: + if (leftLight != LightMain) { + leftLight = LightMain; + changed = true; + } + break; + } + + if (changed) + lights.OnChange(); + } + public override string ToString() { return $"LightLeft={LightLeft} LightMain={LightMain} LightRight={LightRight} CurrentMode={CurrentMode}"; } diff --git a/TLM/TLM/TrafficLight/CustomSegmentLights.cs b/TLM/TLM/TrafficLight/CustomSegmentLights.cs index 78d9b06f..61f839da 100644 --- a/TLM/TLM/TrafficLight/CustomSegmentLights.cs +++ b/TLM/TLM/TrafficLight/CustomSegmentLights.cs @@ -39,7 +39,6 @@ public ushort NodeId { public ushort SegmentId { get { return segmentId; } - set { segmentId = value; housekeeping(true, true); } } public uint LastChangeFrame; @@ -116,6 +115,12 @@ public ICustomSegmentLightsManager LightsManager { } private ICustomSegmentLightsManager lightsManager; + internal void Relocate(ushort segmentId, bool startNode) { + this.segmentId = segmentId; + this.startNode = startNode; + housekeeping(true, true); + } + public override string ToString() { String ret = $"InvalidPedestrianLight={InvalidPedestrianLight} PedestrianLightState={PedestrianLightState} ManualPedestrianMode={ManualPedestrianMode}\n"; foreach (KeyValuePair e in CustomLights) { @@ -307,8 +312,9 @@ internal void SetLights(RoadBaseAI.TrafficLightState lightState) { internal void SetLights(CustomSegmentLights otherLights) { foreach (KeyValuePair e in otherLights.CustomLights) { CustomSegmentLight ourLight = null; - if (!CustomLights.TryGetValue(e.Key, out ourLight)) + if (!CustomLights.TryGetValue(e.Key, out ourLight)) { continue; + } ourLight.SetStates(e.Value.LightMain, e.Value.LightLeft, e.Value.LightRight, false); //ourLight.LightPedestrian = e.Value.LightPedestrian; diff --git a/TLM/TLM/TrafficLight/TimedTrafficLights.cs b/TLM/TLM/TrafficLight/TimedTrafficLights.cs index 91a9ad93..29e772f5 100644 --- a/TLM/TLM/TrafficLight/TimedTrafficLights.cs +++ b/TLM/TLM/TrafficLight/TimedTrafficLights.cs @@ -300,8 +300,17 @@ public void SimulationStep() { Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (2)"); #endif +#if DEBUG + bool debug = GlobalConfig.Instance.DebugSwitches[7] && GlobalConfig.Instance.TTLDebugNodeId == NodeId; +#endif + TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance; if (Steps[CurrentStep].NextStepRefIndex < 0) { +#if DEBUG + if (debug) { + Log._Debug($"Step {CurrentStep} is done at timed light {NodeId}. Determining next step."); + } +#endif // next step has not yet identified yet. check for minTime=0 steps int nextStepIndex = (CurrentStep + 1) % NumSteps(); if (Steps[nextStepIndex].minTime == 0) { @@ -313,21 +322,29 @@ public void SimulationStep() { maxWaitFlowDiff = float.MinValue; int bestNextStepIndex = prevStepIndex; - while (nextStepIndex != prevStepIndex) { - //Log._Debug($"Checking step {nextStepIndex} @ node {NodeId}."); +#if DEBUG + if (debug) { + Log._Debug($"Next step {nextStepIndex} has minTime = 0 at timed light {NodeId}. Old step {CurrentStep} has waitFlowDiff={maxWaitFlowDiff} (flow={Steps[CurrentStep].minFlow}, wait={Steps[CurrentStep].maxWait})."); + } +#endif + while (nextStepIndex != prevStepIndex) { float wait; float flow; Steps[nextStepIndex].calcWaitFlow(false, nextStepIndex, out wait, out flow); - //Log._Debug($"Checking step {nextStepIndex} @ node {NodeId} with minTime=0: flow={flow} wait={wait}"); - float flowWaitDiff = flow - wait; if (flowWaitDiff > maxWaitFlowDiff) { maxWaitFlowDiff = flowWaitDiff; bestNextStepIndex = nextStepIndex; } +#if DEBUG + if (debug) { + Log._Debug($"Checking upcoming step {nextStepIndex} @ node {NodeId}: flow={flow} wait={wait} minTime={Steps[nextStepIndex].minTime}. bestWaitFlowDiff={bestNextStepIndex}, bestNextStepIndex={bestNextStepIndex}"); + } +#endif + if (Steps[nextStepIndex].minTime != 0) { break; } @@ -336,7 +353,11 @@ public void SimulationStep() { } if (bestNextStepIndex == CurrentStep) { - //Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) equals CurrentStep @ node {NodeId}."); +#if DEBUG + if (debug) { + Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) equals CurrentStep @ node {NodeId}."); + } +#endif // restart the current step foreach (ushort slaveNodeId in NodeGroup) { @@ -350,7 +371,11 @@ public void SimulationStep() { } return; } else { - //Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) does not equal CurrentStep @ node {NodeId}."); +#if DEBUG + if (debug) { + Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) does not equal CurrentStep @ node {NodeId}."); + } +#endif // set next step reference index for assuring a correct end transition foreach (ushort slaveNodeId in NodeGroup) { @@ -494,6 +519,7 @@ public void RemoveStep(int id) { } internal void handleNewSegments() { + Log._Debug($"TimedTrafficLights.handleNewSegments: called for timed traffic light @ {NodeId}"); if (NumSteps() <= 0) { // no steps defined, just create live traffic lights /*for (int s = 0; s < 8; ++s) { @@ -504,7 +530,7 @@ internal void handleNewSegments() { CustomTrafficLights.AddSegmentLights(NodeId, segmentId); }*/ - + Log._Debug($"TimedTrafficLights.handleNewSegments: no steps @ {NodeId}"); return; } @@ -517,13 +543,14 @@ internal void handleNewSegments() { foreach (SegmentEndGeometry end in nodeGeometry.SegmentEndGeometries) { if (end == null) continue; + Log._Debug($"TimedTrafficLights.handleNewSegments: handling existing seg. {end.SegmentId} @ {NodeId}"); List invalidSegmentIds = new List(); bool isNewSegment = !Steps[0].segmentLights.ContainsKey(end.SegmentId); if (isNewSegment) { // segment was created - Log._Debug($"New segment detected: {end.SegmentId} @ {NodeId}"); + Log._Debug($"TimedTrafficLights.handleNewSegments: New segment detected: {end.SegmentId} @ {NodeId}"); foreach (KeyValuePair e in Steps[0].segmentLights) { var fromSegmentId = e.Key; @@ -555,7 +582,7 @@ internal void handleNewSegments() { Log._Debug($"Removing old segment {oldSegmentId} @ {NodeId} from step {i}"); Steps[i].segmentLights.Remove(oldSegmentId); Log._Debug($"Setting new segment id {end.SegmentId} at custom light from step {i}"); - customLights.SegmentId = end.SegmentId; + customLights.Relocate(end.SegmentId, end.StartNode); Steps[i].segmentLights.Add(end.SegmentId, customLights); Steps[i].calcMaxSegmentLength(); Log._Debug($"Getting live segment lights of new segment {end.SegmentId} @ {NodeId} and applying mode @ step {i}"); @@ -667,7 +694,7 @@ internal void Join(TimedTrafficLights otherTimedLight) { // build means if (NumSteps() > 0) { for (int i = 0; i < NumSteps(); ++i) { - minTimes[i] = Math.Max(1, minTimes[i] / newNodeGroup.Count); + minTimes[i] = Math.Max(0, minTimes[i] / newNodeGroup.Count); maxTimes[i] = Math.Max(1, maxTimes[i] / newNodeGroup.Count); waitFlowBalances[i] = Math.Max(0.001f, waitFlowBalances[i] / (float)newNodeGroup.Count); } diff --git a/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs b/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs index d026d00d..2bcd227f 100644 --- a/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs +++ b/TLM/TLM/TrafficLight/TimedTrafficLightsStep.cs @@ -219,6 +219,10 @@ public void UpdateLiveLights(bool noTransition) { } #endif + if (PreviousStepRefIndex >= timedNode.NumSteps()) + PreviousStepRefIndex = -1; + if (NextStepRefIndex >= timedNode.NumSteps()) + NextStepRefIndex = -1; TimedTrafficLightsStep previousStep = timedNode.Steps[PreviousStepRefIndex >= 0 ? PreviousStepRefIndex : ((timedNode.CurrentStep + timedNode.Steps.Count - 1) % timedNode.Steps.Count)]; TimedTrafficLightsStep nextStep = timedNode.Steps[NextStepRefIndex >= 0 ? NextStepRefIndex : ((timedNode.CurrentStep + 1) % timedNode.Steps.Count)]; @@ -313,6 +317,7 @@ public void UpdateLiveLights(bool noTransition) { CustomSegmentLight curStepSegmentLight = curStepSegmentLights.GetCustomLight(vehicleType); CustomSegmentLight prevStepSegmentLight = prevStepSegmentLights.GetCustomLight(vehicleType); CustomSegmentLight nextStepSegmentLight = nextStepSegmentLights.GetCustomLight(vehicleType); + #if DEBUG if (curStepSegmentLight == null) { Log.Error($"TimedTrafficLightsStep: curStepSegmentLight is null!"); @@ -331,6 +336,10 @@ public void UpdateLiveLights(bool noTransition) { #endif liveSegmentLight.currentMode = curStepSegmentLight.CurrentMode; + /*curStepSegmentLight.EnsureModeLights(); + prevStepSegmentLight.EnsureModeLights(); + nextStepSegmentLight.EnsureModeLights();*/ + RoadBaseAI.TrafficLightState mainLight = calcLightState(prevStepSegmentLight.LightMain, curStepSegmentLight.LightMain, nextStepSegmentLight.LightMain, atStartTransition, atEndTransition); RoadBaseAI.TrafficLightState leftLight = calcLightState(prevStepSegmentLight.LightLeft, curStepSegmentLight.LightLeft, nextStepSegmentLight.LightLeft, atStartTransition, atEndTransition); RoadBaseAI.TrafficLightState rightLight = calcLightState(prevStepSegmentLight.LightRight, curStepSegmentLight.LightRight, nextStepSegmentLight.LightRight, atStartTransition, atEndTransition); @@ -383,10 +392,13 @@ private RoadBaseAI.TrafficLightState calcLightState(RoadBaseAI.TrafficLightState /// Updates timed segment lights according to "real-world" traffic light states /// public void UpdateLights() { + Log._Debug($"TimedTrafficLightsStep.UpdateLights: Updating lights of timed traffic light step @ {timedNode.NodeId}"); foreach (KeyValuePair e in segmentLights) { var segmentId = e.Key; var segLights = e.Value; - + + Log._Debug($"TimedTrafficLightsStep.UpdateLights: Updating lights of timed traffic light step at seg. {e.Key} @ {timedNode.NodeId}"); + //if (segment == 0) continue; var liveSegLights = CustomSegmentLightsManager.Instance.GetSegmentLights(segmentId, segLights.StartNode, false); if (liveSegLights == null) @@ -524,6 +536,7 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out uint curMeanFlow = 0; uint curMeanWait = 0; + // TODO checking agains getCurrentFrame() is only valid if this is the current step if (onlyMoving && getCurrentFrame() <= startFrame + minTime + 1) { // during start phase all vehicles on "green" segments are counted as flowing onlyMoving = false; } @@ -531,7 +544,6 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance; TrafficPriorityManager prioMan = TrafficPriorityManager.Instance; - // we are the master node. calculate traffic data foreach (ushort timedNodeId in timedNode.NodeGroup) { TrafficLightSimulation sim = tlsMan.GetNodeSimulation(timedNodeId); if (sim == null || !sim.IsTimedLight()) @@ -548,6 +560,8 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out #if DEBUGMETRIC bool debug = sourceSegmentId == 20857 && GlobalConfig.Instance.DebugSwitches[1]; +#elif DEBUG + bool debug = GlobalConfig.Instance.DebugSwitches[7] && GlobalConfig.Instance.TTLDebugNodeId == timedNodeId; #else bool debug = false; #endif @@ -556,7 +570,11 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out if (slaveStep.timedNode.Directions.ContainsKey(sourceSegmentId)) { directions = slaveStep.timedNode.Directions[sourceSegmentId]; } else { - //Log._Debug($"calcWaitFlow: No arrow directions defined for segment {sourceSegmentId} @ {timedNodeId}"); +#if DEBUG + if (debug) { + Log._Debug($"calcWaitFlow: No arrow directions defined for segment {sourceSegmentId} @ {timedNodeId}"); + } +#endif continue; } @@ -580,7 +598,7 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out continue; } -#if DEBUGMETRIC +#if DEBUG if (debug) Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Checking lane {laneIndex} @ seg. {sourceSegmentId}. Vehicle types: {vehicleType}"); #endif @@ -595,19 +613,19 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out // calculate waiting/flowing traffic foreach (KeyValuePair f in allCarsToSegmentMetric) { - ushort toSegmentId = f.Key; + ushort targetSegmentId = f.Key; uint totalNumCars = f.Value; if (evalFlowingVehicles) { uint totalNumFlowingCars = onlyMoving ? carsFlowingToSegmentMetric[f.Key] : totalNumCars; -#if DEBUGMETRIC - /*if (debug) - Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Total norm. car length of vehicles on seg. {fromSegmentId}, lane {laneIndex} going to seg. {toSegmentId}: {totalNormCarLength}");*/ +#if DEBUG + if (debug) + Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Total num of flowing cars on seg. {sourceSegmentId}, lane {laneIndex} going to seg. {targetSegmentId}: {totalNumFlowingCars}"); #endif bool addToFlow = false; - switch (directions[toSegmentId]) { + switch (directions[targetSegmentId]) { case ArrowDirection.Turn: addToFlow = TrafficPriorityManager.IsLeftHandDrive() ? segLight.IsRightGreen() : segLight.IsLeftGreen(); break; @@ -635,9 +653,9 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out ++numWaits; } -#if DEBUGMETRIC +#if DEBUG if (debug) - Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Vehicles on lane {laneIndex} on seg. {sourceSegmentId} going to seg. {toSegmentId} curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}, numFlows={numFlows}, numWaits={numWaits}"); + Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Vehicles on lane {laneIndex} on seg. {sourceSegmentId} going to seg. {targetSegmentId} curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}, numFlows={numFlows}, numWaits={numWaits}"); #endif } } @@ -648,9 +666,9 @@ public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out curMeanFlow /= numFlows; if (numWaits > 0) curMeanWait /= numWaits;*/ - - float fCurMeanFlow = (float)curMeanFlow / (float)numFlows; - float fCurMeanWait = (float)curMeanWait / (float)numWaits; + + float fCurMeanFlow = numFlows > 0 ? (float)curMeanFlow / (float)numFlows : 0; + float fCurMeanWait = numWaits > 0 ? (float)curMeanWait / (float)numWaits : 0; fCurMeanFlow /= waitFlowBalance; // a value smaller than 1 rewards steady traffic currents wait = (float)fCurMeanWait; diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index bd79664f..8dd36989 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -5,7 +5,7 @@ namespace TrafficManager { public class TrafficManagerMod : IUserMod { - public static readonly string Version = "1.8.13"; + public static readonly string Version = "1.8.14"; public static readonly uint GameVersion = 159768848u; public static readonly uint GameVersionA = 1u; diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs index 75855144..2e2493e3 100644 --- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs +++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs @@ -426,7 +426,7 @@ private List GetNodeMarkers(ushort nodeId) { for (byte laneIndex = 0; laneIndex < lanes.Length && laneId != 0; laneIndex++) { NetInfo.Lane laneInfo = lanes[laneIndex]; if ((laneInfo.m_laneType & (NetInfo.LaneType.TransportVehicle | NetInfo.LaneType.Vehicle)) != NetInfo.LaneType.None && - (laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro)) != VehicleInfo.VehicleType.None) { // TODO refactor vehicle mask + (laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro)) != VehicleInfo.VehicleType.None) { // TODO refactor vehicle mask Vector3? pos = null; bool isSource = false; diff --git a/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs index eb5ae01c..439447c2 100644 --- a/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs @@ -274,7 +274,6 @@ private void _guiTimedControlPanel(int num) { } for (var i = 0; i < timedNodeMain.NumSteps(); i++) { - Log._Debug($"Step {i}"); GUILayout.BeginHorizontal(); if (_timedEditStep != i) { @@ -1077,7 +1076,7 @@ private void ShowGUI() { _hoveredNode == nodeId ? 0.92f : 0.6f; - + GUI.color = guiColor; var myRect1 = new Rect(offsetScreenPos.x - modeWidth / 2, diff --git a/TLM/TLM/UI/UITrafficManager.cs b/TLM/TLM/UI/UITrafficManager.cs index 6f2491d8..f1481502 100644 --- a/TLM/TLM/UI/UITrafficManager.cs +++ b/TLM/TLM/UI/UITrafficManager.cs @@ -217,7 +217,11 @@ private UIButton _createButton(string text, int y, MouseEventHandler eventClick) button.playAudioEvents = true; button.text = text; button.relativePosition = new Vector3(15f, y); - button.eventClick += eventClick; + button.eventClick += delegate (UIComponent component, UIMouseEventParameter eventParam) { + deactivateButtons(); + eventClick(component, eventParam); + button.Invalidate(); + }; return button; } @@ -362,71 +366,90 @@ private void clickGoToCitizenInstance(UIComponent component, UIMouseEventParamet private void clickSwitchTraffic(UIComponent component, UIMouseEventParameter eventParam) { if (TrafficManagerTool.GetToolMode() != ToolMode.SwitchTrafficLight) { - _buttonSwitchTraffic.focusedBgSprite = "ButtonMenuFocused"; + _buttonSwitchTraffic.normalBgSprite = _buttonSwitchTraffic.focusedBgSprite = "ButtonMenuFocused"; TrafficManagerTool.SetToolMode(ToolMode.SwitchTrafficLight); } else { - _buttonSwitchTraffic.focusedBgSprite = "ButtonMenu"; + _buttonSwitchTraffic.normalBgSprite = _buttonSwitchTraffic.focusedBgSprite = "ButtonMenu"; TrafficManagerTool.SetToolMode(ToolMode.None); } } private void clickAddPrioritySigns(UIComponent component, UIMouseEventParameter eventParam) { - Log._Debug("Priority Sign Clicked."); if (TrafficManagerTool.GetToolMode() != ToolMode.AddPrioritySigns) { - _buttonPrioritySigns.focusedBgSprite = "ButtonMenuFocused"; + _buttonPrioritySigns.normalBgSprite = _buttonPrioritySigns.focusedBgSprite = "ButtonMenuFocused"; TrafficManagerTool.SetToolMode(ToolMode.AddPrioritySigns); } else { - _buttonPrioritySigns.focusedBgSprite = "ButtonMenu"; + _buttonPrioritySigns.normalBgSprite = _buttonPrioritySigns.focusedBgSprite = "ButtonMenu"; TrafficManagerTool.SetToolMode(ToolMode.None); } } private void clickManualControl(UIComponent component, UIMouseEventParameter eventParam) { if (TrafficManagerTool.GetToolMode() != ToolMode.ManualSwitch) { - _buttonManualControl.focusedBgSprite = "ButtonMenuFocused"; + _buttonManualControl.normalBgSprite = _buttonManualControl.focusedBgSprite = "ButtonMenuFocused"; TrafficManagerTool.SetToolMode(ToolMode.ManualSwitch); } else { - _buttonManualControl.focusedBgSprite = "ButtonMenu"; + _buttonManualControl.normalBgSprite = _buttonManualControl.focusedBgSprite = "ButtonMenu"; TrafficManagerTool.SetToolMode(ToolMode.None); } } private void clickTimedAdd(UIComponent component, UIMouseEventParameter eventParam) { if (TrafficManagerTool.GetToolMode() != ToolMode.TimedLightsSelectNode && TrafficManagerTool.GetToolMode() != ToolMode.TimedLightsShowLights) { - _buttonTimedMain.focusedBgSprite = "ButtonMenuFocused"; + _buttonTimedMain.normalBgSprite = _buttonTimedMain.focusedBgSprite = "ButtonMenuFocused"; TrafficManagerTool.SetToolMode(ToolMode.TimedLightsSelectNode); } else { - _buttonTimedMain.focusedBgSprite = "ButtonMenu"; + _buttonTimedMain.normalBgSprite = _buttonTimedMain.focusedBgSprite = "ButtonMenu"; TrafficManagerTool.SetToolMode(ToolMode.None); } } private void clickSpeedLimits(UIComponent component, UIMouseEventParameter eventParam) { if (TrafficManagerTool.GetToolMode() != ToolMode.SpeedLimits) { - _buttonSpeedLimits.focusedBgSprite = "ButtonMenuFocused"; + _buttonSpeedLimits.normalBgSprite = _buttonSpeedLimits.focusedBgSprite = "ButtonMenuFocused"; TrafficManagerTool.SetToolMode(ToolMode.SpeedLimits); } else { - _buttonSpeedLimits.focusedBgSprite = "ButtonMenu"; + _buttonSpeedLimits.normalBgSprite = _buttonSpeedLimits.focusedBgSprite = "ButtonMenu"; TrafficManagerTool.SetToolMode(ToolMode.None); } } private void clickVehicleRestrictions(UIComponent component, UIMouseEventParameter eventParam) { if (TrafficManagerTool.GetToolMode() != ToolMode.VehicleRestrictions) { - _buttonVehicleRestrictions.focusedBgSprite = "ButtonMenuFocused"; + _buttonVehicleRestrictions.normalBgSprite = _buttonVehicleRestrictions.focusedBgSprite = "ButtonMenuFocused"; TrafficManagerTool.SetToolMode(ToolMode.VehicleRestrictions); } else { - _buttonVehicleRestrictions.focusedBgSprite = "ButtonMenu"; + _buttonVehicleRestrictions.normalBgSprite = _buttonVehicleRestrictions.focusedBgSprite = "ButtonMenu"; TrafficManagerTool.SetToolMode(ToolMode.None); } } private void clickJunctionRestrictions(UIComponent component, UIMouseEventParameter eventParam) { if (TrafficManagerTool.GetToolMode() != ToolMode.JunctionRestrictions) { - _buttonJunctionRestrictions.focusedBgSprite = "ButtonMenuFocused"; + _buttonJunctionRestrictions.normalBgSprite = _buttonJunctionRestrictions.focusedBgSprite = "ButtonMenuFocused"; TrafficManagerTool.SetToolMode(ToolMode.JunctionRestrictions); } else { - _buttonJunctionRestrictions.focusedBgSprite = "ButtonMenu"; + _buttonJunctionRestrictions.normalBgSprite = _buttonJunctionRestrictions.focusedBgSprite = "ButtonMenu"; + TrafficManagerTool.SetToolMode(ToolMode.None); + } + } + + private void clickChangeLanes(UIComponent component, UIMouseEventParameter eventParam) { + if (TrafficManagerTool.GetToolMode() != ToolMode.LaneChange) { + _buttonLaneChange.normalBgSprite = _buttonLaneChange.focusedBgSprite = "ButtonMenuFocused"; + TrafficManagerTool.SetToolMode(ToolMode.LaneChange); + } else { + _buttonLaneChange.normalBgSprite = _buttonLaneChange.focusedBgSprite = "ButtonMenu"; + TrafficManagerTool.SetToolMode(ToolMode.None); + } + } + + private void clickLaneConnector(UIComponent component, UIMouseEventParameter eventParam) { + if (TrafficManagerTool.GetToolMode() != ToolMode.LaneConnector) { + _buttonLaneConnector.normalBgSprite = _buttonLaneConnector.focusedBgSprite = "ButtonMenuFocused"; + TrafficManagerTool.SetToolMode(ToolMode.LaneConnector); + } else { + _buttonLaneConnector.normalBgSprite = _buttonLaneConnector.focusedBgSprite = "ButtonMenu"; TrafficManagerTool.SetToolMode(ToolMode.None); } } @@ -436,28 +459,27 @@ private void clickJunctionRestrictions(UIComponent component, UIMouseEventParame /// public static void deactivateButtons() { if (_buttonSwitchTraffic != null) - _buttonSwitchTraffic.focusedBgSprite = "ButtonMenu"; + _buttonSwitchTraffic.normalBgSprite = _buttonSwitchTraffic.focusedBgSprite = "ButtonMenu"; if (_buttonPrioritySigns != null) - _buttonPrioritySigns.focusedBgSprite = "ButtonMenu"; + _buttonPrioritySigns.normalBgSprite = _buttonPrioritySigns.focusedBgSprite = "ButtonMenu"; if (_buttonManualControl != null) - _buttonManualControl.focusedBgSprite = "ButtonMenu"; + _buttonManualControl.normalBgSprite = _buttonManualControl.focusedBgSprite = "ButtonMenu"; if (_buttonTimedMain != null) - _buttonTimedMain.focusedBgSprite = "ButtonMenu"; + _buttonTimedMain.normalBgSprite = _buttonTimedMain.focusedBgSprite = "ButtonMenu"; if (_buttonLaneChange != null) - _buttonLaneChange.focusedBgSprite = "ButtonMenu"; + _buttonLaneChange.normalBgSprite = _buttonLaneChange.focusedBgSprite = "ButtonMenu"; if (_buttonLaneConnector != null) - _buttonLaneConnector.focusedBgSprite = "ButtonMenu"; - //_buttonLaneRestrictions.focusedBgSprite = "ButtonMenu"; - if (_buttonClearTraffic != null) - _buttonClearTraffic.focusedBgSprite = "ButtonMenu"; + _buttonLaneConnector.normalBgSprite = _buttonLaneConnector.focusedBgSprite = "ButtonMenu"; if (_buttonSpeedLimits != null) - _buttonSpeedLimits.focusedBgSprite = "ButtonMenu"; + _buttonSpeedLimits.normalBgSprite = _buttonSpeedLimits.focusedBgSprite = "ButtonMenu"; if (_buttonVehicleRestrictions != null) - _buttonVehicleRestrictions.focusedBgSprite = "ButtonMenu"; + _buttonVehicleRestrictions.normalBgSprite = _buttonVehicleRestrictions.focusedBgSprite = "ButtonMenu"; if (_buttonJunctionRestrictions != null) - _buttonJunctionRestrictions.focusedBgSprite = "ButtonMenu"; + _buttonJunctionRestrictions.normalBgSprite = _buttonJunctionRestrictions.focusedBgSprite = "ButtonMenu"; + if (_buttonClearTraffic != null) + _buttonClearTraffic.normalBgSprite = _buttonClearTraffic.focusedBgSprite = "ButtonMenu"; if (_buttonToggleDespawn != null) - _buttonToggleDespawn.focusedBgSprite = "ButtonMenu"; + _buttonToggleDespawn.normalBgSprite = _buttonToggleDespawn.focusedBgSprite = "ButtonMenu"; } private void clickClearTraffic(UIComponent component, UIMouseEventParameter eventParam) { @@ -476,26 +498,6 @@ private static void ClickToggleDespawn(UIComponent component, UIMouseEventParame : Translation.GetString("Enable_despawning"); } - private void clickChangeLanes(UIComponent component, UIMouseEventParameter eventParam) { - if (TrafficManagerTool.GetToolMode() != ToolMode.LaneChange) { - _buttonLaneChange.focusedBgSprite = "ButtonMenuFocused"; - TrafficManagerTool.SetToolMode(ToolMode.LaneChange); - } else { - _buttonLaneChange.focusedBgSprite = "ButtonMenu"; - TrafficManagerTool.SetToolMode(ToolMode.None); - } - } - - private void clickLaneConnector(UIComponent component, UIMouseEventParameter eventParam) { - if (TrafficManagerTool.GetToolMode() != ToolMode.LaneConnector) { - _buttonLaneConnector.focusedBgSprite = "ButtonMenuFocused"; - TrafficManagerTool.SetToolMode(ToolMode.LaneConnector); - } else { - _buttonLaneConnector.focusedBgSprite = "ButtonMenu"; - TrafficManagerTool.SetToolMode(ToolMode.None); - } - } - public override void Update() { #if QUEUEDSTATS if (showPathFindStats && title != null) { diff --git a/TLM/TLM/Util/NetUtil.cs b/TLM/TLM/Util/NetUtil.cs index 8da77b3d..3196a3d7 100644 --- a/TLM/TLM/Util/NetUtil.cs +++ b/TLM/TLM/Util/NetUtil.cs @@ -2,12 +2,15 @@ using System; using System.Collections.Generic; using System.Text; +using TrafficManager.Manager; namespace TrafficManager.Util { + // TODO Make separate classes for segments, nodes, lanes, etc. and create an abstraction layer for game interfacing utilities public static class NetUtil { public delegate void NetSegmentHandler(ushort segmentId, ref NetSegment segment); public delegate void NetNodeHandler(ushort nodeId, ref NetNode node); public delegate void NetLaneHandler(uint laneId, ref NetLane lane); + public delegate void NetSegmentLaneHandler(uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segmentId, ref NetSegment segment, byte laneIndex); static bool IsSegmentValid(ref NetSegment segment) { return (segment.m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) == NetSegment.Flags.Created; @@ -59,5 +62,39 @@ public static void ProcessLane(uint laneId, NetLaneHandler handler) { public static void ProcessLane(uint laneId, ref NetLane lane, NetLaneHandler handler) { handler(laneId, ref lane); } + + public static void IterateSegmentLanes(ushort segmentId, NetSegmentLaneHandler handler) { + IterateSegmentLanes(segmentId, ref Singleton.instance.m_segments.m_buffer[segmentId], handler); + } + + public static void IterateSegmentLanes(ushort segmentId, ref NetSegment segment, NetSegmentLaneHandler handler) { + NetInfo segmentInfo = segment.Info; + if (segmentInfo == null) + return; + + byte laneIndex = 0; + uint curLaneId = segment.m_lanes; + while (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) { + NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex]; + handler(curLaneId, ref Singleton.instance.m_lanes.m_buffer[curLaneId], laneInfo, segmentId, ref segment, laneIndex); + + curLaneId = Singleton.instance.m_lanes.m_buffer[curLaneId].m_nextLane; + ++laneIndex; + } + } + + public static NetInfo.Direction GetSegmentEndDirection(ushort segmentId, bool startNode) { + return GetSegmentEndDirection(segmentId, ref Singleton.instance.m_segments.m_buffer[segmentId], startNode); + } + + public static NetInfo.Direction GetSegmentEndDirection(ushort segmentId, ref NetSegment segment, bool startNode) { + NetInfo segmentInfo = segment.Info; + + var dir = startNode ? NetInfo.Direction.Backward : NetInfo.Direction.Forward; + if ((segment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None ^ TrafficPriorityManager.IsLeftHandDrive()) + dir = NetInfo.InvertDirection(dir); + + return dir; + } } }