diff --git a/TLM/.editorconfig b/TLM/.editorconfig new file mode 100644 index 000000000..f9530af21 --- /dev/null +++ b/TLM/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.{cs,vb}] +indent_style = tab +indent_size = 8 +trim_trailing_whitespace = true + +[*.cs] +# csharp_new_line_before_open_brace = methods diff --git a/TLM/TLM/Manager/Impl/SpeedLimitManager.cs b/TLM/TLM/Manager/Impl/SpeedLimitManager.cs index 67f129b3e..a83911a1b 100644 --- a/TLM/TLM/Manager/Impl/SpeedLimitManager.cs +++ b/TLM/TLM/Manager/Impl/SpeedLimitManager.cs @@ -6,6 +6,7 @@ using TrafficManager.Geometry; using TrafficManager.Geometry.Impl; using TrafficManager.State; +using TrafficManager.Traffic.Data; using TrafficManager.Util; using UnityEngine; @@ -14,48 +15,32 @@ public class SpeedLimitManager : AbstractGeometryObservingManager, ICustomDataMa public const NetInfo.LaneType LANE_TYPES = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle; public const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Monorail; + /// Ingame speed units, max possible speed public const float MAX_SPEED = 10f * 2f; // 1000 km/h + private Dictionary vanillaLaneSpeedLimitsByNetInfoName; // For each NetInfo (by name) and lane index: game default speed limit private Dictionary> childNetInfoNamesByCustomizableNetInfoName; // For each NetInfo (by name): Parent NetInfo (name) private List customizableNetInfos; - internal Dictionary CustomLaneSpeedLimitIndexByNetInfoName; // For each NetInfo (by name) and lane index: custom speed limit index + internal Dictionary CustomLaneSpeedLimitByNetInfoName; // For each NetInfo (by name) and lane index: custom speed limit internal Dictionary NetInfoByName; // For each name: NetInfo public static readonly SpeedLimitManager Instance = new SpeedLimitManager(); - protected override void InternalPrintDebugInfo() { - base.InternalPrintDebugInfo(); - Log._Debug($"- Not implemented -"); - // TODO implement - } - - public readonly List AvailableSpeedLimits; - private SpeedLimitManager() { - AvailableSpeedLimits = new List(); - AvailableSpeedLimits.Add(10); - AvailableSpeedLimits.Add(20); - AvailableSpeedLimits.Add(30); - AvailableSpeedLimits.Add(40); - AvailableSpeedLimits.Add(50); - AvailableSpeedLimits.Add(60); - AvailableSpeedLimits.Add(70); - AvailableSpeedLimits.Add(80); - AvailableSpeedLimits.Add(90); - AvailableSpeedLimits.Add(100); - AvailableSpeedLimits.Add(110); - AvailableSpeedLimits.Add(120); - AvailableSpeedLimits.Add(130); - AvailableSpeedLimits.Add(0); - vanillaLaneSpeedLimitsByNetInfoName = new Dictionary(); - CustomLaneSpeedLimitIndexByNetInfoName = new Dictionary(); + CustomLaneSpeedLimitByNetInfoName = new Dictionary(); customizableNetInfos = new List(); childNetInfoNamesByCustomizableNetInfoName = new Dictionary>(); NetInfoByName = new Dictionary(); } + protected override void InternalPrintDebugInfo() { + base.InternalPrintDebugInfo(); + Log._Debug($"- Not implemented -"); + // TODO implement + } + /// /// Determines if custom speed limits may be assigned to the given segment. /// @@ -86,42 +71,47 @@ public bool MayHaveCustomSpeedLimits(NetInfo.Lane laneInfo) { /// /// /// - /// - public ushort GetCustomSpeedLimit(ushort segmentId, NetInfo.Direction finalDir) { + /// Mean speed limit, average for custom and default lane speeds + public float GetCustomSpeedLimit(ushort segmentId, NetInfo.Direction finalDir) { // calculate the currently set mean speed limit if (segmentId == 0 || (Singleton.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) { - return 0; + return 0.0f; } var segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; - uint curLaneId = Singleton.instance.m_segments.m_buffer[segmentId].m_lanes; - int laneIndex = 0; - float meanSpeedLimit = 0f; + var curLaneId = Singleton.instance.m_segments.m_buffer[segmentId].m_lanes; + var laneIndex = 0; + var meanSpeedLimit = 0f; uint validLanes = 0; while (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) { - NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex]; - NetInfo.Direction d = laneInfo.m_finalDirection; - if (d != finalDir) + var laneInfo = segmentInfo.m_lanes[laneIndex]; + var d = laneInfo.m_finalDirection; + if (d != finalDir) { goto nextIter; - if (!MayHaveCustomSpeedLimits(laneInfo)) + } + if (!MayHaveCustomSpeedLimits(laneInfo)) { goto nextIter; + } - ushort? setSpeedLimit = Flags.getLaneSpeedLimit(curLaneId); - if (setSpeedLimit != null) - meanSpeedLimit += ToGameSpeedLimit((ushort)setSpeedLimit); // custom speed limit - else + var setSpeedLimit = Flags.getLaneSpeedLimit(curLaneId); + if (setSpeedLimit != null) { + meanSpeedLimit += ToGameSpeedLimit(setSpeedLimit.Value); // custom speed limit + } else { meanSpeedLimit += laneInfo.m_speedLimit; // game default + } + ++validLanes; - nextIter: + nextIter: curLaneId = Singleton.instance.m_lanes.m_buffer[curLaneId].m_nextLane; laneIndex++; } - if (validLanes > 0) - meanSpeedLimit /= (float)validLanes; - ushort ret = LaneToCustomSpeedLimit(meanSpeedLimit); - return ret; + if (validLanes > 0) { + meanSpeedLimit /= validLanes; + } + + return meanSpeedLimit; } /// @@ -131,25 +121,26 @@ public ushort GetCustomSpeedLimit(ushort segmentId, NetInfo.Direction finalDir) /// /// /// - public ushort GetAverageDefaultCustomSpeedLimit(NetInfo segmentInfo, NetInfo.Direction? finalDir=null) { - float meanSpeedLimit = 0f; + public float GetAverageDefaultCustomSpeedLimit(NetInfo segmentInfo, NetInfo.Direction? finalDir=null) { + var meanSpeedLimit = 0f; uint validLanes = 0; - for (int i = 0; i < segmentInfo.m_lanes.Length; ++i) { - NetInfo.Lane laneInfo = segmentInfo.m_lanes[i]; - NetInfo.Direction d = laneInfo.m_finalDirection; - if (finalDir != null && d != finalDir) + foreach (var laneInfo in segmentInfo.m_lanes) { + var d = laneInfo.m_finalDirection; + if (finalDir != null && d != finalDir) { continue; - if (!MayHaveCustomSpeedLimits(laneInfo)) + } + if (!MayHaveCustomSpeedLimits(laneInfo)) { continue; - + } + meanSpeedLimit += laneInfo.m_speedLimit; ++validLanes; } - if (validLanes > 0) - meanSpeedLimit /= (float)validLanes; - ushort ret = LaneToCustomSpeedLimit(meanSpeedLimit); - return ret; + if (validLanes > 0) { + meanSpeedLimit /= validLanes; + } + return meanSpeedLimit; } /// @@ -188,11 +179,11 @@ public ushort GetAverageCustomSpeedLimit(ushort segmentId, ref NetSegment segmen /// /// /// - public ushort GetCustomSpeedLimit(uint laneId) { + public float GetCustomSpeedLimit(uint laneId) { // check custom speed limit - ushort? setSpeedLimit = Flags.getLaneSpeedLimit(laneId); + var setSpeedLimit = Flags.getLaneSpeedLimit(laneId); if (setSpeedLimit != null) { - return (ushort)setSpeedLimit; + return setSpeedLimit.Value; } // check default speed limit @@ -211,8 +202,7 @@ public ushort GetCustomSpeedLimit(uint laneId) { if (!MayHaveCustomSpeedLimits(laneInfo)) return 0; - ushort ret = LaneToCustomSpeedLimit(laneInfo.m_speedLimit); - return ret; + return laneInfo.m_speedLimit; } laneIndex++; @@ -238,9 +228,9 @@ public float GetLockFreeGameSpeedLimit(ushort segmentId, byte laneIndex, uint la } float speedLimit = 0; - ushort?[] fastArray = Flags.laneSpeedLimitArray[segmentId]; + float?[] fastArray = Flags.laneSpeedLimitArray[segmentId]; if (fastArray != null && fastArray.Length > laneIndex && fastArray[laneIndex] != null) { - speedLimit = ToGameSpeedLimit((ushort)fastArray[laneIndex]); + speedLimit = ToGameSpeedLimit((float)fastArray[laneIndex]); } else { speedLimit = laneInfo.m_speedLimit; } @@ -252,32 +242,10 @@ public float GetLockFreeGameSpeedLimit(ushort segmentId, byte laneIndex, uint la /// /// /// - public float ToGameSpeedLimit(ushort customSpeedLimit) { - if (customSpeedLimit == 0) - return MAX_SPEED; - return (float)customSpeedLimit / 50f; - } - - /// - /// Converts a lane speed limit to a custom speed limit. - /// - /// - /// - public ushort LaneToCustomSpeedLimit(float laneSpeedLimit, bool roundToSignLimits=true) { - laneSpeedLimit /= 2f; // 1 == 100 km/h - - if (! roundToSignLimits) { - return (ushort)Mathf.Round(laneSpeedLimit * 100f); - } - - // translate the floating point speed limit into our discrete version - ushort speedLimit = 0; - if (laneSpeedLimit < 0.15f) - speedLimit = 10; - else if (laneSpeedLimit < 1.35f) - speedLimit = (ushort)((ushort)Mathf.Round(laneSpeedLimit * 10f) * 10u); - - return speedLimit; + public float ToGameSpeedLimit(float customSpeedLimit) { + return SpeedLimit.IsZero(customSpeedLimit) + ? MAX_SPEED + : customSpeedLimit; } /// @@ -298,19 +266,20 @@ public void FixCurrentSpeedLimits(NetInfo info) { #endif return; } - - if (!customizableNetInfos.Contains(info)) + if (!customizableNetInfos.Contains(info)) { return; - + } for (uint laneId = 1; laneId < NetManager.MAX_LANE_COUNT; ++laneId) { - if (!Services.NetService.IsLaneValid(laneId)) + if (!Services.NetService.IsLaneValid(laneId)) { continue; - - ushort segmentId = Singleton.instance.m_lanes.m_buffer[laneId].m_segment; - NetInfo laneInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; - if (laneInfo.name != info.name && (!childNetInfoNamesByCustomizableNetInfoName.ContainsKey(info.name) || !childNetInfoNamesByCustomizableNetInfoName[info.name].Contains(laneInfo.name))) + } + var segmentId = Singleton.instance.m_lanes.m_buffer[laneId].m_segment; + var laneInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; + if (laneInfo.name != info.name + && (!childNetInfoNamesByCustomizableNetInfoName.ContainsKey(info.name) + || !childNetInfoNamesByCustomizableNetInfoName[info.name].Contains(laneInfo.name))) { continue; - + } Flags.setLaneSpeedLimit(laneId, GetCustomSpeedLimit(laneId)); } } @@ -355,7 +324,7 @@ public void ClearCurrentSpeedLimits(NetInfo info) { /// the NetInfo of which the game default speed limit should be determined /// if true, custom speed limit are rounded to speed limits available as speed limit sign /// - public ushort GetVanillaNetInfoSpeedLimit(NetInfo info, bool roundToSignLimits = true) { + public float GetVanillaNetInfoSpeedLimit(NetInfo info, bool roundToSignLimits = true) { if (info == null) { #if DEBUG Log.Warning($"SpeedLimitManager.GetVanillaNetInfoSpeedLimit: info is null!"); @@ -377,10 +346,6 @@ public ushort GetVanillaNetInfoSpeedLimit(NetInfo info, bool roundToSignLimits = return 0; } - /*if (! (info.m_netAI is RoadBaseAI)) - return 0;*/ - - //string infoName = ((RoadBaseAI)info.m_netAI).m_info.name; string infoName = info.name; float[] vanillaSpeedLimits; if (!vanillaLaneSpeedLimitsByNetInfoName.TryGetValue(infoName, out vanillaSpeedLimits)) { @@ -394,18 +359,15 @@ public ushort GetVanillaNetInfoSpeedLimit(NetInfo info, bool roundToSignLimits = } } - if (maxSpeedLimit == null) - return 0; - - return LaneToCustomSpeedLimit((float)maxSpeedLimit, roundToSignLimits); + return maxSpeedLimit ?? 0; } /// /// Determines the custom speed limit of the given NetInfo. /// /// the NetInfo of which the custom speed limit should be determined - /// - public int GetCustomNetInfoSpeedLimitIndex(NetInfo info) { + /// -1 if no custom speed limit was set + public float GetCustomNetInfoSpeedLimit(NetInfo info) { if (info == null) { #if DEBUG Log.Warning($"SpeedLimitManager.SetCustomNetInfoSpeedLimitIndex: info is null!"); @@ -420,25 +382,19 @@ public int GetCustomNetInfoSpeedLimitIndex(NetInfo info) { return -1; } - /*if (!(info.m_netAI is RoadBaseAI)) - return -1;*/ - - //string infoName = ((RoadBaseAI)info.m_netAI).m_info.name; - string infoName = info.name; - int speedLimitIndex; - if (!CustomLaneSpeedLimitIndexByNetInfoName.TryGetValue(infoName, out speedLimitIndex)) { - return AvailableSpeedLimits.IndexOf(GetVanillaNetInfoSpeedLimit(info, true)); - } - - return speedLimitIndex; + var infoName = info.name; + float speedLimit; + return !CustomLaneSpeedLimitByNetInfoName.TryGetValue(infoName, out speedLimit) + ? GetVanillaNetInfoSpeedLimit(info, true) + : speedLimit; } /// /// Sets the custom speed limit of the given NetInfo. /// /// the NetInfo for which the custom speed limit should be set - /// - public void SetCustomNetInfoSpeedLimitIndex(NetInfo info, int customSpeedLimitIndex) { + /// The speed value to set in game speed units + public void SetCustomNetInfoSpeedLimit(NetInfo info, float customSpeedLimit) { if (info == null) { #if DEBUG Log.Warning($"SetCustomNetInfoSpeedLimitIndex: info is null!"); @@ -453,15 +409,10 @@ public void SetCustomNetInfoSpeedLimitIndex(NetInfo info, int customSpeedLimitIn return; } - /*if (!(info.m_netAI is RoadBaseAI)) - return;*/ - - /*RoadBaseAI baseAI = (RoadBaseAI)info.m_netAI; - string infoName = baseAI.m_info.name;*/ string infoName = info.name; - CustomLaneSpeedLimitIndexByNetInfoName[infoName] = customSpeedLimitIndex; + CustomLaneSpeedLimitByNetInfoName[infoName] = customSpeedLimit; - float gameSpeedLimit = ToGameSpeedLimit(AvailableSpeedLimits[customSpeedLimitIndex]); + float gameSpeedLimit = ToGameSpeedLimit(customSpeedLimit); // save speed limit in all NetInfos Log._Debug($"Updating parent NetInfo {infoName}: Setting speed limit to {gameSpeedLimit}"); @@ -469,11 +420,11 @@ public void SetCustomNetInfoSpeedLimitIndex(NetInfo info, int customSpeedLimitIn List childNetInfoNames; if (childNetInfoNamesByCustomizableNetInfoName.TryGetValue(infoName, out childNetInfoNames)) { - foreach (string childNetInfoName in childNetInfoNames) { + foreach (var childNetInfoName in childNetInfoNames) { NetInfo childNetInfo; if (NetInfoByName.TryGetValue(childNetInfoName, out childNetInfo)) { Log._Debug($"Updating child NetInfo {childNetInfoName}: Setting speed limit to {gameSpeedLimit}"); - CustomLaneSpeedLimitIndexByNetInfoName[childNetInfoName] = customSpeedLimitIndex; + CustomLaneSpeedLimitByNetInfoName[childNetInfoName] = customSpeedLimit; UpdateNetInfoGameSpeedLimit(childNetInfo, gameSpeedLimit); } } @@ -512,29 +463,18 @@ private void UpdateNetInfoGameSpeedLimit(NetInfo info, float gameSpeedLimit) { } } - /// - /// Converts a vehicle's velocity to a custom speed. - /// - /// - /// - public ushort VehicleToCustomSpeed(float vehicleSpeed) { - return LaneToCustomSpeedLimit(vehicleSpeed / 8f, false); - } - - /// - /// Sets the speed limit of a given lane. - /// + /// Sets the speed limit of a given lane. /// /// /// /// - /// + /// Game speed units, 0=unlimited /// - public bool SetSpeedLimit(ushort segmentId, uint laneIndex, NetInfo.Lane laneInfo, uint laneId, ushort speedLimit) { + public bool SetSpeedLimit(ushort segmentId, uint laneIndex, NetInfo.Lane laneInfo, uint laneId, float speedLimit) { if (!MayHaveCustomSpeedLimits(laneInfo)) { return false; } - if (!AvailableSpeedLimits.Contains(speedLimit)) { + if (!SpeedLimit.IsValidRange(speedLimit)) { return false; } if (!Services.NetService.IsLaneValid(laneId)) { @@ -552,15 +492,15 @@ public bool SetSpeedLimit(ushort segmentId, uint laneIndex, NetInfo.Lane laneInf /// /// /// - public bool SetSpeedLimit(ushort segmentId, NetInfo.Direction finalDir, ushort speedLimit) { + public bool SetSpeedLimit(ushort segmentId, NetInfo.Direction finalDir, float speedLimit) { if (!MayHaveCustomSpeedLimits(segmentId, ref Singleton.instance.m_segments.m_buffer[segmentId])) { return false; } - if (!AvailableSpeedLimits.Contains(speedLimit)) { + if (!SpeedLimit.IsValidRange(speedLimit)) { return false; } - NetInfo segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; + var segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; if (segmentInfo == null) { #if DEBUG @@ -581,13 +521,15 @@ public bool SetSpeedLimit(ushort segmentId, NetInfo.Direction finalDir, ushort s while (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) { NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex]; NetInfo.Direction d = laneInfo.m_finalDirection; - if (d != finalDir) + if (d != finalDir) { goto nextIter; - if (!MayHaveCustomSpeedLimits(laneInfo)) + } + if (!MayHaveCustomSpeedLimits(laneInfo)) { goto nextIter; - + } #if DEBUG - Log._Debug($"SpeedLimitManager: Setting speed limit of lane {curLaneId} to {speedLimit}"); + Log._Debug($"SpeedLimitManager: Setting speed limit of lane {curLaneId} " + + $"to {speedLimit * SpeedLimit.SPEED_TO_KMPH}"); #endif Flags.setLaneSpeedLimit(curLaneId, speedLimit); @@ -613,7 +555,7 @@ public override void OnBeforeLoadData() { vanillaLaneSpeedLimitsByNetInfoName.Clear(); customizableNetInfos.Clear(); - CustomLaneSpeedLimitIndexByNetInfoName.Clear(); + CustomLaneSpeedLimitByNetInfoName.Clear(); childNetInfoNamesByCustomizableNetInfoName.Clear(); NetInfoByName.Clear(); @@ -756,8 +698,8 @@ protected override void HandleInvalidSegment(SegmentGeometry geometry) { uint curLaneId = Singleton.instance.m_segments.m_buffer[geometry.SegmentId].m_lanes; int laneIndex = 0; while (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) { - NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex]; - ushort? setSpeedLimit = Flags.getLaneSpeedLimit(curLaneId); + // NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex]; + // float? setSpeedLimit = Flags.getLaneSpeedLimit(curLaneId); Flags.setLaneSpeedLimit(curLaneId, null); @@ -776,20 +718,28 @@ public bool LoadData(List data) { foreach (Configuration.LaneSpeedLimit laneSpeedLimit in data) { try { if (!Services.NetService.IsLaneValid(laneSpeedLimit.laneId)) { - Log._Debug($"SpeedLimitManager.LoadData: Skipping lane {laneSpeedLimit.laneId}: Lane is invalid"); + Log._Debug($"SpeedLimitManager.LoadData: Skipping lane {laneSpeedLimit.laneId}: " + + $"Lane is invalid"); continue; } ushort segmentId = Singleton.instance.m_lanes.m_buffer[laneSpeedLimit.laneId].m_segment; NetInfo info = Singleton.instance.m_segments.m_buffer[segmentId].Info; - int customSpeedLimitIndex = GetCustomNetInfoSpeedLimitIndex(info); - Log._Debug($"SpeedLimitManager.LoadData: Handling lane {laneSpeedLimit.laneId}: Custom speed limit index of segment {segmentId} info ({info}, name={info?.name}, lanes={info?.m_lanes} is {customSpeedLimitIndex}"); - if (customSpeedLimitIndex < 0 || AvailableSpeedLimits[customSpeedLimitIndex] != laneSpeedLimit.speedLimit) { + var customSpeedLimit = GetCustomNetInfoSpeedLimit(info); + Log._Debug($"SpeedLimitManager.LoadData: Handling lane {laneSpeedLimit.laneId}: " + + $"Custom speed limit of segment {segmentId} info ({info}, name={info?.name}, " + + $"lanes={info?.m_lanes} is {customSpeedLimit}"); + + if (SpeedLimit.IsValidRange(customSpeedLimit)) { // lane speed limit differs from default speed limit - Log._Debug($"SpeedLimitManager.LoadData: Loading lane speed limit: lane {laneSpeedLimit.laneId} = {laneSpeedLimit.speedLimit}"); - Flags.setLaneSpeedLimit(laneSpeedLimit.laneId, laneSpeedLimit.speedLimit); + Log._Debug($"SpeedLimitManager.LoadData: Loading lane speed limit: " + + $"lane {laneSpeedLimit.laneId} = {laneSpeedLimit.speedLimit} km/h"); + var kmph = laneSpeedLimit.speedLimit / SpeedLimit.SPEED_TO_KMPH; // convert to game units + Flags.setLaneSpeedLimit(laneSpeedLimit.laneId, kmph); } else { - Log._Debug($"SpeedLimitManager.LoadData: Skipping lane speed limit of lane {laneSpeedLimit.laneId} ({laneSpeedLimit.speedLimit})"); + Log._Debug($"SpeedLimitManager.LoadData: " + + $"Skipping lane speed limit of lane {laneSpeedLimit.laneId} " + + $"({laneSpeedLimit.speedLimit} km/h)"); } } catch (Exception e) { // ignore, as it's probably corrupt save data. it'll be culled on next save @@ -801,14 +751,15 @@ public bool LoadData(List data) { } List ICustomDataManager>.SaveData(ref bool success) { - List ret = new List(); - foreach (KeyValuePair e in Flags.getAllLaneSpeedLimits()) { + var ret = new List(); + foreach (var e in Flags.getAllLaneSpeedLimits()) { try { - Configuration.LaneSpeedLimit laneSpeedLimit = new Configuration.LaneSpeedLimit(e.Key, e.Value); - Log._Debug($"Saving speed limit of lane {laneSpeedLimit.laneId}: {laneSpeedLimit.speedLimit}"); + var laneSpeedLimit = new Configuration.LaneSpeedLimit(e.Key, e.Value); + Log._Debug($"Saving speed limit of lane {laneSpeedLimit.laneId}: " + + $"{laneSpeedLimit.speedLimit*SpeedLimit.SPEED_TO_KMPH} km/h"); ret.Add(laneSpeedLimit); } catch (Exception ex) { - Log.Error($"Exception occurred while saving lane speed limit @ {e.Key}: {ex.ToString()}"); + Log.Error($"Exception occurred while saving lane speed limit @ {e.Key}: {ex}"); success = false; } } @@ -816,28 +767,25 @@ public bool LoadData(List data) { } public bool LoadData(Dictionary data) { - bool success = true; Log.Info($"Loading custom default speed limit data. {data.Count} elements"); - foreach (KeyValuePair e in data) { - NetInfo netInfo = null; - if (!NetInfoByName.TryGetValue(e.Key, out netInfo)) + foreach (var e in data) { + NetInfo netInfo; + if (!NetInfoByName.TryGetValue(e.Key, out netInfo)) { continue; + } - ushort customSpeedLimit = LaneToCustomSpeedLimit(e.Value, true); - int customSpeedLimitIndex = AvailableSpeedLimits.IndexOf(customSpeedLimit); - if (customSpeedLimitIndex >= 0) { - SetCustomNetInfoSpeedLimitIndex(netInfo, customSpeedLimitIndex); + if (e.Value >= 0f) { + SetCustomNetInfoSpeedLimit(netInfo, e.Value); } } - return success; + return true; // true = success } Dictionary ICustomDataManager>.SaveData(ref bool success) { - Dictionary ret = new Dictionary(); - foreach (KeyValuePair e in CustomLaneSpeedLimitIndexByNetInfoName) { + var ret = new Dictionary(); + foreach (var e in CustomLaneSpeedLimitByNetInfoName) { try { - ushort customSpeedLimit = AvailableSpeedLimits[e.Value]; - float gameSpeedLimit = ToGameSpeedLimit(customSpeedLimit); + float gameSpeedLimit = ToGameSpeedLimit(e.Value); ret.Add(e.Key, gameSpeedLimit); } catch (Exception ex) { diff --git a/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs b/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs index 7d13a3803..7e2181767 100644 --- a/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs +++ b/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs @@ -1387,14 +1387,18 @@ public int FindBestLane(ushort vehicleId, ref Vehicle vehicleData, ref VehicleSt if (bestStaySpeedDiff < 0 && bestOptSpeedDiff > bestStaySpeedDiff) { // found a lane change that improves vehicle speed //float improvement = 100f * ((bestOptSpeedDiff - bestStaySpeedDiff) / ((bestStayMeanSpeed + bestOptMeanSpeed) / 2f)); - ushort optImprovementInKmH = SpeedLimitManager.Instance.LaneToCustomSpeedLimit(bestOptSpeedDiff - bestStaySpeedDiff, false); - float speedDiff = Mathf.Abs(bestOptMeanSpeed - vehicleCurSpeed); + var speedDiff = Mathf.Abs(bestOptMeanSpeed - vehicleCurSpeed); + var optImprovementSpeed = SpeedLimit.ToKmphRounded(bestOptSpeedDiff - bestStaySpeedDiff); #if DEBUG if (debug) { - Log._Debug($"VehicleBehaviorManager.FindBestLane({vehicleId}): a lane change for speed improvement is possible. optImprovementInKmH={optImprovementInKmH} km/h speedDiff={speedDiff} (bestOptMeanSpeed={bestOptMeanSpeed}, vehicleCurVelocity={vehicleCurSpeed}, foundSafeLaneChange={foundSafeLaneChange})"); + Log._Debug($"VehicleBehaviorManager.FindBestLane({vehicleId}): " + + $"a lane change for speed improvement is possible. " + + $"optImprovementInKmH={optImprovementSpeed} km/h speedDiff={speedDiff} " + + $"(bestOptMeanSpeed={bestOptMeanSpeed}, vehicleCurVelocity={vehicleCurSpeed}, " + + $"foundSafeLaneChange={foundSafeLaneChange})"); } #endif - if (optImprovementInKmH >= vehicleState.minSafeSpeedImprovement && + if (optImprovementSpeed >= vehicleState.minSafeSpeedImprovement && (foundSafeLaneChange || (speedDiff <= vehicleState.maxUnsafeSpeedDiff)) ) { // speed improvement is significant diff --git a/TLM/TLM/Resources/110.png b/TLM/TLM/Resources/110.png deleted file mode 100644 index fdf3a16c7..000000000 Binary files a/TLM/TLM/Resources/110.png and /dev/null differ diff --git a/TLM/TLM/Resources/0.png b/TLM/TLM/Resources/SpeedLimits/Kmh/0.png similarity index 100% rename from TLM/TLM/Resources/0.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/0.png diff --git a/TLM/TLM/Resources/10.png b/TLM/TLM/Resources/SpeedLimits/Kmh/10.png similarity index 100% rename from TLM/TLM/Resources/10.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/10.png diff --git a/TLM/TLM/Resources/100.png b/TLM/TLM/Resources/SpeedLimits/Kmh/100.png similarity index 100% rename from TLM/TLM/Resources/100.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/100.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/105.png b/TLM/TLM/Resources/SpeedLimits/Kmh/105.png new file mode 100644 index 000000000..fb0ca66fa Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/105.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/110.png b/TLM/TLM/Resources/SpeedLimits/Kmh/110.png new file mode 100644 index 000000000..28f2b0f93 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/110.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/115.png b/TLM/TLM/Resources/SpeedLimits/Kmh/115.png new file mode 100644 index 000000000..f61ec322f Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/115.png differ diff --git a/TLM/TLM/Resources/120.png b/TLM/TLM/Resources/SpeedLimits/Kmh/120.png similarity index 100% rename from TLM/TLM/Resources/120.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/120.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/125.png b/TLM/TLM/Resources/SpeedLimits/Kmh/125.png new file mode 100644 index 000000000..004225955 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/125.png differ diff --git a/TLM/TLM/Resources/130.png b/TLM/TLM/Resources/SpeedLimits/Kmh/130.png similarity index 100% rename from TLM/TLM/Resources/130.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/130.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/135.png b/TLM/TLM/Resources/SpeedLimits/Kmh/135.png new file mode 100644 index 000000000..253ad482f Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/135.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/140.png b/TLM/TLM/Resources/SpeedLimits/Kmh/140.png new file mode 100644 index 000000000..5cf5aabb7 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/140.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/15.png b/TLM/TLM/Resources/SpeedLimits/Kmh/15.png new file mode 100644 index 000000000..120fed7b7 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/15.png differ diff --git a/TLM/TLM/Resources/20.png b/TLM/TLM/Resources/SpeedLimits/Kmh/20.png similarity index 100% rename from TLM/TLM/Resources/20.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/20.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/25.png b/TLM/TLM/Resources/SpeedLimits/Kmh/25.png new file mode 100644 index 000000000..108f2ad9e Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/25.png differ diff --git a/TLM/TLM/Resources/30.png b/TLM/TLM/Resources/SpeedLimits/Kmh/30.png similarity index 100% rename from TLM/TLM/Resources/30.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/30.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/35.png b/TLM/TLM/Resources/SpeedLimits/Kmh/35.png new file mode 100644 index 000000000..54b56e4f6 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/35.png differ diff --git a/TLM/TLM/Resources/40.png b/TLM/TLM/Resources/SpeedLimits/Kmh/40.png similarity index 100% rename from TLM/TLM/Resources/40.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/40.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/45.png b/TLM/TLM/Resources/SpeedLimits/Kmh/45.png new file mode 100644 index 000000000..df1406954 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/45.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/5.png b/TLM/TLM/Resources/SpeedLimits/Kmh/5.png new file mode 100644 index 000000000..c6ae24a07 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/5.png differ diff --git a/TLM/TLM/Resources/50.png b/TLM/TLM/Resources/SpeedLimits/Kmh/50.png similarity index 100% rename from TLM/TLM/Resources/50.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/50.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/55.png b/TLM/TLM/Resources/SpeedLimits/Kmh/55.png new file mode 100644 index 000000000..656194e74 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/55.png differ diff --git a/TLM/TLM/Resources/60.png b/TLM/TLM/Resources/SpeedLimits/Kmh/60.png similarity index 100% rename from TLM/TLM/Resources/60.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/60.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/65.png b/TLM/TLM/Resources/SpeedLimits/Kmh/65.png new file mode 100644 index 000000000..99e967802 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/65.png differ diff --git a/TLM/TLM/Resources/70.png b/TLM/TLM/Resources/SpeedLimits/Kmh/70.png similarity index 100% rename from TLM/TLM/Resources/70.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/70.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/75.png b/TLM/TLM/Resources/SpeedLimits/Kmh/75.png new file mode 100644 index 000000000..d85c6fff1 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/75.png differ diff --git a/TLM/TLM/Resources/80.png b/TLM/TLM/Resources/SpeedLimits/Kmh/80.png similarity index 100% rename from TLM/TLM/Resources/80.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/80.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/85.png b/TLM/TLM/Resources/SpeedLimits/Kmh/85.png new file mode 100644 index 000000000..63615a3ea Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/85.png differ diff --git a/TLM/TLM/Resources/90.png b/TLM/TLM/Resources/SpeedLimits/Kmh/90.png similarity index 100% rename from TLM/TLM/Resources/90.png rename to TLM/TLM/Resources/SpeedLimits/Kmh/90.png diff --git a/TLM/TLM/Resources/SpeedLimits/Kmh/95.png b/TLM/TLM/Resources/SpeedLimits/Kmh/95.png new file mode 100644 index 000000000..2c4022279 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Kmh/95.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/0.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/0.png new file mode 100644 index 000000000..f90cdb1c0 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/0.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/10.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/10.png new file mode 100644 index 000000000..a2f9c445a Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/10.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/15.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/15.png new file mode 100644 index 000000000..f656eb7fa Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/15.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/20.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/20.png new file mode 100644 index 000000000..52170aa4b Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/20.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/25.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/25.png new file mode 100644 index 000000000..d5d083205 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/25.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/30.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/30.png new file mode 100644 index 000000000..384c47b95 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/30.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/35.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/35.png new file mode 100644 index 000000000..4080dddcd Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/35.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/40.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/40.png new file mode 100644 index 000000000..a094b8230 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/40.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/45.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/45.png new file mode 100644 index 000000000..a4e07c695 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/45.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/5.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/5.png new file mode 100644 index 000000000..0dba1538d Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/5.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/50.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/50.png new file mode 100644 index 000000000..28401cc0a Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/50.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/55.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/55.png new file mode 100644 index 000000000..ed446b3b6 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/55.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/60.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/60.png new file mode 100644 index 000000000..c90bfc758 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/60.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/65.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/65.png new file mode 100644 index 000000000..f7d208139 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/65.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/70.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/70.png new file mode 100644 index 000000000..4fa829f16 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/70.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/75.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/75.png new file mode 100644 index 000000000..fc70b5c34 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/75.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/80.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/80.png new file mode 100644 index 000000000..bdb8eabcf Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/80.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/85.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/85.png new file mode 100644 index 000000000..04957300b Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/85.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_UK/90.png b/TLM/TLM/Resources/SpeedLimits/Mph_UK/90.png new file mode 100644 index 000000000..322368f8d Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_UK/90.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/0.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/0.png new file mode 100644 index 000000000..28f5a16e0 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/0.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/10.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/10.png new file mode 100644 index 000000000..99328d125 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/10.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/15.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/15.png new file mode 100644 index 000000000..f1c8d1320 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/15.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/20.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/20.png new file mode 100644 index 000000000..b7c78cced Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/20.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/25.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/25.png new file mode 100644 index 000000000..d4f601ff1 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/25.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/30.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/30.png new file mode 100644 index 000000000..fe2bc3c28 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/30.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/35.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/35.png new file mode 100644 index 000000000..1792175b8 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/35.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/40.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/40.png new file mode 100644 index 000000000..6402f08c6 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/40.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/45.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/45.png new file mode 100644 index 000000000..d86f3404f Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/45.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/5.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/5.png new file mode 100644 index 000000000..a1906baa3 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/5.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/50.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/50.png new file mode 100644 index 000000000..c5044d0c4 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/50.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/55.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/55.png new file mode 100644 index 000000000..6f07df67e Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/55.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/60.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/60.png new file mode 100644 index 000000000..59a7bfbcf Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/60.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/65.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/65.png new file mode 100644 index 000000000..2c6d5e3a0 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/65.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/70.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/70.png new file mode 100644 index 000000000..1d7416e15 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/70.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/75.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/75.png new file mode 100644 index 000000000..8d081e91a Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/75.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/80.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/80.png new file mode 100644 index 000000000..008d1cc24 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/80.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/85.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/85.png new file mode 100644 index 000000000..54cfdff62 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/85.png differ diff --git a/TLM/TLM/Resources/SpeedLimits/Mph_US/90.png b/TLM/TLM/Resources/SpeedLimits/Mph_US/90.png new file mode 100644 index 000000000..b842d07f8 Binary files /dev/null and b/TLM/TLM/Resources/SpeedLimits/Mph_US/90.png differ diff --git a/TLM/TLM/Resources/lang.txt b/TLM/TLM/Resources/lang.txt index 68534b7ca..29139fd51 100644 --- a/TLM/TLM/Resources/lang.txt +++ b/TLM/TLM/Resources/lang.txt @@ -231,4 +231,12 @@ Also_apply_to_left/right_turns_between_one-way_streets Also apply to left & righ Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict +Speed_limit_unlimited No limit +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_de.txt b/TLM/TLM/Resources/lang_de.txt index 63b2c749a..ffb7132ef 100644 --- a/TLM/TLM/Resources/lang_de.txt +++ b/TLM/TLM/Resources/lang_de.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Gilt auch für Links- & R Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict Zeige Fehlermeldung, wenn eine Mod-Inkompatibilität erkannt wurde \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Zeige Fehlermeldung, wenn eine Mod-Inkompatibilität erkannt wurde +Display_speed_limits_mph Geschwindigkeitsbegrenzungen anzeigen als MPH statt km/h +Miles_per_hour Meilen pro Stunde +Kilometers_per_hour Kilometer pro Stunde +Road_signs_theme_mph Thema für MPH Verkehrszeichen +theme_Square_US US-Verkehrszeichen +theme_Round_UK Britische Verkehrszeichen +theme_Round_German Deutsche Verkehrszeichen diff --git a/TLM/TLM/Resources/lang_es.txt b/TLM/TLM/Resources/lang_es.txt index 21457ec8a..737e137a7 100644 --- a/TLM/TLM/Resources/lang_es.txt +++ b/TLM/TLM/Resources/lang_es.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Also apply to left & righ Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_fr.txt b/TLM/TLM/Resources/lang_fr.txt index b40634d4b..edc79a174 100644 --- a/TLM/TLM/Resources/lang_fr.txt +++ b/TLM/TLM/Resources/lang_fr.txt @@ -232,3 +232,10 @@ Scan_for_known_incompatible_mods_on_startup Rechercher les mods incompatibles au Ignore_disabled_mods Ignorer les mods désactivés Traffic_Manager_detected_incompatible_mods Le gestionnaire de trafic a détecté des mods incompatibles Notify_me_if_there_is_an_unexpected_mod_conflict Afficher un message d'erreur si un mod incompatible est détecté +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_it.txt b/TLM/TLM/Resources/lang_it.txt index a4f86a030..54a03fdbe 100644 --- a/TLM/TLM/Resources/lang_it.txt +++ b/TLM/TLM/Resources/lang_it.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Also apply to left & righ Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_ja.txt b/TLM/TLM/Resources/lang_ja.txt index fdbd694c5..e7322ffae 100644 --- a/TLM/TLM/Resources/lang_ja.txt +++ b/TLM/TLM/Resources/lang_ja.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets 一方通行路の左右 Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict modの非互換性が検出された場合にエラーメッセージを表示す \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict modの非互換性が検出された場合にエラーメッセージを表示す +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_ko.txt b/TLM/TLM/Resources/lang_ko.txt index 1bc09409f..9b25fab8b 100644 --- a/TLM/TLM/Resources/lang_ko.txt +++ b/TLM/TLM/Resources/lang_ko.txt @@ -232,3 +232,10 @@ Scan_for_known_incompatible_mods_on_startup 게임 실행시 비호환되는 모 Ignore_disabled_mods 비활성화된 모드 무시하기 Traffic_Manager_detected_incompatible_mods Traffic Manager와 비호환되는 모드 감지 Notify_me_if_there_is_an_unexpected_mod_conflict 모드와 비 호환되는 모드 발견 시 에러 보여주기 +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_nl.txt b/TLM/TLM/Resources/lang_nl.txt index 5f748af47..be1900475 100644 --- a/TLM/TLM/Resources/lang_nl.txt +++ b/TLM/TLM/Resources/lang_nl.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Also apply to left & righ Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_pl.txt b/TLM/TLM/Resources/lang_pl.txt index 9200d8b58..786cc5101 100644 --- a/TLM/TLM/Resources/lang_pl.txt +++ b/TLM/TLM/Resources/lang_pl.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Uwzględnia również skr Scan_for_known_incompatible_mods_on_startup Uruchamiaj sprawdzanie przy starcie gry Ignore_disabled_mods Ignoruj wyłączone mody Traffic_Manager_detected_incompatible_mods Traffic Manager wykrył niekompatybilne mody -Notify_me_if_there_is_an_unexpected_mod_conflict Powiadom mnie w razie nieoczekiwanej niezgodność modów \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Powiadom mnie w razie nieoczekiwanej niezgodność modów +Display_speed_limits_mph Wyświetlaj limity prędkości w mph zamiast km/h +Miles_per_hour Mile na godzinę +Kilometers_per_hour Kilometry na godzinę +Road_signs_theme_mph Motyw dla znaków MPH +theme_Square_US Stany zjednoczone +theme_Round_UK Wielka Brytania +theme_Round_German Niemcy diff --git a/TLM/TLM/Resources/lang_pt.txt b/TLM/TLM/Resources/lang_pt.txt index 1993850e4..af559bbef 100644 --- a/TLM/TLM/Resources/lang_pt.txt +++ b/TLM/TLM/Resources/lang_pt.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Also apply to left & righ Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_ru.txt b/TLM/TLM/Resources/lang_ru.txt index 4193e0a8c..c246ec102 100644 --- a/TLM/TLM/Resources/lang_ru.txt +++ b/TLM/TLM/Resources/lang_ru.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Разрешить нап Scan_for_known_incompatible_mods_on_startup Сканирование известных несовместимых модов при запуске Ignore_disabled_mods Игнорировать отключённые моды Traffic_Manager_detected_incompatible_mods Traffic Manager обнаружил несовместимые моды -Notify_me_if_there_is_an_unexpected_mod_conflict Показывать сообщение об ошибке при несовместимости модов \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Показывать сообщение об ошибке при несовместимости модов +Display_speed_limits_mph Показывать скорости в милях в час вместо км/ч +Miles_per_hour Мили в час +Kilometers_per_hour Километры в час +Road_signs_theme_mph Стиль знаков, когда показываются мили в час +theme_Square_US Американские знаки +theme_Round_UK Британские знаки +theme_Round_German Немецкие знаки diff --git a/TLM/TLM/Resources/lang_zh-tw.txt b/TLM/TLM/Resources/lang_zh-tw.txt index ff86907fa..64f3e2f21 100644 --- a/TLM/TLM/Resources/lang_zh-tw.txt +++ b/TLM/TLM/Resources/lang_zh-tw.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets Also apply to left & righ Scan_for_known_incompatible_mods_on_startup Scan for known incompatible mods on startup Ignore_disabled_mods Ignore disabled mods Traffic_Manager_detected_incompatible_mods Traffic Manager detected incompatible mods -Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict Notify me if there is an unexpected mod conflict +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/Resources/lang_zh.txt b/TLM/TLM/Resources/lang_zh.txt index a65dc8ff7..27375c8cd 100644 --- a/TLM/TLM/Resources/lang_zh.txt +++ b/TLM/TLM/Resources/lang_zh.txt @@ -231,4 +231,11 @@ Also_apply_to_left/right_turns_between_one-way_streets 单行道左右转也适 Scan_for_known_incompatible_mods_on_startup 启动时检测是否存在已知不兼容MOD Ignore_disabled_mods 忽略未启用MOD Traffic_Manager_detected_incompatible_mods 检测到的不兼容MOD -Notify_me_if_there_is_an_unexpected_mod_conflict 检测到不兼容MOD时提示 \ No newline at end of file +Notify_me_if_there_is_an_unexpected_mod_conflict 检测到不兼容MOD时提示 +Display_speed_limits_mph Display speed limits as MPH instead of km/h +Miles_per_hour Miles per Hour +Kilometers_per_hour Kilometers per Hour +Road_signs_theme_mph Theme for MPH road signs +theme_Square_US US signs +theme_Round_UK British signs +theme_Round_German German signs diff --git a/TLM/TLM/State/ConfigData/Main.cs b/TLM/TLM/State/ConfigData/Main.cs index ca2ad9d3e..53180e047 100644 --- a/TLM/TLM/State/ConfigData/Main.cs +++ b/TLM/TLM/State/ConfigData/Main.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using TrafficManager.Traffic.Data; using TrafficManager.UI.MainMenu; namespace TrafficManager.State.ConfigData { @@ -50,20 +51,33 @@ public class Main { /// public bool ShowCompatibilityCheckErrorMessage = false; - /// - /// Shows warning dialog if any incompatible mods detected - /// - public bool ScanForKnownIncompatibleModsAtStartup = true; + /// + /// Shows warning dialog if any incompatible mods detected + /// + public bool ScanForKnownIncompatibleModsAtStartup = true; + + /// + /// Skip disabled mods while running incompatible mod detector + /// + public bool IgnoreDisabledMods = true; - /// - /// Skip disabled mods while running incompatible mod detector - /// - public bool IgnoreDisabledMods = true; + /// + /// Prefer Miles per hour instead of Kmph (affects speed limits display + /// but internally Kmph are still used). + /// + public bool DisplaySpeedLimitsMph = false; + + /// + /// Selected theme for road signs when MPH is active. + /// + public MphSignStyle MphRoadSignStyle = MphSignStyle.SquareUS; public void AddDisplayedTutorialMessage(string messageKey) { - HashSet newMessages = DisplayedTutorialMessages != null ? new HashSet(DisplayedTutorialMessages) : new HashSet(); + HashSet newMessages = DisplayedTutorialMessages != null + ? new HashSet(DisplayedTutorialMessages) + : new HashSet(); newMessages.Add(messageKey); DisplayedTutorialMessages = newMessages.ToArray(); } } -} +} \ No newline at end of file diff --git a/TLM/TLM/State/Configuration.cs b/TLM/TLM/State/Configuration.cs index a570bd888..fe32cd6b5 100644 --- a/TLM/TLM/State/Configuration.cs +++ b/TLM/TLM/State/Configuration.cs @@ -4,6 +4,7 @@ using System.Runtime.Serialization; using System.Xml.Serialization; using TrafficManager.State; +using TrafficManager.Traffic.Data; // TODO this class should be moved to TrafficManager.State, but the deserialization fails if we just do that now. Anyway, we should get rid of these crazy lists of arrays. So let's move the class when we decide rework the load/save system. namespace TrafficManager { @@ -14,9 +15,9 @@ public class LaneSpeedLimit { public uint laneId; public ushort speedLimit; - public LaneSpeedLimit(uint laneId, ushort speedLimit) { + public LaneSpeedLimit(uint laneId, float speedLimit) { this.laneId = laneId; - this.speedLimit = speedLimit; + this.speedLimit = (ushort)(speedLimit * SpeedLimit.SPEED_TO_KMPH); } } diff --git a/TLM/TLM/State/Flags.cs b/TLM/TLM/State/Flags.cs index 1bbd7529e..dfe83f7fb 100644 --- a/TLM/TLM/State/Flags.cs +++ b/TLM/TLM/State/Flags.cs @@ -52,9 +52,9 @@ public enum LaneArrowChangeResult { /// /// For each lane: Defines the currently set speed limit /// - private static Dictionary laneSpeedLimit = null; // TODO remove + private static Dictionary laneSpeedLimit = null; // TODO remove - internal static ushort?[][] laneSpeedLimitArray; // for faster, lock-free access, 1st index: segment id, 2nd index: lane index + internal static float?[][] laneSpeedLimitArray; // for faster, lock-free access, 1st index: segment id, 2nd index: lane index /// /// For each lane: Defines the lane arrows which are set in highway rule mode (they are not saved) @@ -445,14 +445,15 @@ internal static bool CheckLane(uint laneId) { // TODO refactor return true; } - public static void setLaneSpeedLimit(uint laneId, ushort? speedLimit) { - if (!CheckLane(laneId)) + public static void setLaneSpeedLimit(uint laneId, float? speedLimit) { + if (!CheckLane(laneId)) { return; + } - ushort segmentId = Singleton.instance.m_lanes.m_buffer[laneId].m_segment; + var segmentId = Singleton.instance.m_lanes.m_buffer[laneId].m_segment; - NetInfo segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; - uint curLaneId = Singleton.instance.m_segments.m_buffer[segmentId].m_lanes; + var segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; + var curLaneId = Singleton.instance.m_segments.m_buffer[segmentId].m_lanes; uint laneIndex = 0; while (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) { if (curLaneId == laneId) { @@ -468,35 +469,42 @@ public static void removeLaneSpeedLimit(uint laneId) { setLaneSpeedLimit(laneId, null); } - public static void setLaneSpeedLimit(ushort segmentId, uint laneIndex, ushort speedLimit) { - if (segmentId <= 0 || laneIndex < 0) + public static void setLaneSpeedLimit(ushort segmentId, uint laneIndex, float speedLimit) { + if (segmentId <= 0 || laneIndex < 0) { return; - if ((Singleton.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created) + } + if ((Singleton.instance.m_segments.m_buffer[segmentId].m_flags & + (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created) { return; - NetInfo segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; + } + var segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; if (laneIndex >= segmentInfo.m_lanes.Length) { return; } // find the lane id - uint laneId = Singleton.instance.m_segments.m_buffer[segmentId].m_lanes; - for (int i = 0; i < laneIndex; ++i) { - if (laneId == 0) + var laneId = Singleton.instance.m_segments.m_buffer[segmentId].m_lanes; + for (var i = 0; i < laneIndex; ++i) { + if (laneId == 0) { return; // no valid lane found + } laneId = Singleton.instance.m_lanes.m_buffer[laneId].m_nextLane; } setLaneSpeedLimit(segmentId, laneIndex, laneId, speedLimit); } - public static void setLaneSpeedLimit(ushort segmentId, uint laneIndex, uint laneId, ushort? speedLimit) { - if (segmentId <= 0 || laneIndex < 0 || laneId <= 0) + public static void setLaneSpeedLimit(ushort segmentId, uint laneIndex, uint laneId, float? speedLimit) { + if (segmentId <= 0 || laneIndex < 0 || laneId <= 0) { return; - if ((Singleton.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created) + } + if ((Singleton.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created) { return; - if (((NetLane.Flags)Singleton.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) != NetLane.Flags.Created) + } + if (((NetLane.Flags)Singleton.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) != NetLane.Flags.Created) { return; - NetInfo segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; + } + var segmentInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info; if (laneIndex >= segmentInfo.m_lanes.Length) { return; } @@ -510,21 +518,23 @@ public static void setLaneSpeedLimit(ushort segmentId, uint laneIndex, uint lane if (speedLimit == null) { laneSpeedLimit.Remove(laneId); - if (laneSpeedLimitArray[segmentId] == null) + if (laneSpeedLimitArray[segmentId] == null) { return; - if (laneIndex >= laneSpeedLimitArray[segmentId].Length) + } + if (laneIndex >= laneSpeedLimitArray[segmentId].Length) { return; + } laneSpeedLimitArray[segmentId][laneIndex] = null; } else { - laneSpeedLimit[laneId] = (ushort)speedLimit; + laneSpeedLimit[laneId] = speedLimit.Value; // save speed limit into the fast-access array. // (1) ensure that the array is defined and large enough if (laneSpeedLimitArray[segmentId] == null) { - laneSpeedLimitArray[segmentId] = new ushort?[segmentInfo.m_lanes.Length]; + laneSpeedLimitArray[segmentId] = new float?[segmentInfo.m_lanes.Length]; } else if (laneSpeedLimitArray[segmentId].Length < segmentInfo.m_lanes.Length) { var oldArray = laneSpeedLimitArray[segmentId]; - laneSpeedLimitArray[segmentId] = new ushort?[segmentInfo.m_lanes.Length]; + laneSpeedLimitArray[segmentId] = new float?[segmentInfo.m_lanes.Length]; Array.Copy(oldArray, laneSpeedLimitArray[segmentId], oldArray.Length); } // (2) insert the custom speed limit @@ -745,11 +755,11 @@ internal static bool mayHaveLaneArrows(uint laneId, bool? startNode=null) { return false; } - public static ushort? getLaneSpeedLimit(uint laneId) { + public static float? getLaneSpeedLimit(uint laneId) { try { Monitor.Enter(laneSpeedLimitLock); - ushort speedLimit; + float speedLimit; if (laneId <= 0 || !laneSpeedLimit.TryGetValue(laneId, out speedLimit)) { return null; } @@ -760,13 +770,12 @@ internal static bool mayHaveLaneArrows(uint laneId, bool? startNode=null) { } } - internal static IDictionary getAllLaneSpeedLimits() { - IDictionary ret = new Dictionary(); + internal static IDictionary getAllLaneSpeedLimits() { + IDictionary ret = new Dictionary(); try { Monitor.Enter(laneSpeedLimitLock); - ret = new Dictionary(laneSpeedLimit); - + ret = new Dictionary(laneSpeedLimit); } finally { Monitor.Exit(laneSpeedLimitLock); } @@ -976,8 +985,8 @@ internal static void OnLevelUnloading() { static Flags() { laneConnections = new uint[NetManager.MAX_LANE_COUNT][][]; - laneSpeedLimitArray = new ushort?[NetManager.MAX_SEGMENT_COUNT][]; - laneSpeedLimit = new Dictionary(); + laneSpeedLimitArray = new float?[NetManager.MAX_SEGMENT_COUNT][]; + laneSpeedLimit = new Dictionary(); laneAllowedVehicleTypesArray = new ExtVehicleType?[NetManager.MAX_SEGMENT_COUNT][]; laneArrowFlags = new LaneArrows?[NetManager.MAX_LANE_COUNT]; highwayLaneArrowFlags = new LaneArrows?[NetManager.MAX_LANE_COUNT]; diff --git a/TLM/TLM/State/Options.cs b/TLM/TLM/State/Options.cs index c295078b3..f97537c80 100644 --- a/TLM/TLM/State/Options.cs +++ b/TLM/TLM/State/Options.cs @@ -14,6 +14,7 @@ using CSUtil.Commons; using System.Reflection; using TrafficManager.Manager.Impl; +using TrafficManager.Traffic.Data; namespace TrafficManager.State { @@ -31,6 +32,8 @@ public class Options : MonoBehaviour { private static UICheckBox showCompatibilityCheckErrorToggle = null; private static UICheckBox scanForKnownIncompatibleModsToggle = null; private static UICheckBox ignoreDisabledModsToggle = null; + private static UICheckBox displayMphToggle = null; + private static UIDropDown roadSignsMphThemeDropdown = null; private static UICheckBox individualDrivingStyleToggle = null; private static UIDropDown recklessDriversDropdown = null; private static UICheckBox relaxedBussesToggle = null; @@ -92,6 +95,7 @@ public class Options : MonoBehaviour { private static UIButton reloadGlobalConfBtn = null; private static UIButton resetGlobalConfBtn = null; + public static int roadSignMphStyleInt; public static bool instantEffects = true; public static int simAccuracy = 0; //public static int laneChangingRandomization = 2; @@ -234,8 +238,12 @@ public static void makeSettings(UIHelperBase helper) { showCompatibilityCheckErrorToggle = generalGroup.AddCheckbox(Translation.GetString("Notify_me_if_there_is_an_unexpected_mod_conflict"), GlobalConfig.Instance.Main.ShowCompatibilityCheckErrorMessage, onShowCompatibilityCheckErrorChanged) as UICheckBox; scanForKnownIncompatibleModsToggle = generalGroup.AddCheckbox(Translation.GetString("Scan_for_known_incompatible_mods_on_startup"), GlobalConfig.Instance.Main.ScanForKnownIncompatibleModsAtStartup, onScanForKnownIncompatibleModsChanged) as UICheckBox; ignoreDisabledModsToggle = generalGroup.AddCheckbox(Translation.GetString("Ignore_disabled_mods"), GlobalConfig.Instance.Main.IgnoreDisabledMods, onIgnoreDisabledModsChanged) as UICheckBox; - Indent(ignoreDisabledModsToggle); + Indent(ignoreDisabledModsToggle); + // General: Speed Limits + setupSpeedLimitsPanel(panelHelper, generalGroup); + + // General: Simulation var simGroup = panelHelper.AddGroup(Translation.GetString("Simulation")); simAccuracyDropdown = simGroup.AddDropdown(Translation.GetString("Simulation_accuracy") + ":", new string[] { Translation.GetString("Very_high"), Translation.GetString("High"), Translation.GetString("Medium"), Translation.GetString("Low"), Translation.GetString("Very_Low") }, simAccuracy, onSimAccuracyChanged) as UIDropDown; instantEffectsToggle = simGroup.AddCheckbox(Translation.GetString("Customizations_come_into_effect_instantaneously"), instantEffects, onInstantEffectsChanged) as UICheckBox; @@ -484,6 +492,24 @@ public static void makeSettings(UIHelperBase helper) { tabStrip.selectedIndex = 0; } + private static void setupSpeedLimitsPanel(UIHelper panelHelper, UIHelperBase generalGroup) { + displayMphToggle = generalGroup.AddCheckbox( + Translation.GetString("Display_speed_limits_mph"), + GlobalConfig.Instance.Main.DisplaySpeedLimitsMph, + onDisplayMphChanged) as UICheckBox; + var mphThemeOptions = new[] { + Translation.GetString("theme_Square_US"), + Translation.GetString("theme_Round_UK"), + Translation.GetString("theme_Round_German"), + }; + roadSignMphStyleInt = (int)GlobalConfig.Instance.Main.MphRoadSignStyle; + roadSignsMphThemeDropdown = generalGroup.AddDropdown( + Translation.GetString("Road_signs_theme_mph") + ":", + mphThemeOptions, roadSignMphStyleInt, + onRoadSignsMphThemeChanged) as UIDropDown; + roadSignsMphThemeDropdown.width = 400; + } + private static void Indent(T component) where T : UIComponent { UILabel label = component.Find("Label"); if (label != null) { @@ -698,7 +724,39 @@ private static void onIgnoreDisabledModsChanged(bool newValue) { GlobalConfig.WriteConfig(); } - private static void onInstantEffectsChanged(bool newValue) { + private static void onDisplayMphChanged(bool newValue) { + Log._Debug($"Display MPH changed to {newValue}"); + GlobalConfig.Instance.Main.DisplaySpeedLimitsMph = newValue; + GlobalConfig.WriteConfig(); + } + + public static void setDisplayInMPH(bool value) { + if (displayMphToggle != null) { + displayMphToggle.isChecked = value; + } + } + + private static void onRoadSignsMphThemeChanged(int newRoadSignStyle) { + if (!checkGameLoaded()) { + return; + } + + // The UI order is: US, UK, German + var newStyle = MphSignStyle.RoundGerman; + switch (newRoadSignStyle) { + case 1: + newStyle = MphSignStyle.RoundUK; + break; + case 0: + newStyle = MphSignStyle.SquareUS; + break; + } + + Log._Debug($"Road Sign theme changed to {newStyle}"); + GlobalConfig.Instance.Main.MphRoadSignStyle = newStyle; + } + + private static void onInstantEffectsChanged(bool newValue) { if (!checkGameLoaded()) return; diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index cb126ecb2..404934bdd 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -186,6 +186,7 @@ + @@ -364,19 +365,6 @@ - - - - - - - - - - - - - @@ -393,9 +381,6 @@ - - - @@ -458,6 +443,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mkdir "$(LOCALAPPDATA)\Colossal Order\Cities_Skylines\Addons\Mods\$(TargetName)" diff --git a/TLM/TLM/TMPE.csproj b/TLM/TLM/TMPE.csproj index 85d2c8c23..8dbd837f2 100644 --- a/TLM/TLM/TMPE.csproj +++ b/TLM/TLM/TMPE.csproj @@ -260,6 +260,12 @@ + + + + + + diff --git a/TLM/TLM/Traffic/Data/SpeedLimit.cs b/TLM/TLM/Traffic/Data/SpeedLimit.cs new file mode 100644 index 000000000..7f72d4c6a --- /dev/null +++ b/TLM/TLM/Traffic/Data/SpeedLimit.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using TrafficManager.State; +using TrafficManager.UI; +using UnityEngine; + +namespace TrafficManager.Traffic.Data { + public enum SpeedUnit { + CurrentlyConfigured, // Currently selected in the options menu + Kmph, + Mph + } + + public enum MphSignStyle { + SquareUS = 0, + RoundUK = 1, + RoundGerman = 2, + } + + /// + /// Defines a speed limit value with default Kmph and display value of Mph + /// for when the option is set to display Mph. The engine still uses kmph. + /// + public struct SpeedLimit { + public const float SPEED_TO_KMPH = 50.0f; // 1.0f equals 50 km/h + private const ushort LOWER_KMPH = 10; + public const ushort UPPER_KMPH = 140; + private const ushort KMPH_STEP = 10; + public const int BREAK_PALETTE_COLUMN_KMPH = 8; // palette shows N in a row, then break and another row + + private const float SPEED_TO_MPH = 32.06f; // 50 km/h converted to mph + private const ushort LOWER_MPH = 5; + public const ushort UPPER_MPH = 90; + private const ushort MPH_STEP = 5; + public const int BREAK_PALETTE_COLUMN_MPH = 10; // palette shows M in a row, then break and another row + + private const float LOWER_SPEED = 0.1f; + private const float UPPER_SPEED = 2 * 10.0f; // 1000 km/h + + /// + /// Produces list of speed limits to offer user in the palette + /// + /// What kind of speed limit list is required + /// List from smallest to largest speed with the given unit. Zero (no limit) is not added to the list. + /// The values are in-game speeds as float. + public static List EnumerateSpeedLimits(SpeedUnit unit) { + var result = new List(); + switch (unit) { + case SpeedUnit.Kmph: + for (var km = LOWER_KMPH; km <= UPPER_KMPH; km += KMPH_STEP) { + result.Add(km / SPEED_TO_KMPH); + } + break; + case SpeedUnit.Mph: + for (var mi = LOWER_MPH; mi <= UPPER_MPH; mi += MPH_STEP) { + result.Add(mi / SPEED_TO_MPH); + } + break; + case SpeedUnit.CurrentlyConfigured: + // Automatically choose from the config + return GlobalConfig.Instance.Main.DisplaySpeedLimitsMph + ? EnumerateSpeedLimits(SpeedUnit.Mph) + : EnumerateSpeedLimits(SpeedUnit.Kmph); + } + + return result; + } + + public static string ToMphPreciseString(float speed) { + if (IsZero(speed)) { + return Translation.GetString("Speed_limit_unlimited"); + } + + return ToMphPrecise(speed) + " MPH"; + } + + public static string ToKmphPreciseString(float speed) { + if (IsZero(speed)) { + return Translation.GetString("Speed_limit_unlimited"); + } + + return ToKmphPrecise(speed) + " km/h"; + } + + public static bool NearlyEqual(float a, float b) { + return Mathf.Abs(a - b) < 0.001f; + } + + public static bool IsZero(float speed) { + return speed < 0.001f; + } + + public static bool IsValidRange(float speed) { + return IsZero(speed) || (speed >= LOWER_SPEED && speed <= UPPER_SPEED); + } + + /// + /// Convert float game speed to mph and round to nearest STEP + /// + /// + /// + public static ushort ToMphRounded(float speed) { + var mph = speed * SPEED_TO_MPH; + return (ushort)(Mathf.Round(mph / MPH_STEP) * MPH_STEP); + } + + public static ushort ToMphPrecise(float speed) { + return (ushort)Mathf.Round(speed * SPEED_TO_MPH); + } + + /// + /// Convert float game speed to km/h and round to nearest STEP + /// + /// + /// + public static ushort ToKmphRounded(float speed) { + var kmph = speed * SPEED_TO_KMPH; + return (ushort)(Mathf.Round(kmph / KMPH_STEP) * KMPH_STEP); + } + + public static ushort ToKmphPrecise(float speed) { + return (ushort)Mathf.Round(speed * SPEED_TO_KMPH); + } + + /// + /// Based on the MPH/KMPH settings round the current speed to the nearest STEP and + /// then decrease by STEP. + /// + /// Ingame speed + /// Ingame speed decreased by the increment for MPH or KMPH + public static float GetPrevious(float speed) { + if (speed < 0f) { + return -1f; + } + if (GlobalConfig.Instance.Main.DisplaySpeedLimitsMph) { + var rounded = ToMphRounded(speed); + return (rounded > LOWER_MPH ? rounded - MPH_STEP : LOWER_MPH) / SPEED_TO_MPH; + } else { + var rounded = ToKmphRounded(speed); + return (rounded > LOWER_KMPH ? rounded - KMPH_STEP : LOWER_KMPH) / SPEED_TO_KMPH; + } + } + + /// + /// Based on the MPH/KMPH settings round the current speed to the nearest STEP and + /// then increase by STEP. + /// + /// Ingame speed + /// Ingame speed increased by the increment for MPH or KMPH + public static float GetNext(float speed) { + if (speed < 0f) { + return -1f; + } + if (GlobalConfig.Instance.Main.DisplaySpeedLimitsMph) { + var rounded = ToMphRounded(speed); + return (rounded < UPPER_MPH ? rounded + MPH_STEP : UPPER_MPH) / SPEED_TO_MPH; + } + else { + var rounded = ToKmphRounded(speed); + return (rounded < UPPER_KMPH ? rounded + KMPH_STEP : UPPER_KMPH) / SPEED_TO_KMPH; + } + } + + /// + /// For US signs and MPH enabled, scale textures vertically by 1.25f. + /// Other signs are round. + /// + /// Multiplier for horizontal sign size + public static float GetVerticalTextureScale() { + return (GlobalConfig.Instance.Main.DisplaySpeedLimitsMph && + GlobalConfig.Instance.Main.MphRoadSignStyle == MphSignStyle.SquareUS) + ? 1.25f + : 1.0f; + } + } +} \ No newline at end of file diff --git a/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs b/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs index 8df46d33a..71da933c1 100644 --- a/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs +++ b/TLM/TLM/UI/SubTools/SpeedLimitsTool.cs @@ -13,6 +13,7 @@ using TrafficManager.Manager.Impl; using TrafficManager.State; using TrafficManager.Traffic; +using TrafficManager.Traffic.Data; using TrafficManager.TrafficLight; using TrafficManager.Util; using UnityEngine; @@ -21,22 +22,28 @@ namespace TrafficManager.UI.SubTools { public class SpeedLimitsTool : SubTool { + /// Visible sign size, slightly reduced from 100 to accomodate another column for MPH + private const int GuiSpeedSignSize = 80; + private readonly float speedLimitSignSize = 70f; + private bool _cursorInSecondaryPanel; - private int curSpeedLimitIndex = 0; + + /// Currently selected speed limit on the limits palette + private float currentPaletteSpeedLimit = -1f; + private bool overlayHandleHovered; private Dictionary> segmentCenterByDir = new Dictionary>(); - private readonly float speedLimitSignSize = 80f; - private readonly int guiSpeedSignSize = 100; - private Rect windowRect = TrafficManagerTool.MoveGUI(new Rect(0, 0, 7 * 105, 225)); - private Rect defaultsWindowRect = TrafficManagerTool.MoveGUI(new Rect(0, 280, 400, 400)); + + private Rect paletteWindowRect = TrafficManagerTool.MoveGUI(new Rect(0, 0, 10 * (GuiSpeedSignSize + 5), 150)); + private Rect defaultsWindowRect = TrafficManagerTool.MoveGUI(new Rect(0, 80, 50, 50)); private HashSet currentlyVisibleSegmentIds; private bool defaultsWindowVisible = false; private int currentInfoIndex = -1; - private int currentSpeedLimitIndex = -1; + private float currentSpeedLimit = -1f; private Texture2D RoadTexture { get { if (roadTexture == null) { - roadTexture = new Texture2D(guiSpeedSignSize, guiSpeedSignSize); + roadTexture = new Texture2D(GuiSpeedSignSize, GuiSpeedSignSize); } return roadTexture; } @@ -63,11 +70,24 @@ public override void OnPrimaryClickOverlay() { public override void OnToolGUI(Event e) { base.OnToolGUI(e); - windowRect = GUILayout.Window(254, windowRect, _guiSpeedLimitsWindow, Translation.GetString("Speed_limits"), WindowStyle); + var unitTitle = " (" + (GlobalConfig.Instance.Main.DisplaySpeedLimitsMph + ? Translation.GetString("Miles_per_hour") + : Translation.GetString("Kilometers_per_hour")) + ")"; + paletteWindowRect.width = GlobalConfig.Instance.Main.DisplaySpeedLimitsMph + ? 10 * (GuiSpeedSignSize + 5) + : 8 * (GuiSpeedSignSize + 5); + paletteWindowRect = GUILayout.Window(254, paletteWindowRect, _guiSpeedLimitsWindow, + Translation.GetString("Speed_limits") + unitTitle, + WindowStyle); if (defaultsWindowVisible) { - defaultsWindowRect = GUILayout.Window(258, defaultsWindowRect, _guiDefaultsWindow, Translation.GetString("Default_speed_limits"), WindowStyle); + defaultsWindowRect = GUILayout.Window( + 258, defaultsWindowRect, _guiDefaultsWindow, + Translation.GetString("Default_speed_limits"), + WindowStyle); } - _cursorInSecondaryPanel = windowRect.Contains(Event.current.mousePosition) || (defaultsWindowVisible && defaultsWindowRect.Contains(Event.current.mousePosition)); + _cursorInSecondaryPanel = paletteWindowRect.Contains(Event.current.mousePosition) + || (defaultsWindowVisible + && defaultsWindowRect.Contains(Event.current.mousePosition)); //overlayHandleHovered = false; //ShowSigns(false); @@ -91,7 +111,7 @@ public override void Cleanup() { lastCamPos = null; lastCamRot = null; currentInfoIndex = -1; - currentSpeedLimitIndex = -1; + currentSpeedLimit = -1f; } private Quaternion? lastCamRot = null; @@ -153,8 +173,12 @@ private void ShowSigns(bool viewOnly) { overlayHandleHovered = handleHovered; } + /// + /// The window for setting the defaullt speeds per road type + /// + /// private void _guiDefaultsWindow(int num) { - List mainNetInfos = SpeedLimitManager.Instance.GetCustomizableNetInfos(); + var mainNetInfos = SpeedLimitManager.Instance.GetCustomizableNetInfos(); if (mainNetInfos == null || mainNetInfos.Count <= 0) { Log._Debug($"mainNetInfos={mainNetInfos?.Count}"); @@ -173,19 +197,12 @@ private void _guiDefaultsWindow(int num) { if (updateRoadTex) UpdateRoadTex(info); - if (currentSpeedLimitIndex < 0) { - currentSpeedLimitIndex = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimitIndex(info); - Log._Debug($"set currentSpeedLimitIndex to {currentSpeedLimitIndex}"); + if (currentSpeedLimit < 0f) { + currentSpeedLimit = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimit(info); + Log._Debug($"set currentSpeedLimit to {currentSpeedLimit}"); } //Log._Debug($"currentInfoIndex={currentInfoIndex} currentSpeedLimitIndex={currentSpeedLimitIndex}"); - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button("X", GUILayout.Width(25))) { - defaultsWindowVisible = false; - } - GUILayout.EndHorizontal(); - // Road type label GUILayout.BeginVertical(); GUILayout.Space(10); @@ -194,15 +211,17 @@ private void _guiDefaultsWindow(int num) { // switch between NetInfos GUILayout.BeginHorizontal(); - + GUILayout.BeginVertical(); GUILayout.FlexibleSpace(); if (GUILayout.Button("←", GUILayout.Width(50))) { - currentInfoIndex = (currentInfoIndex + mainNetInfos.Count - 1) % mainNetInfos.Count; + currentInfoIndex = + (currentInfoIndex + mainNetInfos.Count - 1) % mainNetInfos.Count; info = mainNetInfos[currentInfoIndex]; - currentSpeedLimitIndex = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimitIndex(info); + currentSpeedLimit = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimit(info); UpdateRoadTex(info); } + GUILayout.FlexibleSpace(); GUILayout.EndVertical(); @@ -211,7 +230,7 @@ private void _guiDefaultsWindow(int num) { GUILayout.FlexibleSpace(); // NetInfo thumbnail - GUILayout.Box(RoadTexture, GUILayout.Height(guiSpeedSignSize)); + GUILayout.Box(RoadTexture, GUILayout.Height(GuiSpeedSignSize)); GUILayout.FlexibleSpace(); GUILayout.EndVertical(); @@ -222,16 +241,16 @@ private void _guiDefaultsWindow(int num) { if (GUILayout.Button("→", GUILayout.Width(50))) { currentInfoIndex = (currentInfoIndex + 1) % mainNetInfos.Count; info = mainNetInfos[currentInfoIndex]; - currentSpeedLimitIndex = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimitIndex(info); + currentSpeedLimit = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimit(info); UpdateRoadTex(info); } + GUILayout.FlexibleSpace(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); - GUIStyle centeredTextStyle = new GUIStyle("label"); - centeredTextStyle.alignment = TextAnchor.MiddleCenter; + var centeredTextStyle = new GUIStyle("label") { alignment = TextAnchor.MiddleCenter }; // NetInfo name GUILayout.Label(info.name, centeredTextStyle); @@ -248,8 +267,10 @@ private void _guiDefaultsWindow(int num) { GUILayout.BeginVertical(); GUILayout.FlexibleSpace(); if (GUILayout.Button("←", GUILayout.Width(50))) { - currentSpeedLimitIndex = (currentSpeedLimitIndex + SpeedLimitManager.Instance.AvailableSpeedLimits.Count - 1) % SpeedLimitManager.Instance.AvailableSpeedLimits.Count; + // currentSpeedLimit = (currentSpeedLimitIndex + SpeedLimitManager.Instance.AvailableSpeedLimits.Count - 1) % SpeedLimitManager.Instance.AvailableSpeedLimits.Count; + currentSpeedLimit = SpeedLimit.GetPrevious(currentSpeedLimit); } + GUILayout.FlexibleSpace(); GUILayout.EndVertical(); @@ -259,7 +280,12 @@ private void _guiDefaultsWindow(int num) { GUILayout.FlexibleSpace(); // speed limit sign - GUILayout.Box(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.AvailableSpeedLimits[currentSpeedLimitIndex]], GUILayout.Width(guiSpeedSignSize), GUILayout.Height(guiSpeedSignSize)); + GUILayout.Box(TextureResources.GetSpeedLimitTexture(currentSpeedLimit), + GUILayout.Width(GuiSpeedSignSize), + GUILayout.Height(GuiSpeedSignSize)); + GUILayout.Label(GlobalConfig.Instance.Main.DisplaySpeedLimitsMph + ? Translation.GetString("Miles_per_hour") + : Translation.GetString("Kilometers_per_hour")); GUILayout.FlexibleSpace(); GUILayout.EndVertical(); @@ -269,8 +295,10 @@ private void _guiDefaultsWindow(int num) { GUILayout.BeginVertical(); GUILayout.FlexibleSpace(); if (GUILayout.Button("→", GUILayout.Width(50))) { - currentSpeedLimitIndex = (currentSpeedLimitIndex + 1) % SpeedLimitManager.Instance.AvailableSpeedLimits.Count; + // currentSpeedLimitIndex = (currentSpeedLimitIndex + 1) % SpeedLimitManager.Instance.AvailableSpeedLimits.Count; + currentSpeedLimit = SpeedLimit.GetNext(currentSpeedLimit); } + GUILayout.FlexibleSpace(); GUILayout.EndVertical(); @@ -281,16 +309,27 @@ private void _guiDefaultsWindow(int num) { GUILayout.Space(10); GUILayout.BeginHorizontal(); + + // Close button. TODO: Make more visible or obey 'Esc' pressed or something + GUILayout.FlexibleSpace(); + if (GUILayout.Button("X", GUILayout.Width(80))) { + defaultsWindowVisible = false; + } + GUILayout.FlexibleSpace(); if (GUILayout.Button(Translation.GetString("Save"), GUILayout.Width(70))) { SpeedLimitManager.Instance.FixCurrentSpeedLimits(info); - SpeedLimitManager.Instance.SetCustomNetInfoSpeedLimitIndex(info, currentSpeedLimitIndex); + SpeedLimitManager.Instance.SetCustomNetInfoSpeedLimit(info, currentSpeedLimit); } + GUILayout.FlexibleSpace(); - if (GUILayout.Button(Translation.GetString("Save") + " & " + Translation.GetString("Apply"), GUILayout.Width(160))) { - SpeedLimitManager.Instance.SetCustomNetInfoSpeedLimitIndex(info, currentSpeedLimitIndex); + if (GUILayout.Button( + Translation.GetString("Save") + " & " + Translation.GetString("Apply"), + GUILayout.Width(160))) { + SpeedLimitManager.Instance.SetCustomNetInfoSpeedLimit(info, currentSpeedLimit); SpeedLimitManager.Instance.ClearCurrentSpeedLimits(info); } + GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); @@ -322,73 +361,151 @@ private void UpdateRoadTex(NetInfo info) { roadTexture = TextureResources.NoImageTexture2D; } + /// + /// The window for selecting and applying a speed limit + /// + /// private void _guiSpeedLimitsWindow(int num) { GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + var oldColor = GUI.color; + var allSpeedLimits = SpeedLimit.EnumerateSpeedLimits(SpeedUnit.CurrentlyConfigured); + allSpeedLimits.Add(0); // add last item: no limit - Color oldColor = GUI.color; - for (int i = 0; i < SpeedLimitManager.Instance.AvailableSpeedLimits.Count; ++i) { - if (curSpeedLimitIndex != i) + var showMph = GlobalConfig.Instance.Main.DisplaySpeedLimitsMph; + var column = 0u; // break palette to a new line at breakColumn + var breakColumn = showMph ? SpeedLimit.BREAK_PALETTE_COLUMN_MPH + : SpeedLimit.BREAK_PALETTE_COLUMN_KMPH; + + foreach (var speedLimit in allSpeedLimits) { + // Highlight palette item if it is very close to its float speed + if (SpeedLimit.NearlyEqual(currentPaletteSpeedLimit, speedLimit)) { GUI.color = Color.gray; - float signSize = TrafficManagerTool.AdaptWidth(guiSpeedSignSize); - if (GUILayout.Button(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.AvailableSpeedLimits[i]], GUILayout.Width(signSize), GUILayout.Height(signSize))) { - curSpeedLimitIndex = i; } + + _guiSpeedLimitsWindow_AddButton(showMph, speedLimit); GUI.color = oldColor; - if (i == 6) { + // TODO: This can be calculated from SpeedLimit MPH or KMPH limit constants + column++; + if (column % breakColumn == 0) { + GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); - } + GUILayout.FlexibleSpace(); + } } - + GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); - if (GUILayout.Button(Translation.GetString("Default_speed_limits"))) { + //--------------------- + // UI buttons row + //--------------------- + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button(Translation.GetString("Default_speed_limits"), + GUILayout.Width(200))) { TrafficManagerTool.ShowAdvisor(this.GetType().Name + "_Defaults"); defaultsWindowVisible = true; } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + //--------------------- + // Checkboxes row + //--------------------- + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); showLimitsPerLane = GUILayout.Toggle(showLimitsPerLane, Translation.GetString("Show_lane-wise_speed_limits")); + GUILayout.FlexibleSpace(); + + // Display MPH checkbox, if ticked will save global config + var displayMph = GlobalConfig.Instance.Main.DisplaySpeedLimitsMph; + displayMph = GUILayout.Toggle(displayMph, Translation.GetString("Display_speed_limits_mph")); + if (GlobalConfig.Instance.Main.DisplaySpeedLimitsMph != displayMph) { + Options.setDisplayInMPH(displayMph); + } + + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); - DragWindow(ref windowRect); + DragWindow(ref paletteWindowRect); + } + + /// Helper to create speed limit sign + label below converted to the opposite unit + /// Config value from GlobalConfig.I.M.ShowMPH + /// The float speed to show + private void _guiSpeedLimitsWindow_AddButton(bool showMph, float speedLimit) { + // The button is wrapped in vertical sub-layout and a label for MPH/KMPH is added + GUILayout.BeginVertical(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + var signSize = TrafficManagerTool.AdaptWidth(GuiSpeedSignSize); + if (GUILayout.Button( + TextureResources.GetSpeedLimitTexture(speedLimit), + GUILayout.Width(signSize), + GUILayout.Height(signSize * SpeedLimit.GetVerticalTextureScale()))) { + currentPaletteSpeedLimit = speedLimit; + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + // For MPH setting display KM/H below, for KM/H setting display MPH + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label(showMph ? SpeedLimit.ToKmphPreciseString(speedLimit) + : SpeedLimit.ToMphPreciseString(speedLimit)); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.EndVertical(); } private bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, bool viewOnly, ref Vector3 camPos) { - if (viewOnly && !Options.speedLimitsOverlay) + if (viewOnly && !Options.speedLimitsOverlay) { return false; + } - Vector3 center = segment.m_bounds.center; - NetManager netManager = Singleton.instance; + var center = segment.m_bounds.center; + var netManager = Singleton.instance; - bool hovered = false; - ushort speedLimitToSet = viewOnly ? (ushort)0 : SpeedLimitManager.Instance.AvailableSpeedLimits[curSpeedLimitIndex]; + var hovered = false; + var speedLimitToSet = viewOnly ? -1f : currentPaletteSpeedLimit; bool showPerLane = showLimitsPerLane; if (!viewOnly) { showPerLane = showLimitsPerLane ^ (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)); } + + // US signs are rectangular, all other are round + var speedLimitSignVerticalScale = SpeedLimit.GetVerticalTextureScale(); + if (showPerLane) { // show individual speed limit handle per lane int numDirections; - int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(segmentId, null, out numDirections, SpeedLimitManager.VEHICLE_TYPES); + var numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(segmentId, null, out numDirections, SpeedLimitManager.VEHICLE_TYPES); - NetInfo segmentInfo = segment.Info; - Vector3 yu = (segment.m_endDirection - segment.m_startDirection).normalized; - Vector3 xu = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized; + var segmentInfo = segment.Info; + var yu = (segment.m_endDirection - segment.m_startDirection).normalized; + var xu = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized; /*if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) { xu = -xu; }*/ - float f = viewOnly ? 4f : 7f; // reserved sign size in game coordinates - Vector3 zero = center - 0.5f * (float)(numLanes - 1 + numDirections - 1) * f * xu; + var f = viewOnly ? 4f : 7f; // reserved sign size in game coordinates + var zero = center - 0.5f * (float)(numLanes - 1 + numDirections - 1) * f * xu; uint x = 0; var guiColor = GUI.color; - IList sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(segmentId, ref segment, null, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES); - bool onlyMonorailLanes = sortedLanes.Count > 0; + var sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes( + segmentId, ref segment, null, SpeedLimitManager.LANE_TYPES, + SpeedLimitManager.VEHICLE_TYPES); + var onlyMonorailLanes = sortedLanes.Count > 0; if (!viewOnly) { foreach (LanePos laneData in sortedLanes) { - byte laneIndex = laneData.laneIndex; - NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex]; + var laneIndex = laneData.laneIndex; + var laneInfo = segmentInfo.m_lanes[laneIndex]; if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None) { onlyMonorailLanes = false; @@ -397,8 +514,8 @@ private bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, boo } } - HashSet directions = new HashSet(); - int sortedLaneIndex = -1; + var directions = new HashSet(); + var sortedLaneIndex = -1; foreach (LanePos laneData in sortedLanes) { ++sortedLaneIndex; uint laneId = laneData.laneId; @@ -411,12 +528,23 @@ private bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, boo directions.Add(laneInfo.m_finalDirection); } - bool hoveredHandle = MainTool.DrawGenericSquareOverlayGridTexture(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(laneId)], camPos, zero, f, xu, yu, x, 0, speedLimitSignSize, !viewOnly); - if (!viewOnly && !onlyMonorailLanes && (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) != VehicleInfo.VehicleType.None) { - MainTool.DrawStaticSquareOverlayGridTexture(TextureResources.VehicleInfoSignTextures[ExtVehicleType.PassengerTrain], camPos, zero, f, xu, yu, x, 1, speedLimitSignSize); + var laneSpeedLimit = SpeedLimitManager.Instance.GetCustomSpeedLimit(laneId); + var hoveredHandle = MainTool.DrawGenericOverlayGridTexture( + TextureResources.GetSpeedLimitTexture(laneSpeedLimit), + camPos, zero, f, f, xu, yu, x, 0, + speedLimitSignSize, speedLimitSignSize * speedLimitSignVerticalScale, + !viewOnly); + + if (!viewOnly + && !onlyMonorailLanes + && (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) != VehicleInfo.VehicleType.None) { + MainTool.DrawStaticSquareOverlayGridTexture( + TextureResources.VehicleInfoSignTextures[ExtVehicleType.PassengerTrain], + camPos, zero, f, xu, yu, x, 1, speedLimitSignSize); } - if (hoveredHandle) + if (hoveredHandle) { hovered = true; + } if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel()) { SpeedLimitManager.Instance.SetSpeedLimit(segmentId, laneIndex, laneInfo, laneId, speedLimitToSet); @@ -455,16 +583,20 @@ private bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, boo foreach (KeyValuePair e in segCenter) { Vector3 screenPos; - bool visible = MainTool.WorldToScreenPoint(e.Value, out screenPos); + var visible = MainTool.WorldToScreenPoint(e.Value, out screenPos); - if (!visible) + if (!visible) { continue; + } - float zoom = 1.0f / (e.Value - camPos).magnitude * 100f * MainTool.GetBaseZoom(); - float size = (viewOnly ? 0.8f : 1f) * speedLimitSignSize * zoom; - Color guiColor = GUI.color; - Rect boundingBox = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size); - bool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox); + var zoom = 1.0f / (e.Value - camPos).magnitude * 100f * MainTool.GetBaseZoom(); + var size = (viewOnly ? 0.8f : 1f) * speedLimitSignSize * zoom; + var guiColor = GUI.color; + var boundingBox = new Rect(screenPos.x - (size / 2), + screenPos.y - (size / 2), + size, + size * speedLimitSignVerticalScale); + var hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox); guiColor.a = MainTool.GetHandleAlpha(hoveredHandle); if (hoveredHandle) { @@ -472,13 +604,16 @@ private bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, boo hovered = true; } + // Draw something right here, the road sign texture GUI.color = guiColor; - GUI.DrawTexture(boundingBox, TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key)]); + var displayLimit = SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key); + var tex = TextureResources.GetSpeedLimitTexture(displayLimit); + GUI.DrawTexture(boundingBox, tex); if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel()) { // change the speed limit to the selected one //Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()} to {speedLimitToSet}"); - SpeedLimitManager.Instance.SetSpeedLimit(segmentId, e.Key, speedLimitToSet); + SpeedLimitManager.Instance.SetSpeedLimit(segmentId, e.Key, currentPaletteSpeedLimit); if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { diff --git a/TLM/TLM/UI/TextureResources.cs b/TLM/TLM/UI/TextureResources.cs index 60c2d49b0..c5108b9a6 100644 --- a/TLM/TLM/UI/TextureResources.cs +++ b/TLM/TLM/UI/TextureResources.cs @@ -6,7 +6,9 @@ using TrafficManager.Geometry; using TrafficManager.Manager; using TrafficManager.Manager.Impl; +using TrafficManager.State; using TrafficManager.Traffic; +using TrafficManager.Traffic.Data; using TrafficManager.UI; using TrafficManager.Util; using UnityEngine; @@ -14,39 +16,40 @@ namespace TrafficManager.UI { - public class TextureResources - { - public static readonly Texture2D RedLightTexture2D; - public static readonly Texture2D YellowRedLightTexture2D; - public static readonly Texture2D YellowLightTexture2D; - public static readonly Texture2D GreenLightTexture2D; - public static readonly Texture2D RedLightStraightTexture2D; - public static readonly Texture2D YellowLightStraightTexture2D; - public static readonly Texture2D GreenLightStraightTexture2D; - public static readonly Texture2D RedLightRightTexture2D; - public static readonly Texture2D YellowLightRightTexture2D; - public static readonly Texture2D GreenLightRightTexture2D; - public static readonly Texture2D RedLightLeftTexture2D; - public static readonly Texture2D YellowLightLeftTexture2D; - public static readonly Texture2D GreenLightLeftTexture2D; - public static readonly Texture2D RedLightForwardRightTexture2D; - public static readonly Texture2D YellowLightForwardRightTexture2D; - public static readonly Texture2D GreenLightForwardRightTexture2D; - public static readonly Texture2D RedLightForwardLeftTexture2D; - public static readonly Texture2D YellowLightForwardLeftTexture2D; - public static readonly Texture2D GreenLightForwardLeftTexture2D; - public static readonly Texture2D PedestrianRedLightTexture2D; - public static readonly Texture2D PedestrianGreenLightTexture2D; - public static readonly Texture2D LightModeTexture2D; - public static readonly Texture2D LightCounterTexture2D; - public static readonly Texture2D PedestrianModeAutomaticTexture2D; - public static readonly Texture2D PedestrianModeManualTexture2D; + public class TextureResources { + public static readonly Texture2D RedLightTexture2D; + public static readonly Texture2D YellowRedLightTexture2D; + public static readonly Texture2D YellowLightTexture2D; + public static readonly Texture2D GreenLightTexture2D; + public static readonly Texture2D RedLightStraightTexture2D; + public static readonly Texture2D YellowLightStraightTexture2D; + public static readonly Texture2D GreenLightStraightTexture2D; + public static readonly Texture2D RedLightRightTexture2D; + public static readonly Texture2D YellowLightRightTexture2D; + public static readonly Texture2D GreenLightRightTexture2D; + public static readonly Texture2D RedLightLeftTexture2D; + public static readonly Texture2D YellowLightLeftTexture2D; + public static readonly Texture2D GreenLightLeftTexture2D; + public static readonly Texture2D RedLightForwardRightTexture2D; + public static readonly Texture2D YellowLightForwardRightTexture2D; + public static readonly Texture2D GreenLightForwardRightTexture2D; + public static readonly Texture2D RedLightForwardLeftTexture2D; + public static readonly Texture2D YellowLightForwardLeftTexture2D; + public static readonly Texture2D GreenLightForwardLeftTexture2D; + public static readonly Texture2D PedestrianRedLightTexture2D; + public static readonly Texture2D PedestrianGreenLightTexture2D; + public static readonly Texture2D LightModeTexture2D; + public static readonly Texture2D LightCounterTexture2D; + public static readonly Texture2D PedestrianModeAutomaticTexture2D; + public static readonly Texture2D PedestrianModeManualTexture2D; public static readonly IDictionary PrioritySignTextures; public static readonly Texture2D SignRemoveTexture2D; public static readonly Texture2D ClockPlayTexture2D; public static readonly Texture2D ClockPauseTexture2D; public static readonly Texture2D ClockTestTexture2D; - public static readonly IDictionary SpeedLimitTextures; + public static readonly IDictionary SpeedLimitTexturesKmph; + public static readonly IDictionary SpeedLimitTexturesMphUS; + public static readonly IDictionary SpeedLimitTexturesMphUK; public static readonly IDictionary> VehicleRestrictionTextures; public static readonly IDictionary VehicleInfoSignTextures; public static readonly IDictionary ParkingRestrictionTextures; @@ -54,10 +57,10 @@ public class TextureResources public static readonly Texture2D LaneChangeAllowedTexture2D; public static readonly Texture2D UturnAllowedTexture2D; public static readonly Texture2D UturnForbiddenTexture2D; - public static readonly Texture2D RightOnRedForbiddenTexture2D; - public static readonly Texture2D RightOnRedAllowedTexture2D; - public static readonly Texture2D LeftOnRedForbiddenTexture2D; - public static readonly Texture2D LeftOnRedAllowedTexture2D; + public static readonly Texture2D RightOnRedForbiddenTexture2D; + public static readonly Texture2D RightOnRedAllowedTexture2D; + public static readonly Texture2D LeftOnRedForbiddenTexture2D; + public static readonly Texture2D LeftOnRedAllowedTexture2D; public static readonly Texture2D EnterBlockedJunctionAllowedTexture2D; public static readonly Texture2D EnterBlockedJunctionForbiddenTexture2D; public static readonly Texture2D PedestrianCrossingAllowedTexture2D; @@ -68,8 +71,7 @@ public class TextureResources public static readonly Texture2D RemoveButtonTexture2D; public static readonly Texture2D WindowBackgroundTexture2D; - static TextureResources() - { + static TextureResources() { // missing image NoImageTexture2D = LoadDllResource("noimage.png", 64, 64); @@ -83,39 +85,41 @@ static TextureResources() // simple RedLightTexture2D = LoadDllResource("light_1_1.png", 103, 243); - YellowRedLightTexture2D = LoadDllResource("light_1_2.png", 103, 243); - GreenLightTexture2D = LoadDllResource("light_1_3.png", 103, 243); - // forward - RedLightStraightTexture2D = LoadDllResource("light_2_1.png", 103, 243); - YellowLightStraightTexture2D = LoadDllResource("light_2_2.png", 103, 243); - GreenLightStraightTexture2D = LoadDllResource("light_2_3.png", 103, 243); - // right - RedLightRightTexture2D = LoadDllResource("light_3_1.png", 103, 243); - YellowLightRightTexture2D = LoadDllResource("light_3_2.png", 103, 243); - GreenLightRightTexture2D = LoadDllResource("light_3_3.png", 103, 243); - // left - RedLightLeftTexture2D = LoadDllResource("light_4_1.png", 103, 243); - YellowLightLeftTexture2D = LoadDllResource("light_4_2.png", 103, 243); - GreenLightLeftTexture2D = LoadDllResource("light_4_3.png", 103, 243); - // forwardright - RedLightForwardRightTexture2D = LoadDllResource("light_5_1.png", 103, 243); - YellowLightForwardRightTexture2D = LoadDllResource("light_5_2.png", 103, 243); - GreenLightForwardRightTexture2D = LoadDllResource("light_5_3.png", 103, 243); - // forwardleft - RedLightForwardLeftTexture2D = LoadDllResource("light_6_1.png", 103, 243); - YellowLightForwardLeftTexture2D = LoadDllResource("light_6_2.png", 103, 243); - GreenLightForwardLeftTexture2D = LoadDllResource("light_6_3.png", 103, 243); - // yellow - YellowLightTexture2D = LoadDllResource("light_yellow.png", 103, 243); - // pedestrian - PedestrianRedLightTexture2D = LoadDllResource("pedestrian_light_1.png", 73, 123); - PedestrianGreenLightTexture2D = LoadDllResource("pedestrian_light_2.png", 73, 123); - // light mode - LightModeTexture2D = LoadDllResource(Translation.GetTranslatedFileName("light_mode.png"), 103, 95); - LightCounterTexture2D = LoadDllResource(Translation.GetTranslatedFileName("light_counter.png"), 103, 95); - // pedestrian mode - PedestrianModeAutomaticTexture2D = LoadDllResource("pedestrian_mode_1.png", 73, 70); - PedestrianModeManualTexture2D = LoadDllResource("pedestrian_mode_2.png", 73, 73); + YellowRedLightTexture2D = LoadDllResource("light_1_2.png", 103, 243); + GreenLightTexture2D = LoadDllResource("light_1_3.png", 103, 243); + // forward + RedLightStraightTexture2D = LoadDllResource("light_2_1.png", 103, 243); + YellowLightStraightTexture2D = LoadDllResource("light_2_2.png", 103, 243); + GreenLightStraightTexture2D = LoadDllResource("light_2_3.png", 103, 243); + // right + RedLightRightTexture2D = LoadDllResource("light_3_1.png", 103, 243); + YellowLightRightTexture2D = LoadDllResource("light_3_2.png", 103, 243); + GreenLightRightTexture2D = LoadDllResource("light_3_3.png", 103, 243); + // left + RedLightLeftTexture2D = LoadDllResource("light_4_1.png", 103, 243); + YellowLightLeftTexture2D = LoadDllResource("light_4_2.png", 103, 243); + GreenLightLeftTexture2D = LoadDllResource("light_4_3.png", 103, 243); + // forwardright + RedLightForwardRightTexture2D = LoadDllResource("light_5_1.png", 103, 243); + YellowLightForwardRightTexture2D = LoadDllResource("light_5_2.png", 103, 243); + GreenLightForwardRightTexture2D = LoadDllResource("light_5_3.png", 103, 243); + // forwardleft + RedLightForwardLeftTexture2D = LoadDllResource("light_6_1.png", 103, 243); + YellowLightForwardLeftTexture2D = LoadDllResource("light_6_2.png", 103, 243); + GreenLightForwardLeftTexture2D = LoadDllResource("light_6_3.png", 103, 243); + // yellow + YellowLightTexture2D = LoadDllResource("light_yellow.png", 103, 243); + // pedestrian + PedestrianRedLightTexture2D = LoadDllResource("pedestrian_light_1.png", 73, 123); + PedestrianGreenLightTexture2D = LoadDllResource("pedestrian_light_2.png", 73, 123); + // light mode + LightModeTexture2D = + LoadDllResource(Translation.GetTranslatedFileName("light_mode.png"), 103, 95); + LightCounterTexture2D = + LoadDllResource(Translation.GetTranslatedFileName("light_counter.png"), 103, 95); + // pedestrian mode + PedestrianModeAutomaticTexture2D = LoadDllResource("pedestrian_mode_1.png", 73, 70); + PedestrianModeManualTexture2D = LoadDllResource("pedestrian_mode_2.png", 73, 73); // priority signs PrioritySignTextures = new TinyDictionary(); @@ -123,7 +127,7 @@ static TextureResources() PrioritySignTextures[PriorityType.Main] = LoadDllResource("sign_priority.png", 200, 200); PrioritySignTextures[PriorityType.Stop] = LoadDllResource("sign_stop.png", 200, 200); PrioritySignTextures[PriorityType.Yield] = LoadDllResource("sign_yield.png", 200, 200); - + // delete priority sign SignRemoveTexture2D = LoadDllResource("remove_signs.png", 256, 256); @@ -132,9 +136,25 @@ static TextureResources() ClockPauseTexture2D = LoadDllResource("clock_pause.png", 512, 512); ClockTestTexture2D = LoadDllResource("clock_test.png", 512, 512); - SpeedLimitTextures = new TinyDictionary(); - foreach (ushort speedLimit in SpeedLimitManager.Instance.AvailableSpeedLimits) { - SpeedLimitTextures.Add(speedLimit, LoadDllResource(speedLimit.ToString() + ".png", 200, 200)); + // TODO: Split loading here into dynamic sections, static enforces everything to stay in this ctor + SpeedLimitTexturesKmph = new TinyDictionary(); + SpeedLimitTexturesMphUS = new TinyDictionary(); + SpeedLimitTexturesMphUK = new TinyDictionary(); + + // Load shared speed limit signs for Kmph and Mph + // Assumes that signs from 0 to 140 with step 5 exist, 0 denotes no limit sign + for (var speedLimit = 0; speedLimit <= 140; speedLimit += 5) { + var resource = LoadDllResource($"SpeedLimits.Kmh.{speedLimit}.png", 200, 200); + SpeedLimitTexturesKmph.Add(speedLimit, resource ?? SpeedLimitTexturesKmph[5]); + } + // Signs from 0 to 90 for MPH + for (var speedLimit = 0; speedLimit <= 90; speedLimit += 5) { + // Load US textures, they are rectangular + var resourceUs = LoadDllResource($"SpeedLimits.Mph_US.{speedLimit}.png", 200, 250); + SpeedLimitTexturesMphUS.Add(speedLimit, resourceUs ?? SpeedLimitTexturesMphUS[5]); + // Load UK textures, they are square + var resourceUk = LoadDllResource($"SpeedLimits.Mph_UK.{speedLimit}.png", 200, 200); + SpeedLimitTexturesMphUK.Add(speedLimit, resourceUk ?? SpeedLimitTexturesMphUK[5]); } VehicleRestrictionTextures = new TinyDictionary>(); @@ -143,14 +163,18 @@ static TextureResources() VehicleRestrictionTextures[ExtVehicleType.CargoTruck] = new TinyDictionary(); VehicleRestrictionTextures[ExtVehicleType.Emergency] = new TinyDictionary(); VehicleRestrictionTextures[ExtVehicleType.PassengerCar] = new TinyDictionary(); - VehicleRestrictionTextures[ExtVehicleType.PassengerTrain] = new TinyDictionary(); + VehicleRestrictionTextures[ExtVehicleType.PassengerTrain] = + new TinyDictionary(); VehicleRestrictionTextures[ExtVehicleType.Service] = new TinyDictionary(); VehicleRestrictionTextures[ExtVehicleType.Taxi] = new TinyDictionary(); - foreach (KeyValuePair> e in VehicleRestrictionTextures) { - foreach (bool b in new bool[]{false, true}) { + foreach (KeyValuePair> e in + VehicleRestrictionTextures) { + foreach (bool b in new bool[] {false, true}) { string suffix = b ? "allowed" : "forbidden"; - e.Value[b] = LoadDllResource(e.Key.ToString().ToLower() + "_" + suffix + ".png", 200, 200); + e.Value[b] = + LoadDllResource(e.Key.ToString().ToLower() + "_" + suffix + ".png", 200, + 200); } } @@ -164,27 +188,36 @@ static TextureResources() UturnAllowedTexture2D = LoadDllResource("uturn_allowed.png", 200, 200); UturnForbiddenTexture2D = LoadDllResource("uturn_forbidden.png", 200, 200); - RightOnRedAllowedTexture2D = LoadDllResource("right_on_red_allowed.png", 200, 200); - RightOnRedForbiddenTexture2D = LoadDllResource("right_on_red_forbidden.png", 200, 200); - LeftOnRedAllowedTexture2D = LoadDllResource("left_on_red_allowed.png", 200, 200); - LeftOnRedForbiddenTexture2D = LoadDllResource("left_on_red_forbidden.png", 200, 200); + RightOnRedAllowedTexture2D = LoadDllResource("right_on_red_allowed.png", 200, 200); + RightOnRedForbiddenTexture2D = LoadDllResource("right_on_red_forbidden.png", 200, 200); + LeftOnRedAllowedTexture2D = LoadDllResource("left_on_red_allowed.png", 200, 200); + LeftOnRedForbiddenTexture2D = LoadDllResource("left_on_red_forbidden.png", 200, 200); - EnterBlockedJunctionAllowedTexture2D = LoadDllResource("enterblocked_allowed.png", 200, 200); - EnterBlockedJunctionForbiddenTexture2D = LoadDllResource("enterblocked_forbidden.png", 200, 200); + EnterBlockedJunctionAllowedTexture2D = LoadDllResource("enterblocked_allowed.png", 200, 200); + EnterBlockedJunctionForbiddenTexture2D = + LoadDllResource("enterblocked_forbidden.png", 200, 200); PedestrianCrossingAllowedTexture2D = LoadDllResource("crossing_allowed.png", 200, 200); PedestrianCrossingForbiddenTexture2D = LoadDllResource("crossing_forbidden.png", 200, 200); VehicleInfoSignTextures = new TinyDictionary(); - VehicleInfoSignTextures[ExtVehicleType.Bicycle] = LoadDllResource("bicycle_infosign.png", 449, 411); + VehicleInfoSignTextures[ExtVehicleType.Bicycle] = + LoadDllResource("bicycle_infosign.png", 449, 411); VehicleInfoSignTextures[ExtVehicleType.Bus] = LoadDllResource("bus_infosign.png", 449, 411); - VehicleInfoSignTextures[ExtVehicleType.CargoTrain] = LoadDllResource("cargotrain_infosign.png", 449, 411); - VehicleInfoSignTextures[ExtVehicleType.CargoTruck] = LoadDllResource("cargotruck_infosign.png", 449, 411); - VehicleInfoSignTextures[ExtVehicleType.Emergency] = LoadDllResource("emergency_infosign.png", 449, 411); - VehicleInfoSignTextures[ExtVehicleType.PassengerCar] = LoadDllResource("passengercar_infosign.png", 449, 411); - VehicleInfoSignTextures[ExtVehicleType.PassengerTrain] = LoadDllResource("passengertrain_infosign.png", 449, 411); - VehicleInfoSignTextures[ExtVehicleType.RailVehicle] = VehicleInfoSignTextures[ExtVehicleType.PassengerTrain]; - VehicleInfoSignTextures[ExtVehicleType.Service] = LoadDllResource("service_infosign.png", 449, 411); + VehicleInfoSignTextures[ExtVehicleType.CargoTrain] = + LoadDllResource("cargotrain_infosign.png", 449, 411); + VehicleInfoSignTextures[ExtVehicleType.CargoTruck] = + LoadDllResource("cargotruck_infosign.png", 449, 411); + VehicleInfoSignTextures[ExtVehicleType.Emergency] = + LoadDllResource("emergency_infosign.png", 449, 411); + VehicleInfoSignTextures[ExtVehicleType.PassengerCar] = + LoadDllResource("passengercar_infosign.png", 449, 411); + VehicleInfoSignTextures[ExtVehicleType.PassengerTrain] = + LoadDllResource("passengertrain_infosign.png", 449, 411); + VehicleInfoSignTextures[ExtVehicleType.RailVehicle] = + VehicleInfoSignTextures[ExtVehicleType.PassengerTrain]; + VehicleInfoSignTextures[ExtVehicleType.Service] = + LoadDllResource("service_infosign.png", 449, 411); VehicleInfoSignTextures[ExtVehicleType.Taxi] = LoadDllResource("taxi_infosign.png", 449, 411); VehicleInfoSignTextures[ExtVehicleType.Tram] = LoadDllResource("tram_infosign.png", 449, 411); @@ -193,6 +226,59 @@ static TextureResources() WindowBackgroundTexture2D = LoadDllResource("WindowBackground.png", 16, 60); } + /// + /// Given speed limit, round it up to nearest Kmph or Mph and produce a texture + /// + /// Ingame speed + /// The texture, hopefully it existed + public static Texture2D GetSpeedLimitTexture(float speedLimit) { + var m = GlobalConfig.Instance.Main; + var unit = m.DisplaySpeedLimitsMph ? SpeedUnit.Mph : SpeedUnit.Kmph; + return GetSpeedLimitTexture(speedLimit, m.MphRoadSignStyle, unit); + } + + /// + /// Given the float speed, style and MPH option return a texture to render. + /// + /// float speed + /// Signs theme + /// Mph or km/h + /// + public static Texture2D GetSpeedLimitTexture(float speedLimit, MphSignStyle mphStyle, SpeedUnit unit) { + // Select the source for the textures based on unit and the theme + var mph = unit == SpeedUnit.Mph; + var textures = SpeedLimitTexturesKmph; + if (mph) { + switch (mphStyle) { + case MphSignStyle.SquareUS: + textures = SpeedLimitTexturesMphUS; + break; + case MphSignStyle.RoundUK: + textures = SpeedLimitTexturesMphUK; + break; + case MphSignStyle.RoundGerman: + // Do nothing, this is the default above + break; + } + } + + // Trim the range + if (speedLimit > SpeedLimitManager.MAX_SPEED * 0.95f) { + return textures[0]; + } + + // Round to nearest 5 MPH or nearest 10 km/h + var index = mph ? SpeedLimit.ToMphRounded(speedLimit) : SpeedLimit.ToKmphRounded(speedLimit); + + // Trim the index since 140 km/h / 90 MPH is the max sign we have + var upper = mph ? SpeedLimit.UPPER_MPH : SpeedLimit.UPPER_KMPH; + if (index > upper) { + Log.Info($"Trimming speed={speedLimit} index={index} to {upper}"); + } + var trimIndex = Math.Min(upper, Math.Max((ushort)0, index)); + return textures[trimIndex]; + } + private static Texture2D LoadDllResource(string resourceName, int width, int height) { #if DEBUG diff --git a/TLM/TLM/UI/TrafficManagerTool.cs b/TLM/TLM/UI/TrafficManagerTool.cs index 524b8d9ae..1d54baa89 100644 --- a/TLM/TLM/UI/TrafficManagerTool.cs +++ b/TLM/TLM/UI/TrafficManagerTool.cs @@ -948,16 +948,26 @@ private void _guiVehicles() { ExtCitizenInstance driverInst = ExtCitizenInstanceManager.Instance.ExtInstances[CustomPassengerCarAI.GetDriverInstanceId((ushort)i, ref Singleton.instance.m_vehicles.m_buffer[i])]; bool startNode = vState.currentStartNode; ushort segmentId = vState.currentSegmentId; - ushort vehSpeed = SpeedLimitManager.Instance.VehicleToCustomSpeed(vehicle.GetLastFrameVelocity().magnitude); + // Some magical constant converting magnitudes into km/h + float vehSpeed = SpeedLimit.ToKmphPrecise(vehicle.GetLastFrameVelocity().magnitude / 8f); #if DEBUG if (GlobalConfig.Instance.Debug.ExtPathMode != ExtPathMode.None && driverInst.pathMode != GlobalConfig.Instance.Debug.ExtPathMode) { continue; } #endif - - String labelStr = "V #" + i + " is a " + (vState.recklessDriver ? "reckless " : "") + vState.flags + " " + vState.vehicleType + " @ ~" + vehSpeed + " km/h [^2=" + vState.SqrVelocity + "] (len: " + vState.totalLength + ", " + vState.JunctionTransitState + " @ " + vState.currentSegmentId + " (" + vState.currentStartNode + "), l. " + vState.currentLaneIndex + " -> " + vState.nextSegmentId + ", l. " + vState.nextLaneIndex + "), w: " + vState.waitTime + "\n" + - "di: " + driverInst.instanceId + " dc: " + driverInst.GetCitizenId() + " m: " + driverInst.pathMode.ToString() + " f: " + driverInst.failedParkingAttempts + " l: " + driverInst.parkingSpaceLocation + " lid: " + driverInst.parkingSpaceLocationId + " ltsu: " + vState.lastTransitStateUpdate + " lpu: " + vState.lastPositionUpdate + " als: " + vState.lastAltLaneSelSegmentId + " srnd: " + Constants.ManagerFactory.VehicleBehaviorManager.GetStaticVehicleRand((ushort)i) + " trnd: " + Constants.ManagerFactory.VehicleBehaviorManager.GetTimedVehicleRand((ushort)i); + var labelStr = + $"V #{i} is a {(vState.recklessDriver ? "reckless " : string.Empty)}{vState.flags} " + + $"{vState.vehicleType} @ ~{vehSpeed:0.0} km/h (len: {vState.totalLength:0.0}, " + + $"{vState.JunctionTransitState} @ {vState.currentSegmentId} " + + $"({vState.currentStartNode}), l. {vState.currentLaneIndex} -> {vState.nextSegmentId}, " + + $"l. {vState.nextLaneIndex}), w: {vState.waitTime}\n" + + $"di: {driverInst.instanceId} dc: {driverInst.GetCitizenId()} m: {driverInst.pathMode} " + + $"f: {driverInst.failedParkingAttempts} l: {driverInst.parkingSpaceLocation} " + + $"lid: {driverInst.parkingSpaceLocationId} ltsu: {vState.lastTransitStateUpdate} " + + $"lpu: {vState.lastPositionUpdate} als: {vState.lastAltLaneSelSegmentId} " + + $"srnd: {Constants.ManagerFactory.VehicleBehaviorManager.GetStaticVehicleRand((ushort) i)} " + + $"trnd: {Constants.ManagerFactory.VehicleBehaviorManager.GetTimedVehicleRand((ushort) i)}"; 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);