public override void RenderOverlay(RenderManager.CameraInfo cameraInfo)
        {
            //Log._Debug($"TppLaneConnectorTool: RenderOverlay. SelectedNodeId={SelectedNodeId} SelectedSegmentId={SelectedSegmentId} HoveredNodeId={HoveredNodeId} HoveredSegmentId={HoveredSegmentId} IsInsideUI={MainTool.GetToolController().IsInsideUI}");
            // draw lane markers and connections

            hoveredMarker = null;

            ShowOverlay(false, cameraInfo);

            // draw bezier from source marker to mouse position in target marker selection
            if (SelectedNodeId != 0)
            {
                if (GetMarkerSelectionMode() == MarkerSelectionMode.SelectTarget)
                {
                    Vector3 selNodePos = NetManager.instance.m_nodes.m_buffer[SelectedNodeId].m_position;

                    ToolBase.RaycastOutput output;
                    if (RayCastSegmentAndNode(out output))
                    {
                        RenderLane(cameraInfo, selectedMarker.position, output.m_hitPos, selNodePos, selectedMarker.color);
                    }
                }

                if (Input.GetKey(KeyCode.Delete))
                {
                    // remove all connections at selected node

                    List <NodeLaneMarker> nodeMarkers = GetNodeMarkers(SelectedNodeId);
                    if (nodeMarkers != null)
                    {
                        selectedMarker = null;
                        foreach (NodeLaneMarker sourceLaneMarker in nodeMarkers)
                        {
                            foreach (NodeLaneMarker targetLaneMarker in sourceLaneMarker.connectedMarkers)
                            {
                                LaneConnectionManager.Instance().RemoveLaneConnection(sourceLaneMarker.laneId, targetLaneMarker.laneId, sourceLaneMarker.startNode);
                            }
                        }
                    }
                    RefreshCurrentNodeMarkers();
                }
            }

            if (GetMarkerSelectionMode() == MarkerSelectionMode.None && HoveredNodeId != 0)
            {
                // draw hovered node
                MainTool.DrawNodeCircle(cameraInfo, HoveredNodeId, Input.GetMouseButton(0));
            }
        }
Exemple #2
0
        /// <summary>
        /// Removes lane connections that exist between two given lanes
        /// </summary>
        /// <param name="lane1Id"></param>
        /// <param name="lane2Id"></param>
        /// <param name="startNode1"></param>
        /// <returns></returns>
        internal static bool RemoveLaneConnection(uint lane1Id, uint lane2Id, bool startNode1)
        {
            if (!IsInitDone())
            {
                return(false);
            }

            bool lane1Valid = CheckLane(lane1Id);
            bool lane2Valid = CheckLane(lane2Id);

            bool ret = false;

            if (!lane1Valid)
            {
                // remove all incoming/outgoing lane connections
                RemoveLaneConnections(lane1Id);
                ret = true;
            }

            if (!lane2Valid)
            {
                // remove all incoming/outgoing lane connections
                RemoveLaneConnections(lane2Id);
                ret = true;
            }

            if (lane1Valid || lane2Valid)
            {
                ushort commonNodeId;
                bool   startNode2;

                LaneConnectionManager.Instance().GetCommonNodeId(lane1Id, lane2Id, startNode1, out commonNodeId, out startNode2);                 // TODO refactor

                if (CleanupLaneConnections(lane1Id, lane2Id, startNode1))
                {
                    ret = true;
                }
                if (CleanupLaneConnections(lane2Id, lane1Id, startNode2))
                {
                    ret = true;
                }
            }

            return(ret);
        }
Exemple #3
0
        /// <summary>
        /// adds lane connections between two given lanes
        /// </summary>
        /// <param name="lane1Id"></param>
        /// <param name="lane2Id"></param>
        /// <param name="startNode1"></param>
        /// <returns></returns>
        internal static bool AddLaneConnection(uint lane1Id, uint lane2Id, bool startNode1)
        {
            if (!IsInitDone())
            {
                return(false);
            }

            bool lane1Valid = CheckLane(lane1Id);
            bool lane2Valid = CheckLane(lane2Id);

            if (!lane1Valid)
            {
                // remove all incoming/outgoing lane connections
                RemoveLaneConnections(lane1Id);
            }

            if (!lane2Valid)
            {
                // remove all incoming/outgoing lane connections
                RemoveLaneConnections(lane2Id);
            }

            if (!lane1Valid || !lane2Valid)
            {
                return(false);
            }

            ushort commonNodeId;
            bool   startNode2;

            LaneConnectionManager.Instance().GetCommonNodeId(lane1Id, lane2Id, startNode1, out commonNodeId, out startNode2);             // TODO refactor

            if (commonNodeId != 0)
            {
                CreateLaneConnection(lane1Id, lane2Id, startNode1);
                CreateLaneConnection(lane2Id, lane1Id, startNode2);

                return(true);
            }
            else
            {
                return(false);
            }
        }
