/// <summary>
        ///     First we get the cost for the main selected segment, then we compute the cost for each parallel/stacked ones to get to the final cost.
        /// </summary>

        // ReSharper disable once UnusedMember.Local
        private static int GetConstructionCost(Vector3 startPos, Vector3 endPos, float startHeight, float endHeight)
        {
            // Disable our detour so that we can call the original method
            Revert();

            try
            {
                // Get initial cost for the currently selected network
                var cost = ParallelRoadTool.CurrentNetwork.m_netAI.GetConstructionCost(startPos, endPos, startHeight, endHeight);

                foreach (var currentRoadInfos in Singleton <ParallelRoadTool> .instance.SelectedRoadTypes)
                {
                    // Horizontal offset must be negated to appear on the correct side of the original segment
                    var verticalOffset = currentRoadInfos.VerticalOffset;

                    // If the user didn't select a NetInfo we'll use the one he's using for the main road
                    var selectedNetInfo = currentRoadInfos.NetInfo.GetNetInfoWithElevation(currentRoadInfos.NetInfo, out _);

                    // If the user is using a vertical offset we try getting the relative elevated net info and use it
                    if (verticalOffset > 0 && selectedNetInfo.m_netAI.GetCollisionType() != ItemClass.CollisionType.Elevated)
                    {
                        selectedNetInfo = new RoadAIWrapper(selectedNetInfo.m_netAI).elevated ?? selectedNetInfo;
                    }

                    // Add the cost for the parallel segment
                    // TODO: it would be good to have startPos and endPos for the parallel segments instead of the ones of the first segment, but we don't have the direction here so we can't offset properly.
                    cost += selectedNetInfo.m_netAI.GetConstructionCost(startPos, endPos, startHeight, endHeight);
                }

                return(cost);
            }
            catch (Exception e)
            {
                // Log the exception and return 0 as we can't recover from this
                Log._DebugOnlyError($"[{nameof(NetAIDetour)}.{nameof(GetConstructionCost)}] GetConstructionCost failed.");
                Log.Exception(e);

                return(0);
            }
            finally
            {
                // Restore our detour so that we can call the original method
                Deploy();
            }
        }
示例#2
0
        /// <summary>
        ///     Returns a destination NetInfo with the same road type (elevated, tunnel etc.) as source one.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <returns></returns>
        private NetInfo GetNetInfoWithElevation(NetInfo source, NetInfo destination)
        {
            if (destination.m_netAI == null || source.m_netAI == null)
            {
                return(destination);
            }
            var     destinationWrapper = new RoadAIWrapper(destination.m_netAI);
            NetInfo result;

            switch (source.m_netAI.GetCollisionType())
            {
            case ItemClass.CollisionType.Undefined:
            case ItemClass.CollisionType.Zoned:
            case ItemClass.CollisionType.Terrain:
                result = destinationWrapper.info;
                break;

            case ItemClass.CollisionType.Underground:
                result = destinationWrapper.tunnel;
                break;

            case ItemClass.CollisionType.Elevated:
                result = destinationWrapper.elevated;
                break;

            default:
                result = null;
                break;
            }

            result = result ?? destination;

            DebugUtils.Log(
                $"Checking source.m_netAI.IsUnderground() && destination.m_netAI.SupportUnderground() == {source.m_netAI.IsUnderground()} && {destination.m_netAI.SupportUnderground()}");

            if (source.m_netAI.IsUnderground() && destination.m_netAI.SupportUnderground())
            {
                result = destinationWrapper.tunnel;
            }

            DebugUtils.Log($"Got a {source.m_netAI.GetCollisionType()}, new road is {result.name}");

            return(result);
        }
示例#3
0
        /// <summary>
        ///     Returns a destination NetInfo with the same road type (elevated, tunnel etc.) as source one.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <param name="isSlope"></param>
        /// <returns></returns>
        public static NetInfo GetNetInfoWithElevation(this NetInfo source, NetInfo destination, out bool isSlope)
        {
            isSlope = false;
            if (destination.m_netAI == null || source.m_netAI == null)
            {
                return(destination);
            }

            var sourceWrapper      = new RoadAIWrapper(source.m_netAI);
            var destinationWrapper = new RoadAIWrapper(destination.m_netAI);

            NetInfo result;

            if (source == sourceWrapper.bridge || source.name.ToLowerInvariant().Contains("bridge"))
            {
                result = destinationWrapper.bridge;
            }
            else if (source == sourceWrapper.elevated || source.name.ToLowerInvariant().Contains("elevated"))
            {
                result = destinationWrapper.elevated;
            }
            else if (source == sourceWrapper.slope || source.name.ToLowerInvariant().Contains("slope"))
            {
                result  = destinationWrapper.slope;
                isSlope = true;
            }
            else if (source == sourceWrapper.tunnel || source.name.ToLowerInvariant().Contains("tunnel"))
            {
                result = destinationWrapper.tunnel;
            }
            else
            {
                result = destination;
            }

            // Sanity check, of them may be null
            result = result ?? destination;

            DebugUtils.Log($"Got a {destination.name}, new road is {result.name} [source = {source.name}]");

            return(result);
        }
