Beispiel #1
0
        public override Instance Clone(InstanceState instanceState, ref Matrix4x4 matrix4x, float deltaHeight, float deltaAngle, Vector3 center, bool followTerrain, Dictionary <ushort, ushort> clonedNodes)
        {
            NodeState state = instanceState as NodeState;

            Vector3 newPosition = matrix4x.MultiplyPoint(state.position - center);

            newPosition.y = state.position.y + deltaHeight;

            if (followTerrain)
            {
                newPosition.y = newPosition.y + TerrainManager.instance.SampleOriginalRawHeightSmooth(newPosition) - state.terrainHeight;
            }

            Instance cloneInstance = null;

            if (NetManager.instance.CreateNode(out ushort clone, ref SimulationManager.instance.m_randomizer, state.info as NetInfo,
                                               newPosition, SimulationManager.instance.m_currentBuildIndex))
            {
                SimulationManager.instance.m_currentBuildIndex++;

                InstanceID cloneID = default(InstanceID);
                cloneID.NetNode = clone;
                cloneInstance   = new MoveableNode(cloneID);

                nodeBuffer[clone].m_flags = state.flags;

                // TODO: Clone pillar instead?
                nodeBuffer[clone].Info.m_netAI.GetNodeBuilding(clone, ref nodeBuffer[clone], out BuildingInfo newBuilding, out float heightOffset);
                nodeBuffer[clone].UpdateBuilding(clone, newBuilding, heightOffset);
            }

            return(cloneInstance);
        }
Beispiel #2
0
        public override void ReplaceInstance(Instance instance)
        {
            base.ReplaceInstance(instance);

            MoveableNode node = instance as MoveableNode;

            if (pillarState != null)
            {
                pillarState.instance = node.Pillar;
                if (pillarState.instance == null)
                {
                    pillarState = null;
                }
            }
        }
Beispiel #3
0
        public override Instance Clone(InstanceState instanceState, Dictionary <ushort, ushort> clonedNodes)
        {
            NodeState state = instanceState as NodeState;

            MoveableNode cloneInstance = null;

            if (NetManager.instance.CreateNode(out ushort clone, ref SimulationManager.instance.m_randomizer, state.Info.Prefab as NetInfo,
                                               state.position, SimulationManager.instance.m_currentBuildIndex))
            {
                SimulationManager.instance.m_currentBuildIndex++;

                InstanceID cloneID = default;
                cloneID.NetNode = clone;
                cloneInstance   = new MoveableNode(cloneID);

                nodeBuffer[clone].m_flags = state.flags;

                // TODO: Clone pillar instead?
                nodeBuffer[clone].Info.m_netAI.GetNodeBuilding(clone, ref nodeBuffer[clone], out BuildingInfo newBuilding, out float heightOffset);
                nodeBuffer[clone].UpdateBuilding(clone, newBuilding, heightOffset);
            }

            return(cloneInstance);
        }