Exemple #4
0
        public static bool toggleLaneArrowFlags(uint laneId, bool startNode, LaneArrows flags, out LaneArrowChangeResult res)
        {
            if (!mayHaveLaneArrows(laneId))
            {
                removeLaneArrowFlags(laneId);
                res = LaneArrowChangeResult.Invalid;
                return(false);
            }

            if (highwayLaneArrowFlags[laneId] != null)
            {
                res = LaneArrowChangeResult.HighwayArrows;
                return(false);                // disallow custom lane arrows in highway rule mode
            }

            if (LaneConnectionManager.Instance().HasConnections(laneId, startNode))               // TODO refactor
            {
                res = LaneArrowChangeResult.LaneConnection;
                return(false);                // custom lane connection present
            }

            LaneArrows?arrows = laneArrowFlags[laneId];

            if (arrows == null)
            {
                // read currently defined arrows
                uint laneFlags = (uint)Singleton <NetManager> .instance.m_lanes.m_buffer[laneId].m_flags;
                laneFlags &= lfr;                 // filter arrows
                arrows     = (LaneArrows)laneFlags;
            }

            arrows ^= flags;
            laneArrowFlags[laneId] = arrows;
            applyLaneArrowFlags(laneId, false);
            res = LaneArrowChangeResult.Success;
            return(true);
        }