示例#4
0
        /// <summary>
        ///     Overlay's core method.
        ///     First we render the base overlay, then we render an overlay for each of the selected roads, shifting them with the
        ///     correct offsets.
        ///     TODO: Probably RenderHelperLines is what we need to fix the look with curves, but detouring it makes Unity crash so
        ///     we have to live with this little issue.
        /// </summary>
        /// <param name="cameraInfo"></param>
        /// <param name="info"></param>
        /// <param name="color"></param>
        /// <param name="startPoint"></param>
        /// <param name="middlePoint"></param>
        /// <param name="endPoint"></param>

        // ReSharper disable once UnusedMember.Local
        private void RenderOverlay(RenderManager.CameraInfo cameraInfo,
                                   NetInfo info,
                                   Color color,
                                   NetTool.ControlPoint startPoint,
                                   NetTool.ControlPoint middlePoint,
                                   NetTool.ControlPoint endPoint)
        {
            // Disable our detour so that we can call the original method
            Revert();

            try
            {
                var netTool = ToolsModifierControl.GetTool <NetTool>();

                // Let's render the original segment
                From.Invoke(netTool, new object[]
                {
                    cameraInfo,
                    info,
                    color,
                    startPoint,
                    middlePoint,
                    endPoint
                });

                for (var i = 0; i < Singleton <ParallelRoadTool> .instance.SelectedRoadTypes.Count; i++)
                {
                    var currentRoadInfos = Singleton <ParallelRoadTool> .instance.SelectedRoadTypes[i];

                    // Horizontal offset must be negated to appear on the correct side of the original segment
                    var horizontalOffset = currentRoadInfos.HorizontalOffset *
                                           (Singleton <ParallelRoadTool> .instance.IsLeftHandTraffic ? 1 : -1);
                    var verticalOffset = currentRoadInfos.VerticalOffset;

                    // If the user didn't select a NetInfo we'll use the one he's using for the main road
                    var selectedNetInfo = info.GetNetInfoWithElevation(currentRoadInfos.NetInfo ?? info, out _);

                    // If the user is using a vertical offset we try getting the relative elevated net info and use it
                    if (verticalOffset > 0 && selectedNetInfo.m_netAI.GetCollisionType() !=
                        ItemClass.CollisionType.Elevated)
                    {
                        selectedNetInfo = new RoadAIWrapper(selectedNetInfo.m_netAI).elevated ?? selectedNetInfo;
                    }

                    // 50 shades of blue
                    var newColor = new Color(
                        Mathf.Clamp(color.r * 255 + i, 0, 255) / 255,
                        Mathf.Clamp(color.g * 255 + i, 0, 255) / 255,
                        Mathf.Clamp(color.b * 255 + i, 0, 255) / 255,
                        color.a);

                    // Render parallel segments by shifting the position of the 3 ControlPoint
                    From.Invoke(netTool, new object[]
                    {
                        cameraInfo, selectedNetInfo, newColor,
                        new NetTool.ControlPoint
                        {
                            m_direction = startPoint.m_direction,
                            m_elevation = startPoint.m_elevation,
                            m_node      = 0, //startPoint.m_node,
                            m_outside   = startPoint.m_outside,

                            // startPoint may have a (0,0,0) direction, in that case we use the one from the middlePoint which is accurate enough to avoid overlapping starting nodes
                            m_position =
                                startPoint.m_position.Offset(
                                    startPoint.m_direction == Vector3.zero
                                                                 ? middlePoint.m_direction
                                                                 : startPoint.m_direction,
                                    horizontalOffset, verticalOffset),
                            m_segment = 0 //startPoint.m_segment
                        },
                        new NetTool.ControlPoint
                        {
                            m_direction = middlePoint.m_direction,
                            m_elevation = middlePoint.m_elevation,
                            m_node      = 0, //middlePoint.m_node,
                            m_outside   = middlePoint.m_outside,
                            m_position  =
                                middlePoint.m_position.Offset(middlePoint.m_direction, horizontalOffset, verticalOffset),
                            m_segment = 0 //middlePoint.m_segment
                        },
                        new NetTool.ControlPoint
                        {
                            m_direction = endPoint.m_direction,
                            m_elevation = endPoint.m_elevation,
                            m_node      = 0, //endPoint.m_node,
                            m_outside   = endPoint.m_outside,
                            m_position  = endPoint.m_position.Offset(endPoint.m_direction, horizontalOffset, verticalOffset),
                            m_segment   = 0 //endPoint.m_segment
                        }
                    });
                }
            }
            catch (Exception e)
            {
                // Log the exception
                Log._DebugOnlyError($"[{nameof(NetToolDetour)}.{nameof(RenderOverlay)}] CreateSegment failed.");
                Log.Exception(e);
            }
            finally
            {
                // Restore our detour so that we can call the original method
                Deploy();
            }
        }