Beispiel #4
0
        private void OnLeftClick()
        {
            DebugUtils.Log("OnLeftClick: " + ToolState);

            if (POProcessing > 0)
            {
                return;
            }

            if (ToolState == ToolStates.Default || ToolState == ToolStates.DrawingSelection || ToolState == ToolStates.ToolActive)
            {
                Event e = Event.current;
                if (m_hoverInstance == null)
                {
                    return;
                }

                #region Debug Ouput
                //Instance instance = m_hoverInstance;
                //InstanceID instanceID = instance.id;
                //Log.Debug($"instance:{(instance == null ? "null" : instance.GetType().ToString())}");

                //if (instanceID.Building > 0)
                //{
                //    MoveableBuilding mb = (MoveableBuilding)instance;
                //    string msg = $"{mb.id.Building}:{mb.Info.Name}\n";
                //    //Log.Debug(msg);
                //    foreach (Instance subInstance in mb.subInstances)
                //    {
                //        msg += $" - {subInstance.id.Building}/{subInstance.id.NetNode}: {subInstance.Info.Name}\n";
                //        //Log.Debug(msg);
                //        if (subInstance.id.Building > 0)
                //        {
                //            foreach (Instance subSubInstance in ((MoveableBuilding)subInstance).subInstances)
                //            {
                //                msg += $"    - {subSubInstance.id.Building}/{subSubInstance.id.NetNode}: {subSubInstance.Info.Name}\n";
                //                //Log.Debug(msg);
                //            }
                //        }
                //    }
                //    msg += "End";
                //    Log.Debug(msg);
                //}
                #endregion

                if (!(ActionQueue.instance.current is SelectAction))
                {
                    ActionQueue.instance.Push(new SelectAction(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)));
                }
                else
                {
                    ActionQueue.instance.Invalidate();
                }

                if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //"if (e.shift)" apparently fails in Linux
                {
                    if (e.alt && m_hoverInstance is MoveableSegment ms && FindOwnerBuilding(ms.id.NetSegment, 363f) == 0)
                    {
                        MoveableNode closest  = ms.GetNodeByDistance();
                        MoveableNode furthest = ms.GetNodeByDistance(true);

                        if (!Action.selection.Contains(closest))
                        {
                            Action.selection.Add(closest);
                        }
                        else if (!Action.selection.Contains(furthest))
                        {
                            Action.selection.Add(furthest);
                        }
                        else
                        {
                            Action.selection.Remove(furthest);
                        }
                    }
                    else
                    {
                        if (Action.selection.Contains(m_hoverInstance))
                        {
                            Action.selection.RemoveObject(m_hoverInstance);
                        }
                        else
                        {
                            Action.selection.AddObject(m_hoverInstance);
                        }
                    }
                }
                else
                {
                    if (e.alt && m_hoverInstance is MoveableSegment ms && FindOwnerBuilding(ms.id.NetSegment, 363f) == 0)
                    {
                        MoveableNode closest  = ms.GetNodeByDistance();
                        MoveableNode furthest = ms.GetNodeByDistance(true);

                        if (Action.selection.Contains(closest) && !Action.selection.Contains(furthest))
                        {
                            Action.selection.Clear();
                            Action.selection.Add(furthest);
                        }
                        else
                        {
                            Action.selection.Clear();
                            Action.selection.Add(closest);
                        }
                    }
Beispiel #5
0
        public BulldozeAction()
        {
            HashSet <Instance> newSelection = new HashSet <Instance>(selection);
            HashSet <Instance> extraNodes   = new HashSet <Instance>();
            HashSet <ushort>   segments     = new HashSet <ushort>(); // Segments to be removed

            NetManager netManager = Singleton <NetManager> .instance;

            // Add any segments whose node is selected
            foreach (Instance instance in selection)
            {
                if (instance.isValid)
                {
                    if (instance.id.Type == InstanceType.NetSegment)
                    {
                        segments.Add(instance.id.NetSegment);
                    }
                    else if (instance.id.Type == InstanceType.NetNode)
                    {
                        for (int i = 0; i < 8; i++)
                        {
                            ushort segment = netManager.m_nodes.m_buffer[instance.id.NetNode].GetSegment(i);
                            if (segment != 0)
                            {
                                InstanceID instanceID = default;
                                instanceID.NetSegment = segment;

                                if (selection.Contains(instanceID))
                                {
                                    continue;
                                }

                                newSelection.Add(new MoveableSegment(instanceID));
                                segments.Add(segment);
                            }
                        }
                    }
                    else if (instance.id.Type == InstanceType.Building)
                    {
                        Building building = (Building)((MoveableBuilding)instance).data;
                        ushort   nodeId   = building.m_netNode;

                        int c = 0;
                        while (nodeId != 0)
                        {
                            NetNode node = netManager.m_nodes.m_buffer[nodeId];

                            if (node.m_building == 0 || node.Info.m_class.m_layer == ItemClass.Layer.WaterPipes)
                            { // Exclude attached nodes with attached buildings (e.g. water buildings)
                                for (int i = 0; i < 8; i++)
                                {
                                    ushort segmentId = node.GetSegment(i);
                                    if (segmentId != 0 && MoveableSegment.isSegmentValid(segmentId) &&
                                        ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Untouchable) == NetSegment.Flags.None))
                                    {
                                        InstanceID instanceID = default;
                                        instanceID.NetSegment = segmentId;

                                        if (selection.Contains(instanceID))
                                        {
                                            continue;
                                        }

                                        newSelection.Add(new MoveableSegment(instanceID));
                                        segments.Add(segmentId);
                                    }
                                }
                            }

                            nodeId = node.m_nextBuildingNode;

                            if (++c > 32768)
                            {
                                CODebugBase <LogChannel> .Error(LogChannel.Core, "Nodes: Invalid list detected!\n" + Environment.StackTrace);

                                break;
                            }
                        }
                    }
                }
            }

            // Add any nodes whose segments are all selected
            foreach (Instance instance in newSelection)
            {
                if (instance.isValid)
                {
                    if (instance.id.Type == InstanceType.NetSegment)
                    {
                        ushort   segId   = instance.id.NetSegment;
                        ushort[] nodeIds = { netManager.m_segments.m_buffer[segId].m_startNode, netManager.m_segments.m_buffer[segId].m_endNode };
                        foreach (ushort id in nodeIds)
                        {
                            bool    toDelete = true;
                            NetNode node     = netManager.m_nodes.m_buffer[id];
                            for (int i = 0; i < 8; i++)
                            {
                                if (node.GetSegment(i) != 0 && !segments.Contains(node.GetSegment(i)))
                                {
                                    toDelete = false;
                                    break;
                                }
                            }
                            if (toDelete)
                            {
                                if (node.Info.m_class.m_layer == ItemClass.Layer.WaterPipes)
                                {
                                    foreach (Building b in BuildingManager.instance.m_buildings.m_buffer)
                                    {
                                        if (b.m_netNode == id)
                                        {
                                            toDelete = false;
                                            break;
                                        }
                                    }
                                }
                            }
                            if (toDelete)
                            {
                                InstanceID instanceId = default;
                                instanceId.NetNode = id;
                                MoveableNode mn = new MoveableNode(instanceId);
                                if (newSelection.Contains(mn))
                                {
                                    continue;
                                }

                                extraNodes.Add(mn);
                            }
                        }
                    }
                }
            }

            // Sort segments by buildIndex
            HashSet <Instance> sorted  = new HashSet <Instance>();
            List <uint>        indexes = new List <uint>();

            foreach (Instance instance in newSelection)
            {
                if (instance.id.Type != InstanceType.NetSegment)
                {
                    sorted.Add(instance);
                }
                else
                {
                    uint bi = ((NetSegment)instance.data).m_buildIndex;
                    if (!indexes.Contains(bi))
                    {
                        indexes.Add(bi);
                    }
                }
            }

            indexes.Sort();

            foreach (uint i in indexes)
            {
                foreach (Instance instance in newSelection)
                {
                    if (instance.id.Type == InstanceType.NetSegment)
                    {
                        if (((NetSegment)instance.data).m_buildIndex == i)
                        {
                            sorted.Add(instance);
                        }
                    }
                }
            }

            foreach (Instance instance in sorted)
            {
                m_states.Add(instance.SaveToState());
            }
            foreach (Instance instance in extraNodes)
            {
                m_states.Add(instance.SaveToState());
            }
        }