Exemple #5
0
        /// <summary>
        /// Displays vehicle ids over vehicles
        /// </summary>
        private void _guiVehicles()
        {
            GUIStyle              _counterStyle   = new GUIStyle();
            Array16 <Vehicle>     vehicles        = Singleton <VehicleManager> .instance.m_vehicles;
            LaneConnectionManager connManager     = LaneConnectionManager.Instance();
            SimulationManager     simManager      = Singleton <SimulationManager> .instance;
            NetManager            netManager      = Singleton <NetManager> .instance;
            VehicleStateManager   vehStateManager = VehicleStateManager.Instance();

            for (int i = 1; i < vehicles.m_size; ++i)
            {
                Vehicle vehicle = vehicles.m_buffer[i];
                if (vehicle.m_flags == 0)                 // node is unused
                {
                    continue;
                }

                Vector3 vehPos    = vehicle.GetLastFramePosition();
                var     screenPos = Camera.main.WorldToScreenPoint(vehPos);
                screenPos.y = Screen.height - screenPos.y;

                if (screenPos.z < 0)
                {
                    continue;
                }

                var camPos = simManager.m_simulationView.m_position;
                var diff   = vehPos - camPos;
                if (diff.magnitude > DebugCloseLod)
                {
                    continue;                     // do not draw if too distant
                }
                var zoom = 1.0f / diff.magnitude * 150f;

                _counterStyle.fontSize         = (int)(10f * zoom);
                _counterStyle.normal.textColor = new Color(1f, 1f, 1f);
                //_counterStyle.normal.background = MakeTex(1, 1, new Color(0f, 0f, 0f, 0.4f));

                VehicleState      vState  = vehStateManager._GetVehicleState((ushort)i);
                PathUnit.Position?curPos  = vState?.GetCurrentPathPosition(ref vehicle);
                PathUnit.Position?nextPos = vState?.GetNextPathPosition(ref vehicle);
                bool?  startNode          = vState?.CurrentSegmentEnd?.StartNode;
                ushort?segmentId          = vState?.CurrentSegmentEnd?.SegmentId;
                ushort?transitNodeId      = vState?.CurrentSegmentEnd?.NodeId;

                /*float distanceToTransitNode = Single.NaN;
                 * float timeToTransitNode = Single.NaN;*/
                ushort vehSpeed = SpeedLimitManager.Instance().VehicleToCustomSpeed(vehicle.GetLastFrameVelocity().magnitude);

                Vector3?targetPos = null;
                if (transitNodeId != null)
                {
                    targetPos = netManager.m_nodes.m_buffer[(ushort)transitNodeId].m_position;
                }

                /*if (transitNodeId != null && segmentId != null && startNode != null && curPos != null) {
                 *      bool outgoing = false;
                 *      connManager.GetLaneEndPoint((ushort)segmentId, (bool)startNode, ((PathUnit.Position)curPos).m_lane, null, null, out outgoing, out targetPos);
                 * }*/

                float distanceToTransitNode = Single.NaN;
                if (targetPos != null)
                {
                    distanceToTransitNode = ((Vector3)targetPos - vehPos).magnitude;

                    /*if (vehSpeed > 0)
                     *      timeToTransitNode = distanceToTransitNode / vehSpeed;
                     * else
                     *      timeToTransitNode = Single.PositiveInfinity;*/
                }
                String labelStr = "V #" + i + " is a " + (vState.Valid ? "valid" : "invalid") + " " + vState.VehicleType + " @ ~" + vehSpeed + " km/h (" + vState.JunctionTransitState + ") dist: " + distanceToTransitNode;
#if USEPATHWAITCOUNTER
                labelStr += ", pwc: " + vState.PathWaitCounter + ", seg. " + vState.CurrentSegmentEnd?.SegmentId;
#endif
                //String labelStr = "Veh. " + i + " @ " + String.Format("{0:0.##}", vehSpeed) + "/" + (vState != null ? vState.CurrentMaxSpeed.ToString() : "-") + " (" + (vState != null ? vState.VehicleType.ToString() : "-") + ", valid? " + (vState != null ? vState.Valid.ToString() : "-") + ")" + ", len: " + (vState != null ? vState.TotalLength.ToString() : "-") + ", state: " + (vState != null ? vState.JunctionTransitState.ToString() : "-");
#if PATHRECALC
                labelStr += ", recalc: " + (vState != null ? vState.LastPathRecalculation.ToString() : "-");
#endif
                //labelStr += "\npos: " + curPos?.m_segment + "(" + curPos?.m_lane + ")->" + nextPos?.m_segment + "(" + nextPos?.m_lane + ")" /* + ", dist: " + distanceToTransitNode + ", time: " + timeToTransitNode*/ + ", last update: " + vState?.LastPositionUpdate;

                Vector2 dim       = _counterStyle.CalcSize(new GUIContent(labelStr));
                Rect    labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y - dim.y - 50f, dim.x, dim.y);

                GUI.Box(labelRect, labelStr, _counterStyle);

                //_counterStyle.normal.background = null;
            }
        }
        public override void OnLoadData()
        {
            Log.Info("Loading Traffic Manager: PE Data");
            StateLoading = true;
            bool loadingSucceeded = true;

            try {
                Log.Info("Initializing flags");
                Flags.OnBeforeLoadData();
            } catch (Exception e) {
                Log.Error($"OnLoadData: Error while initializing Flags: {e.ToString()}");
                loadingSucceeded = false;
            }

            try {
                Log.Info("Initializing node geometries");
                NodeGeometry.OnBeforeLoadData();
            } catch (Exception e) {
                Log.Error($"OnLoadData: Error while initializing NodeGeometry: {e.ToString()}");
                loadingSucceeded = false;
            }

            try {
                Log.Info("Initializing segment geometries");
                SegmentGeometry.OnBeforeLoadData();
            } catch (Exception e) {
                Log.Error($"OnLoadData: Error while initializing SegmentGeometry: {e.ToString()}");
                loadingSucceeded = false;
            }

            try {
                Log.Info("Initializing lane connection manager");
                LaneConnectionManager.Instance().OnBeforeLoadData();                 // requires segment geometries
            } catch (Exception e) {
                Log.Error($"OnLoadData: Error while initializing LaneConnectionManager: {e.ToString()}");
                loadingSucceeded = false;
            }

            try {
                Log.Info("Initializing CustomRoadAI");
                CustomRoadAI.OnBeforeLoadData();
            } catch (Exception e) {
                Log.Error($"OnLoadData: Error while initializing CustomRoadAI: {e.ToString()}");
                loadingSucceeded = false;
            }

            Log.Info("Initialization done. Loading mod data now.");

            try {
                byte[] data = _serializableData.LoadData(DataId);
                DeserializeData(data);
            } catch (Exception e) {
                Log.Error($"OnLoadData: Error while deserializing data: {e.ToString()}");
                loadingSucceeded = false;
            }

            // load options
            try {
                byte[] options = _serializableData.LoadData("TMPE_Options");
                if (options != null)
                {
                    if (options.Length >= 1)
                    {
                        Options.setSimAccuracy(options[0]);
                    }

                    if (options.Length >= 2)
                    {
                        //Options.setLaneChangingRandomization(options[1]);
                    }

                    if (options.Length >= 3)
                    {
                        Options.setRecklessDrivers(options[2]);
                    }

                    if (options.Length >= 4)
                    {
                        Options.setRelaxedBusses(options[3] == (byte)1);
                    }

                    if (options.Length >= 5)
                    {
                        Options.setNodesOverlay(options[4] == (byte)1);
                    }

                    if (options.Length >= 6)
                    {
                        Options.setMayEnterBlockedJunctions(options[5] == (byte)1);
                    }

                    if (options.Length >= 7)
                    {
#if !TAM
                        if (!LoadingExtension.IsPathManagerCompatible)
                        {
                            Options.setAdvancedAI(false);
                        }
                        else
                        {
#endif
                        Options.setAdvancedAI(options[6] == (byte)1);
#if !TAM
                    }
#endif
                    }

                    if (options.Length >= 8)
                    {
                        Options.setHighwayRules(options[7] == (byte)1);
                    }

                    if (options.Length >= 9)
                    {
                        Options.setPrioritySignsOverlay(options[8] == (byte)1);
                    }

                    if (options.Length >= 10)
                    {
                        Options.setTimedLightsOverlay(options[9] == (byte)1);
                    }

                    if (options.Length >= 11)
                    {
                        Options.setSpeedLimitsOverlay(options[10] == (byte)1);
                    }

                    if (options.Length >= 12)
                    {
                        Options.setVehicleRestrictionsOverlay(options[11] == (byte)1);
                    }

                    if (options.Length >= 13)
                    {
                        Options.setStrongerRoadConditionEffects(options[12] == (byte)1);
                    }

                    if (options.Length >= 14)
                    {
                        Options.setAllowUTurns(options[13] == (byte)1);
                    }

                    if (options.Length >= 15)
                    {
                        Options.setAllowLaneChangesWhileGoingStraight(options[14] == (byte)1);
                    }

                    if (options.Length >= 16)
                    {
                        Options.setEnableDespawning(options[15] == (byte)1);
                    }

                    if (options.Length >= 17)
                    {
                        Options.setDynamicPathRecalculation(options[16] == (byte)1);
                    }

                    if (options.Length >= 18)
                    {
                        Options.setConnectedLanesOverlay(options[17] == (byte)1);
                    }

                    if (options.Length >= 19)
                    {
                        Options.setPrioritySignsEnabled(options[18] == (byte)1);
                    }

                    if (options.Length >= 20)
                    {
                        Options.setTimedLightsEnabled(options[19] == (byte)1);
                    }

                    if (options.Length >= 21)
                    {
                        Options.setCustomSpeedLimitsEnabled(options[20] == (byte)1);
                    }

                    if (options.Length >= 22)
                    {
                        Options.setVehicleRestrictionsEnabled(options[21] == (byte)1);
                    }

                    if (options.Length >= 23)
                    {
                        Options.setLaneConnectorEnabled(options[22] == (byte)1);
                    }

                    if (options.Length >= 24)
                    {
                        Options.setJunctionRestrictionsOverlay(options[23] == (byte)1);
                    }

                    if (options.Length >= 25)
                    {
                        Options.setJunctionRestrictionsEnabled(options[24] == (byte)1);
                    }
                }
            } catch (Exception e) {
                Log.Error($"OnLoadData: Error while loading options: {e.ToString()}");
                loadingSucceeded = false;
            }

            if (loadingSucceeded)
            {
                Log.Info("OnLoadData completed successfully.");
            }
            else
            {
                Log.Info("An error occurred while loading.");
                //UIView.library.ShowModal<ExceptionPanel>("ExceptionPanel").SetMessage("An error occurred while loading", "Traffic Manager: President Edition detected an error while loading. Please do NOT save this game under the old filename, otherwise your timed traffic lights, custom lane arrows, etc. are in danger. Instead, please navigate to http://steamcommunity.com/sharedfiles/filedetails/?id=583429740 and follow the steps under 'In case problems arise'.", true);
            }
            StateLoading = false;
        }
        private static void LoadDataState(out bool error)
        {
            error = false;

            Log.Info("Loading State from Config");
            if (_configuration == null)
            {
                Log.Warning("Configuration NULL, Couldn't load save data. Possibly a new game?");
                return;
            }

            TrafficPriorityManager prioMan = TrafficPriorityManager.Instance();

            // load priority segments
            if (_configuration.PrioritySegments != null)
            {
                Log.Info($"Loading {_configuration.PrioritySegments.Count()} priority segments");
                foreach (var segment in _configuration.PrioritySegments)
                {
                    try {
                        if (segment.Length < 3)
                        {
                            continue;
                        }
#if DEBUG
                        bool debug = segment[0] == 13630;
#endif

                        if ((SegmentEnd.PriorityType)segment[2] == SegmentEnd.PriorityType.None)
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: Not adding 'None' priority segment: {segment[1]} @ node {segment[0]}");
                            }
#endif
                            continue;
                        }

                        if (!NetUtil.IsNodeValid((ushort)segment[0]))
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: node {segment[0]} is invalid");
                            }
#endif
                            continue;
                        }
                        if (!NetUtil.IsSegmentValid((ushort)segment[1]))
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: segment {segment[1]} @ node {segment[0]} is invalid");
                            }