示例#5
0
        /// <summary>
        ///     Overlay's core method.
        ///     First we render the base overlay, then we render an overlay for each of the selected roads, shifting them with the
        ///     correct offsets.
        ///     TODO: Probably RenderHelperLines is what we need to fix the look with curves, but detouring it makes Unity crash so
        ///     we have to live with this little issue.
        /// </summary>
        /// <param name="cameraInfo"></param>
        /// <param name="info"></param>
        /// <param name="color"></param>
        /// <param name="startPoint"></param>
        /// <param name="middlePoint"></param>
        /// <param name="endPoint"></param>
        private void RenderOverlay(RenderManager.CameraInfo cameraInfo, NetInfo info, Color color,
                                   NetTool.ControlPoint startPoint, NetTool.ControlPoint middlePoint, NetTool.ControlPoint endPoint)
        {
            // Let's render the original segment
            RenderOverlayOriginal(cameraInfo, info, color, startPoint, middlePoint, endPoint);

            for (var i = 0; i < ParallelRoadTool.SelectedRoadTypes.Count; i++)
            {
                var currentRoadInfos = ParallelRoadTool.SelectedRoadTypes[i];

                // Horizontal offset must be negated to appear on the correct side of the original segment
                var horizontalOffset = -currentRoadInfos.HorizontalOffset;
                var verticalOffset   = currentRoadInfos.VerticalOffset;

                // If the user didn't select a NetInfo we'll use the one he's using for the main road
                var selectedNetInfo = info.GetNetInfoWithElevation(currentRoadInfos.NetInfo ?? info, out var isSlope);
                // If the user is using a vertical offset we try getting the relative elevated net info and use it
                if (verticalOffset > 0 && selectedNetInfo.m_netAI.GetCollisionType() !=
                    ItemClass.CollisionType.Elevated)
                {
                    selectedNetInfo = new RoadAIWrapper(selectedNetInfo.m_netAI).elevated ?? selectedNetInfo;
                }

                // 50 shades of blue
                var newColor = new Color(Mathf.Clamp(color.r * 255 + i, 0, 255) / 255,
                                         Mathf.Clamp(color.g * 255 + i, 0, 255) / 255, Mathf.Clamp(color.b * 255 + i, 0, 255) / 255,
                                         color.a);

                // Render parallel segments by shifting the position of the 3 ControlPoint
                RenderOverlayOriginal(cameraInfo, selectedNetInfo, newColor, new NetTool.ControlPoint
                {
                    m_direction = startPoint.m_direction,
                    m_elevation = startPoint.m_elevation,
                    m_node      = startPoint.m_node,
                    m_outside   = startPoint.m_outside,
                    // startPoint may have a (0,0,0) direction, in that case we use the one from the middlePoint which is accurate enough to avoid overlapping starting nodes
                    m_position =
                        startPoint.m_position.Offset(
                            startPoint.m_direction == Vector3.zero ? middlePoint.m_direction : startPoint.m_direction,
                            horizontalOffset, verticalOffset),
                    m_segment = startPoint.m_segment
                }, new NetTool.ControlPoint
                {
                    m_direction = middlePoint.m_direction,
                    m_elevation = middlePoint.m_elevation,
                    m_node      = middlePoint.m_node,
                    m_outside   = middlePoint.m_outside,
                    m_position  =
                        middlePoint.m_position.Offset(middlePoint.m_direction, horizontalOffset, verticalOffset),
                    m_segment = middlePoint.m_segment
                }, new NetTool.ControlPoint
                {
                    m_direction = endPoint.m_direction,
                    m_elevation = endPoint.m_elevation,
                    m_node      = endPoint.m_node,
                    m_outside   = endPoint.m_outside,
                    m_position  = endPoint.m_position.Offset(endPoint.m_direction, horizontalOffset, verticalOffset),
                    m_segment   = endPoint.m_segment
                });
            }
        }