Beispiel #6
0
        public override void Do()
        {
            float     angleDelta;
            float     heightDelta;
            float     distance;
            Matrix4x4 matrix = default(Matrix4x4);

            if (IsQuick)
            {
                if (selection.Count != 1)
                {
                    return;
                }
                foreach (Instance instance in selection) // Is this really the best way to get the value of selection[0]?
                {
                    if (!instance.isValid || !(instance is MoveableNode nodeInstance))
                    {
                        return;
                    }

                    NetNode node = nodeBuffer[nodeInstance.id.NetNode];

                    int c = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        ushort segId = 0;
                        if ((segId = node.GetSegment(i)) > 0)
                        {
                            if (c > 1)
                            {
                                return;        // More than 2 segments found
                            }
                            NetSegment segment    = segmentBuffer[segId];
                            InstanceID instanceID = default(InstanceID);
                            if (segment.m_startNode == nodeInstance.id.NetNode)
                            {
                                instanceID.NetNode = segment.m_endNode;
                            }
                            else
                            {
                                instanceID.NetNode = segment.m_startNode;
                            }
                            keyInstance[c] = new MoveableNode(instanceID);
                            c++;
                        }
                    }
                    if (c != 2)
                    {
                        return;
                    }
                }
            }

            //bool isAllNodes = true;
            //foreach (Instance instance in selection)
            //{
            //    if(instance.isValid && instance.id.Type != InstanceType.NetNode && instance.id.Type != InstanceType.NetSegment)
            //    {
            //        isAllNodes = false;
            //        break;
            //    }
            //}

            //if (isAllNodes)
            //{
            //    List<ushort> unsortedNodes = new List<ushort>();
            //    List<ushort> connectingSegments = new List<ushort>();
            //    foreach(Instance instance in selection)
            //    {
            //        if (instance.id.Type != InstanceType.NetSegment)
            //        {
            //            unsortedNodes.Add(instance.id.NetNode);
            //        }
            //    }
            //    if (unsortedNodes.Count < 3)
            //    {
            //        Debug.LogError("[Segment Slope Smoother] Not enough nodes to complete process. You probably won't see this error but it's best to put it here anyway, just in case.");
            //        return;
            //    }
            //    List<List<ushort>> fragmentXZ = new List<List<ushort>>();
            //    List<ushort> endpoints = new List<ushort>();
            //    for (int i = 0; i < unsortedNodes.Count; i++)
            //    {
            //        NetNode node = nodeBuffer[unsortedNodes[i]];
            //        List<ushort> connections = new List<ushort>();
            //        connections.Add(unsortedNodes[i]);
            //        for (int j = 0; j < unsortedNodes.Count; j++)
            //        {
            //            if (i == j) continue;

            //            if (node.IsConnectedTo(unsortedNodes[j]))
            //            {
            //                connections.Add(unsortedNodes[j]);
            //            }

            //            if (connections.Count > 3)
            //            { // the node itself, then both connections
            //                Debug.LogError("[Segment Slope Smoother] Validation error: Too many connections! Each node should be only connected by segments to at most two other nodes.");
            //                return;
            //            }
            //        }

            //        if (connections.Count == 2)
            //        { // the node itself, and one connection
            //            endpoints.Add(unsortedNodes[i]);
            //        }

            //        if (connections.Count == 1)
            //        {
            //            Debug.LogError("[Segment Slope Smoother] Validation error: No connections! Each node needs at least one other segment connection.");
            //            return;
            //        }
            //        fragmentXZ.Add(connections);
            //    }


            //    List<ushort> sortedNodes = new List<ushort>();
            //    bool incomplete = true;
            //    int index = -1;
            //    for (int i = 0; i < fragmentXZ.Count; i++)
            //    {
            //        if (fragmentXZ[i].Count == 2)
            //        {
            //            index = fragmentXZ.FindIndex(e => e[0] == fragmentXZ[i][1]);
            //            sortedNodes.Add(fragmentXZ[i][0]);
            //            sortedNodes.Add(fragmentXZ[i][1]);
            //            if (index == -1)
            //            {
            //                Debug.LogError("[Segment Slope Smoother] Sort error: Invalid path! Endpoint is connected to undefined node.");
            //                return;
            //            }
            //            break;
            //        }
            //    }
            //    while (incomplete)
            //    {
            //        for (int i = 0; i < fragmentXZ.Count; i++)
            //        {
            //            for (int j = 1; j <= 2; j++)
            //            {
            //                if ((fragmentXZ[i][0] == fragmentXZ[index][j] && !sortedNodes.Contains(fragmentXZ[index][j])))
            //                {
            //                    sortedNodes.Add(fragmentXZ[i][0]);
            //                    if (fragmentXZ[i].Count == 2)
            //                    {
            //                        incomplete = false;
            //                        break;
            //                    }
            //                    index = i;
            //                }
            //            }
            //        }
            //    }
            //    for (var i = 0; i < sortedNodes.Count - 1; i++)
            //    {
            //        for (var j = 0; j < nodeBuffer[sortedNodes[i]].CountSegments(); j++)
            //        {
            //            ushort segmentID = nodeBuffer[sortedNodes[i]].GetSegment(j);
            //            NetSegment testedSegment = segmentBuffer[segmentID];
            //            if ((testedSegment.m_startNode == sortedNodes[i] || testedSegment.m_endNode == sortedNodes[i]) && (testedSegment.m_startNode == sortedNodes[i + 1] || testedSegment.m_endNode == sortedNodes[i + 1]))
            //            {
            //                connectingSegments.Add(segmentID);
            //                break;
            //            }
            //        }
            //    }
            //    float totalLength = 0;
            //    List<float> segmentLinearLengthsXZ = new List<float>();
            //    for (int i = 0; i < connectingSegments.Count; i++)
            //    {
            //        NetSegment calcSegment = segmentBuffer[connectingSegments[i]];
            //        float linearDistanceXZ = (float)Math.Sqrt(Math.Pow(calcSegment.m_averageLength, 2) - Math.Pow(nodeBuffer[calcSegment.m_startNode].m_position.y - nodeBuffer[calcSegment.m_endNode].m_position.y, 2));
            //        totalLength += linearDistanceXZ;
            //        segmentLinearLengthsXZ.Add(linearDistanceXZ);

            //    }
            //    float incrementLength = 0;
            //    NetNode startNode = nodeBuffer[sortedNodes[0]];
            //    NetNode endNode = nodeBuffer[sortedNodes[sortedNodes.Count - 1]];
            //    for (int i = 0; i < sortedNodes.Count; i++)
            //    {
            //        float Nln = ((endNode.m_position.y - startNode.m_position.y) / totalLength) * incrementLength + startNode.m_position.y;
            //        Singleton<NetManager>.instance.MoveNode(sortedNodes[i], new Vector3(nodeBuffer[sortedNodes[i]].m_position.x, Nln, nodeBuffer[sortedNodes[i]].m_position.z));
            //        if (i != sortedNodes.Count - 1) incrementLength += segmentLinearLengthsXZ[i];
            //    }
            //}
            //else
            {
                angleDelta  = 0 - (float)Math.Atan2(PointB.position.z - PointA.position.z, PointB.position.x - PointA.position.x);
                heightDelta = PointB.position.y - PointA.position.y;
                distance    = (float)Math.Sqrt(Math.Pow(PointB.position.z - PointA.position.z, 2) + Math.Pow(PointB.position.x - PointA.position.x, 2));

                //string msg = $"\nA:{PointA.position}, B:{PointB.position}\nAng:{angleDelta} ({angleDelta * Mathf.Rad2Deg}) - H:{heightDelta} - D:{distance}";
                foreach (InstanceState state in m_states)
                {
                    float distanceOffset, heightOffset;
                    matrix.SetTRS(PointA.position, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one);
                    distanceOffset = (matrix.MultiplyPoint(state.position - PointA.position) - PointA.position).x;
                    heightOffset   = distanceOffset / distance * heightDelta;

                    state.instance.SetHeight(Mathf.Clamp(PointA.position.y + heightOffset, 0f, 1000f));

                    //msg += $"\nx-offset:{distanceOffset} h-offset:{heightOffset}";
                }
                //Debug.Log(msg);
            }
        }
        public override void Do()
        {
            float     angleDelta;
            float     heightDelta;
            float     distance;
            Matrix4x4 matrix = default;

            if (mode == Modes.Quick)
            {
                if (selection.Count != 1)
                {
                    return;
                }
                Instance instance = selection.First();

                if (!instance.isValid || !(instance is MoveableNode nodeInstance))
                {
                    return;
                }

                NetNode node = nodeBuffer[nodeInstance.id.NetNode];

                int c = 0;
                for (int i = 0; i < 8; i++)
                {
                    ushort segId;
                    if ((segId = node.GetSegment(i)) > 0)
                    {
                        if (c > 1)
                        {
                            return;        // More than 2 segments found
                        }
                        NetSegment segment    = segmentBuffer[segId];
                        InstanceID instanceID = default;
                        if (segment.m_startNode == nodeInstance.id.NetNode)
                        {
                            instanceID.NetNode = segment.m_endNode;
                        }
                        else
                        {
                            instanceID.NetNode = segment.m_startNode;
                        }
                        keyInstance[c] = new MoveableNode(instanceID);
                        c++;
                    }
                }
            }
            else if (mode == Modes.Auto)
            {
                if (selection.Count < 2)
                {
                    return;
                }
                GetExtremeObjects(out keyInstance[0], out keyInstance[1]);
            }

            angleDelta  = 0 - (float)Math.Atan2(PointB.position.z - PointA.position.z, PointB.position.x - PointA.position.x);
            heightDelta = PointB.position.y - PointA.position.y;
            distance    = (float)Math.Sqrt(Math.Pow(PointB.position.z - PointA.position.z, 2) + Math.Pow(PointB.position.x - PointA.position.x, 2));

            foreach (InstanceState state in m_states)
            {
                float distanceOffset, heightOffset;
                matrix.SetTRS(PointA.position, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one);
                distanceOffset = (matrix.MultiplyPoint(state.position - PointA.position) - PointA.position).x;
                heightOffset   = distanceOffset / distance * heightDelta;

                state.instance.SetHeight(Mathf.Clamp(PointA.position.y + heightOffset, 0f, 1000f));
            }
        }
        public BulldozeAction()
        {
            HashSet <Instance> newSelection = new HashSet <Instance>(selection);
            HashSet <Instance> extraNodes   = new HashSet <Instance>();
            HashSet <ushort>   segments     = new HashSet <ushort>(); // Segments to be removed

            //string msg = $"\nBasic Selection: {selection.Count}\n";

            // Add any segments whose node is selected
            foreach (Instance instance in selection)
            {
                if (instance.isValid)
                {
                    if (instance.id.Type == InstanceType.NetSegment)
                    {
                        segments.Add(instance.id.NetSegment);
                    }
                    else if (instance.id.Type == InstanceType.NetNode)
                    {
                        for (int i = 0; i < 8; i++)
                        {
                            ushort segment = NetManager.instance.m_nodes.m_buffer[instance.id.NetNode].GetSegment(i);
                            if (segment != 0)
                            {
                                InstanceID instanceID = default(InstanceID);
                                instanceID.NetSegment = segment;

                                if (selection.Contains(instanceID))
                                {
                                    continue;
                                }

                                newSelection.Add(new MoveableSegment(instanceID));
                                segments.Add(segment);
                            }
                        }
                    }
                    else if (instance.id.Type == InstanceType.Building)
                    {
                        Building building = (Building)((MoveableBuilding)instance).data;
                        ushort   nodeId   = building.m_netNode;

                        int c = 0;
                        while (nodeId != 0)
                        {
                            NetNode node = NetManager.instance.m_nodes.m_buffer[nodeId];

                            for (int i = 0; i < 8; i++)
                            {
                                ushort segmentId = node.GetSegment(i);
                                if (segmentId != 0 && MoveableSegment.isSegmentValid(segmentId) &&
                                    ((NetManager.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Untouchable) == NetSegment.Flags.None))
                                {
                                    InstanceID instanceID = default(InstanceID);
                                    instanceID.NetSegment = segmentId;

                                    if (selection.Contains(instanceID))
                                    {
                                        continue;
                                    }

                                    newSelection.Add(new MoveableSegment(instanceID));
                                    segments.Add(segmentId);
                                }
                            }

                            nodeId = node.m_nextBuildingNode;

                            if (++c > 32768)
                            {
                                CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);

                                break;
                            }
                        }
                    }
                }
            }

            //msg += $"Selection With Extra Segments: {newSelection.Count}\nTotal Segments: {segments.Count}\n";

            // Add any nodes whose segments are all selected
            foreach (Instance instance in newSelection)
            {
                if (instance.isValid)
                {
                    if (instance.id.Type == InstanceType.NetSegment)
                    {
                        ushort   segId   = instance.id.NetSegment;
                        ushort[] nodeIds = { NetManager.instance.m_segments.m_buffer[segId].m_startNode, NetManager.instance.m_segments.m_buffer[segId].m_endNode };
                        foreach (ushort id in nodeIds)
                        {
                            bool    toDelete = true;
                            NetNode node     = NetManager.instance.m_nodes.m_buffer[id];
                            for (int i = 0; i < 8; i++)
                            {
                                if (node.GetSegment(i) != 0 && !segments.Contains(node.GetSegment(i)))
                                {
                                    toDelete = false;
                                    break;
                                }
                            }
                            if (toDelete)
                            {
                                InstanceID instanceId = default(InstanceID);
                                instanceId.NetNode = id;
                                MoveableNode mn = new MoveableNode(instanceId);
                                if (newSelection.Contains(mn))
                                {
                                    continue;
                                }

                                extraNodes.Add(mn);
                            }
                        }
                    }
                }
            }

            foreach (Instance instance in newSelection)
            {
                m_states.Add(instance.GetState());
            }
            foreach (Instance instance in extraNodes)
            {
                m_states.Add(instance.GetState());
            }

            //Debug.Log(msg + $"Final Selection: {m_states.Count}");
        }
        public override void Do()
        {
            float     angleDelta;
            float     heightDelta;
            float     distance;
            Matrix4x4 matrix = default(Matrix4x4);

            if (IsQuick)
            {
                if (selection.Count != 1)
                {
                    return;
                }
                foreach (Instance instance in selection)// Is this really the best way to get the value of selection[0]?
                {
                    if (!instance.isValid || !(instance is MoveableNode nodeInstance))
                    {
                        return;
                    }

                    NetNode node = nodeBuffer[nodeInstance.id.NetNode];

                    int c = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        ushort segId = 0;
                        if ((segId = node.GetSegment(i)) > 0)
                        {
                            if (c > 1)
                            {
                                return;        // More than 2 segments found
                            }
                            NetSegment segment = segmentBuffer[segId];
                            if (segment.m_startNode == nodeInstance.id.NetNode)
                            {
                                InstanceID instanceID = default(InstanceID);
                                instanceID.NetNode = segment.m_endNode;
                                keyInstance[c]     = new MoveableNode(instanceID);
                            }
                            else
                            {
                                InstanceID instanceID = default(InstanceID);
                                instanceID.NetNode = segment.m_startNode;
                                keyInstance[c]     = new MoveableNode(instanceID);
                            }
                            c++;
                        }
                    }
                    if (c != 2)
                    {
                        return;
                    }
                }
            }

            angleDelta  = 0 - (float)Math.Atan2(PointB.position.z - PointA.position.z, PointB.position.x - PointA.position.x);
            heightDelta = PointB.position.y - PointA.position.y;
            distance    = (float)Math.Sqrt(Math.Pow(PointB.position.z - PointA.position.z, 2) + Math.Pow(PointB.position.x - PointA.position.x, 2));

            string msg = $"\nA:{PointA.position}, B:{PointB.position}\nAng:{angleDelta} ({angleDelta * Mathf.Rad2Deg}) - H:{heightDelta} - D:{distance}";

            foreach (InstanceState state in m_states)
            {
                float distanceOffset, heightOffset;
                matrix.SetTRS(PointA.position, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one);
                distanceOffset = (matrix.MultiplyPoint(state.position - PointA.position) - PointA.position).x;
                heightOffset   = distanceOffset / distance * heightDelta;

                state.instance.SetHeight(Mathf.Clamp(PointA.position.y + heightOffset, 0f, 4000f));

                msg += $"\nx-offset:{distanceOffset} h-offset:{heightOffset}";
            }
            //Debug.Log(msg);
        }