#endif
                            continue;
                        }
                        if (prioMan.IsPrioritySegment((ushort)segment[0], (ushort)segment[1]))
                        {
#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"Loading priority segment: segment {segment[1]} @ node {segment[0]} is already a priority segment");
                            }
#endif
                            prioMan.GetPrioritySegment((ushort)segment[0], (ushort)segment[1]).Type = (SegmentEnd.PriorityType)segment[2];
                            continue;
                        }
#if DEBUG
                        Log._Debug($"Adding Priority Segment of type: {segment[2].ToString()} to segment {segment[1]} @ node {segment[0]}");
#endif
                        prioMan.AddPrioritySegment((ushort)segment[0], (ushort)segment[1], (SegmentEnd.PriorityType)segment[2]);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading data from Priority segments: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Priority segments data structure undefined!");
            }

            // load vehicle restrictions (warning: has to be done before loading timed lights!)
            if (_configuration.LaneAllowedVehicleTypes != null)
            {
                Log.Info($"Loading lane vehicle restriction data. {_configuration.LaneAllowedVehicleTypes.Count} elements");
                foreach (Configuration.LaneVehicleTypes laneVehicleTypes in _configuration.LaneAllowedVehicleTypes)
                {
                    try {
                        ExtVehicleType baseMask   = VehicleRestrictionsManager.Instance().GetBaseMask(laneVehicleTypes.laneId);
                        ExtVehicleType maskedType = laneVehicleTypes.vehicleTypes & baseMask;
                        Log._Debug($"Loading lane vehicle restriction: lane {laneVehicleTypes.laneId} = {laneVehicleTypes.vehicleTypes}, masked = {maskedType}");
                        if (maskedType != baseMask)
                        {
                            Flags.setLaneAllowedVehicleTypes(laneVehicleTypes.laneId, maskedType);
                        }
                        else
                        {
                            Log._Debug($"Masked type does not differ from base type. Ignoring.");
                        }
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading data from vehicle restrictions: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Vehicle restrctions structure undefined!");
            }

            NetManager netManager = Singleton <NetManager> .instance;
            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            if (_configuration.TimedLights != null)
            {
                Log.Info($"Loading {_configuration.TimedLights.Count()} timed traffic lights (new method)");

                foreach (Configuration.TimedTrafficLights cnfTimedLights in _configuration.TimedLights)
                {
                    try {
                        if (!NetUtil.IsNodeValid(cnfTimedLights.nodeId))
                        {
                            continue;
                        }
                        Flags.setNodeTrafficLight(cnfTimedLights.nodeId, true);

                        Log._Debug($"Adding Timed Node at node {cnfTimedLights.nodeId}");

                        TrafficLightSimulation sim = tlsMan.AddNodeToSimulation(cnfTimedLights.nodeId);
                        sim.SetupTimedTrafficLight(cnfTimedLights.nodeGroup);
                        var timedNode = sim.TimedLight;

                        int j = 0;
                        foreach (Configuration.TimedTrafficLightsStep cnfTimedStep in cnfTimedLights.timedSteps)
                        {
                            Log._Debug($"Loading timed step {j} at node {cnfTimedLights.nodeId}");
                            TimedTrafficLightsStep step = timedNode.AddStep(cnfTimedStep.minTime, cnfTimedStep.maxTime, cnfTimedStep.waitFlowBalance);

                            foreach (KeyValuePair <ushort, Configuration.CustomSegmentLights> e in cnfTimedStep.segmentLights)
                            {
                                Log._Debug($"Loading timed step {j}, segment {e.Key} at node {cnfTimedLights.nodeId}");
                                CustomSegmentLights lights = null;
                                if (!step.segmentLights.TryGetValue(e.Key, out lights))
                                {
                                    Log._Debug($"No segment lights found at timed step {j} for segment {e.Key}, node {cnfTimedLights.nodeId}");
                                    continue;
                                }
                                Configuration.CustomSegmentLights cnfLights = e.Value;

                                Log._Debug($"Loading pedestrian light @ seg. {e.Key}, step {j}: {cnfLights.pedestrianLightState} {cnfLights.manualPedestrianMode}");

                                lights.ManualPedestrianMode = cnfLights.manualPedestrianMode;
                                lights.PedestrianLightState = cnfLights.pedestrianLightState;

                                foreach (KeyValuePair <ExtVehicleType, Configuration.CustomSegmentLight> e2 in cnfLights.customLights)
                                {
                                    Log._Debug($"Loading timed step {j}, segment {e.Key}, vehicleType {e2.Key} at node {cnfTimedLights.nodeId}");
                                    CustomSegmentLight light = null;
                                    if (!lights.CustomLights.TryGetValue(e2.Key, out light))
                                    {
                                        Log._Debug($"No segment light found for timed step {j}, segment {e.Key}, vehicleType {e2.Key} at node {cnfTimedLights.nodeId}");
                                        continue;
                                    }
                                    Configuration.CustomSegmentLight cnfLight = e2.Value;

                                    light.CurrentMode = (CustomSegmentLight.Mode)cnfLight.currentMode;
                                    light.LightLeft   = cnfLight.leftLight;
                                    light.LightMain   = cnfLight.mainLight;
                                    light.LightRight  = cnfLight.rightLight;
                                }
                            }
                            ++j;
                        }

                        if (cnfTimedLights.started)
                        {
                            timedNode.Start();
                        }
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading data from TimedNode (new method): " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Timed traffic lights data structure undefined!");
            }

            if (_configuration.NodeTrafficLights != null)
            {
                var trafficLightDefs = _configuration.NodeTrafficLights.Split(',');

                Log.Info($"Loading junction traffic light data");

                // new method
                foreach (var split in trafficLightDefs.Select(def => def.Split(':')).Where(split => split.Length > 1))
                {
                    try {
                        Log._Debug($"Traffic light split data: {split[0]} , {split[1]}");
                        var  nodeId = Convert.ToUInt16(split[0]);
                        uint flag   = Convert.ToUInt16(split[1]);

                        Flags.setNodeTrafficLight(nodeId, flag > 0);
                    } catch (Exception e) {
                        // ignore as it's probably bad save data.
                        Log.Error($"Error setting the NodeTrafficLights: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Junction traffic lights data structure undefined!");
            }

            if (_configuration.LaneFlags != null)
            {
                Log.Info($"Loading lane arrow data");
#if DEBUG
                Log._Debug($"LaneFlags: {_configuration.LaneFlags}");
#endif
                var lanes = _configuration.LaneFlags.Split(',');

                if (lanes.Length > 1)
                {
                    foreach (var split in lanes.Select(lane => lane.Split(':')).Where(split => split.Length > 1))
                    {
                        try {
                            Log._Debug($"Split Data: {split[0]} , {split[1]}");
                            var  laneId = Convert.ToUInt32(split[0]);
                            uint flags  = Convert.ToUInt32(split[1]);

                            //make sure we don't cause any overflows because of bad save data.
                            if (Singleton <NetManager> .instance.m_lanes.m_buffer.Length <= laneId)
                            {
                                continue;
                            }

                            if (flags > ushort.MaxValue)
                            {
                                continue;
                            }

                            if (!NetUtil.IsLaneValid(laneId))
                            {
                                continue;
                            }

                            //Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags = fixLaneFlags(Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags);

                            uint laneArrowFlags = flags & Flags.lfr;
                            uint origFlags      = (Singleton <NetManager> .instance.m_lanes.m_buffer[laneId].m_flags & Flags.lfr);
#if DEBUG
                            Log._Debug("Setting flags for lane " + laneId + " to " + flags + " (" + ((Flags.LaneArrows)(laneArrowFlags)).ToString() + ")");
                            if ((origFlags | laneArrowFlags) == origFlags)                               // only load if setting differs from default
                            {
                                Log._Debug("Flags for lane " + laneId + " are original (" + ((NetLane.Flags)(origFlags)).ToString() + ")");
                            }
#endif
                            Flags.setLaneArrowFlags(laneId, (Flags.LaneArrows)(laneArrowFlags));
                        } catch (Exception e) {
                            Log.Error($"Error loading Lane Split data. Length: {split.Length} value: {split}\nError: {e.ToString()}");
                            error = true;
                        }
                    }
                }
            }
            else
            {
                Log.Warning("Lane arrow data structure undefined!");
            }

            // load lane connections
            if (_configuration.LaneConnections != null)
            {
                Log.Info($"Loading {_configuration.LaneConnections.Count()} lane connections");
                foreach (Configuration.LaneConnection conn in _configuration.LaneConnections)
                {
                    try {
                        Log._Debug($"Loading lane connection: lane {conn.lowerLaneId} -> {conn.higherLaneId}");
                        LaneConnectionManager.Instance().AddLaneConnection(conn.lowerLaneId, conn.higherLaneId, conn.lowerStartNode);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Error("Error loading data from lane connection: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Lane connection data structure undefined!");
            }

            // load speed limits
            if (_configuration.LaneSpeedLimits != null)
            {
                Log.Info($"Loading lane speed limit data. {_configuration.LaneSpeedLimits.Count} elements");
                foreach (Configuration.LaneSpeedLimit laneSpeedLimit in _configuration.LaneSpeedLimits)
                {
                    try {
                        Log._Debug($"Loading lane speed limit: lane {laneSpeedLimit.laneId} = {laneSpeedLimit.speedLimit}");
                        Flags.setLaneSpeedLimit(laneSpeedLimit.laneId, laneSpeedLimit.speedLimit);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading speed limits: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Lane speed limit structure undefined!");
            }

            // Load segment-at-node flags
            if (_configuration.SegmentNodeConfs != null)
            {
                Log.Info($"Loading segment-at-node data. {_configuration.SegmentNodeConfs.Count} elements");
                foreach (Configuration.SegmentNodeConf segNodeConf in _configuration.SegmentNodeConfs)
                {
                    try {
                        if (!NetUtil.IsSegmentValid(segNodeConf.segmentId))
                        {
                            continue;
                        }
                        Flags.setSegmentNodeFlags(segNodeConf.segmentId, true, segNodeConf.startNodeFlags);
                        Flags.setSegmentNodeFlags(segNodeConf.segmentId, false, segNodeConf.endNodeFlags);
                    } catch (Exception e) {
                        // ignore, as it's probably corrupt save data. it'll be culled on next save
                        Log.Warning("Error loading segment-at-node config: " + e.ToString());
                        error = true;
                    }
                }
            }
            else
            {
                Log.Warning("Segment-at-node structure undefined!");
            }
        }
        private List <NodeLaneMarker> GetNodeMarkers(ushort nodeId)
        {
            if (nodeId == 0)
            {
                return(null);
            }
            if ((NetManager.instance.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Created) == NetNode.Flags.None)
            {
                return(null);
            }

            List <NodeLaneMarker> nodeMarkers = new List <NodeLaneMarker>();
            LaneConnectionManager connManager = LaneConnectionManager.Instance();

            int offsetMultiplier = NetManager.instance.m_nodes.m_buffer[nodeId].CountSegments() <= 2 ? 3 : 1;

            for (int i = 0; i < 8; i++)
            {
                ushort segmentId = NetManager.instance.m_nodes.m_buffer[nodeId].GetSegment(i);
                if (segmentId == 0)
                {
                    continue;
                }

                bool           isEndNode = NetManager.instance.m_segments.m_buffer[segmentId].m_endNode == nodeId;
                Vector3        offset    = NetManager.instance.m_segments.m_buffer[segmentId].FindDirection(segmentId, nodeId) * offsetMultiplier;
                NetInfo.Lane[] lanes     = NetManager.instance.m_segments.m_buffer[segmentId].Info.m_lanes;
                uint           laneId    = NetManager.instance.m_segments.m_buffer[segmentId].m_lanes;
                for (byte laneIndex = 0; laneIndex < lanes.Length && laneId != 0; laneIndex++)
                {
                    if ((lanes[laneIndex].m_laneType & (NetInfo.LaneType.TransportVehicle | NetInfo.LaneType.Vehicle)) != NetInfo.LaneType.None &&
                        (lanes[laneIndex].m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) != VehicleInfo.VehicleType.None)
                    {
                        Vector3?pos      = null;
                        bool    isSource = false;
                        if (connManager.GetLaneEndPoint(segmentId, !isEndNode, laneIndex, laneId, lanes[laneIndex], out isSource, out pos))
                        {
                            nodeMarkers.Add(new NodeLaneMarker()
                            {
                                segmentId   = segmentId,
                                laneId      = laneId,
                                nodeId      = nodeId,
                                startNode   = !isEndNode,
                                position    = (Vector3)pos + offset,
                                color       = colors[nodeMarkers.Count],
                                isSource    = isSource,
                                laneType    = lanes[laneIndex].m_laneType,
                                vehicleType = lanes[laneIndex].m_vehicleType
                            });
                        }
                    }

                    laneId = NetManager.instance.m_lanes.m_buffer[laneId].m_nextLane;
                }
            }

            if (nodeMarkers.Count == 0)
            {
                return(null);
            }

            foreach (NodeLaneMarker laneMarker1 in nodeMarkers)
            {
                if (!laneMarker1.isSource)
                {
                    continue;
                }

                uint[] connections = LaneConnectionManager.Instance().GetLaneConnections(laneMarker1.laneId, laneMarker1.startNode);
                if (connections == null || connections.Length == 0)
                {
                    continue;
                }

                foreach (NodeLaneMarker laneMarker2 in nodeMarkers)
                {
                    if (laneMarker2.isSource)
                    {
                        continue;
                    }

                    if (connections.Contains(laneMarker2.laneId))
                    {
                        laneMarker1.connectedMarkers.Add(laneMarker2);
                    }
                }
            }

            return(nodeMarkers);
        }
        public override void OnPrimaryClickOverlay()
        {
#if DEBUGCONN
            Log._Debug($"TppLaneConnectorTool: OnPrimaryClickOverlay. SelectedNodeId={SelectedNodeId} SelectedSegmentId={SelectedSegmentId} HoveredNodeId={HoveredNodeId} HoveredSegmentId={HoveredSegmentId}");
#endif

            if (GetMarkerSelectionMode() == MarkerSelectionMode.None)
            {
                if (HoveredNodeId != 0)
                {
#if DEBUGCONN
                    Log._Debug($"TppLaneConnectorTool: HoveredNode != 0");
#endif

                    if (NetManager.instance.m_nodes.m_buffer[HoveredNodeId].CountSegments() < 2)
                    {
                        // this node cannot be configured (dead end)
#if DEBUGCONN
                        Log._Debug($"TppLaneConnectorTool: Node is a dead end");
#endif
                        SelectedNodeId = 0;
                        selectedMarker = null;
                        return;
                    }

                    if (SelectedNodeId != HoveredNodeId)
                    {
#if DEBUGCONN
                        Log._Debug($"Node {HoveredNodeId} has been selected. Creating markers.");
#endif

                        // selected node has changed. create markers
                        List <NodeLaneMarker> markers = GetNodeMarkers(HoveredNodeId);
                        if (markers != null)
                        {
                            SelectedNodeId = HoveredNodeId;
                            selectedMarker = null;

                            currentNodeMarkers[SelectedNodeId] = markers;
                        }
                        //this.allNodeMarkers[SelectedNodeId] = GetNodeMarkers(SelectedNodeId);
                    }
                }
                else
                {
#if DEBUGCONN
                    Log._Debug($"TppLaneConnectorTool: Node {SelectedNodeId} has been deselected.");
#endif

                    // click on free spot. deselect node
                    SelectedNodeId = 0;
                    selectedMarker = null;
                    return;
                }
            }

            if (hoveredMarker != null)
            {
#if DEBUGCONN
                Log._Debug($"TppLaneConnectorTool: hoveredMarker != null. selMode={GetMarkerSelectionMode()}");
#endif

                // hovered marker has been clicked
                if (GetMarkerSelectionMode() == MarkerSelectionMode.SelectSource)
                {
                    // select source marker
                    selectedMarker = hoveredMarker;
#if DEBUGCONN
                    Log._Debug($"TppLaneConnectorTool: set selected marker");
#endif
                }
                else if (GetMarkerSelectionMode() == MarkerSelectionMode.SelectTarget)
                {
                    // select target marker
                    //bool success = false;
                    if (LaneConnectionManager.Instance().RemoveLaneConnection(selectedMarker.laneId, hoveredMarker.laneId, selectedMarker.startNode))                       // try to remove connection
                    {
                        selectedMarker.connectedMarkers.Remove(hoveredMarker);
#if DEBUGCONN
                        Log._Debug($"TppLaneConnectorTool: removed lane connection: {selectedMarker.laneId}, {hoveredMarker.laneId}");
#endif
                        //success = true;
                    }
                    else if (LaneConnectionManager.Instance().AddLaneConnection(selectedMarker.laneId, hoveredMarker.laneId, selectedMarker.startNode))                         // try to add connection
                    {
                        selectedMarker.connectedMarkers.Add(hoveredMarker);
#if DEBUGCONN
                        Log._Debug($"TppLaneConnectorTool: added lane connection: {selectedMarker.laneId}, {hoveredMarker.laneId}");
#endif
                        //success = true;
                    }

                    /*if (success) {
                     *      // connection has been modified. switch back to source marker selection
                     *      Log._Debug($"TppLaneConnectorTool: switch back to source marker selection");
                     *      selectedMarker = null;
                     *      selMode = MarkerSelectionMode.SelectSource;
                     * }*/
                }
            }
        }