diff --git a/NodeController/GUI/TrafficRulesOverlay.cs b/NodeController/GUI/TrafficRulesOverlay.cs
index 314b6bc..26d8222 100644
--- a/NodeController/GUI/TrafficRulesOverlay.cs
+++ b/NodeController/GUI/TrafficRulesOverlay.cs
@@ -262,6 +262,7 @@ public bool DrawSignHandles(ushort nodeId,
baseZoom: TMPEUtils.GetBaseZoom());
JunctionRestrictionsManager jrMan = JunctionRestrictionsManager.Instance;
+ RoadSignTheme theme = RoadSignThemeManager.ActiveTheme;
#region UTURN
// draw "u-turns allowed" sign at (1; 0)
@@ -278,9 +279,7 @@ public bool DrawSignHandles(ushort nodeId,
handleClick: configurable && handleClick_,
camPos: ref camPos,
guiColor: guiColor,
- signTexture: allowed
- ? JunctionRestrictions.Instance.UturnAllowed
- : JunctionRestrictions.Instance.UturnForbidden);
+ signTexture: theme.GetOtherRestriction(RoadSignTheme.OtherRestriction.UTurn, allowed));
if (signHovered && handleClick_ && configurable) {
isAnyHovered = true;
@@ -317,9 +316,7 @@ public bool DrawSignHandles(ushort nodeId,
handleClick: configurable && handleClick_,
camPos: ref camPos,
guiColor: guiColor,
- signTexture: allowed
- ? JunctionRestrictions.Instance.EnterBlockedJunctionAllowed
- : JunctionRestrictions.Instance.EnterBlockedJunctionForbidden);
+ signTexture: theme.GetOtherRestriction(RoadSignTheme.OtherRestriction.EnterBlockedJunction, allowed));
if (signHovered && this.handleClick_ && configurable) {
isAnyHovered = true;
@@ -369,9 +366,7 @@ public bool DrawSignHandles(ushort nodeId,
handleClick: configurable && handleClick_,
camPos: ref camPos,
guiColor: guiColor,
- signTexture: allowed
- ? JunctionRestrictions.Instance.PedestrianCrossingAllowed
- : JunctionRestrictions.Instance.PedestrianCrossingForbidden);
+ signTexture: theme.GetOtherRestriction(RoadSignTheme.OtherRestriction.Crossing, allowed));
if (signHovered && this.handleClick_ && configurable) {
isAnyHovered = true;
diff --git a/NodeController/LifeCycle/LifeCycle.cs b/NodeController/LifeCycle/LifeCycle.cs
index 9b9d071..9c8d843 100644
--- a/NodeController/LifeCycle/LifeCycle.cs
+++ b/NodeController/LifeCycle/LifeCycle.cs
@@ -19,6 +19,11 @@ public static class LifeCycle {
public static bool Loaded;
+ ///
+ /// A reference to ensure initialization of the hook.
+ ///
+ private static Patches.TMPE.JunctionRestrictions junctionRestrictionsHook = Patches.TMPE.JunctionRestrictions.Instance;
+
public static void Enable() {
Log.Debug("Testing StackTrace:\n" + new StackTrace(true).ToString(), copyToGameLog: false);
KianCommons.UI.TextureUtil.EmbededResources = false;
diff --git a/NodeController/Manager/NodeData.cs b/NodeController/Manager/NodeData.cs
index ab913f7..8b4623e 100644
--- a/NodeController/Manager/NodeData.cs
+++ b/NodeController/Manager/NodeData.cs
@@ -764,91 +764,92 @@ public void ShiftPilar() {
// undefined -> don't touch prev value
// true -> force true
// false -> force false.
- public TernaryBool IsUturnAllowedConfigurable() {
+
+ public bool? IsUturnAllowedConfigurable() {
switch (NodeType) {
case NodeTypeT.Crossing:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.UTurn:
- return TernaryBool.Undefined; // default on
+ return null; // default on
case NodeTypeT.Stretch:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Nodeless:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Bend:
- return TernaryBool.False; // always default
+ return false; // always default
case NodeTypeT.Custom:
- return TernaryBool.Undefined; // default
+ return null; // default
case NodeTypeT.End:
- return TernaryBool.Undefined;
+ return null;
default:
throw new Exception("Unreachable code");
}
}
- public TernaryBool GetDefaultUturnAllowed() {
+ public bool? GetDefaultUturnAllowed() {
switch (NodeType) {
case NodeTypeT.Crossing:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.UTurn:
- return TernaryBool.True; // default on
+ return true; // default on
case NodeTypeT.Stretch:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Nodeless:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Bend:
- return TernaryBool.Undefined; // don't care
+ return null; // don't care
case NodeTypeT.Custom:
- return TernaryBool.Undefined; // default
+ return null; // default
case NodeTypeT.End:
- return TernaryBool.Undefined;
+ return null;
default:
throw new Exception("Unreachable code");
}
}
- public TernaryBool IsPedestrianCrossingAllowedConfigurable() {
+ public bool? IsPedestrianCrossingAllowedConfigurable() {
switch (NodeType) {
case NodeTypeT.Crossing:
- return TernaryBool.False; // always on
+ return false; // always on
case NodeTypeT.UTurn:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Stretch:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Nodeless:
case NodeTypeT.Bend:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Custom:
if (SegmentCount == 2 && !HasPedestrianLanes) {
- return TernaryBool.False; // TODO move to TMPE.
+ return false; // TODO move to TMPE.
}
- return TernaryBool.Undefined; // default off
+ return null; // default off
case NodeTypeT.End:
- return TernaryBool.Undefined;
+ return null;
default:
throw new Exception("Unreachable code");
}
}
- public TernaryBool GetDefaultPedestrianCrossingAllowed() {
+ public bool? GetDefaultPedestrianCrossingAllowed() {
switch (NodeType) {
case NodeTypeT.Crossing:
- return TernaryBool.True; // always on
+ return true; // always on
case NodeTypeT.UTurn:
- return TernaryBool.False; // default off
+ return false; // default off
case NodeTypeT.Stretch:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Nodeless:
case NodeTypeT.Bend:
- return TernaryBool.False; // always off
+ return false; // always off
case NodeTypeT.Custom:
var netAI1 = segmentID1.ToSegment().Info.m_netAI;
var netAI2 = segmentID2.ToSegment().Info.m_netAI;
- bool sameAIType = netAI1.GetType() == netAI2.GetType();
+ bool sameAIType = netAI1.GetType() == netAI2.GetType();
if (SegmentCount == 2 && !sameAIType) // eg: at bridge/tunnel entrances.
- return TernaryBool.False; // default off
- return TernaryBool.Undefined; // don't care
+ return false; // default off
+ return null; // don't care
case NodeTypeT.End:
- return TernaryBool.Undefined;
+ return null;
default:
throw new Exception("Unreachable code");
}
@@ -880,54 +881,54 @@ public TernaryBool CanHaveTrafficLights(out ToggleTrafficLightError reason) {
}
}
- public TernaryBool IsEnteringBlockedJunctionAllowedConfigurable() {
+ public bool? IsEnteringBlockedJunctionAllowedConfigurable() {
switch (NodeType) {
case NodeTypeT.Crossing:
- return TernaryBool.Undefined; // default off
+ return null; // default off
case NodeTypeT.UTurn:
- return TernaryBool.Undefined; // default
+ return null; // default
case NodeTypeT.Stretch:
- return TernaryBool.False; // always on
+ return false; // always on
case NodeTypeT.Nodeless:
case NodeTypeT.Bend:
- return TernaryBool.False; // always default
+ return false; // always default
case NodeTypeT.Custom:
if (SegmentCount > 2)
- return TernaryBool.Undefined;
+ return null;
bool oneway = DefaultFlags.IsFlagSet(NetNode.Flags.OneWayIn) & DefaultFlags.IsFlagSet(NetNode.Flags.OneWayOut);
if (oneway & !HasPedestrianLanes) {
- return TernaryBool.False; // always on.
+ return false; // always on.
}
- return TernaryBool.Undefined; // default on.
+ return null; // default on.
case NodeTypeT.End:
- return TernaryBool.Undefined;
+ return null;
default:
throw new Exception("Unreachable code");
}
}
- public TernaryBool GetDefaultEnteringBlockedJunctionAllowed() {
+ public bool? GetDefaultEnteringBlockedJunctionAllowed() {
switch (NodeType) {
case NodeTypeT.Crossing:
- return TernaryBool.False; // default off
+ return false; // default off
case NodeTypeT.UTurn:
- return TernaryBool.Undefined; // default
+ return null; // default
case NodeTypeT.Stretch:
- return TernaryBool.True; // always on
+ return true; // always on
case NodeTypeT.Nodeless:
case NodeTypeT.Bend:
- return TernaryBool.Undefined; // don't care
+ return null; // don't care
case NodeTypeT.Custom:
if (SegmentCount > 2)
- return TernaryBool.Undefined;
- return TernaryBool.True;
+ return null;
+ return true;
//bool oneway = DefaultFlags.IsFlagSet(NetNode.Flags.OneWayIn) & DefaultFlags.IsFlagSet(NetNode.Flags.OneWayOut);
//if (oneway & !HasPedestrianLanes) {
- // return TernaryBool.True; // always on.
+ // return true; // always on.
//}
- //return TernaryBool.Undefined;
+ //return null;
case NodeTypeT.End:
- return TernaryBool.Undefined;
+ return null;
default:
throw new Exception("Unreachable code");
}
diff --git a/NodeController/Manager/NodeManager.cs b/NodeController/Manager/NodeManager.cs
index 0db7daf..475b250 100644
--- a/NodeController/Manager/NodeManager.cs
+++ b/NodeController/Manager/NodeManager.cs
@@ -85,6 +85,14 @@ public NodeData InsertNode(NetTool.ControlPoint controlPoint, NodeTypeT nodeType
return buffer[nodeID];
}
+ public bool TryGet(ushort segmentId, bool startNode, out NodeData nodeData)
+ => TryGet(segmentId.ToSegment().GetNode(startNode), out nodeData);
+
+ public bool TryGet(ushort nodeId, out NodeData nodeData) {
+ nodeData = Instance.buffer[nodeId];
+ return nodeData != null;
+ }
+
public ref NodeData GetOrCreate(ushort nodeID) {
ref NodeData data = ref Instance.buffer[nodeID];
if (data == null) {
diff --git a/NodeController/Patches/TMPE/GetDefaultEnteringBlockedJunctionAllowed.cs b/NodeController/Patches/TMPE/GetDefaultEnteringBlockedJunctionAllowed.cs
deleted file mode 100644
index 60510a2..0000000
--- a/NodeController/Patches/TMPE/GetDefaultEnteringBlockedJunctionAllowed.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace NodeController.Patches.TMPE {
- using System.Reflection;
- using TrafficManager.Manager.Impl;
- using NodeController;
- using HarmonyLib;
- using KianCommons.Patches;
- using KianCommons;
- using KianCommons.Plugins;
-
- [HarmonyPatch]
- static class GetDefaultEnteringBlockedJunctionAllowed {
- static bool Prepare() => PluginUtil.GetTrafficManager().IsActive();
-
- public static MethodBase TargetMethod() {
- return typeof(JunctionRestrictionsManager).
- GetMethod(nameof(JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed));
- }
-
- public static bool Prefix(ushort segmentId, bool startNode, ref bool __result) {
- ushort nodeID = startNode ? segmentId.ToSegment().m_startNode : segmentId.ToSegment().m_endNode;
- var data = NodeManager.Instance.buffer[nodeID];
- return PrefixUtils.HandleTernaryBool(
- data?.GetDefaultEnteringBlockedJunctionAllowed(),
- ref __result);
- }
- }
-}
\ No newline at end of file
diff --git a/NodeController/Patches/TMPE/GetDefaultPedestrianCrossingAllowed.cs b/NodeController/Patches/TMPE/GetDefaultPedestrianCrossingAllowed.cs
deleted file mode 100644
index 3dea0ad..0000000
--- a/NodeController/Patches/TMPE/GetDefaultPedestrianCrossingAllowed.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace NodeController.Patches.TMPE {
- using System.Reflection;
- using TrafficManager.Manager.Impl;
- using KianCommons.Patches;
- using KianCommons;
- using NodeController;
- using HarmonyLib;
- using KianCommons.Plugins;
-
- [HarmonyPatch]
- static class GetDefaultPedestrianCrossingAllowed {
- static bool Prepare() => PluginUtil.GetTrafficManager().IsActive();
- public static MethodBase TargetMethod() {
- return typeof(JunctionRestrictionsManager).
- GetMethod(nameof(JunctionRestrictionsManager.GetDefaultPedestrianCrossingAllowed));
- }
-
- public static bool Prefix(ushort segmentId, bool startNode, ref bool __result) {
- ushort nodeID = startNode ? segmentId.ToSegment().m_startNode : segmentId.ToSegment().m_endNode;
- NodeData data = NodeManager.Instance.buffer[nodeID];
-
- return PrefixUtils.HandleTernaryBool(
- data?.GetDefaultPedestrianCrossingAllowed(),
- ref __result);
-
- }
- }
-}
\ No newline at end of file
diff --git a/NodeController/Patches/TMPE/GetDefaultUturnAllowed.cs b/NodeController/Patches/TMPE/GetDefaultUturnAllowed.cs
deleted file mode 100644
index db2692c..0000000
--- a/NodeController/Patches/TMPE/GetDefaultUturnAllowed.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-namespace NodeController.Patches.TMPE {
- using System.Reflection;
- using TrafficManager.Manager.Impl;
- using KianCommons.Patches;
- using KianCommons;
- using NodeController;
- using HarmonyLib;
- using KianCommons.Plugins;
-
- [HarmonyPatch]
- static class GetDefaultUturnAllowed {
- static bool Prepare() => PluginUtil.GetTrafficManager().IsActive();
- public static MethodBase TargetMethod() {
- return typeof(JunctionRestrictionsManager).
- GetMethod(nameof(JunctionRestrictionsManager.GetDefaultUturnAllowed));
- }
-
- public static bool Prefix(ushort segmentId, bool startNode, ref bool __result) {
- ushort nodeID = startNode ? segmentId.ToSegment().m_startNode : segmentId.ToSegment().m_endNode;
- var data = NodeManager.Instance.buffer[nodeID];
- return PrefixUtils.HandleTernaryBool(
- data?.GetDefaultUturnAllowed(),
- ref __result);
- }
- }
-}
\ No newline at end of file
diff --git a/NodeController/Patches/TMPE/IsEnteringBlockedJunctionAllowedConfigurable.cs b/NodeController/Patches/TMPE/IsEnteringBlockedJunctionAllowedConfigurable.cs
deleted file mode 100644
index 9788f07..0000000
--- a/NodeController/Patches/TMPE/IsEnteringBlockedJunctionAllowedConfigurable.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace NodeController.Patches.TMPE {
- using System.Reflection;
- using TrafficManager.Manager.Impl;
- using KianCommons.Patches;
- using KianCommons;
- using NodeController;
- using HarmonyLib;
- using ColossalFramework;
- using KianCommons.Plugins;
-
- [HarmonyPatch]
- static class IsEnteringBlockedJunctionAllowedConfigurable {
- static bool Prepare() => PluginUtil.GetTrafficManager().IsActive();
- public static MethodBase TargetMethod() {
- return typeof(JunctionRestrictionsManager).
- GetMethod(nameof(JunctionRestrictionsManager.IsEnteringBlockedJunctionAllowedConfigurable));
- }
-
- public static bool Prefix(ushort segmentId, bool startNode, ref bool __result) {
- ushort nodeID = startNode ? segmentId.ToSegment().m_startNode : segmentId.ToSegment().m_endNode;
- var data = NodeManager.Instance.buffer[nodeID];
- if (data == null) {
- var flags = nodeID.ToNode().m_flags;
- bool oneway = flags.IsFlagSet(NetNode.Flags.OneWayIn) & flags.IsFlagSet(NetNode.Flags.OneWayOut);
- if (oneway & !segmentId.ToSegment().Info.m_hasPedestrianLanes) {
- __result = false;
- return false;
- }
- }
-
-
- return PrefixUtils.HandleTernaryBool(
- data?.IsEnteringBlockedJunctionAllowedConfigurable(),
- ref __result);
-
- }
- }
-}
\ No newline at end of file
diff --git a/NodeController/Patches/TMPE/IsPedestrianCrossingAllowedConfigurable.cs b/NodeController/Patches/TMPE/IsPedestrianCrossingAllowedConfigurable.cs
deleted file mode 100644
index 9f4b6fd..0000000
--- a/NodeController/Patches/TMPE/IsPedestrianCrossingAllowedConfigurable.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace NodeController.Patches.TMPE {
- using System.Reflection;
- using TrafficManager.Manager.Impl;
- using NodeController;
- using KianCommons.Patches;
- using KianCommons;
- using HarmonyLib;
- using KianCommons.Plugins;
-
- [HarmonyPatch]
- static class IsPedestrianCrossingAllowedConfigurable {
- static bool Prepare() => PluginUtil.GetTrafficManager().IsActive();
-
- public static MethodBase TargetMethod() {
- return typeof(JunctionRestrictionsManager).
- GetMethod(nameof(JunctionRestrictionsManager.IsPedestrianCrossingAllowedConfigurable));
- }
-
- public static bool Prefix(ushort segmentId, bool startNode, ref bool __result) {
- ushort nodeID = startNode ? segmentId.ToSegment().m_startNode : segmentId.ToSegment().m_endNode;
- var data = NodeManager.Instance.buffer[nodeID];
- return PrefixUtils.HandleTernaryBool(
- data?.IsPedestrianCrossingAllowedConfigurable(),
- ref __result);
- }
- }
-}
\ No newline at end of file
diff --git a/NodeController/Patches/TMPE/IsUturnAllowedConfigurable.cs b/NodeController/Patches/TMPE/IsUturnAllowedConfigurable.cs
deleted file mode 100644
index 7ea3ca5..0000000
--- a/NodeController/Patches/TMPE/IsUturnAllowedConfigurable.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace NodeController.Patches.TMPE {
- using System.Reflection;
- using TrafficManager.Manager.Impl;
- using NodeController;
- using KianCommons;
- using HarmonyLib;
- using KianCommons.Patches;
- using KianCommons.Plugins;
-
- [HarmonyPatch]
- static class IsUturnAllowedConfigurable {
- static bool Prepare() => PluginUtil.GetTrafficManager().IsActive();
-
- public static MethodBase TargetMethod() {
- return typeof(JunctionRestrictionsManager).
- GetMethod(nameof(JunctionRestrictionsManager.IsUturnAllowedConfigurable));
- }
-
- public static bool Prefix(ushort segmentId, bool startNode, ref bool __result) {
- ushort nodeID = startNode ? segmentId.ToSegment().m_startNode : segmentId.ToSegment().m_endNode;
- var data = NodeManager.Instance.buffer[nodeID];
- return PrefixUtils.HandleTernaryBool(
- data?.IsUturnAllowedConfigurable(),
- ref __result);
- }
- }
-}
\ No newline at end of file
diff --git a/NodeController/Patches/TMPE/JunctionRestrictions.cs b/NodeController/Patches/TMPE/JunctionRestrictions.cs
new file mode 100644
index 0000000..61163e7
--- /dev/null
+++ b/NodeController/Patches/TMPE/JunctionRestrictions.cs
@@ -0,0 +1,56 @@
+using ColossalFramework;
+using HarmonyLib;
+using KianCommons;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using TrafficManager.API.Traffic.Enums;
+using static TrafficManager.API.Hook.IJunctionRestrictionsHook;
+using static TrafficManager.API.Implementations;
+
+namespace NodeController.Patches.TMPE {
+
+ [HarmonyPatch]
+ internal class JunctionRestrictions {
+
+ public static JunctionRestrictions Instance = new JunctionRestrictions();
+
+ private JunctionRestrictions() {
+ try {
+ var junctionRestrictionsHook = HookFactory.JunctionRestrictionsHook;
+ junctionRestrictionsHook.GetDefaultsHook += GetDefaultsHook;
+ junctionRestrictionsHook.GetConfigurableHook += GetConfigurableHook;
+ Log.Info("TMPE API hooks added");
+ }
+ catch (Exception ex) {
+ Log.Error($"Failed to add TMPE API hooks: {ex}");
+ Log.Info(ex.StackTrace);
+ }
+ }
+
+ private void GetDefaultsHook(FlagsHookArgs args) {
+ if (NodeManager.Instance.TryGet(args.SegmentId, args.StartNode, out var nodeData)) {
+ UpdateFlag(args, JunctionRestrictionsFlags.AllowUTurn, nodeData.GetDefaultUturnAllowed);
+ UpdateFlag(args, JunctionRestrictionsFlags.AllowPedestrianCrossing, nodeData.GetDefaultPedestrianCrossingAllowed);
+ UpdateFlag(args, JunctionRestrictionsFlags.AllowEnterWhenBlocked, nodeData.GetDefaultEnteringBlockedJunctionAllowed);
+ }
+ }
+
+ private void GetConfigurableHook(FlagsHookArgs args) {
+ if (NodeManager.Instance.TryGet(args.SegmentId, args.StartNode, out var nodeData)) {
+ UpdateFlag(args, JunctionRestrictionsFlags.AllowUTurn, nodeData.IsUturnAllowedConfigurable);
+ UpdateFlag(args, JunctionRestrictionsFlags.AllowPedestrianCrossing, nodeData.IsPedestrianCrossingAllowedConfigurable);
+ UpdateFlag(args, JunctionRestrictionsFlags.AllowEnterWhenBlocked, nodeData.IsEnteringBlockedJunctionAllowedConfigurable);
+ }
+ }
+
+ public static void UpdateFlag(FlagsHookArgs args, JunctionRestrictionsFlags flag, Func func) {
+ if (args.Mask.IsFlagSet(flag)) {
+ var value = func();
+ if (value.HasValue)
+ args.Result = args.Result.SetFlags(flag, value.Value);
+ }
+ }
+ }
+}
diff --git a/libs/CSUtil.Commons.dll b/libs/CSUtil.Commons.dll
index 958f0d8..fefe3b6 100644
Binary files a/libs/CSUtil.Commons.dll and b/libs/CSUtil.Commons.dll differ
diff --git a/libs/TMPE.API.dll b/libs/TMPE.API.dll
index d5bd4c0..3dcf340 100644
Binary files a/libs/TMPE.API.dll and b/libs/TMPE.API.dll differ
diff --git a/libs/TrafficManager.dll b/libs/TrafficManager.dll
index a08b0f4..8c125cc 100644
Binary files a/libs/TrafficManager.dll and b/libs/TrafficManager.dll differ