示例#6
0
        /// <summary>
        ///     Mod's core.
        ///     First, we create the segment using game's original code.
        ///     Then we offset the 2 nodes of the segment, based on both direction and curve, so that we can finally create a
        ///     segment between the 2 offset nodes.
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="randomizer"></param>
        /// <param name="info"></param>
        /// <param name="treeInfo"></param>
        /// <param name="startNode"></param>
        /// <param name="endNode"></param>
        /// <param name="startDirection"></param>
        /// <param name="endDirection"></param>
        /// <param name="buildIndex"></param>
        /// <param name="modifiedIndex"></param>
        /// <param name="invert"></param>
        /// <returns></returns>

        // ReSharper disable once UnusedMember.Local
        private bool CreateSegment(out ushort segment,
                                   ref Randomizer randomizer,
                                   NetInfo info,
                                   TreeInfo treeInfo,
                                   ushort startNode,
                                   ushort endNode,
                                   Vector3 startDirection,
                                   Vector3 endDirection,
                                   uint buildIndex,
                                   uint modifiedIndex,
                                   bool invert)
        {
            Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] Starting...");

            // Disable our detour so that we can call the original method
            Revert();

            try
            {
                Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] Creating original segment for {info}");

                // Let's create the segment that the user requested
                var result = NetManager.instance.CreateSegment(out segment, ref randomizer, info, treeInfo, startNode, endNode, startDirection,
                                                               endDirection, buildIndex, modifiedIndex, invert);

                if (Singleton <ParallelRoadTool> .instance.IsMouseLongPress &&
                    ToolsModifierControl.GetTool <NetTool>().m_mode == NetTool.Mode.Upgrade &&
                    startNode == _startNodeId[0] &&
                    endNode == _endNodeId[0])
                {
                    // HACK - [ISSUE-84] Prevent executing multiple times when we have a long mouse press on the very same segment
                    Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] Skipping because mouse has not been released yet from the previous upgrade.");

                    return(result);
                }

                // HACK - [ISSUE-10] [ISSUE-18] Check if we've been called by an allowed caller, otherwise we can stop here
                if (!IsAllowedCaller(new StackTrace(false)))
                {
                    Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] Skipped because caller is not allowed.");

                    return(result);
                }

                Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] Adding {Singleton<ParallelRoadTool>.instance.SelectedRoadTypes.Count} parallel segments");

                if (Singleton <ParallelRoadTool> .instance.IsLeftHandTraffic)
                {
                    _isPreviousInvert = invert;
                }

                // If we're in upgrade mode we must stop here
                // HACK - [ISSUE 25] Enabling tool during upgrade mode so that we can add to existing roads
                var isUpgradeActive = false;
                var upgradeInvert   = false;
                if (ToolsModifierControl.GetTool <NetTool>().m_mode == NetTool.Mode.Upgrade)
                {
                    isUpgradeActive = true;
                    upgradeInvert   = invert;

                    // ReSharper disable CompareOfFloatsByEqualityOperator
                    if (startDirection.x == endDirection.x && startDirection.y == endDirection.y)
                    {
                        ToolsModifierControl.GetTool <NetTool>().m_mode = NetTool.Mode.Straight;
                    }
                    else
                    {
                        ToolsModifierControl.GetTool <NetTool>().m_mode = NetTool.Mode.Curved;
                    }

                    // ReSharper restore CompareOfFloatsByEqualityOperator
                }

                // True if we have a slope that is going down from start to end node
                var isEnteringSlope = NetManager.instance.m_nodes.m_buffer[invert ? startNode : endNode].m_elevation >
                                      NetManager.instance.m_nodes.m_buffer[invert ? endNode : startNode].m_elevation;

                for (var i = 0; i < Singleton <ParallelRoadTool> .instance.SelectedRoadTypes.Count; i++)
                {
                    var currentRoadInfos = Singleton <ParallelRoadTool> .instance.SelectedRoadTypes[i];

                    var horizontalOffset = currentRoadInfos.HorizontalOffset;
                    var verticalOffset   = currentRoadInfos.VerticalOffset;

                    Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] Using offsets: h {horizontalOffset} | v {verticalOffset}");

                    // If the user didn't select a NetInfo we'll use the one he's using for the main road
                    var selectedNetInfo = info.GetNetInfoWithElevation(currentRoadInfos.NetInfo ?? info, out var isSlope);

                    // If the user is using a vertical offset we try getting the relative elevated net info and use it
                    if (verticalOffset > 0 && selectedNetInfo.m_netAI.GetCollisionType() !=
                        ItemClass.CollisionType.Elevated)
                    {
                        selectedNetInfo = new RoadAIWrapper(selectedNetInfo.m_netAI).elevated ?? selectedNetInfo;
                    }

                    var isReversed = currentRoadInfos.IsReversed;

                    // Left-hand drive means that any condition must be reversed
                    if (Singleton <ParallelRoadTool> .instance.IsLeftHandTraffic)
                    {
                        invert           = !invert;
                        isReversed       = !isReversed;
                        horizontalOffset = -horizontalOffset;

                        //Singleton<ParallelRoadTool>.instance.IsSnappingEnabled = true;
                    }

                    Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] Using netInfo {selectedNetInfo.name} | reversed={isReversed} | invert={invert}");

                    // Get original nodes to clone them
                    var startNetNode = NetManager.instance.m_nodes.m_buffer[startNode];
                    var endNetNode   = NetManager.instance.m_nodes.m_buffer[endNode];

                    // Create two clone nodes by offsetting the original ones.
                    // If we're not in "invert" mode (aka final part of a curve) and we already have an ending node with the same id of our starting node, we need to use that so that the segments can be connected.
                    // If the previous segment was in "invert" mode and the current startNode is the same as the previous one, we need to connect them.
                    // If we don't have any previous node matching our starting one, we need to clone startNode as this may be a new segment.
                    ushort newStartNodeId;
                    if (!invert && _endNodeId[i].HasValue && _endNodeId[i].Value == startNode)
                    {
                        newStartNodeId = _clonedEndNodeId[i].Value;

                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [START] Using old node from previous iteration {_clonedEndNodeId[i].Value} instead of the given one {startNode}");
                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [START] Start node {startNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newStartNodeId].m_position}");
                    }
                    else if (!invert && _isPreviousInvert && _startNodeId[i].HasValue &&
                             _startNodeId[i].Value == startNode)
                    {
                        newStartNodeId = _clonedStartNodeId[i].Value;

                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [START] Using old node from previous iteration {_clonedStartNodeId[i].Value} instead of the given one {startNode}");
                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [START] Start node{startNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newStartNodeId].m_position}");
                    }
                    else
                    {
                        var newStartPosition = startNetNode.m_position.Offset(startDirection, horizontalOffset,
                                                                              verticalOffset, invert);

                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [START] {startNetNode.m_position} --> {newStartPosition} | isLeftHand = {Singleton<ParallelRoadTool>.instance.IsLeftHandTraffic} | invert = {invert}  | isSlope = {isSlope}");

                        newStartNodeId = NodeAtPositionOrNew(ref randomizer, info, newStartPosition, verticalOffset);
                    }

                    // Same thing as startNode, but this time we don't clone if we're in "invert" mode as we may need to connect this ending node with the previous ending one.
                    ushort newEndNodeId;
                    if (invert && _endNodeId[i].HasValue && _endNodeId[i].Value == endNode)
                    {
                        newEndNodeId = _clonedEndNodeId[i].Value;

                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [END] Using old node from previous iteration {_clonedEndNodeId[i].Value} instead of the given one {endNode}");
                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [END] End node{endNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newEndNodeId].m_position}");
                    }
                    else
                    {
                        var newEndPosition = endNetNode.m_position.Offset(endDirection, horizontalOffset, verticalOffset,
                                                                          !(invert && isSlope && isEnteringSlope));

                        Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] [END] {endNetNode.m_position} --> {newEndPosition} | isEnteringSlope = {isEnteringSlope} | invert = {invert} | isSlope = {isSlope}");

                        newEndNodeId = NodeAtPositionOrNew(ref randomizer, info, newEndPosition, verticalOffset);
                    }

                    // Store current end nodes in case we may need to connect the following segment to them
                    _endNodeId[i]         = endNode;
                    _clonedEndNodeId[i]   = newEndNodeId;
                    _startNodeId[i]       = startNode;
                    _clonedStartNodeId[i] = newStartNodeId;

                    if (isReversed)
                    {
                        Vector3 tempStartDirection;
                        Vector3 tempEndDirection;
                        if (startDirection == -endDirection)
                        {
                            // Straight segment, we invert both directions
                            tempStartDirection = -startDirection;
                            tempEndDirection   = -endDirection;
                        }
                        else
                        {
                            // Curve, we need to swap start and end direction
                            tempStartDirection = endDirection;
                            tempEndDirection   = startDirection;
                        }

                        // Create the segment between the two cloned nodes, inverting start and end node
                        result = NetManager.instance.CreateSegment(out segment, ref randomizer, selectedNetInfo, newEndNodeId,
                                                                   newStartNodeId,
                                                                   tempStartDirection, tempEndDirection,
                                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1,
                                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex, invert);
                    }
                    else
                    {
                        // Create the segment between the two cloned nodes
                        result = NetManager.instance.CreateSegment(out segment, ref randomizer, selectedNetInfo, newStartNodeId,
                                                                   newEndNodeId, startDirection, endDirection,
                                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1,
                                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex, invert);
                    }

                    // Left-hand drive revert conditions back
                    if (!Singleton <ParallelRoadTool> .instance.IsLeftHandTraffic)
                    {
                        continue;
                    }
                    invert = !invert;

                    // isReversed = !isReversed;
                    _isPreviousInvert = invert;
                }

                _isPreviousInvert = invert;

                // HACK - [ISSUE 25] Enabling tool during upgrade mode so that we can add to existing roads
                if (!isUpgradeActive)
                {
                    return(result);
                }
                ToolsModifierControl.GetTool <NetTool>().m_mode = NetTool.Mode.Upgrade;
                _isPreviousInvert = upgradeInvert;

                return(result);
            }
            catch (Exception e)
            {
                // Log the exception and return false as we can't recover from this
                Log._DebugOnlyError($"[{nameof(NetManagerDetour)}.{nameof(CreateSegment)}] CreateSegment failed.");
                Log.Exception(e);

                segment = 0;
                return(false);
            }
            finally
            {
                // Restore our detour so that we can call the original method
                Deploy();
            }
        }
