diff --git a/CHANGELOG.md b/CHANGELOG.md
index fdebec571..0442515fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,28 @@ This changelog includes all versions and major variants of the mod going all the
> Date format: dd/mm/yyyy
+#### TM:PE V[11.7.3.0](https://github.com/CitiesSkylinesMods/TMPE/compare/11.7.2.0...11.7.3.0) STABLE, 13/12/2022
+
+- [Meta] Compatibility patch for the game update 1.16.0-f3 (krzychu124)
+- [New] Improved bus/trolleybus stop path (pull to side earlier and use more of available space) #1688, #1690 (kianzarrin)
+- [Fixed] Cargo truck pathfinding via CargoVehicle networks (cargo stations/terminals) #1701, #1703 (krzychu124)
+- [Fixed] Lane connector on overlapping lanes #1706 (kianzarrin)
+- [Fixed] Parking signs on lanes with bus stop #1707 (kianzarrin)
+- [Updated] Minor changes in debug tools #1696, #1697 (kianzarrin)
+- [Updated] TMPE API Updates compatibility with other mods #1689 #1692 (kianzarrin)
+- [Steam] [TM:PE v11 STABLE](https://steamcommunity.com/sharedfiles/filedetails/?id=1637663252)
+
+#### TM:PE V11.7.3.0 TEST, 13/12/2022
+
+- [Meta] Compatibility patch for the game update 1.16.0-f3 (krzychu124)
+- [New] Improved bus/trolleybus stop path (pull to side earlier and use more of available space) #1688, #1690 (kianzarrin)
+- [Fixed] Cargo truck pathfinding via CargoVehicle networks (cargo stations/terminals) #1701, #1703 (krzychu124)
+- [Fixed] Lane connector on overlapping lanes #1706 (kianzarrin)
+- [Fixed] Parking signs on lanes with bus stop #1707 (kianzarrin)
+- [Updated] Minor changes in debug tools #1696, #1697 (kianzarrin)
+- [Updated] TMPE API Updates compatibility with other mods #1689 #1692 (kianzarrin)
+- [Steam] [TM:PE v11 TEST](https://steamcommunity.com/sharedfiles/filedetails/?id=2489276785)
+
#### TM:PE V[11.7.2.0](https://github.com/CitiesSkylinesMods/TMPE/compare/11.7.1.2...11.7.2.0) STABLE, 15/11/2022
- [Meta] Compatibility patch for the game update 1.15.1-f4 (krzychu124)
diff --git a/README.md b/README.md
index 3e15feebd..47401d08b 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
Report a Bug
-
+
diff --git a/TLM/SharedAssemblyInfo.cs b/TLM/SharedAssemblyInfo.cs
index 1078f5cd6..b7a4568a5 100644
--- a/TLM/SharedAssemblyInfo.cs
+++ b/TLM/SharedAssemblyInfo.cs
@@ -20,4 +20,4 @@
// Minor Version
// Build Number
// Revision
-[assembly: AssemblyVersion("11.7.2.*")]
+[assembly: AssemblyVersion("11.7.3.*")]
diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs
index 9286ac0ae..cfcd20ccd 100644
--- a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs
+++ b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs
@@ -142,6 +142,11 @@ public override string ToString() {
// custom fields
private PathUnitQueueItem queueItem_;
+ ///
+ /// Contains allowed extVehicle types, in normal conditions just QueueIten.vehicleType,
+ /// when path may include CargoVehicle lanes then it contains also respective cargo vehicle types
+ ///
+ private ExtVehicleType allowedPathVehicleTypes_;
private bool isHeavyVehicle_;
private bool debugLog_; // this will be false in non-Debug
@@ -293,8 +298,15 @@ private void PathFindImplementation(uint unit, ref PathUnit data) {
disableMask_ |= NetSegment.Flags.Flooded;
}
+ allowedPathVehicleTypes_ = queueItem_.vehicleType;
if ((laneTypes_ & NetInfo.LaneType.Vehicle) != NetInfo.LaneType.None) {
laneTypes_ |= NetInfo.LaneType.TransportVehicle;
+
+ // allow mixed vehicle path when requested path is Vehicle + CargoVehicle (e.g.: CargoTruck and PostVanAI)
+ bool allowMixedCargoPath = (laneTypes_ & NetInfo.LaneType.CargoVehicle) != NetInfo.LaneType.None;
+ if (allowMixedCargoPath) {
+ allowedPathVehicleTypes_ |= VehicleTypesToMixedCargoExtVehicle(vehicleTypes_);
+ }
}
#if ROUTING
@@ -2634,12 +2646,6 @@ private bool ProcessItemCosts(
$"\toffsetLength={offsetLength}");
}
#endif
- if ((vehicleCategory_ & VehicleInfo.VehicleCategory.PublicTransportRoad) != 0 &&
- (vehicleCategory_ & ~(VehicleInfo.VehicleCategory.Bus | VehicleInfo.VehicleCategory.Trolleybus | VehicleInfo.VehicleCategory.Taxi)) == 0)
- {
- offsetLength *= 0.75f;
- }
-
float baseLength = offsetLength / (prevLaneSpeed * maxLength_); // NON-STOCK CODE
float comparisonValue = item.ComparisonValue; // NON-STOCK CODE
#if ROUTING
@@ -3134,6 +3140,11 @@ private bool ProcessItemCosts(
$"ProcessItemCosts: Adding next item\n\tnextItem={nextItem}");
}
+ if ((nextLaneInfo.vehicleCategory & VehicleInfo.VehicleCategory.PublicTransportRoad) != 0 &&
+ (nextLaneInfo.vehicleCategory & ~(VehicleInfo.VehicleCategory.Bus | VehicleInfo.VehicleCategory.Trolleybus | VehicleInfo.VehicleCategory.Taxi)) == 0) {
+ nextItem.ComparisonValue -= baseLength * 0.25f;
+ }
+
AddBufferItem(
#if DEBUG
isLogEnabled,
@@ -4092,15 +4103,36 @@ private bool CanUseLane(ushort segmentId,
return true;
}
- ExtVehicleType allowedTypes = vehicleRestrictionsManager.GetAllowedVehicleTypes(
+ ExtVehicleType allowedLaneVehicleTypes = vehicleRestrictionsManager.GetAllowedVehicleTypes(
segmentId,
segmentInfo,
(uint)laneIndex,
laneInfo,
VehicleRestrictionsMode.Configured);
+ return (allowedLaneVehicleTypes & allowedPathVehicleTypes_) != ExtVehicleType.None;
+ }
+
+ ///
+ /// Maps all allowed vehicle types to mixed ExtVehicleType
+ /// Later used to correctly test lane for match with configured allowed vehicle types
+ ///
+ /// Allowed additional vehicle types
+ /// Combined ExtVehicleType of all alowed cargo vehicle lane types
+ private ExtVehicleType VehicleTypesToMixedCargoExtVehicle(VehicleInfo.VehicleType vehicleTypes) {
+ ExtVehicleType extVehicleType = ExtVehicleType.None;
+ if ((vehicleTypes & VehicleInfo.VehicleType.Train) != VehicleInfo.VehicleType.None) {
+ extVehicleType |= ExtVehicleType.CargoTrain;
+ }
+ if ((vehicleTypes & VehicleInfo.VehicleType.Plane) != VehicleInfo.VehicleType.None) {
+ extVehicleType |= ExtVehicleType.CargoPlane;
+ }
+ if ((vehicleTypes & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None) {
+ extVehicleType |= ExtVehicleType.CargoShip;
+ }
- return (allowedTypes & queueItem_.vehicleType) != ExtVehicleType.None;
+ return extVehicleType;
}
+
#endif
#if ADVANCEDAI && ROUTING
diff --git a/TLM/TLM/Custom/PathFinding/PathfinderUpdates.cs b/TLM/TLM/Custom/PathFinding/PathfinderUpdates.cs
index c42859f8f..7a4bf5b55 100644
--- a/TLM/TLM/Custom/PathFinding/PathfinderUpdates.cs
+++ b/TLM/TLM/Custom/PathFinding/PathfinderUpdates.cs
@@ -17,6 +17,7 @@ public static class PathfinderUpdates {
// Edition History:
// 0 - An old save, unknown pathfinder edition
// 1 - #1338 Aircraft pathfinding fix
+ // TODO: This feature is not used and does not persist. if we increment this we need to also serialize this.
///
/// Update this each time a despawn-requiring change is
diff --git a/TLM/TLM/Lifecycle/SerializableDataExtension.cs b/TLM/TLM/Lifecycle/SerializableDataExtension.cs
index 78d4d3fd6..c4a47b7a4 100644
--- a/TLM/TLM/Lifecycle/SerializableDataExtension.cs
+++ b/TLM/TLM/Lifecycle/SerializableDataExtension.cs
@@ -22,6 +22,9 @@ public class SerializableDataExtension
private const string DATA_ID = "TrafficManager_v1.0";
private const string VERSION_INFO_DATA_ID = "TrafficManager_VersionInfo_v1.0";
+ private const string OPTIONS_ID = "TMPE_OptionsXML";
+ private const string OPTIONS_LEGACY_ID = "TMPE_Options";
+
private static ISerializableData SerializableData => SimulationManager.instance.m_SerializableDataWrapper;
private static Configuration _configuration;
@@ -80,16 +83,21 @@ public static void Load() {
if (TMPELifecycle.InGameOrEditor()) {
// Always force default options on new game
// See: https://github.com/CitiesSkylinesMods/TMPE/pull/1425
- byte[] options = TMPELifecycle.IsNewGame
- ? null
- : SerializableData.LoadData("TMPE_Options");
-
- if (!OptionsManager.Instance.LoadData(options ?? new byte[0])) {
- loadingSucceeded = false;
+ if (!TMPELifecycle.IsNewGame) {
+ if (Version <= 3) {
+ byte[] data = SerializableData.LoadData(OPTIONS_ID);
+ if (data != null) {
+ loadingSucceeded &= OptionsManager.Instance.LoadData(data);
+ }
+ } else {
+ byte[] data = SerializableData.LoadData(OPTIONS_LEGACY_ID);
+ if (data != null) {
+ loadingSucceeded &= OptionsManager.Instance.LoadDataLegacy(data);
+ }
+ }
}
}
- }
- catch (Exception e) {
+ } catch (Exception e) {
Log.Error($"OnLoadData: Error while loading options: {e}");
loadingSucceeded = false;
}
@@ -217,9 +225,6 @@ private static void LoadDataState(out bool error) {
return;
}
- // load Path Find Update
- PathfinderUpdates.SavegamePathfinderEdition = _configuration.SavegamePathfinderEdition;
-
// load ext. citizens
if (_configuration.ExtCitizens != null) {
if (!ExtCitizenManager.Instance.LoadData(_configuration.ExtCitizens)) {
@@ -454,7 +459,10 @@ public static void Save() {
try {
if (TMPELifecycle.PlayMode) {
- SerializableData.SaveData("TMPE_Options", OptionsManager.Instance.SaveData(ref success));
+ SerializableData.SaveData(OPTIONS_ID, OptionsManager.Instance.SaveData(ref success));
+
+ // forward compatibility. only needed for a short time:
+ SerializableData.SaveData(OPTIONS_LEGACY_ID, OptionsManager.Instance.SaveDataLegacy(ref success));
}
} catch (Exception ex) {
Log.Error("Unexpected error while saving options: " + ex.Message);
diff --git a/TLM/TLM/Lifecycle/TMPELifecycle.cs b/TLM/TLM/Lifecycle/TMPELifecycle.cs
index 68e909d59..f3dc0b68d 100644
--- a/TLM/TLM/Lifecycle/TMPELifecycle.cs
+++ b/TLM/TLM/Lifecycle/TMPELifecycle.cs
@@ -299,6 +299,8 @@ internal void Load() {
IsGameLoaded = true;
+ SerializableUIOptionBase.UpdateAll();
+
ModUI.OnLevelLoaded();
if (PlayMode) {
Log._Debug("PlayMode");
diff --git a/TLM/TLM/Manager/Impl/OptionsManager.cs b/TLM/TLM/Manager/Impl/OptionsManager.cs
index 5acf26b5c..57344802c 100644
--- a/TLM/TLM/Manager/Impl/OptionsManager.cs
+++ b/TLM/TLM/Manager/Impl/OptionsManager.cs
@@ -17,7 +17,7 @@ namespace TrafficManager.Manager.Impl {
public class OptionsManager
: AbstractCustomManager,
- IOptionsManager {
+ IOptionsManager, ICustomDataManager {
public static OptionsManager Instance = new OptionsManager();
@@ -67,6 +67,7 @@ public override void OnBeforeLoadData() {
public override void OnAfterLoadData() {
base.OnAfterLoadData();
Log.Info("OptionsManger.OnAfterLoadData() checking for queued method calls");
+ SerializableUIOptionBase.UpdateAll();
if (_needUpdateDedicatedTurningLanes) {
_needUpdateDedicatedTurningLanes = false;
@@ -103,29 +104,112 @@ private static void ToOption(byte[] data, uint idx, ILegacySerializableOption op
/// Returns true if successful, otherwise false.
public byte[] SaveData(ref bool success) {
try {
- return SavedGameOptions.Instance.Serialize();
+ string xml = SavedGameOptions.Instance.Serialize();
+ return Encoding.ASCII.GetBytes(xml);
} catch (Exception ex) {
ex.LogException();
+ success = false;
return null; // try and salvage some of the settings
}
}
public bool LoadData(byte[] data) {
try {
+ string xml = Encoding.ASCII.GetString(data);
+ Log._Debug("OptionsManager.LoadedData() called. XML =\n" + xml);
SavedGameOptions.Available = false;
- int dataVersion = SerializableDataExtension.Version;
- if (data.IsNullOrEmpty()) {
- return true;
- } else if (dataVersion < 4) {
- return LoadDataLegacy(data);
- } else {
- return SavedGameOptions.Deserialize(data);
- }
+ return SavedGameOptions.Deserialize(xml);
+ } catch(Exception ex) {
+ ex.LogException();
+ return false;
} finally {
SavedGameOptions.Available = true;
}
}
+ // forward compatibility. This method is only necessary for a short time.
+ public byte[] SaveDataLegacy(ref bool success) {
+
+ var save = new byte[61];
+
+ try {
+ save[0] = GeneralTab_SimulationGroup.SimulationAccuracy.Save();
+ save[1] = 0; // SavedGameOptions.Instance.laneChangingRandomization
+ save[2] = GameplayTab_VehicleBehaviourGroup.RecklessDrivers.Save();
+ save[3] = (byte)(SavedGameOptions.Instance.relaxedBusses ? 1 : 0);
+ save[4] = (byte)(SavedGameOptions.Instance.nodesOverlay ? 1 : 0);
+ save[5] = (byte)(SavedGameOptions.Instance.allowEnterBlockedJunctions ? 1 : 0);
+ save[6] = (byte)(SavedGameOptions.Instance.advancedAI ? 1 : 0);
+ save[7] = (byte)(SavedGameOptions.Instance.highwayRules ? 1 : 0);
+ save[8] = (byte)(SavedGameOptions.Instance.prioritySignsOverlay ? 1 : 0);
+ save[9] = (byte)(SavedGameOptions.Instance.timedLightsOverlay ? 1 : 0);
+ save[10] = (byte)(SavedGameOptions.Instance.speedLimitsOverlay ? 1 : 0);
+ save[11] = (byte)(SavedGameOptions.Instance.vehicleRestrictionsOverlay ? 1 : 0);
+ save[12] = GameplayTab_VehicleBehaviourGroup.StrongerRoadConditionEffects.Save();
+ save[13] = (byte)(SavedGameOptions.Instance.allowUTurns ? 1 : 0);
+ save[14] = (byte)(SavedGameOptions.Instance.allowLaneChangesWhileGoingStraight ? 1 : 0);
+ save[15] = (byte)(SavedGameOptions.Instance.disableDespawning ? 1 : 0);
+ save[16] = 0; // SavedGameOptions.Instance.IsDynamicPathRecalculationActive
+ save[17] = (byte)(SavedGameOptions.Instance.connectedLanesOverlay ? 1 : 0);
+ save[18] = (byte)(SavedGameOptions.Instance.prioritySignsEnabled ? 1 : 0);
+ save[19] = (byte)(SavedGameOptions.Instance.timedLightsEnabled ? 1 : 0);
+ save[20] = (byte)(SavedGameOptions.Instance.customSpeedLimitsEnabled ? 1 : 0);
+ save[21] = (byte)(SavedGameOptions.Instance.vehicleRestrictionsEnabled ? 1 : 0);
+ save[22] = (byte)(SavedGameOptions.Instance.laneConnectorEnabled ? 1 : 0);
+ save[23] = (byte)(SavedGameOptions.Instance.junctionRestrictionsOverlay ? 1 : 0);
+ save[24] = (byte)(SavedGameOptions.Instance.junctionRestrictionsEnabled ? 1 : 0);
+ save[25] = (byte)(SavedGameOptions.Instance.parkingAI ? 1 : 0);
+ save[26] = (byte)(SavedGameOptions.Instance.preferOuterLane ? 1 : 0);
+ save[27] = GameplayTab_VehicleBehaviourGroup.IndividualDrivingStyle.Save();
+ save[28] = (byte)(SavedGameOptions.Instance.evacBussesMayIgnoreRules ? 1 : 0);
+ save[29] = 0; // (byte)(SavedGameOptions.Instance.instantEffects ? 1 : 0);
+ save[30] = (byte)(SavedGameOptions.Instance.parkingRestrictionsEnabled ? 1 : 0);
+ save[31] = (byte)(SavedGameOptions.Instance.parkingRestrictionsOverlay ? 1 : 0);
+ save[32] = (byte)(SavedGameOptions.Instance.banRegularTrafficOnBusLanes ? 1 : 0);
+ save[33] = (byte)(SavedGameOptions.Instance.showPathFindStats ? 1 : 0);
+ save[34] = SavedGameOptions.Instance.altLaneSelectionRatio;
+ save[35] = PoliciesTab_OnRoadsGroup.VehicleRestrictionsAggression.Save();
+ save[36] = (byte)(SavedGameOptions.Instance.trafficLightPriorityRules ? 1 : 0);
+ save[37] = (byte)(SavedGameOptions.Instance.realisticPublicTransport ? 1 : 0);
+ save[38] = (byte)(SavedGameOptions.Instance.turnOnRedEnabled ? 1 : 0);
+ save[39] = (byte)(SavedGameOptions.Instance.allowNearTurnOnRed ? 1 : 0);
+ save[40] = (byte)(SavedGameOptions.Instance.allowFarTurnOnRed ? 1 : 0);
+ save[41] = (byte)(SavedGameOptions.Instance.automaticallyAddTrafficLightsIfApplicable ? 1 : 0);
+
+ save[42] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_StayInLaneMainR.Save();
+ save[43] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_StayInLaneNearRabout.Save();
+ save[44] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_DedicatedExitLanes.Save();
+ save[45] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_NoCrossMainR.Save();
+ save[46] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_NoCrossYieldR.Save();
+ save[47] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_PrioritySigns.Save();
+
+ save[48] = PoliciesTab_PriorityRoadsGroup.PriorityRoad_CrossMainR.Save();
+ save[49] = PoliciesTab_PriorityRoadsGroup.PriorityRoad_AllowLeftTurns.Save();
+ save[50] = PoliciesTab_PriorityRoadsGroup.PriorityRoad_EnterBlockedYeild.Save();
+ save[51] = PoliciesTab_PriorityRoadsGroup.PriorityRoad_StopAtEntry.Save();
+
+ save[52] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_KeepClearYieldR.Save();
+ save[53] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_RealisticSpeedLimits.Save();
+ save[54] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_ParkingBanMainR.Save();
+ save[55] = PoliciesTab_RoundaboutsGroup.RoundAboutQuickFix_ParkingBanYieldR.Save();
+
+ save[56] = PoliciesTab_OnRoadsGroup.NoDoubleCrossings.Save();
+ save[57] = PoliciesTab_AtJunctionsGroup.DedicatedTurningLanes.Save();
+
+ save[58] = PathfinderUpdates.SavegamePathfinderEdition;
+
+ save[59] = OverlaysTab_OverlaysGroup.ShowDefaultSpeedSubIcon.Save();
+
+ save[60] = PoliciesTab_OnHighwaysGroup.HighwayMergingRules.Save();
+
+ return save;
+ } catch (Exception ex) {
+ ex.LogException();
+ return save; // try and salvage some of the settings
+ }
+ }
+
+
///
/// Restores the mod options based on supplied .
///
diff --git a/TLM/TLM/Manager/Impl/ParkingRestrictionsManager.cs b/TLM/TLM/Manager/Impl/ParkingRestrictionsManager.cs
index db3e70242..95dd30341 100644
--- a/TLM/TLM/Manager/Impl/ParkingRestrictionsManager.cs
+++ b/TLM/TLM/Manager/Impl/ParkingRestrictionsManager.cs
@@ -25,15 +25,30 @@ private ParkingRestrictionsManager() { }
public bool MayHaveParkingRestriction(ushort segmentId) {
ref NetSegment segment = ref segmentId.ToSegment();
- if ((segment.m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) {
- return true;
+ if (!segment.IsValid()) {
+ return false;
}
ItemClass connectionClass = segment.Info.GetConnectionClass();
-
return connectionClass.m_service == ItemClass.Service.Road && segment.Info.m_hasParkingSpaces;
}
+ public bool MayHaveParkingRestriction(ushort segmentId, NetInfo.Direction finalDir) {
+ ref NetSegment segment = ref segmentId.ToSegment();
+ var dir = Shortcuts.RHT ? finalDir : NetInfo.InvertDirection(finalDir);
+ bool right = (dir == NetInfo.Direction.Forward) != segment.m_flags.IsFlagSet(NetSegment.Flags.Invert);
+ if (right) {
+ if (segment.m_flags.IsFlagSet(NetSegment.Flags.StopRight | NetSegment.Flags.StopRight2)) {
+ return false;
+ }
+ } else {
+ if (segment.m_flags.IsFlagSet(NetSegment.Flags.StopLeft | NetSegment.Flags.StopLeft2)) {
+ return false;
+ }
+ }
+ return MayHaveParkingRestriction(segmentId);
+ }
+
public bool IsParkingAllowed(ushort segmentId, NetInfo.Direction finalDir) {
return parkingAllowed[segmentId][GetDirIndex(finalDir)];
}
diff --git a/TLM/TLM/Manager/Impl/RoutingManager.cs b/TLM/TLM/Manager/Impl/RoutingManager.cs
index 40eb50aef..eac229125 100644
--- a/TLM/TLM/Manager/Impl/RoutingManager.cs
+++ b/TLM/TLM/Manager/Impl/RoutingManager.cs
@@ -27,7 +27,7 @@ public class RoutingManager
private RoutingManager() { }
public const NetInfo.LaneType ROUTED_LANE_TYPES =
- NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
+ NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle | NetInfo.LaneType.CargoVehicle;
public const VehicleInfo.VehicleType ROUTED_VEHICLE_TYPES =
VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Metro |
@@ -558,7 +558,7 @@ void _ExtendedLogImpl(params object[] lines) => DetailLogger.LogDebug(
bool onHighway = SavedGameOptions.Instance.highwayRules && onOnewayHighway;
bool applyHighwayRules = onHighway && nodeIsSimpleJunction;
bool applyHighwayRulesAtJunction = applyHighwayRules && nodeIsRealJunction;
- bool iterateViaGeometry = (applyHighwayRulesAtJunction || applyHighwayMergingRules) && prevLaneInfo.MatchesRoad();
+ bool iterateViaGeometry = (applyHighwayRulesAtJunction || applyHighwayMergingRules) && prevLaneInfo.MatchesRoutedRoad();
// start with u-turns at highway junctions
ushort nextSegmentId = iterateViaGeometry ? prevSegmentId : (ushort)0;
@@ -759,7 +759,7 @@ void _ExtendedLogImpl(params object[] lines) => DetailLogger.LogDebug(
}
}
- if (nextLaneInfo.MatchesRoad() && prevLaneInfo.MatchesRoad()) {
+ if (nextLaneInfo.MatchesRoutedRoad() && prevLaneInfo.MatchesRoutedRoad()) {
// routing road vehicles (car, SOS, bus, trolleybus, ...)
// lane may be mixed car+tram
++incomingCarLanes;
@@ -905,7 +905,7 @@ void _ExtendedLogImpl(params object[] lines) => DetailLogger.LogDebug(
nextLaneInfo = new { nextLaneInfo.m_finalDirection }, nextExpectedDirection });
bool outgoing = (nextLaneInfo.m_finalDirection & NetInfo.InvertDirection(nextExpectedDirection)) != NetInfo.Direction.None;
- if (outgoing && nextLaneInfo.MatchesRoad()) {
+ if (outgoing && nextLaneInfo.MatchesRoutedRoad()) {
++outgoingCarLanes;
extendedLog?.Invoke(new { _ = "increasing number of outgoing lanes at ",
diff --git a/TLM/TLM/Resources/Translations/Strings.csv b/TLM/TLM/Resources/Translations/Strings.csv
index 96b95d07a..1b41e5352 100644
--- a/TLM/TLM/Resources/Translations/Strings.csv
+++ b/TLM/TLM/Resources/Translations/Strings.csv
@@ -23,7 +23,7 @@
"Often","Häufig","Often","Frecuentemente","Souvent","Gyakran","Di frequente","頻繁","자주","Vaak","Często","Frequentemente","Часто","經常","经常","Often"
"Only_if_necessary","Nur falls nötig","Only if necessary","Sólo si es necesario","Seulement si nécessaire","Csak ha szükséges","Solo se necessario","必要時のみ","필요한 경우에만","Alleen indien nodig","Tylko jeśli konieczna","Apenas se necessário","Только при необходимости","僅必要時","仅在必要时","Only if necessary"
"Options","Optionen","Options","Opciones","Paramètres","Opciók","Opzioni","オプション","설정","Instellingen","Opcje","Opções","Свойства","選項","选项","Options"
-"SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","Opzioni. Semaforo a tempo","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights","SavedGameOptions.Instance.Timed traffic lights"
+"Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Opzioni. Semaforo a tempo","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights","Options.Timed traffic lights"
"Persistently_visible_overlays","Dauerhaft sichtbare Overlays","Persistently visible overlays","Datos mostrados en el juego","Annonces en superposition visibles de façon permanente","Tartósan látható átfedések","Overlays visibili persistentemente","オーバーレイ表示し続ける情報","화면에 출력할 코드 선택","Permanent zichtbare projecties","Trwale widoczne nakładki","Sobreposições persistentemente visíveis","Постоянно видимые оверлеи","固定檢視的項目","持续可见的覆盖","Persistently visible overlays"
"Please_wait","Bitte warten","Please wait","Espera por favor","Merci de patienter","Kérlek várj","Attendere prego","お待ちください","잠시만 기다려주십시오","Even geduld graag","Proszę czekać","Por favor, aguarde","Подождите, пожалуйста","請稍候","请稍候","Please wait"
"Prohibit_spawning_of_pocket_cars","Verbiete Cims das Spawnen von Autos ""aus der Tasche heraus""","Prohibit cims to spawn pocket cars","Prohibir la creación de autos pequeños","Empêcher l'apparition de voitures de poche","Tilos a cimkéknek zsebjárművek ívását","Vieta ai ""cims"" di spawnare ""pocket cars""","ポケットカーの生成禁止","시민들의 포켓차량 소환 금지","Verbied opduiken van broekzakautomobielen","Zabroń używania ""kieszonkowych samochodów""","Proibir o spawn de carros de bolso","Запрет появления ""карманных""(из сумки) автомобилей","禁止行人憑空變出汽車移動","禁止袖珍车产生","Prohibit cims to spawn pocket cars"
diff --git a/TLM/TLM/Resources/whats_new.txt b/TLM/TLM/Resources/whats_new.txt
index 440e1dadc..919312d54 100644
--- a/TLM/TLM/Resources/whats_new.txt
+++ b/TLM/TLM/Resources/whats_new.txt
@@ -1,10 +1,12 @@
-[Version] 11.7.2.0
-[Released] Nov 15th 2022
-[Link] tmpe-v11720-stable-15112022
+[Version] 11.7.3.0
+[Released] Dec 13th 2022
+[Link] tmpe-v11730-stable-13122022
[Stable]
-[Meta] Compatibility patch for the game update 1.15.1-f4 (krzychu124)
-[New] Clear all TM:PE rules from selected node #692, #1668 (kianzarrin)
-[Fixed] Timed Traffic Lights wait = 0 issues #1497, #1674 (kianzarrin)
-[Updated] Minor changes to Timed Traffic Lights sensitivity slider #1670 (kianzarrin)
-[Updated] Pedestrian zone roads have now lower priority when setting up high priority road #1653 (kianzarrin)
+[Meta] Compatibility patch for the game update 1.16.0-f3 (krzychu124)
+[New] Improved bus/trolleybus stop path (pull to side earlier and use more of available space) #1688, #1690 (kianzarrin)
+[Fixed] Cargo truck pathfinding via CargoVehicle networks (cargo stations/terminals) #1701, #1703 (krzychu124)
+[Fixed] Lane connector on overlapping lanes #1706 (kianzarrin)
+[Fixed] Parking signs on lanes with bus stop #1707 (kianzarrin)
+[Updated] Minor changes in debug tools #1696, #1697 (kianzarrin)
+[Updated] TMPE API Updates compatibility with other mods #1689 #1692 (kianzarrin)
[/Version]
\ No newline at end of file
diff --git a/TLM/TLM/State/Configuration.cs b/TLM/TLM/State/Configuration.cs
index 0a8331e18..474c239ff 100644
--- a/TLM/TLM/State/Configuration.cs
+++ b/TLM/TLM/State/Configuration.cs
@@ -8,7 +8,6 @@ namespace TrafficManager {
using TrafficManager.Traffic;
using System.Runtime.Serialization;
using TrafficManager.Lifecycle;
- using TrafficManager.Custom.PathFinding;
using LaneEndTransitionGroup = TrafficManager.API.Traffic.Enums.LaneEndTransitionGroup;
[Serializable]
@@ -362,8 +361,6 @@ public ExtCitizenData(uint citizenId) {
///
public List ParkingRestrictions = new List();
- public byte SavegamePathfinderEdition = PathfinderUpdates.LatestPathfinderEdition;
-
[Obsolete]
public string NodeTrafficLights = string.Empty;
diff --git a/TLM/TLM/State/GlobalConfig.cs b/TLM/TLM/State/GlobalConfig.cs
index 9f738e976..f0d0ff695 100644
--- a/TLM/TLM/State/GlobalConfig.cs
+++ b/TLM/TLM/State/GlobalConfig.cs
@@ -8,9 +8,7 @@ namespace TrafficManager.State {
using TrafficManager.Util;
using TrafficManager.Lifecycle;
- [XmlRootAttribute(
- "GlobalConfig",
- IsNullable = false)]
+ [XmlRoot("GlobalConfig", IsNullable = false)]
public class GlobalConfig : GenericObservable {
public const string FILENAME = "TMPE_GlobalConfig.xml";
public const string BACKUP_FILENAME = FILENAME + ".bak";
@@ -83,13 +81,18 @@ internal static void WriteConfig() {
ModifiedTime = WriteConfig(Instance);
}
+ ///
+ /// Replaces global config file with default config.
+ ///
+ /// if null, all is reset, otherwise old config is migrated to new config.
+ /// time of the file created
+ /// the new default config
private static GlobalConfig WriteDefaultConfig(GlobalConfig oldConfig,
- bool resetAll,
out DateTime modifiedTime) {
Log._Debug($"Writing default config...");
GlobalConfig conf = new GlobalConfig();
- if (!resetAll && oldConfig != null) {
+ if (oldConfig != null) {
conf.Main.MainMenuButtonX = oldConfig.Main.MainMenuButtonX;
conf.Main.MainMenuButtonY = oldConfig.Main.MainMenuButtonY;
@@ -190,7 +193,7 @@ public static GlobalConfig Load(out DateTime modifiedTime) {
}
catch (Exception e) {
Log.Warning($"Could not load global config: {e} Generating default config.");
- return WriteDefaultConfig(null, false, out modifiedTime);
+ return WriteDefaultConfig(null, out modifiedTime);
}
}
@@ -214,17 +217,17 @@ public static void Reload(bool checkVersion = true) {
$"Error occurred while saving backup config to '{filename}': {e.ToString()}");
}
- Reset(conf);
+ Reset(oldConfig: conf);
} else {
Instance = conf;
ModifiedTime = WriteConfig(Instance);
}
}
- public static void Reset(GlobalConfig oldConfig, bool resetAll = false) {
+ public static void Reset(GlobalConfig oldConfig) {
Log.Info($"Resetting global config.");
DateTime modifiedTime;
- Instance = WriteDefaultConfig(oldConfig, resetAll, out modifiedTime);
+ Instance = WriteDefaultConfig(oldConfig, out modifiedTime);
ModifiedTime = modifiedTime;
}
diff --git a/TLM/TLM/State/OptionsTabs/MaintenanceTab_ConfigGroup.cs b/TLM/TLM/State/OptionsTabs/MaintenanceTab_ConfigGroup.cs
index fddb12857..d40bbff4b 100644
--- a/TLM/TLM/State/OptionsTabs/MaintenanceTab_ConfigGroup.cs
+++ b/TLM/TLM/State/OptionsTabs/MaintenanceTab_ConfigGroup.cs
@@ -4,6 +4,7 @@ namespace TrafficManager.State {
using TrafficManager.UI.Helpers;
#if DEBUG
using TrafficManager.UI.DebugSwitches;
+ using UnityEngine;
#endif
public static class MaintenanceTab_ConfigGroup {
@@ -17,6 +18,15 @@ public static class MaintenanceTab_ConfigGroup {
Handler = OnResetGlobalConfigClicked,
};
+ public static ActionButton SetAsDefaultForNewGames = new() {
+ Label = "Maintenance.Button:Set as default for new games",
+ Handler = OnSetAsDefaultForNewGamesClicked,
+ };
+ public static ActionButton ResetDefaultsForNewGames = new() {
+ Label = "Maintenance.Button:Reset defaults for new games",
+ Handler = OnResetDefaultForNewGamesClicked,
+ };
+
#if DEBUG
public static ActionButton DebugSwiches = new() {
Translator = key => key,
@@ -30,6 +40,8 @@ internal static void AddUI(UIHelperBase tab) {
ReloadGlobalConfig.AddUI(group);
ResetGlobalConfig.AddUI(group);
+ SetAsDefaultForNewGames.AddUI(group);
+ ResetDefaultsForNewGames.AddUI(group);
#if DEBUG
DebugSwiches.AddUI(group);
#endif
@@ -37,10 +49,22 @@ internal static void AddUI(UIHelperBase tab) {
private static string T(string key) => Translation.Options.Get(key);
- private static void OnReloadGlobalConfigClicked()
- => GlobalConfig.Reload();
+ private static void OnReloadGlobalConfigClicked() {
+ GlobalConfig.Reload();
+ SerializableUIOptionBase.UpdateAll();
+ }
- private static void OnResetGlobalConfigClicked()
- => GlobalConfig.Reset(oldConfig: null, resetAll: true);
+ private static void OnResetGlobalConfigClicked() {
+ GlobalConfig.Reset(oldConfig: null);
+ SerializableUIOptionBase.UpdateAll();
+ }
+ private static void OnSetAsDefaultForNewGamesClicked() {
+ SavedGameOptions.Instance.SerializeDefaults();
+ SerializableUIOptionBase.UpdateAll();
+ }
+ private static void OnResetDefaultForNewGamesClicked() {
+ SavedGameOptions.ResetDefaults();
+ SerializableUIOptionBase.UpdateAll();
+ }
}
}
\ No newline at end of file
diff --git a/TLM/TLM/State/SavedGameOptions.cs b/TLM/TLM/State/SavedGameOptions.cs
index a6195925b..5620277d6 100644
--- a/TLM/TLM/State/SavedGameOptions.cs
+++ b/TLM/TLM/State/SavedGameOptions.cs
@@ -1,12 +1,13 @@
namespace TrafficManager.State;
using CSUtil.Commons;
using System;
-using System.Text;
+using System.IO;
using TrafficManager.API.Traffic.Enums;
-using TrafficManager.Custom.PathFinding;
using TrafficManager.Util;
public class SavedGameOptions {
+ private const string FILE = "TMPE_DefaultGameSettings.xml";
+
public bool individualDrivingStyle = true;
public RecklessDrivers recklessDrivers = RecklessDrivers.HolyCity;
@@ -115,7 +116,7 @@ public bool IsDynamicLaneSelectionActive() {
public static void Ensure() {
Log.Info("SavedGameOptions.Ensure() called");
if (Instance == null) {
- Create();
+ DeserializeDefaults();
}
}
private static void Create() {
@@ -133,20 +134,18 @@ private void Awake() {
Log.Info("SavedGameOptions.Awake() called");
}
- public byte[] Serialize() {
+ public string Serialize() {
try {
- string xml = XMLUtil.Serialize(this);
- return Encoding.ASCII.GetBytes(xml);
+ return XMLUtil.Serialize(this);
} catch (Exception ex) {
ex.LogException();
return null;
}
}
- public static bool Deserialize(byte[] data) {
+ public static bool Deserialize(string xml) {
try {
- if (!data.IsNullOrEmpty()) {
- string xml = Encoding.ASCII.GetString(data);
+ if (!string.IsNullOrEmpty(xml)) {
Instance = XMLUtil.Deserialize(xml);
Instance.Awake();
return true;
@@ -156,4 +155,34 @@ public static bool Deserialize(byte[] data) {
}
return false;
}
+
+ public void SerializeDefaults() {
+ try {
+ Log.Info("SavedGameOptions.SerializeDefaults() called");
+ XMLUtil.Serialize(this, FILE);
+ } catch (Exception ex) {
+ ex.LogException();
+ }
+ }
+
+ public static void DeserializeDefaults() {
+ try {
+ Log.Info("SavedGameOptions.DeserializeDefaults() called");
+ Instance = XMLUtil.DeserializeFile(FILE) ?? new();
+ Instance.Awake();
+ } catch (Exception ex) {
+ ex.LogException();
+ }
+ }
+
+ public static void ResetDefaults() {
+ try {
+ Log.Info("SavedGameOptions.ResetDefaults() called");
+ if (File.Exists(FILE)) {
+ File.Delete(FILE);
+ }
+ } catch (Exception ex) {
+ ex.LogException();
+ }
+ }
}
diff --git a/TLM/TLM/State/XMLUtil.cs b/TLM/TLM/State/XMLUtil.cs
index f4c855af7..3e50ec0cc 100644
--- a/TLM/TLM/State/XMLUtil.cs
+++ b/TLM/TLM/State/XMLUtil.cs
@@ -28,5 +28,26 @@ internal static T Deserialize(string data) {
return (T)ser.Deserialize(reader);
}
}
+
+ internal static void Serialize(T obj, string filePath) {
+ var serializer = new XmlSerializer(typeof(T));
+ using (StreamWriter writer = new StreamWriter(filePath)) {
+ using (var xmlWriter = new XmlTextWriter(writer)) {
+ xmlWriter.Formatting = Formatting.Indented;
+ serializer.Serialize(xmlWriter, obj, NoNamespaces);
+ }
+ }
+ }
+
+ public static T DeserializeFile(string filePath) {
+ if (!File.Exists(filePath)) {
+ return default;
+ }
+
+ XmlSerializer ser = new XmlSerializer(typeof(T));
+ using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
+ return (T)ser.Deserialize(fs);
+ }
+ }
}
}
diff --git a/TLM/TLM/UI/Helpers/CheckboxOption.cs b/TLM/TLM/UI/Helpers/CheckboxOption.cs
index 971fa70c4..3a8f41d81 100644
--- a/TLM/TLM/UI/Helpers/CheckboxOption.cs
+++ b/TLM/TLM/UI/Helpers/CheckboxOption.cs
@@ -67,12 +67,11 @@ public override bool Value {
}
}
- if (value == base.Value)
- return;
-
- base.Value = value;
- Log.Info($"CheckboxOption.Value: `{FieldName}` changed to {value}");
- PropagateAll(value);
+ if (value != base.Value) {
+ base.Value = value;
+ Log.Info($"CheckboxOption.Value: `{FieldName}` changed to {value}");
+ PropagateAll(value);
+ }
if (Shortcuts.IsMainThread()) {
if (HasUI) {
@@ -112,22 +111,15 @@ protected override void UpdateLabel() {
protected override void UpdateTooltip() {
if (!HasUI) return;
- _ui.tooltip = IsInScope
- ? string.IsNullOrEmpty(_tooltip)
- ? string.Empty // avoid invalidating UI if already no tooltip
- : Translate(_tooltip)
- : Translate(INGAME_ONLY_SETTING);
+ _ui.tooltip = Tooltip;
}
protected override void UpdateReadOnly() {
if (!HasUI) return;
+ Log._Debug($"CheckboxOption.UpdateReadOnly() - `{FieldName}` is {(ReadOnly ? "read-only" : "writeable")}");
- var readOnly = !IsInScope || _readOnly;
-
- Log._Debug($"CheckboxOption.UpdateReadOnly() - `{FieldName}` is {(readOnly ? "read-only" : "writeable")}");
-
- _ui.readOnly = readOnly;
- _ui.opacity = readOnly ? 0.3f : 1f;
+ _ui.readOnly = ReadOnly;
+ _ui.opacity = ReadOnly ? 0.3f : 1f;
}
/* UI helper methods */
diff --git a/TLM/TLM/UI/Helpers/DLCRestrictedCheckboxOption.cs b/TLM/TLM/UI/Helpers/DLCRestrictedCheckboxOption.cs
index 3b1221dd0..b0bac4c3b 100644
--- a/TLM/TLM/UI/Helpers/DLCRestrictedCheckboxOption.cs
+++ b/TLM/TLM/UI/Helpers/DLCRestrictedCheckboxOption.cs
@@ -15,7 +15,7 @@ public DLCRestrictedCheckboxOption(string fieldName,
SteamHelper.DLC requiredDLC,
Scope scope = Scope.Savegame) : base(fieldName, scope) {
_requiredDLC = requiredDLC;
- _readOnly = !SteamHelper.IsDLCOwned(_requiredDLC);
+ ReadOnly = !SteamHelper.IsDLCOwned(_requiredDLC);
}
public override CheckboxOption AddUI(UIHelperBase container) {
@@ -36,7 +36,7 @@ public override CheckboxOption AddUI(UIHelperBase container) {
innerPanel.autoFitChildrenVertically = true;
innerPanel.autoLayout = true;
- if (_readOnly) {
+ if (ReadOnly) {
icon.tooltip = Locale.Get("CONTENT_REQUIRED", _requiredDLC.ToString());
_ui.tooltip = Translate("Checkbox:DLC is required to change this option and see effects in game");
}
diff --git a/TLM/TLM/UI/Helpers/DropDownOption.cs b/TLM/TLM/UI/Helpers/DropDownOption.cs
index 30733e9b4..877e49259 100644
--- a/TLM/TLM/UI/Helpers/DropDownOption.cs
+++ b/TLM/TLM/UI/Helpers/DropDownOption.cs
@@ -94,25 +94,18 @@ protected override void UpdateLabel() {
}
protected override void UpdateTooltip() {
- if (!HasUI) return;
-
- //UIDropDown parent(UIPanel) handles tooltip
- _ui.parent.tooltip = IsInScope
- ? $"{_tooltip}"
- : Translate(INGAME_ONLY_SETTING);
+ if (HasUI) {
+ _ui.parent.tooltip = Tooltip;
+ }
}
protected override void UpdateReadOnly() {
if (!HasUI) return;
+ Log._Debug($"DropDownOption.UpdateReadOnly() - `{FieldName}` is {(ReadOnly ? "read-only" : "writeable")}");
- var readOnly = !IsInScope || _readOnly;
-
- Log._Debug($"DropDownOption.UpdateReadOnly() - `{FieldName}` is {(readOnly ? "read-only" : "writeable")}");
-
- _ui.isInteractive = !readOnly;
- _ui.opacity = readOnly ? 0.3f : 1f;
- // parent is UIPanel containing text label and dropdown
- _dropdownLabel.opacity = readOnly ? 0.3f : 1f;
+ _ui.isInteractive = !ReadOnly;
+ _ui.opacity = ReadOnly ? 0.3f : 1f;
+ _dropdownLabel.opacity = ReadOnly ? 0.3f : 1f;
}
}
}
\ No newline at end of file
diff --git a/TLM/TLM/UI/Helpers/SerializableUIOptionBase.cs b/TLM/TLM/UI/Helpers/SerializableUIOptionBase.cs
index 4b961ee1b..9bd33b232 100644
--- a/TLM/TLM/UI/Helpers/SerializableUIOptionBase.cs
+++ b/TLM/TLM/UI/Helpers/SerializableUIOptionBase.cs
@@ -10,8 +10,23 @@ namespace TrafficManager.UI.Helpers {
using JetBrains.Annotations;
using TrafficManager.Lifecycle;
using TrafficManager.Util;
+ using TrafficManager.API.Util;
+ using System.Collections.Generic;
- public abstract class SerializableUIOptionBase : ILegacySerializableOption
+ public abstract class SerializableUIOptionBase {
+ private static List _options = new();
+
+ public SerializableUIOptionBase() => _options.Add(this);
+ public static void UpdateAll() {
+ foreach (var option in _options) {
+ option.OnUpdate();
+ }
+ }
+
+ public abstract void OnUpdate();
+ }
+
+ public abstract class SerializableUIOptionBase : SerializableUIOptionBase, ILegacySerializableOption
where TUI : UIComponent
{
@@ -39,8 +54,6 @@ public OnChanged Handler {
///
public ValidatorDelegate Validator { get; set; }
- protected Scope _scope;
-
[CanBeNull]
private FieldInfo _fieldInfo;
@@ -49,10 +62,9 @@ public OnChanged Handler {
// used as internal store of value if _fieldInfo is null
private TVal _value = default;
- public SerializableUIOptionBase(string fieldName, Scope scope) {
-
+ public SerializableUIOptionBase(string fieldName, Scope scope)
+ : base() {
_fieldName = fieldName;
- _scope = scope;
if (scope.IsFlagSet(Scope.Savegame)) {
_fieldInfo = typeof(SavedGameOptions).GetField(fieldName);
@@ -98,16 +110,6 @@ public virtual TVal Value {
public string FieldName => _fieldInfo?.Name ?? _fieldName;
- /// Returns true if setting can persist in current .
- ///
- /// When false, UI component should be
- /// and should be set to .
- ///
- protected bool IsInScope =>
- _scope.IsFlagSet(Scope.Global) ||
- (_scope.IsFlagSet(Scope.Savegame) && TMPELifecycle.AppMode != null) ||
- _scope == Scope.None;
-
public static implicit operator TVal(SerializableUIOptionBase a) => a.Value;
public void DefaultOnValueChanged(TVal newVal) {
@@ -121,15 +123,22 @@ public void DefaultOnValueChanged(TVal newVal) {
public abstract void Load(byte data);
public abstract byte Save();
+ public override void OnUpdate() {
+ try {
+ Value = Value;
+ } catch (Exception ex) {
+ ex.LogException();
+ }
+ }
/* UI: */
public bool HasUI => _ui != null;
protected TUI _ui;
- protected string _label;
- protected string _tooltip;
+ private string _label;
+ private string _tooltip;
- protected bool _readOnly;
+ private bool _readOnly;
private TranslatorDelegate _translator;
public delegate string TranslatorDelegate(string key);
@@ -161,7 +170,7 @@ public string Label {
}
public string Tooltip {
- get => _tooltip;
+ get => _tooltip ?? string.Empty;
set {
_tooltip = value;
UpdateTooltip();
@@ -171,11 +180,11 @@ public string Tooltip {
public bool ReadOnly {
get => _readOnly;
set {
- _readOnly = !IsInScope || value;
+ _readOnly = value;
UpdateReadOnly();
}
}
- public bool Indent { get; set; }
+ public bool Indent { get; set; }
}
}
\ No newline at end of file
diff --git a/TLM/TLM/UI/Helpers/SliderOption.cs b/TLM/TLM/UI/Helpers/SliderOption.cs
index 214b51194..87fff71de 100644
--- a/TLM/TLM/UI/Helpers/SliderOption.cs
+++ b/TLM/TLM/UI/Helpers/SliderOption.cs
@@ -110,7 +110,7 @@ public override SliderOption AddUI(UIHelperBase container) {
protected override void UpdateLabel() {
if (!HasUI) return;
- string tooltip = IsInScope ? $"{Value}{_tooltip}" : Translate(INGAME_ONLY_SETTING);
+ string tooltip = $"{Value}{Tooltip}";
string label = Translate(Label);
_sliderLabel.text = label + ": " + tooltip;
}
@@ -119,16 +119,12 @@ protected override void UpdateLabel() {
protected override void UpdateReadOnly() {
if (!HasUI) return;
+ Log._Debug($"SliderOption.UpdateReadOnly() - `{FieldName}` is {(ReadOnly ? "read-only" : "writeable")}");
- var readOnly = !IsInScope || _readOnly;
-
- Log._Debug($"SliderOption.UpdateReadOnly() - `{FieldName}` is {(readOnly ? "read-only" : "writeable")}");
-
- _ui.isInteractive = !readOnly;
- _ui.thumbObject.isInteractive = !readOnly;
- _ui.thumbObject.opacity = readOnly ? 0.3f : 1f;
- // parent is UIPanel containing text label and slider
- _sliderLabel.opacity = readOnly ? 0.3f : 1f;
+ _ui.isInteractive = !ReadOnly;
+ _ui.thumbObject.isInteractive = !ReadOnly;
+ _ui.thumbObject.opacity = ReadOnly ? 0.3f : 1f;
+ _sliderLabel.opacity = ReadOnly ? 0.3f : 1f;
}
}
}
\ No newline at end of file
diff --git a/TLM/TLM/UI/Helpers/TriStateCheckboxOption.cs b/TLM/TLM/UI/Helpers/TriStateCheckboxOption.cs
index 873e82b0c..edcfc36af 100644
--- a/TLM/TLM/UI/Helpers/TriStateCheckboxOption.cs
+++ b/TLM/TLM/UI/Helpers/TriStateCheckboxOption.cs
@@ -65,18 +65,17 @@ public override bool? Value {
base.Value = value;
Log.Info($"TriStateCheckboxOption.Value: `{FieldName}` changed to {value}");
PropagateAll(value.HasValue);
-
- if (Shortcuts.IsMainThread()) {
+ }
+ if (Shortcuts.IsMainThread()) {
+ if (HasUI) {
+ _ui.Value = value;
+ }
+ } else {
+ SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => {
if (HasUI) {
_ui.Value = value;
}
- } else {
- SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => {
- if (HasUI) {
- _ui.Value = value;
- }
- });
- }
+ });
}
}
}
@@ -101,24 +100,18 @@ protected override void UpdateLabel() {
}
protected override void UpdateTooltip() {
- if (!HasUI) return;
-
- _ui.tooltip = IsInScope
- ? string.IsNullOrEmpty(_tooltip)
- ? string.Empty // avoid invalidating UI if already no tooltip
- : Translate(_tooltip)
- : Translate(INGAME_ONLY_SETTING);
+ if (HasUI) {
+ _ui.tooltip = Tooltip;
+ }
}
protected override void UpdateReadOnly() {
if (!HasUI) return;
- var readOnly = !IsInScope || _readOnly;
-
- Log._Debug($"TriStateCheckboxOption.UpdateReadOnly() - `{FieldName}` is {(readOnly ? "read-only" : "writeable")}");
+ Log._Debug($"TriStateCheckboxOption.UpdateReadOnly() - `{FieldName}` is {(ReadOnly ? "read-only" : "writeable")}");
- _ui.readOnly = readOnly;
- _ui.opacity = readOnly ? 0.3f : 1f;
+ _ui.readOnly = ReadOnly;
+ _ui.opacity = ReadOnly ? 0.3f : 1f;
}
/* UI helper methods */
diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs
index 07155a032..78a7b5413 100644
--- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs
+++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs
@@ -537,6 +537,10 @@ private void RenderLaneOverlays(RenderManager.CameraInfo cameraInfo) {
}
foreach (LaneEnd laneEnd in laneEnds) {
+ if((laneEnd.TransitionGroup & group_) == 0) {
+ continue;
+ }
+
bool drawMarker = false;
bool acute = true;
bool sourceMode = GetSelectionMode() == SelectionMode.SelectSource;
diff --git a/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs b/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs
index da3a8be03..17b1a3572 100644
--- a/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs
+++ b/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs
@@ -274,13 +274,17 @@ private NetInfo.Direction DrawParkingRestrictionHandles(ushort segmentId,
}
foreach (KeyValuePair e in segCenter) {
+ bool configurable = parkingManager.MayHaveParkingRestriction(segmentId, e.Key);
+ if (!configurable) {
+ continue;
+ }
+
bool allowed = parkingManager.IsParkingAllowed(segmentId, e.Key);
if (allowed && viewOnly) {
continue;
}
bool visible = GeometryUtil.WorldToScreenPoint(e.Value, out Vector3 screenPos);
-
if (!visible) {
continue;
}
diff --git a/TLM/TLM/UI/WhatsNew/WhatsNew.cs b/TLM/TLM/UI/WhatsNew/WhatsNew.cs
index cc8a84775..a73e2ee1c 100644
--- a/TLM/TLM/UI/WhatsNew/WhatsNew.cs
+++ b/TLM/TLM/UI/WhatsNew/WhatsNew.cs
@@ -11,7 +11,7 @@ namespace TrafficManager.UI.WhatsNew {
public class WhatsNew {
// bump and update what's new changelogs when new features added
- public static readonly Version CurrentVersion = new Version(11,7,2,0);
+ public static readonly Version CurrentVersion = new Version(11,7,3,0);
private const string WHATS_NEW_FILE = "whats_new.txt";
private const string RESOURCES_PREFIX = "TrafficManager.Resources.";
diff --git a/TLM/TLM/Util/Extensions/VehicleExtensions.cs b/TLM/TLM/Util/Extensions/VehicleExtensions.cs
index 97f220242..4c6fab030 100644
--- a/TLM/TLM/Util/Extensions/VehicleExtensions.cs
+++ b/TLM/TLM/Util/Extensions/VehicleExtensions.cs
@@ -118,7 +118,8 @@ public static ExtVehicleType MapCarVehicleCategoryToExtVehicle(this VehicleInfo.
VehicleInfo.VehicleCategory.MaintenanceTruck |
VehicleInfo.VehicleCategory.ParkTruck |
VehicleInfo.VehicleCategory.PostTruck |
- VehicleInfo.VehicleCategory.SnowTruck)) != 0) {
+ VehicleInfo.VehicleCategory.SnowTruck |
+ VehicleInfo.VehicleCategory.BankTruck)) != 0) {
return ExtVehicleType.Service;
} else if ((category & VehicleInfo.VehicleCategory.Bus) != 0) {
return ExtVehicleType.Bus;
diff --git a/TLM/TLM/Util/TrackUtils.cs b/TLM/TLM/Util/TrackUtils.cs
index b7cbd41fe..bd42f884b 100644
--- a/TLM/TLM/Util/TrackUtils.cs
+++ b/TLM/TLM/Util/TrackUtils.cs
@@ -17,22 +17,31 @@ internal static class TrackUtils {
VehicleInfo.VehicleType.Monorail;
internal const NetInfo.LaneType ROAD_LANE_TYPES = LaneArrowManager.LANE_TYPES;
+ internal const NetInfo.LaneType ROUTED_ROAD_LANE_TYPES = LaneArrowManager.LANE_TYPES | NetInfo.LaneType.CargoVehicle;
internal const VehicleInfo.VehicleType ROAD_VEHICLE_TYPES = LaneArrowManager.VEHICLE_TYPES | VehicleInfo.VehicleType.Trolleybus;
internal static bool MatchesTrack([NotNull] this NetInfo.Lane laneInfo) =>
laneInfo.Matches(TRACK_LANE_TYPES, TRACK_VEHICLE_TYPES);
-
+
internal static bool MatchesRoad([NotNull] this NetInfo.Lane laneInfo) =>
laneInfo.Matches(ROAD_LANE_TYPES, ROAD_VEHICLE_TYPES);
+ ///
+ /// Testing lane info if matches with predefined allowed road lane types and vehicle types
+ ///
+ /// Lane info instance to test
+ /// True if matches, otherwise False
+ internal static bool MatchesRoutedRoad([NotNull] this NetInfo.Lane laneInfo) =>
+ laneInfo.Matches(ROUTED_ROAD_LANE_TYPES, ROAD_VEHICLE_TYPES);
+
internal static bool Matches([NotNull] this NetInfo.Lane laneInfo , NetInfo.LaneType laneType, VehicleInfo.VehicleType vehicleType) {
return (laneType & laneInfo.m_laneType) != 0 && (vehicleType & laneInfo.m_vehicleType) != 0;
}
internal static bool IsTrackOnly(this NetInfo.Lane laneInfo) {
return
- laneInfo.MatchesTrack() &&
+ laneInfo.MatchesTrack() &&
!laneInfo.m_laneType.IsFlagSet(~TRACK_LANE_TYPES) &&
!laneInfo.m_vehicleType.IsFlagSet(~TRACK_VEHICLE_TYPES);
}
diff --git a/TLM/TLM/Util/VersionUtil.cs b/TLM/TLM/Util/VersionUtil.cs
index b86a38619..8e0bd27ac 100644
--- a/TLM/TLM/Util/VersionUtil.cs
+++ b/TLM/TLM/Util/VersionUtil.cs
@@ -35,10 +35,10 @@ public static class VersionUtil {
// we could alternatively use BuildConfig.APPLICATION_VERSION because const values are evaluated at compile time.
// but I have decided not to do this because I don't want this to happen automatically with a rebuild if
// CS updates. these values should be changed manually so as to force us to acknowledge that they have changed.
- public const uint EXPECTED_GAME_VERSION_U = 197387280U;
+ public const uint EXPECTED_GAME_VERSION_U = 201450256U;
// see comments for EXPECTED_GAME_VERSION_U.
- public static Version ExpectedGameVersion => new Version(1, 15, 1, 4);
+ public static Version ExpectedGameVersion => new Version(1, 16, 0, 3);
public static string ExpectedGameVersionString => BuildConfig.VersionToString(EXPECTED_GAME_VERSION_U, false);
diff --git a/TLM/TMPE.API/Manager/IOptionsManager.cs b/TLM/TMPE.API/Manager/IOptionsManager.cs
index bf53ecc30..08b8f49ce 100644
--- a/TLM/TMPE.API/Manager/IOptionsManager.cs
+++ b/TLM/TMPE.API/Manager/IOptionsManager.cs
@@ -4,7 +4,7 @@ namespace TrafficManager.API.Manager {
///
/// Manages mod options
///
- public interface IOptionsManager : ICustomDataManager {
+ public interface IOptionsManager {
///
/// Determines if modifications to segments may be published in the current state.
///