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