示例#7
0
        /// <summary>
        ///     Mod's core.
        ///     First, we create the segment using game's original code.
        ///     Then we offset the 2 nodes of the segment, based on both direction and curve, so that we can finally create a
        ///     segment between the 2 offset nodes.
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="randomizer"></param>
        /// <param name="info"></param>
        /// <param name="startNode"></param>
        /// <param name="endNode"></param>
        /// <param name="startDirection"></param>
        /// <param name="endDirection"></param>
        /// <param name="buildIndex"></param>
        /// <param name="modifiedIndex"></param>
        /// <param name="invert"></param>
        /// <returns></returns>
        private bool CreateSegment(out ushort segment, ref Randomizer randomizer, NetInfo info, ushort startNode,
                                   ushort endNode, Vector3 startDirection, Vector3 endDirection, uint buildIndex, uint modifiedIndex,
                                   bool invert)
        {
            DebugUtils.Log($"Creating a segment and {ParallelRoadTool.SelectedRoadTypes.Count} parallel segments");

            // Let's create the segment that the user requested
            var result = CreateSegmentOriginal(out segment, ref randomizer, info, startNode, endNode, startDirection,
                                               endDirection, buildIndex, modifiedIndex, invert);

            // If we're in upgrade mode we must stop here
            if (ParallelRoadTool.NetTool.m_mode == NetTool.Mode.Upgrade)
            {
                return(result);
            }

            // True if we have a slope that is going down from start to end node
            var isEnteringSlope = NetManager.instance.m_nodes.m_buffer[invert ? startNode : endNode].m_elevation >
                                  NetManager.instance.m_nodes.m_buffer[invert ? endNode : startNode].m_elevation;

            // HACK - [ISSUE-10] [ISSUE-18] Check if we've been called by an allowed caller, otherwise we can stop here
            var caller = string.Join(".", new []
            {
                new System.Diagnostics.StackFrame(3).GetMethod().DeclaringType?.Name,
                new System.Diagnostics.StackFrame(2).GetMethod().Name,
                new System.Diagnostics.StackFrame(1).GetMethod().Name
            });

            DebugUtils.Log($"Caller trace is {caller}");

            if (!_allowedCallers.Contains(caller))
            {
                return(result);
            }

            for (var i = 0; i < ParallelRoadTool.SelectedRoadTypes.Count; i++)
            {
                var currentRoadInfos = ParallelRoadTool.SelectedRoadTypes[i];

                var horizontalOffset = currentRoadInfos.HorizontalOffset;
                var verticalOffset   = currentRoadInfos.VerticalOffset;
                DebugUtils.Log($"Using offsets: h {horizontalOffset} | v {verticalOffset}");

                // If the user didn't select a NetInfo we'll use the one he's using for the main road
                var selectedNetInfo = info.GetNetInfoWithElevation(currentRoadInfos.NetInfo ?? info, out var isSlope);
                // If the user is using a vertical offset we try getting the relative elevated net info and use it
                if (verticalOffset > 0 && selectedNetInfo.m_netAI.GetCollisionType() !=
                    ItemClass.CollisionType.Elevated)
                {
                    selectedNetInfo = new RoadAIWrapper(selectedNetInfo.m_netAI).elevated ?? selectedNetInfo;
                }

                var isReversed = currentRoadInfos.IsReversed;

                // Left-hand drive means that any condition must be reversed
                if (ParallelRoadTool.Instance.IsLeftHandTraffic)
                {
                    invert     = !invert;
                    isReversed = !isReversed;
                }

                DebugUtils.Log($"Using netInfo {selectedNetInfo.name} | reversed={isReversed} | invert={invert}");

                // Get original nodes to clone them
                var startNetNode = NetManager.instance.m_nodes.m_buffer[startNode];
                var endNetNode   = NetManager.instance.m_nodes.m_buffer[endNode];

                // Create two clone nodes by offsetting the original ones.
                // If we're not in "invert" mode (aka final part of a curve) and we already have an ending node with the same id of our starting node, we need to use that so that the segments can be connected.
                // If the previous segment was in "invert" mode and the current startNode is the same as the previous one, we need to connect them.
                // If we don't have any previous node matching our starting one, we need to clone startNode as this may be a new segment.
                ushort newStartNodeId;
                if (!invert && _endNodeId[i].HasValue && _endNodeId[i].Value == startNode)
                {
                    DebugUtils.Log(
                        $"[START] Using old node from previous iteration {_clonedEndNodeId[i].Value} instead of the given one {startNode}");
                    newStartNodeId = _clonedEndNodeId[i].Value;
                    DebugUtils.Log(
                        $"[START] Start node{startNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newStartNodeId].m_position}");
                }
                else if (!invert && _isPreviousInvert && _startNodeId[i].HasValue &&
                         _startNodeId[i].Value == startNode)
                {
                    DebugUtils.Log(
                        $"[START] Using old node from previous iteration {_clonedStartNodeId[i].Value} instead of the given one {startNode}");
                    newStartNodeId = _clonedStartNodeId[i].Value;
                    DebugUtils.Log(
                        $"[START] Start node{startNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newStartNodeId].m_position}");
                }
                else
                {
                    var newStartPosition = startNetNode.m_position.Offset(startDirection, horizontalOffset,
                                                                          verticalOffset, invert);

                    DebugUtils.Log($"[START] {startNetNode.m_position} --> {newStartPosition} | isLeftHand = {ParallelRoadTool.Instance.IsLeftHandTraffic} | invert = {invert}  | isSlope = {isSlope}");
                    newStartNodeId = NodeAtPositionOrNew(ref randomizer, info, newStartPosition);
                }

                // Same thing as startNode, but this time we don't clone if we're in "invert" mode as we may need to connect this ending node with the previous ending one.
                ushort newEndNodeId;
                if (invert && _endNodeId[i].HasValue && _endNodeId[i].Value == endNode)
                {
                    DebugUtils.Log(
                        $"[END] Using old node from previous iteration {_clonedEndNodeId[i].Value} instead of the given one {endNode}");
                    newEndNodeId = _clonedEndNodeId[i].Value;
                    DebugUtils.Log(
                        $"[END] End node{endNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newEndNodeId].m_position}");
                }
                else
                {
                    var newEndPosition = endNetNode.m_position.Offset(endDirection, horizontalOffset, verticalOffset, !(invert && isSlope && isEnteringSlope));

                    DebugUtils.Log($"[END] {endNetNode.m_position} --> {newEndPosition} | isEnteringSlope = {isEnteringSlope} | invert = {invert} | isSlope = {isSlope}");
                    newEndNodeId = NodeAtPositionOrNew(ref randomizer, info, newEndPosition);
                }

                // Store current end nodes in case we may need to connect the following segment to them
                _endNodeId[i]         = endNode;
                _clonedEndNodeId[i]   = newEndNodeId;
                _startNodeId[i]       = startNode;
                _clonedStartNodeId[i] = newStartNodeId;

                if (isReversed)
                {
                    Vector3 tempStartDirection;
                    Vector3 tempEndDirection;
                    if (startDirection == -endDirection)
                    {
                        // Straight segment, we invert both directions
                        tempStartDirection = -startDirection;
                        tempEndDirection   = -endDirection;
                    }
                    else
                    {
                        // Curve, we need to swap start and end direction
                        tempStartDirection = endDirection;
                        tempEndDirection   = startDirection;
                    }

                    // Create the segment between the two cloned nodes, inverting start and end node
                    result = CreateSegmentOriginal(out segment, ref randomizer, selectedNetInfo, newEndNodeId,
                                                   newStartNodeId,
                                                   tempStartDirection, tempEndDirection,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex, invert);
                }
                else
                {
                    // Create the segment between the two cloned nodes
                    result = CreateSegmentOriginal(out segment, ref randomizer, selectedNetInfo, newStartNodeId,
                                                   newEndNodeId, startDirection, endDirection,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex, invert);
                }
            }

            _isPreviousInvert = invert;
            return(result);
        }
