private void deleteRDNode(RDNode node)
        {
            // function to effectively remove stock RD nodes

            node.tech.hideIfNoParts = true;

            clearParentsFromNode(node);
            clearChildrenFromNode(node);

            // move node outside the visible area in case it contains parts already researched (purchased parts doesn't seem to be reliably initialized upon entering space center)

            moveNode(node, 500, 0);

            // clear all part assignments on both the node and part side

            node.tech.partsAssigned.Clear();

            foreach (AvailablePart tempPart in PartLoader.LoadedPartsList)
            {
                if (tempPart.TechRequired == node.tech.techID)
                {
                    tempPart.TechRequired = "unassigned";
                }
            }
        }
        //draws arrows manually (not required atm)

        private void recreateArrows(RDNode rdNode)
        {
            if (rdNode.state != RDNode.State.HIDDEN)
            {
                for (int i = 0; i < rdNode.parents.Count(); ++i)
                {
                    //Recreate Parent hopefully recreates incoming array? nope, doesnt, also not with calling UpdateGraphics and/or Setup not...
                    //just changing the line does not update the graphics either.
                    RDNode.Parent parentStruct = rdNode.parents[i];
                    if (parentStruct.line != null)
                    {
                        Vector.DestroyLine(ref parentStruct.line);
                    }
                    if (parentStruct.arrowHead != null)
                    {
                        GameObject.Destroy(parentStruct.arrowHead);
                    }
                }//endfor foreach parentnode

                RDGridArea gridArea = GameObject.FindObjectOfType <RDGridArea>();
                //typeof(RDNode).GetMethod("InitializeArrows", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(treeNode, new object[] { });
                if (rdNode.state == RDNode.State.RESEARCHED || rdNode.state == RDNode.State.RESEARCHABLE)
                {
                    typeof(RDNode).GetMethod("DrawArrow", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(rdNode, new object[] { gridArea.LineMaterial });
                }
                else
                {
                    typeof(RDNode).GetMethod("DrawArrow", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(rdNode, new object[] { gridArea.LineMaterialGray });
                }
            }
        }
        private void visitNode(RDNode rdNode, ref List <RDNode> sortedList, ref HashSet <RDNode> unmarkedNodes, ref HashSet <RDNode> markedNodes, ref HashSet <RDNode> tempMarkedNodes)
        {
            //Debug.Log("TOPOSORT visiting " + rdNode.gameObject.name);
            if (tempMarkedNodes.Contains(rdNode))
            {
                throw new Exception("ATC: Circular dependency in Tech-Node Graph! RDNode " + rdNode.gameObject.name + " is in a circular dependency with one of its direct or indirect parents");
            }

            if (!markedNodes.Contains(rdNode)) //this node has not been visited yet
            {
                tempMarkedNodes.Add(rdNode);

                //DFS-search recursive for all children
                foreach (RDNode child in rdNode.children)
                {
                    visitNode(child, ref sortedList, ref unmarkedNodes, ref markedNodes, ref tempMarkedNodes);
                }

                //Debug.Log("TOPOSORT marking node " + rdNode.gameObject.name);
                markedNodes.Add(rdNode);
                tempMarkedNodes.Remove(rdNode);

                unmarkedNodes.Remove(rdNode);

                sortedList.Insert(0, rdNode);//prepend to list
            }
        }
Beispiel #4
0
        private void processNewNodes(ConfigNode tree)
        {
            Debug.Log("processing all NEW_NODE items");
            //RDController rdControl = GameObject.FindObjectOfType<RDController>();
            List <RDNode> newRDNodes = new List <RDNode>();

            try
            {
                foreach (ConfigNode cfgNodeNew in tree.GetNodes("NEW_NODE"))
                {
                    //only create RDNodes that are not yet in the Techtree
                    string newName = cfgNodeNew.GetValue("gameObjectName");

                    RDNode existingNode = GetNodeNamed(newName);


                    //RDNode newNode = createNode();
                    RDNode newNode = RDNodeFactory.Instance.Create();

                    if (newNode.tech == null)
                    {
                        Debug.Log("newNode.tech is null after createNode");
                        newNode.tech = new RDTech();
                    }



                    if (newNode.gameObject == null)
                    {
                        Debug.Log("newNode.gameObject is still null after Setup");
                    }


                    Debug.Log("created node and tech, now setting basic tech parameters");
                    //setup all the basic parameters that are not handled in updatenode
                    newNode.treeNode           = true;
                    newNode.gameObject.name    = newName;
                    newNode.name               = newName.Substring(newName.IndexOf("_") + 1);
                    newNode.tech.techID        = newNode.name;
                    newNode.tech.hideIfNoParts = false;



                    Debug.Log("updating node with cfgFile-parameters");
                    updateNode(newNode, cfgNodeNew);
                    Debug.Log("created new RDNode " + newNode.gameObject.name + " with RDTech.title=" + newNode.tech.title + " techId=" + newNode.tech.techID);

                    newRDNodes.Add(newNode);

                    _newNodes.Add(newNode);
                }//endof all newnodes

                nodesWithConfigEntries.AddRange(newRDNodes);
            }
            catch (Exception ex)
            {
                Debug.LogError("Exception in NEWNODE processing - " + ex.ToString());
            }
        } //end loadTree()
        private void clearParentsFromNode(RDNode treeNode)
        {
            foreach (RDNode.Parent oldParent in treeNode.parents)
            {
                oldParent.parent.node.children.Remove(treeNode);
            }

            treeNode.parents = new RDNode.Parent[0];
        }
        private void clearParentsFromNode(RDNode treeNode)
        {
            foreach (RDNode.Parent oldParent in treeNode.parents)
            {
                oldParent.parent.node.children.Remove(treeNode);
            }

            Array.Clear(treeNode.parents, 0, treeNode.parents.Count());
        }
        private void moveNode(RDNode treeNode, float xPos, float yPos)
        {
            Vector3 newPos = treeNode.transform.localPosition;

            newPos.x = xPos;
            newPos.y = yPos;

            treeNode.transform.localPosition = newPos;
        }
Beispiel #8
0
        public void Update()
        {
            // Do nothing if there is no PartList
            if (RDController.Instance == null || RDController.Instance.partList == null)
            {
                return;
            }

            // In case the node is deselected
            if (RDController.Instance.node_selected == null)
            {
                selectedNode = null;
            }

            // Do nothing if the tooltip hasn't changed since last update
            if (selectedNode == RDController.Instance.node_selected)
            {
                return;
            }

            // Get the the selected node and partList ui object
            selectedNode = RDController.Instance.node_selected;

            // retrieve fieldInfo type
            if (_fieldInfoRdPartList == null)
            {
                _fieldInfoRdPartList = typeof(RDPartList).GetField("partListItems", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
            }

            if (_fieldInfoRdPartList == null)
            {
                return;
            }

            var items = (List <RDPartListItem>)_fieldInfoRdPartList.GetValue(RDController.Instance.partList);

            if (items == null)
            {
                return;
            }

            var upgradedTemplateItems = items.Where(p => !p.isPart && p.upgrade != null).ToList();

            if (upgradedTemplateItems.Count == 0)
            {
                return;
            }

            foreach (RDPartListItem item in upgradedTemplateItems)
            {
                //Debug.Log("[KSPI]: RDColoredUpgradeIcon upgrade name " + item.upgrade.name + " upgrade techRequired: " + item.upgrade.techRequired);

                item.gameObject.GetComponent <UnityEngine.UI.Image>().color = greenColor;
            }
        }
Beispiel #9
0
        private void updateParentsForNode(RDNode treeNode, ConfigNode treeCfg)
        {
            //Debug.Log("updating parents for node " + treeNode.gameObject.name);
            //clear all old parents. The RD-Scene will take care of drawing the arrows
            clearParentsFromNode(treeNode);

            List <RDNode.Parent> connectionList = new List <RDNode.Parent>();

            foreach (ConfigNode parentCfg in treeCfg.GetNodes("PARENT_NODE"))
            {
                if (parentCfg.HasValue("name"))
                {
                    string parentName = parentCfg.GetValue("name");

                    //RDNode parentNode = Array.Find<RDNode>(AssetBase.RnDTechTree.GetTreeNodes(), x => x.gameObject.name == parentName);
                    //RDNode parentNode = AssetBase.RnDTechTree.GetTreeNodes().FirstOrDefault(rdn => rdn.gameObject.name == parentName);
                    //RDNode parentNode = GameObject.FindObjectsOfType(typeof(RDNode)).Cast<RDNode>().FirstOrDefault(rdn => rdn.gameObject.name == parentName);
                    RDNode parentNode = GetNodeNamed(parentName);

                    if (parentNode) //Default-constructed RDNode (if search fails) fails this test
                    {
                        //Debug.Log("   --- parentnode: " + parentName);

                        parentNode.children.Add(treeNode);

                        RDNode.Parent connection;

                        // only manually override the anchor points if BOTH are specified in the config
                        if (parentCfg.HasValue("parentSide") && parentCfg.HasValue("childSide"))
                        {
                            RDNode.Anchor parentAnchor = (RDNode.Anchor)Enum.Parse(typeof(RDNode.Anchor), parentCfg.GetValue("parentSide"));
                            RDNode.Anchor childAnchor  = (RDNode.Anchor)Enum.Parse(typeof(RDNode.Anchor), parentCfg.GetValue("childSide"));

                            //Debug.Log("Overriding auto-assignment for node " + treeNode.gameObject.name + " to " + parentAnchor + "->" + childAnchor);
                            connection = new RDNode.Parent(new RDNode.ParentAnchor(parentNode, parentAnchor), childAnchor);

                            parentConnectionsAlreadyProcessed.Add(connection);
                        }
                        else
                        {
                            //create RDNode.Parent structure - anchors will be corrected once all nodes have been loaded
                            connection = new RDNode.Parent(new RDNode.ParentAnchor(parentNode, RDNode.Anchor.RIGHT), RDNode.Anchor.LEFT);
                        }

                        connectionList.Add(connection);
                    }
                    else
                    {
                        Debug.LogError("ATC: Invalid parent node specified for: " + treeNode.gameObject.name + " parent: " + parentName);
                    }
                }
            }

            treeNode.parents = connectionList.ToArray();
        }
        private void ForceUnlockTech(string techID)
        {
            Debug.Log("[Toppler] Force unlocking " + techID);
            RDNode node = RDController.Instance.nodes.Find(n => n.tech.techID == techID);

            lastNode = techID;
            if (node != null)
            {
                node.tech.UnlockTech(true);
            }
        }
Beispiel #11
0
        int NodeDepth(RDNode current)
        {
            int depth = 1;

            while (current.parents.Count() > 0)
            {
                current = current.parents[0].parent.node;
                depth++;
            }

            return(depth);
        }
        private bool isAnchorAvailableForOutgoingArrows(RDNode node, RDNode.Anchor anchor)
        {
            foreach (RDNode.Parent incomingConnection in node.parents)
            {
                if (incomingConnection.anchor == anchor)
                {
                    return(false);
                }
            }

            return(true);
        }
        private void clearChildrenFromNode(RDNode treeNode)
        {
            foreach (RDNode tempChild in treeNode.children)
            {
                List <RDNode.Parent> childParentList = tempChild.parents.ToList();

                childParentList.Remove(childParentList.Find(tempParentConnection => tempParentConnection.parent.node == treeNode));

                tempChild.parents = childParentList.ToArray();
            }

            treeNode.children.Clear();
        }
Beispiel #14
0
        public void OnTreeSpawn(RDController controller)
        {
            if (TestFlightManagerScenario.Instance == null || controller.nodes == null)
            {
                return;
            }
            List <RDNode> nodes = controller.nodes;

            if (this.baseCost == null)
            {
                baseCost = new Dictionary <string, int>();
                for (int i = 0; i < nodes.Count; i++)
                {
                    RDNode node = nodes[i];
                    if (node != null && node.tech != null)
                    {
                        baseCost.Add(nodes[i].tech.techID, nodes[i].tech.scienceCost);
                    }
                }
            }
            for (int n = 0; n < nodes.Count; n++)
            {
                RDNode node = nodes[n];
                if (node != null && node.tech != null && !node.IsResearched && node.tech.partsAssigned != null)
                {
                    float discount             = 0f;
                    List <AvailablePart> parts = node.tech.partsAssigned;
                    for (int p = 0; p < parts.Count; p++)
                    {
                        if (parts[p] != null)
                        {
                            Part prefab = parts[p].partPrefab;
                            TestFlightPartData partData = TestFlightManagerScenario.Instance.GetPartDataForPart(parts[p].name);
                            if (partData != null && prefab != null)
                            {
                                TestFlightCore core       = (TestFlightCore)prefab.Modules.OfType <TestFlightCore>().FirstOrDefault();
                                float          flightData = partData.GetFloat("flightData");
                                if (core != null && flightData > core.startFlightData)
                                {
                                    discount += (int)(((flightData - core.startFlightData) / (core.maxData - core.startFlightData)) * core.scienceDataValue);
                                }
                            }
                        }
                    }
                    if (discount > 0)
                    {
                        node.tech.scienceCost = (int)Math.Max(0, baseCost[node.tech.techID] - discount);
                    }
                }
            }
        }
        private RDNode createNode()
        {
            //Debug.Log("creating new RDNode");

            RDNode startNode = findStartNode();

            GameObject nodePrefab;

            //if (startNode.prefab == null)
            //{
            Debug.Log("creating new GameObject()");
            nodePrefab = new GameObject("newnode", typeof(RDNode), typeof(RDTech));

            if (nodePrefab.GetComponent <RDNode>() == null)
            {
                Debug.Log("wtf - nodePrefab.getComponent<RDNode> is null");
            }
            if (nodePrefab.GetComponent <RDTech>() == null)
            {
                Debug.Log("wtf - nodePrefab.getComponent<RDTech> is null");
            }

            nodePrefab.GetComponent <RDTech>().techID     = "newTech_RenameMe";
            nodePrefab.GetComponent <RDNode>().tech       = nodePrefab.GetComponent <RDTech>();
            nodePrefab.GetComponent <RDNode>().prefab     = startNode.prefab;
            nodePrefab.GetComponent <RDNode>().parents    = new RDNode.Parent[0];
            nodePrefab.GetComponent <RDNode>().icon       = startNode.icon;
            nodePrefab.GetComponent <RDNode>().controller = startNode.controller;
            nodePrefab.GetComponent <RDNode>().scale      = startNode.scale;
            nodePrefab.SetActive(false);
            //}
            //else
            //{
            //    nodePrefab = startNode.prefab;
            //}


            Debug.Log("Instantiating prefab nodeObject");

            GameObject clone = (GameObject)GameObject.Instantiate(nodePrefab);

            clone.SetActive(true);
            clone.transform.parent                 = startNode.transform.parent;
            clone.transform.localPosition          = new Vector3(0, 50, 0);
            clone.GetComponent <RDNode>().children = new List <RDNode>();


            clone.GetComponent <RDNode>().tech = new RDTech();
            return(clone.GetComponent <RDNode>());
        }
Beispiel #16
0
 public void UpdateTechlistIconColor()
 {
     if (RDController.Instance != null)
     {
         for (int i = RDController.Instance.nodes.Count; i-- > 0;)
         {
             RDNode node = RDController.Instance.nodes[i];
             if (node?.tech != null)
             {
                 if (HasTechInList(node.tech.techID))
                 {
                     node.graphics?.SetIconColor(XKCDColors.KSPNotSoGoodOrange);
                 }
                 // else reset? Bleh, why bother.
             }
         }
     }
 }
Beispiel #17
0
        public RDNode Create()
        {
            GameObject clone = GameObject.Instantiate(NodePrefab) as GameObject;
            RDNode     node  = clone.GetComponent <RDNode>();

            node.tech = clone.GetComponent <RDTech>();

            node.name        = _newNodeName;
            node.children    = new List <RDNode>();
            node.description = "";
            clone.transform.localPosition = NodePrefab.transform.localPosition;
            clone.transform.parent        = NodePrefab.transform.parent;
            //clone.transform.localRotation = NodePrefab.transform.localRotation;
            //clone.transform.localScale = NodePrefab.transform.localScale;
            node.tech.Start();
            clone.SetActive(true);

            return(node);
        }
Beispiel #18
0
        internal static void Find(bool clean = false)
        {
            List <RDNode> _nodes = RDController.Instance.nodes;

            for (int _i = _nodes.Count - 1; _i >= 0; --_i)
            {
                RDNode _node   = _nodes[_i];
                RDTech _rdTech = _node.tech;
                if (_node.graphics != null)
                {
                    UIStateButton _button = _node.graphics.button;
                    if (!clean && _rdTech.partsAssigned.Find(aPart => QSearch.FindPart(aPart)) != null)
                    {
                        _button.Image.color = new Color(1f, 0f, 0f);
                        continue;
                    }
                    _button.Image.color = new Color(1f, 1f, 1f);
                }
            }
            //QDebug.Log ("Find: " + QSearch.Text, "QRnD");
        }
        private List <RDNode> calculateTopologicalSorting()
        {
            List <RDNode>    sortedList      = new List <RDNode>();
            HashSet <RDNode> tempMarkedNodes = new HashSet <RDNode>();
            HashSet <RDNode> markedNodes     = new HashSet <RDNode>();

            HashSet <RDNode> unmarkedNodes = new HashSet <RDNode>();

            foreach (RDNode rdNode in GameObject.FindObjectsOfType(typeof(RDNode)).Cast <RDNode>())
            {
                unmarkedNodes.Add(rdNode);
            }


            while (unmarkedNodes.Count > 0)
            {
                RDNode n = unmarkedNodes.First();
                visitNode(n, ref sortedList, ref unmarkedNodes, ref markedNodes, ref tempMarkedNodes);
            }

            return(sortedList);
        }
        private List <RDNode> calculateTopologicalSorting()
        {
            List <RDNode>    sortedList      = new List <RDNode>();
            HashSet <RDNode> tempMarkedNodes = new HashSet <RDNode>();
            HashSet <RDNode> markedNodes     = new HashSet <RDNode>();

            HashSet <RDNode> unmarkedNodes = new HashSet <RDNode>();

            foreach (RDNode rdNode in AssetBase.RnDTechTree.GetTreeNodes())
            {
                unmarkedNodes.Add(rdNode);
            }


            while (unmarkedNodes.Count > 0)
            {
                RDNode n = unmarkedNodes.First();
                visitNode(n, ref sortedList, ref unmarkedNodes, ref markedNodes, ref tempMarkedNodes);
            }

            return(sortedList);
        }
        static private void AddTechNodeToTreeNode(RDNode techNode, ConfigNode treeConfigNode)
        {
            ConfigNode techNodeConfigNode = new ConfigNode("TECH_NODE");

            techNodeConfigNode.AddValue("name", techNode.gameObject.name);

            techNodeConfigNode.AddValue("title", techNode.tech.title);
            techNodeConfigNode.AddValue("description", techNode.tech.description);
            techNodeConfigNode.AddValue("icon", techNode.icon.ToString());

            techNodeConfigNode.AddValue("scienceCost", techNode.tech.scienceCost);

            techNodeConfigNode.AddValue("anyParentUnlocks", techNode.AnyParentToUnlock);

            techNodeConfigNode.AddValue("hideIfNoparts", techNode.hideIfNoParts);

            techNodeConfigNode.AddValue("posX", techNode.transform.localPosition.x);
            techNodeConfigNode.AddValue("posY", techNode.transform.localPosition.y);

            if (techNode.parents != null)
            {
                foreach (RDNode.Parent tempParentConnection in techNode.parents)
                {
                    if (tempParentConnection != null)
                    {
                        ConfigNode parentConfigNode = new ConfigNode("PARENT_NODE");

                        parentConfigNode.AddValue("name", tempParentConnection.parent.node.gameObject.name);

                        techNodeConfigNode.AddNode(parentConfigNode);
                    }
                }
            }

            treeConfigNode.AddNode(techNodeConfigNode);
        }
Beispiel #22
0
        public void Update()
        {
            // Do nothing if there is no PartList
            if (RDController.Instance == null)
            {
                return;
            }
            if (RDController.Instance.partList == null)
            {
                return;
            }

            // In case the node is deselected
            if (RDController.Instance.node_selected == null)
            {
                selectedNode = null;
            }

            // Do nothing if the tooltip hasn't changed since last update
            if (selectedNode == RDController.Instance.node_selected)
            {
                return;
            }

            // Get the the selected node and partlist ui object
            selectedNode = RDController.Instance.node_selected;
            RDPartList partList = RDController.Instance.partList;

            var field = typeof(RDPartList).GetField("partListItems", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
            List <RDPartListItem> items = (List <RDPartListItem>)field.GetValue(partList);

            foreach (RDPartListItem item in items.Where(p => !p.isPart && p.upgrade != null))
            {
                item.gameObject.GetComponent <UnityEngine.UI.Image>().color = new Color(0.717f, 0.819f, 0.561f); // light green RGB(183,209,143)
            }
        }
        public void Update()
        {
            // Do nothing if there is no PartList
            if (RDController.Instance == null || RDController.Instance.partList == null)
            {
                return;
            }

            // In case the node is deselected
            if (RDController.Instance.node_selected == null)
            {
                selectedNode = null;
            }

            // Do nothing if the tooltip hasn't changed since last update
            if (selectedNode == RDController.Instance.node_selected)
            {
                return;
            }

            // Get the the selected node and partlist ui object
            selectedNode = RDController.Instance.node_selected;

            // retrieve fieldInfo type
            if (fieldInfoRDPartList == null)
            {
                fieldInfoRDPartList = typeof(RDPartList).GetField("partListItems", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
            }

            var items = (List <RDPartListItem>)fieldInfoRDPartList.GetValue(RDController.Instance.partList);

            foreach (RDPartListItem item in items.Where(p => !p.isPart && p.upgrade != null))
            {
                item.gameObject.GetComponent <UnityEngine.UI.Image>().color = greenColor;
            }
        }
Beispiel #24
0
 public RDNodeWrapper(RDNode node)
 {
     this.node     = node;
     this.traverse = Traverse.Create(node);
 }
Beispiel #25
0
 public static List <Vector2> Execute(RDNode node, RDNode.Parent parent) =>
 new RDNodeWrapper(node).GetVectorArray(parent);
        private void setupAnchors(RDNode target, ref RDNode.Parent connection)
        {
            RDNode source = connection.parent.node;

            //find main direction from outgoing node (parent) to target (connectionOwner) node to set anchor tags
            //Exception: Cannot display incoming and outgoing nodes on the same anchor
            Vector3 connectionVec = target.transform.localPosition - source.transform.localPosition;

            //calculate/setup anchors
            List <RDNode.Anchor> possibleParentAnchors = new List <RDNode.Anchor>();
            List <RDNode.Anchor> possibleTargetAnchors = new List <RDNode.Anchor>();

            if (connectionVec.x >= 0)
            {//left to right
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.RIGHT))
                {
                    possibleParentAnchors.Add(RDNode.Anchor.RIGHT);
                }
                possibleTargetAnchors.Add(RDNode.Anchor.LEFT);
            }
            else
            {
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.LEFT))
                {
                    possibleParentAnchors.Add(RDNode.Anchor.LEFT);
                }
                possibleTargetAnchors.Add(RDNode.Anchor.RIGHT);
            }

            if (connectionVec.y >= 0) //up
            {
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.TOP))
                {
                    possibleParentAnchors.Add(RDNode.Anchor.TOP);
                }
                possibleTargetAnchors.Add(RDNode.Anchor.BOTTOM);
            }
            else // TOP-DOWN connection doesnt work because of parent or target anchor
            {
                //neither does or BOTTOM->LEFT  RIGHT->TOP neiter
                //possibleParentAnchors.Add(RDNode.Anchor.BOTTOM);
                //possibleTargetAnchors.Add(RDNode.Anchor.TOP);
            }


            //Debug.Log("options remaining after filtering: " + possibleParentAnchors.Count());
            //foreach (RDNode.Anchor anchor in possibleParentAnchors)
            //    Debug.Log("available anchor: " + anchor);

            //if two options are available, pick the larger distance
            if (Math.Abs(connectionVec.x) < Math.Abs(connectionVec.y)) //preferrably vertical            {
            {
                possibleParentAnchors.Reverse();
                possibleTargetAnchors.Reverse();
            }

            if (possibleParentAnchors.Count == 0 || possibleTargetAnchors.Count == 0)
            {
                Debug.LogWarning("no valid anchor for connection " + source.gameObject.name + "->" + target.gameObject.name + ", direction = " + connectionVec.ToString() + ", defaulting to anchors RIGHT->LEFT");
                if (possibleParentAnchors.Count == 0)
                {
                    possibleParentAnchors.Add(RDNode.Anchor.RIGHT);
                }
                if (possibleTargetAnchors.Count == 0)
                {
                    possibleTargetAnchors.Add(RDNode.Anchor.LEFT);
                }
            }



            //Debug.Log("            anchors for connection " + source.gameObject.name + "->" + target.gameObject.name+ ", direction = " + connectionVec.ToString() + " anchors : " + possibleParentAnchors.First() + " -> " + possibleTargetAnchors.First());

            connection.anchor        = possibleTargetAnchors.First();
            connection.parent.anchor = possibleParentAnchors.First();
        }
        } //end loadTree()


        private void updateNode(RDNode treeNode, ConfigNode cfgNode)
        {
            if (cfgNode.HasValue("title"))
            {
                //treeNode.name = cfgNode.GetValue("title");
                treeNode.tech.title = cfgNode.GetValue("title");
            }

            if (cfgNode.HasValue("description"))
            {
                treeNode.description = cfgNode.GetValue("description").Replace("\\n", "\n");
                treeNode.tech.description = treeNode.description;
            }


            if (cfgNode.HasValue("scienceCost"))
                treeNode.tech.scienceCost = int.Parse(cfgNode.GetValue("scienceCost"));

            //Debug.Log("checking icon");
            if (cfgNode.HasValue("icon"))
            {
                //bool success = Enum.TryParse<RDNode.Icon>(cfgNode.GetValue("icon"), out icon); //.NET >= 4.0
                try
                {
                    RDNode.Icon icon = (RDNode.Icon)Enum.Parse(typeof(RDNode.Icon), cfgNode.GetValue("icon"));
                    treeNode.icon = icon;
                    //Debug.Log("Setting iconstate");
                    //treeNode.SetIconState(icon); //not required, game handles this automatically for stocknodes
                }
                catch (Exception ex)
                {
                    Debug.LogError("Invalid Icon name '" + cfgNode.GetValue("icon") + "'" + ex.Message);
                }

            }


            if (cfgNode.HasValue("anyParentUnlocks"))
                treeNode.AnyParentToUnlock = bool.Parse(cfgNode.GetValue("anyParentUnlocks"));

            if (cfgNode.HasValue("hideIfNoparts"))
            {
                treeNode.tech.hideIfNoParts = bool.Parse(cfgNode.GetValue("hideIfNoparts"));
            }
            else
            {
                treeNode.tech.hideIfNoParts = false;
            }

            //setup parent/child relations
            updateParentsForNode(treeNode, cfgNode);

            if (cfgNode.HasValue("posX") || cfgNode.HasValue("posY"))
            {
                Vector3 newPos = treeNode.transform.localPosition;

                if (cfgNode.HasValue("posX"))
                    newPos.x = float.Parse(cfgNode.GetValue("posX"));
                if (cfgNode.HasValue("posY"))
                    newPos.y = float.Parse(cfgNode.GetValue("posY"));

                moveNode(treeNode, newPos.x, newPos.y);
            }

        }
        private bool isAnchorAvailableForOutgoingArrows(RDNode node, RDNode.Anchor anchor)
        {
            foreach (RDNode.Parent incomingConnection in node.parents)
                if (incomingConnection.anchor == anchor)
                    return false;

            return true;
        }
        private void visitNode(RDNode rdNode, ref List<RDNode> sortedList, ref HashSet<RDNode> unmarkedNodes, ref HashSet<RDNode> markedNodes, ref HashSet<RDNode> tempMarkedNodes)
        {
            //Debug.Log("TOPOSORT visiting " + rdNode.gameObject.name);
            if (tempMarkedNodes.Contains(rdNode))
            {
                throw new Exception("ATC: Circular dependency in Tech-Node Graph! RDNode " + rdNode.gameObject.name + " is in a circular dependency with one of its direct or indirect parents");
            }

            if (!markedNodes.Contains(rdNode)) //this node has not been visited yet
            {
                tempMarkedNodes.Add(rdNode);

                //DFS-search recursive for all children
                foreach (RDNode child in rdNode.children)
                    visitNode(child, ref sortedList, ref unmarkedNodes, ref markedNodes, ref tempMarkedNodes);

                //Debug.Log("TOPOSORT marking node " + rdNode.gameObject.name);
                markedNodes.Add(rdNode);
                tempMarkedNodes.Remove(rdNode);

                unmarkedNodes.Remove(rdNode);

                sortedList.Insert(0, rdNode);//prepend to list
            }
        }
        } //end loadTree()

        private void updateNode(RDNode treeNode, ConfigNode cfgNode)
        {
            if (cfgNode.HasValue("title"))
            {
                //treeNode.name = cfgNode.GetValue("title");
                treeNode.tech.title = cfgNode.GetValue("title");
            }

            if (cfgNode.HasValue("description"))
            {
                treeNode.description      = cfgNode.GetValue("description").Replace("\\n", "\n");
                treeNode.tech.description = treeNode.description;
            }


            if (cfgNode.HasValue("scienceCost"))
            {
                treeNode.tech.scienceCost = int.Parse(cfgNode.GetValue("scienceCost"));
            }

            //Debug.Log("checking icon");
            if (cfgNode.HasValue("icon"))
            {
                //bool success = Enum.TryParse<RDNode.Icon>(cfgNode.GetValue("icon"), out icon); //.NET >= 4.0
                try
                {
                    RDNode.Icon icon = (RDNode.Icon)Enum.Parse(typeof(RDNode.Icon), cfgNode.GetValue("icon"));
                    treeNode.icon = icon;
                    //Debug.Log("Setting iconstate");
                    //treeNode.SetIconState(icon); //not required, game handles this automatically for stocknodes
                }
                catch (Exception ex)
                {
                    Debug.LogError("Invalid Icon name '" + cfgNode.GetValue("icon") + "'" + ex.Message);
                }
            }


            if (cfgNode.HasValue("anyParentUnlocks"))
            {
                treeNode.AnyParentToUnlock = bool.Parse(cfgNode.GetValue("anyParentUnlocks"));
            }

            if (cfgNode.HasValue("hideIfNoparts"))
            {
                treeNode.tech.hideIfNoParts = bool.Parse(cfgNode.GetValue("hideIfNoparts"));
            }
            else
            {
                treeNode.tech.hideIfNoParts = false;
            }

            //setup parent/child relations
            updateParentsForNode(treeNode, cfgNode);

            if (cfgNode.HasValue("posX") || cfgNode.HasValue("posY"))
            {
                Vector3 newPos = treeNode.transform.localPosition;

                if (cfgNode.HasValue("posX"))
                {
                    newPos.x = float.Parse(cfgNode.GetValue("posX"));
                }
                if (cfgNode.HasValue("posY"))
                {
                    newPos.y = float.Parse(cfgNode.GetValue("posY"));
                }

                moveNode(treeNode, newPos.x, newPos.y);
            }
        }
        private void clearChildrenFromNode(RDNode treeNode)
        {
            foreach (RDNode tempChild in treeNode.children)
            {
                List<RDNode.Parent> childParentList = tempChild.parents.ToList();

                childParentList.Remove(childParentList.Find(tempParentConnection => tempParentConnection.parent.node == treeNode));

                tempChild.parents = childParentList.ToArray();
            }

            treeNode.children.Clear();
        }
        private void LoadTree()
        {
            if (!ATCTreeDumper.m_bHasTreeAlreadyBeenDumped && ATCTreeDumper.m_bIsEnabled)
            {
                ATCTreeDumper.DumpCurrentTreeToFile("StockTree.cfg", "stock");
            }

            settings = getActiveSettingCfg();

            if (!settings.HasData)
            {
                return;
            }

            debugCombo  = settings.GetValue("debugDumpKeyCombo");
            reloadCombo = settings.GetValue("reloadKeyCombo");

            nodesWithConfigEntries.Clear();
            parentConnectionsAlreadyProcessed.Clear();

            foreach (ConfigNode activeTreeCfg in settings.GetNodes("ACTIVE_TECH_TREE"))
            {
                ConfigNode tree = getTreeCfgForActiveTreeCfg(activeTreeCfg);

                if (!tree.HasData)
                {
                    Debug.LogError("TechChanger: Treeconfig '" + activeTreeCfg.GetValue("name") + "' empty/not found!");
                    continue;
                }

                setupBodyScienceParamsForTree(tree);

                Debug.Log("ATC: processing all TECH_NODE items");
                //check modify-nodes
                foreach (ConfigNode cfgNodeModify in tree.GetNodes("TECH_NODE"))
                {
                    string gameObjectName = cfgNodeModify.GetValue("name");
                    //Debug.Log("processing MODIFY_NODE " + gameObjectName);
                    //RDNode treeNode = Array.Find<RDNode>(AssetBase.RnDTechTree.GetTreeNodes(), x => x.gameObject.name == gameObjectName);
                    RDNode treeNode = GameObject.FindObjectsOfType(typeof(RDNode)).Cast <RDNode>().FirstOrDefault(rdn => rdn.gameObject.name == gameObjectName);

                    if (treeNode.treeNode)
                    {
                        updateNode(treeNode, cfgNodeModify);

                        nodesWithConfigEntries.Add(treeNode);
                    }
                    else
                    {
                        Debug.LogWarning("Could not find RDNode with gameObjectName == " + gameObjectName);
                    }
                }//end for all nodes;

                //deactivated for now
                processNewNodes(tree);
            }//end foreach tree-config

            deleteAbsentNodes();

            List <RDNode> topoSortedNodes = calculateTopologicalSorting();

            foreach (RDNode rdNode in topoSortedNodes)
            {
                //Debug.Log("setting up anchors for " + rdNode.gameObject.name);

                for (int i = 0; i < rdNode.parents.Count(); ++i)
                {
                    if (parentConnectionsAlreadyProcessed.Contains(rdNode.parents[i]))
                    {
                        //Debug.Log("Skipping auto-anchor assignment for node " + rdNode.gameObject.name);
                    }
                    else
                    {
                        setupAnchors(rdNode, ref rdNode.parents[i]);
                    }

                    //warn for anchors that cannot be displayed properly. This might happen if a user-config overrides the auto-assignment. Or if the auto-assignment screws up
                    if (rdNode.parents[i].parent.anchor == RDNode.Anchor.BOTTOM)
                    {
                        Debug.LogWarning("ATC: Warning: Arrow from " + rdNode.parents[i].parent.node.gameObject.name + "to" + rdNode.gameObject.name + " will cannot be displayed because it uses parent anchor BOTTOM!");
                    }
                    if (rdNode.parents[i].anchor == RDNode.Anchor.TOP)
                    {
                        Debug.LogWarning("ATC: Warning: Arrow from " + rdNode.parents[i].parent.node.gameObject.name + "to" + rdNode.gameObject.name + " will cannot be displayed because it uses anchor TOP!");
                    }
                }
            }
        }
        private void processNewNodes(ConfigNode tree)
        {
            Debug.Log("processing all NEW_NODE items");
            //RDController rdControl = GameObject.FindObjectOfType<RDController>();
            List <RDNode> newRDNodes = new List <RDNode>();

            try
            {
                foreach (ConfigNode cfgNodeNew in tree.GetNodes("NEW_NODE"))
                {
                    //only create RDNodes that are not yet in the Techtree
                    string newName = cfgNodeNew.GetValue("gameObjectName");

                    //if (!Array.Exists<RDNode>(AssetBase.RnDTechTree.GetTreeNodes(), x => x.gameObject.name == newName))
                    if (!GameObject.FindObjectsOfType(typeof(RDNode)).Cast <RDNode>().Any(rdn => rdn.gameObject.name == newName))
                    {
                        RDNode newNode = createNode();

                        if (newNode.tech == null)
                        {
                            Debug.Log("newNode.tech is null after createNode");
                            newNode.tech = new RDTech();
                        }



                        if (newNode.gameObject == null)
                        {
                            Debug.Log("newNode.gameObject is still null after Setup");
                        }


                        //Debug.Log("calling rdNode.Warmup()");
                        //newNode.Warmup(newTech);
                        //Debug.Log("NEWNODE: after Setup(), startNode has " + findStartNode().children.Count() + " children");


                        Debug.Log("created node and tech, now setting basic tech parameters");
                        //setup all the basic parameters that are not handled in updatenode
                        newNode.treeNode           = true;
                        newNode.gameObject.name    = newName;
                        newNode.name               = newName.Substring(newName.IndexOf("_") + 1);
                        newNode.tech.techID        = newNode.name;
                        newNode.tech.hideIfNoParts = false;



                        Debug.Log("updating node with cfgFile-parameters");
                        updateNode(newNode, cfgNodeNew);
                        Debug.Log("created new RDNode " + newNode.gameObject.name + " with RDTech.title=" + newNode.tech.title + " techId=" + newNode.tech.techID);
                        //Debug.Log("NEWNODE: after updateNode(), startNode has " + findStartNode().children.Count() + " children");

                        //Debug.Log("NEWNODE: calling RegisterNode(), AssetBase.TechTree has  " + AssetBase.RnDTechTree.GetTreeNodes().Count() + " entries");
                        //newNode.controller.RegisterNode(newNode);
                        //Debug.Log("NEWNODE: after RegisterNode(), AssetBase.TechTree has  " + AssetBase.RnDTechTree.GetTreeNodes().Count() + " entries");
                        //newNode.SetButtonState(RDNode.State.RESEARCHABLE);

                        //newNode.SetButtonState(RDNode.State.RESEARCHABLE);
                        Debug.Log("calling newnode.setup");
                        newNode.Setup(); //This sets the anchor offsets
                        //Debug.Log("Invoking rdController.registerNode");
                        typeof(RDNode).GetMethod("InitializeArrows", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(newNode, null);

                        newRDNodes.Add(newNode);

                        //addedNewNodes.Add(newName);
                    }//endif tech not yet added
                    else
                    {
                        RDNode newNode = GameObject.FindObjectsOfType(typeof(RDNode)).Cast <RDNode>().FirstOrDefault(rdn => rdn.gameObject.name == newName);
                        newNode.Setup();
                        newRDNodes.Add(newNode);
                    }
                }//endof all newnodes
                nodesWithConfigEntries.AddRange(newRDNodes);

                if (newRDNodes.Any())
                {
                    Debug.Log("Invoking Warmup");
                    RDTechTree rd_tree       = AssetBase.RnDTechTree;
                    MethodInfo rd_tree_minfo = typeof(RDTechTree).GetMethod("Warmup", BindingFlags.NonPublic | BindingFlags.Instance); //.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(mtd => mtd.Name.Contains("warmup")).FirstOrDefault();
                    rd_tree_minfo.Invoke(rd_tree, null);
                    Debug.Log("Warmup Complete");
                }

                //List<RDNode> rdnodes = AssetBase.RnDTechTree.GetTreeNodes().ToList();
                //List<RDTech> rdtechs = AssetBase.RnDTechTree.GetTreeTechs().ToList();
                //rdnodes.AddRange(newRDNodes);
                //rdtechs.AddRange(newRDNodes.Select(rdn => rdn.tech));
                //FieldInfo rdnodes_info = typeof(RDTechTree).GetFields(BindingFlags.NonPublic | BindingFlags.Static).Where(fld => fld.FieldType.FullName.Contains("RDNode")).FirstOrDefault();
                //FieldInfo rdtechs_info = typeof(RDTechTree).GetFields(BindingFlags.NonPublic | BindingFlags.Static).Where(fld => fld.FieldType.FullName.Contains("RDTech")).FirstOrDefault();
                //rdnodes_info.SetValue(null, rdnodes.ToArray());
                //rdtechs_info.SetValue(null, rdtechs.ToArray());
            }
            catch (Exception ex)
            {
                Debug.LogError("Exception in NEWNODE processing - " + ex.Message + " at " + ex.StackTrace);
            }
        } //end loadTree()
        static private void AddTechNodeToTreeNode( RDNode techNode, ConfigNode treeConfigNode )
        {
            ConfigNode techNodeConfigNode = new ConfigNode( "TECH_NODE" );

            techNodeConfigNode.AddValue( "name", techNode.gameObject.name );

            techNodeConfigNode.AddValue( "title", techNode.tech.title );
            techNodeConfigNode.AddValue( "description", techNode.tech.description );
            techNodeConfigNode.AddValue( "icon", techNode.icon.ToString());

            techNodeConfigNode.AddValue( "scienceCost", techNode.tech.scienceCost );

            techNodeConfigNode.AddValue( "anyParentUnlocks", techNode.AnyParentToUnlock );

            techNodeConfigNode.AddValue( "hideIfNoparts", techNode.hideIfNoParts );

            techNodeConfigNode.AddValue( "posX", techNode.transform.localPosition.x );
            techNodeConfigNode.AddValue( "posY", techNode.transform.localPosition.y );

            if ( techNode.parents != null )
            {
                foreach ( RDNode.Parent tempParentConnection in techNode.parents )
                {
                    if ( tempParentConnection != null )
                    {
                        ConfigNode parentConfigNode = new ConfigNode( "PARENT_NODE" );

                        parentConfigNode.AddValue( "name", tempParentConnection.parent.node.gameObject.name );

                        techNodeConfigNode.AddNode( parentConfigNode );
                    }
                }
            }

            treeConfigNode.AddNode( techNodeConfigNode );
        }
        private void deleteRDNode(RDNode node)
        {
            // function to effectively remove stock RD nodes

            node.tech.hideIfNoParts = true;

            clearParentsFromNode(node);
            clearChildrenFromNode(node);

            // move node outside the visible area in case it contains parts already researched (purchased parts doesn't seem to be reliably initialized upon entering space center)

            moveNode(node, 500, 0);

            // clear all part assignments on both the node and part side

            node.tech.partsAssigned.Clear();

            foreach (AvailablePart tempPart in PartLoader.LoadedPartsList)
            {
                if (tempPart.TechRequired == node.tech.techID)
                {
                    tempPart.TechRequired = "unassigned";
                }
            }
        }
        private void moveNode(RDNode treeNode, float xPos, float yPos)
        {
            Vector3 newPos = treeNode.transform.localPosition;

            newPos.x = xPos;
            newPos.y = yPos;

            treeNode.transform.localPosition = newPos;
        }
        private void setupAnchors(RDNode target, ref RDNode.Parent connection)
        {
            RDNode source = connection.parent.node;
            
            //find main direction from outgoing node (parent) to target (connectionOwner) node to set anchor tags
            //Exception: Cannot display incoming and outgoing nodes on the same anchor
            Vector3 connectionVec = target.transform.localPosition - source.transform.localPosition;
    
            //calculate/setup anchors
            List<RDNode.Anchor> possibleParentAnchors = new List<RDNode.Anchor>();
            List<RDNode.Anchor> possibleTargetAnchors = new List<RDNode.Anchor>();

            if (connectionVec.x >= 0)
            {//left to right
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.RIGHT))
                    possibleParentAnchors.Add(RDNode.Anchor.RIGHT);
                possibleTargetAnchors.Add(RDNode.Anchor.LEFT);
            }
            else
            {
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.LEFT))
                    possibleParentAnchors.Add(RDNode.Anchor.LEFT);
                possibleTargetAnchors.Add(RDNode.Anchor.RIGHT);
            }

            if (connectionVec.y >= 0) //up
            {
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.TOP))
                    possibleParentAnchors.Add(RDNode.Anchor.TOP);
                possibleTargetAnchors.Add(RDNode.Anchor.BOTTOM);
            }
            else // TOP-DOWN connection doesnt work because of parent or target anchor 
            {
                //neither does or BOTTOM->LEFT  RIGHT->TOP neiter
                //possibleParentAnchors.Add(RDNode.Anchor.BOTTOM);
                //possibleTargetAnchors.Add(RDNode.Anchor.TOP);
            }
            

            //Debug.Log("options remaining after filtering: " + possibleParentAnchors.Count());
            //foreach (RDNode.Anchor anchor in possibleParentAnchors)
            //    Debug.Log("available anchor: " + anchor);

            //if two options are available, pick the larger distance
            if (Math.Abs(connectionVec.x) < Math.Abs(connectionVec.y)) //preferrably vertical            {
            {    
                possibleParentAnchors.Reverse();
                possibleTargetAnchors.Reverse();
            }

            if (possibleParentAnchors.Count == 0 || possibleTargetAnchors.Count == 0)
            {
                Debug.LogWarning("no valid anchor for connection " + source.gameObject.name + "->" + target.gameObject.name + ", direction = " + connectionVec.ToString() + ", defaulting to anchors RIGHT->LEFT");
                if (possibleParentAnchors.Count == 0)
                    possibleParentAnchors.Add(RDNode.Anchor.RIGHT);
                if (possibleTargetAnchors.Count == 0)
                    possibleTargetAnchors.Add(RDNode.Anchor.LEFT);
            }



            //Debug.Log("            anchors for connection " + source.gameObject.name + "->" + target.gameObject.name+ ", direction = " + connectionVec.ToString() + " anchors : " + possibleParentAnchors.First() + " -> " + possibleTargetAnchors.First());
        
            connection.anchor = possibleTargetAnchors.First();
            connection.parent.anchor = possibleParentAnchors.First();
        }
        private void setupAnchors(RDNode target, ref RDNode.Parent connection)
        {
            RDNode source = connection.parent.node;
            //RDNode source = GetNodeNamed(connection.parent.node.name);

            //Debug.Log("Setting up anchors for " + target.name);

            //string state = string.Format("target: {0}\nconnection: {1}\nsource: {2}", target, connection, source);

            //Debug.Log(state);

            if (source == null) return;

            //find main direction from outgoing node (parent) to target (connectionOwner) node to set anchor tags
            //Exception: Cannot display incoming and outgoing nodes on the same anchor
            Vector3 connectionVec;
            try
            {
                connectionVec = target.transform.localPosition - source.transform.localPosition;
            }
            catch
            {
                Debug.Log("A problem occurred while finding the connection vector between " + source.name + " (source) and " + target.name + " (target)");
                try
                {
                    Debug.Log(target.name + " is able to access its transform: " + (target.transform != null));
                }
                catch { }

                try
                {
                    Debug.Log(source.name + " is able to access its transform: " + (source.transform != null));
                }
                catch { }

                return;
            }

            //calculate/setup anchors
            List<RDNode.Anchor> possibleParentAnchors = new List<RDNode.Anchor>();
            List<RDNode.Anchor> possibleTargetAnchors = new List<RDNode.Anchor>();

            if (connectionVec.x >= 0)
            {//left to right
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.RIGHT))
                    possibleParentAnchors.Add(RDNode.Anchor.RIGHT);
                possibleTargetAnchors.Add(RDNode.Anchor.LEFT);
            }
            else
            {
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.LEFT))
                    possibleParentAnchors.Add(RDNode.Anchor.LEFT);
                possibleTargetAnchors.Add(RDNode.Anchor.RIGHT);
            }

            if (connectionVec.y >= 0) //up
            {
                if (isAnchorAvailableForOutgoingArrows(source, RDNode.Anchor.TOP))
                    possibleParentAnchors.Add(RDNode.Anchor.TOP);
                possibleTargetAnchors.Add(RDNode.Anchor.BOTTOM);
            }
            else // TOP-DOWN connection doesnt work because of parent or target anchor 
            {
                //neither does or BOTTOM->LEFT  RIGHT->TOP neiter
                //possibleParentAnchors.Add(RDNode.Anchor.BOTTOM);
                //possibleTargetAnchors.Add(RDNode.Anchor.TOP);
            }


            //Debug.Log("options remaining after filtering: " + possibleParentAnchors.Count());
            //foreach (RDNode.Anchor anchor in possibleParentAnchors)
            //    Debug.Log("available anchor: " + anchor);

            //if two options are available, pick the larger distance
            if (Math.Abs(connectionVec.x) < Math.Abs(connectionVec.y)) //preferrably vertical            {
            {
                possibleParentAnchors.Reverse();
                possibleTargetAnchors.Reverse();
            }

            if (possibleParentAnchors.Count == 0 || possibleTargetAnchors.Count == 0)
            {
                Debug.LogWarning("no valid anchor for connection " + source.gameObject.name + "->" + target.gameObject.name + ", direction = " + connectionVec.ToString() + ", defaulting to anchors RIGHT->LEFT");
                if (possibleParentAnchors.Count == 0)
                    possibleParentAnchors.Add(RDNode.Anchor.RIGHT);
                if (possibleTargetAnchors.Count == 0)
                    possibleTargetAnchors.Add(RDNode.Anchor.LEFT);
            }



            //Debug.Log("            anchors for connection " + source.gameObject.name + "->" + target.gameObject.name+ ", direction = " + connectionVec.ToString() + " anchors : " + possibleParentAnchors.First() + " -> " + possibleTargetAnchors.First());

            connection.anchor = possibleTargetAnchors.First();
            connection.parent.anchor = possibleParentAnchors.First();
        }
        //draws arrows manually (not required atm)

        private void recreateArrows(RDNode rdNode)
        {
            if (rdNode.state != RDNode.State.HIDDEN)
            {
                for (int i = 0; i < rdNode.parents.Count(); ++i)
                {
                    //Recreate Parent hopefully recreates incoming array? nope, doesnt, also not with calling UpdateGraphics and/or Setup not...
                    //just changing the line does not update the graphics either.
                    RDNode.Parent parentStruct = rdNode.parents[i];
                    if (parentStruct.line != null)
                        Vector.DestroyLine(ref parentStruct.line);
                    if (parentStruct.arrowHead != null)
                        GameObject.Destroy(parentStruct.arrowHead);
                }//endfor foreach parentnode

                RDGridArea gridArea = GameObject.FindObjectOfType<RDGridArea>();
                //typeof(RDNode).GetMethod("InitializeArrows", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(treeNode, new object[] { });
                if (rdNode.state == RDNode.State.RESEARCHED || rdNode.state == RDNode.State.RESEARCHABLE)
                    typeof(RDNode).GetMethod("DrawArrow", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(rdNode, new object[] { gridArea.LineMaterial });
                else
                    typeof(RDNode).GetMethod("DrawArrow", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(rdNode, new object[] { gridArea.LineMaterialGray });
            }
        }
        private void clearParentsFromNode(RDNode treeNode)
        {
            foreach (RDNode.Parent oldParent in treeNode.parents)
            {
                oldParent.parent.node.children.Remove(treeNode);
            }

            treeNode.parents = new RDNode.Parent[0];
        }
 public RDParentAnchor(RDNode node, RDNode.Anchor anchor)
 {
     this.node   = node;
     this.anchor = anchor;
 }
        private void updateParentsForNode(RDNode treeNode, ConfigNode treeCfg)
        {
            //Debug.Log("updating parents for node " + treeNode.gameObject.name);
            //clear all old parents. The RD-Scene will take care of drawing the arrows
            clearParentsFromNode(treeNode);

            List<RDNode.Parent> connectionList = new List<RDNode.Parent>();

            foreach (ConfigNode parentCfg in treeCfg.GetNodes("PARENT_NODE"))
            {
                if (parentCfg.HasValue("name"))
                {
                    string parentName = parentCfg.GetValue("name");

                    //RDNode parentNode = Array.Find<RDNode>(AssetBase.RnDTechTree.GetTreeNodes(), x => x.gameObject.name == parentName);
                    //RDNode parentNode = AssetBase.RnDTechTree.GetTreeNodes().FirstOrDefault(rdn => rdn.gameObject.name == parentName);
                    //RDNode parentNode = GameObject.FindObjectsOfType(typeof(RDNode)).Cast<RDNode>().FirstOrDefault(rdn => rdn.gameObject.name == parentName);
                    RDNode parentNode = GetNodeNamed(parentName);

                    if (parentNode) //Default-constructed RDNode (if search fails) fails this test
                    {
                        //Debug.Log("   --- parentnode: " + parentName);

                        parentNode.children.Add(treeNode);

                        RDNode.Parent connection;

                        // only manually override the anchor points if BOTH are specified in the config
                        if (parentCfg.HasValue("parentSide") && parentCfg.HasValue("childSide"))
                        {

                            RDNode.Anchor parentAnchor = (RDNode.Anchor)Enum.Parse(typeof(RDNode.Anchor), parentCfg.GetValue("parentSide"));
                            RDNode.Anchor childAnchor = (RDNode.Anchor)Enum.Parse(typeof(RDNode.Anchor), parentCfg.GetValue("childSide"));

                            //Debug.Log("Overriding auto-assignment for node " + treeNode.gameObject.name + " to " + parentAnchor + "->" + childAnchor);
                            connection = new RDNode.Parent(new RDNode.ParentAnchor(parentNode, parentAnchor), childAnchor);

                            parentConnectionsAlreadyProcessed.Add(connection);
                        }
                        else
                        {
                            //create RDNode.Parent structure - anchors will be corrected once all nodes have been loaded
                            connection = new RDNode.Parent(new RDNode.ParentAnchor(parentNode, RDNode.Anchor.RIGHT), RDNode.Anchor.LEFT);
                        }

                        connectionList.Add(connection);
                    }
                    else
                    {
                        Debug.LogError("ATC: Invalid parent node specified for: " + treeNode.gameObject.name + " parent: " + parentName);
                    }
                }

            }

            treeNode.parents = connectionList.ToArray();
        }