示例#8
0
        /// <summary>
        ///     Mod's core.
        ///     First, we create the segment using game's original code.
        ///     Then we offset the 2 nodes of the segment, based on both direction and curve, so that we can finally create a
        ///     segment between the 2 offset nodes.
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="randomizer"></param>
        /// <param name="info"></param>
        /// <param name="startNode"></param>
        /// <param name="endNode"></param>
        /// <param name="startDirection"></param>
        /// <param name="endDirection"></param>
        /// <param name="buildIndex"></param>
        /// <param name="modifiedIndex"></param>
        /// <param name="invert"></param>
        /// <returns></returns>
        private bool CreateSegment(out ushort segment, ref Randomizer randomizer, NetInfo info, ushort startNode,
                                   ushort endNode, Vector3 startDirection, Vector3 endDirection, uint buildIndex, uint modifiedIndex,
                                   bool invert)
        {
            DebugUtils.Log($"Creating a segment and {ParallelRoadTool.SelectedRoadTypes.Count} parallel segments");

            // Let's create the segment that the user requested
            var result = CreateSegmentOriginal(out segment, ref randomizer, info, startNode, endNode, startDirection,
                                               endDirection, buildIndex, modifiedIndex, invert);

            // If we're in upgrade mode we must stop here
            if (ParallelRoadTool.NetTool.m_mode == NetTool.Mode.Upgrade)
            {
                return(result);
            }

            for (var i = 0; i < ParallelRoadTool.SelectedRoadTypes.Count; i++)
            {
                var currentRoadInfos = ParallelRoadTool.SelectedRoadTypes[i];

                var horizontalOffset = currentRoadInfos.HorizontalOffset;
                var verticalOffset   = currentRoadInfos.VerticalOffset;
                DebugUtils.Log($"Using offsets: h {horizontalOffset} | v {verticalOffset}");

                // If the user didn't select a NetInfo we'll use the one he's using for the main road
                var selectedNetInfo = GetNetInfoWithElevation(info, currentRoadInfos.NetInfo ?? info);
                // If the user is using a vertical offset we try getting the relative elevated net info and use it
                if (verticalOffset > 0 && selectedNetInfo.m_netAI.GetCollisionType() !=
                    ItemClass.CollisionType.Elevated)
                {
                    selectedNetInfo = new RoadAIWrapper(selectedNetInfo.m_netAI).elevated ?? selectedNetInfo;
                }

                var isReversed = currentRoadInfos.IsReversed;

                DebugUtils.Log($"Using netInfo {selectedNetInfo.name} | reversed={isReversed}");

                // Get original nodes to clone them
                var startNetNode = NetManager.instance.m_nodes.m_buffer[startNode];
                var endNetNode   = NetManager.instance.m_nodes.m_buffer[endNode];

                // Create two clone nodes by offsetting the original ones.
                // If we're not in "invert" mode (aka final part of a curve) and we already have an ending node with the same id of our starting node, we need to use that so that the segments can be connected.
                // If the previous segment was in "invert" mode and the current startNode is the same as the previous one, we need to connect them.
                // If we don't have any previous node matching our starting one, we need to clone startNode as this may be a new segment.
                ushort newStartNodeId;
                if (!invert && _endNodeId[i].HasValue && _endNodeId[i].Value == startNode)
                {
                    DebugUtils.Log(
                        $"[START] Using old node from previous iteration {_clonedEndNodeId[i].Value} instead of the given one {startNode}");
                    newStartNodeId = _clonedEndNodeId[i].Value;
                    DebugUtils.Log(
                        $"[START] Start node{startNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newStartNodeId].m_position}");
                }
                else if (!invert && _isPreviousInvert && _startNodeId[i].HasValue &&
                         _startNodeId[i].Value == startNode)
                {
                    DebugUtils.Log(
                        $"[START] Using old node from previous iteration {_clonedStartNodeId[i].Value} instead of the given one {startNode}");
                    newStartNodeId = _clonedStartNodeId[i].Value;
                    DebugUtils.Log(
                        $"[START] Start node{startNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newStartNodeId].m_position}");
                }
                else
                {
                    var newStartPosition = Offset(startNetNode.m_position, startDirection, horizontalOffset,
                                                  verticalOffset, invert);
                    DebugUtils.Log($"[START] {startNetNode.m_position} --> {newStartPosition} | {invert}");
                    NetManager.instance.CreateNode(out newStartNodeId, ref randomizer, info, newStartPosition,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1);
                }

                // Same thing as startNode, but this time we don't clone if we're in "invert" mode as we may need to connect this ending node with the previous ending one.
                ushort newEndNodeId;
                if (invert && _endNodeId[i].HasValue && _endNodeId[i].Value == endNode)
                {
                    DebugUtils.Log(
                        $"[END] Using old node from previous iteration {_clonedEndNodeId[i].Value} instead of the given one {endNode}");
                    newEndNodeId = _clonedEndNodeId[i].Value;
                    DebugUtils.Log(
                        $"[END] End node{endNetNode.m_position} becomes {NetManager.instance.m_nodes.m_buffer[newEndNodeId].m_position}");
                }
                else
                {
                    var newEndPosition = Offset(endNetNode.m_position, endDirection, horizontalOffset, verticalOffset);
                    DebugUtils.Log($"[END] {endNetNode.m_position} --> {newEndPosition} | {invert}");
                    NetManager.instance.CreateNode(out newEndNodeId, ref randomizer, info, newEndPosition,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1);
                }

                // Store current end nodes in case we may need to connect the following segment to them
                _endNodeId[i]         = endNode;
                _clonedEndNodeId[i]   = newEndNodeId;
                _startNodeId[i]       = startNode;
                _clonedStartNodeId[i] = newStartNodeId;

                if (isReversed)
                {
                    Vector3 tempStartDirection;
                    Vector3 tempEndDirection;
                    if (startDirection == -endDirection)
                    {
                        // Straight segment, we invert both directions
                        tempStartDirection = -startDirection;
                        tempEndDirection   = -endDirection;
                    }
                    else
                    {
                        // Curve, we need to swap start and end direction
                        tempStartDirection = endDirection;
                        tempEndDirection   = startDirection;
                    }

                    // Create the segment between the two cloned nodes, inverting start and end node
                    result = CreateSegmentOriginal(out segment, ref randomizer, selectedNetInfo, newEndNodeId,
                                                   newStartNodeId,
                                                   tempStartDirection, tempEndDirection,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex, invert);
                }
                else
                {
                    // Create the segment between the two cloned nodes
                    result = CreateSegmentOriginal(out segment, ref randomizer, selectedNetInfo, newStartNodeId,
                                                   newEndNodeId, startDirection, endDirection,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex + 1,
                                                   Singleton <SimulationManager> .instance.m_currentBuildIndex, invert);
                }
            }

            _isPreviousInvert = invert;
            return(result);
        }