606 lines
22 KiB
C#
606 lines
22 KiB
C#
|
//============= Copyright (c) Ludic GmbH, All rights reserved. ==============
|
|||
|
//
|
|||
|
// Purpose: Part of the My Behaviour Tree Code
|
|||
|
//
|
|||
|
//=============================================================================
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
using System.Text.RegularExpressions;
|
|||
|
using System.Xml.Serialization;
|
|||
|
#if UNITY_EDITOR
|
|||
|
using UnityEditor;
|
|||
|
#endif
|
|||
|
using UnityEngine;
|
|||
|
|
|||
|
namespace MyBT {
|
|||
|
//public class CondictionNode : TaskNode {
|
|||
|
// Task conditionTask;
|
|||
|
// Task trueTask;
|
|||
|
// Task falseTask;
|
|||
|
//}
|
|||
|
|
|||
|
// used in HandleExecute
|
|||
|
public enum NodeExecute {
|
|||
|
// start or restart a node
|
|||
|
Run,
|
|||
|
// abort a running node
|
|||
|
Abort
|
|||
|
}
|
|||
|
public enum NodeState {
|
|||
|
// the node is not initialized
|
|||
|
NotRunning,
|
|||
|
// the Node has not been called before
|
|||
|
FirstRun,
|
|||
|
// state after FirstRun
|
|||
|
Running,
|
|||
|
// this node has to stop in this exec, also all children need to stop
|
|||
|
Aborting
|
|||
|
}
|
|||
|
public enum NodeResult {
|
|||
|
Undefined,
|
|||
|
Continue,
|
|||
|
Succeeded,
|
|||
|
Failed,
|
|||
|
Error
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
[XmlInclude(typeof(Node))]
|
|||
|
|
|||
|
public class Node : ScriptableObject {
|
|||
|
protected const int recursionLimit = 200;
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
// used in editor inspector
|
|||
|
[SerializeField]
|
|||
|
public bool isFolded = true;
|
|||
|
#endif
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
public TaskController taskController;
|
|||
|
|
|||
|
// associated with code
|
|||
|
[SerializeField]
|
|||
|
protected Token[] tokens = null;
|
|||
|
[SerializeField]
|
|||
|
public Token[] closingTokens = null;
|
|||
|
|
|||
|
// associated with this or other nodes
|
|||
|
[XmlIgnore]
|
|||
|
public Node parentNode;
|
|||
|
[SerializeField]
|
|||
|
public NodeList children = new NodeList();
|
|||
|
[SerializeField]
|
|||
|
public int nodeDepth;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
public List<List<NodeResult>> lastResults = new List<List<NodeResult>>();
|
|||
|
public float lastResultsTime = 0;
|
|||
|
|
|||
|
// runtime data
|
|||
|
[SerializeField]
|
|||
|
public List<NodeRuntimeData> nodeRuntimeDataList = new List<NodeRuntimeData>();
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private bool _logStringDisplay = false;
|
|||
|
public bool logStringDisplay {
|
|||
|
get {
|
|||
|
if (taskController != null) {
|
|||
|
return _logStringDisplay || taskController.overrideLogStringDisplay;
|
|||
|
}
|
|||
|
Debug.Log(NodeLogger("logStringDisplay", $"taskController is null, regenerate the nodes!"));
|
|||
|
return _logStringDisplay;
|
|||
|
}
|
|||
|
set {
|
|||
|
_logStringDisplay = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private bool _debugChangesActive = false;
|
|||
|
public bool debugChangesActive {
|
|||
|
get {
|
|||
|
if (taskController != null) {
|
|||
|
return _debugChangesActive || taskController.overrideDebugChangesActive;
|
|||
|
}
|
|||
|
Debug.Log(NodeLogger("debugChangesActive", $"taskController is null, regenerate the nodes!"));
|
|||
|
return _debugChangesActive;
|
|||
|
}
|
|||
|
set {
|
|||
|
_debugChangesActive = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private bool _debugInternalActive = false;
|
|||
|
public bool debugInternalActive {
|
|||
|
get {
|
|||
|
if (taskController != null) {
|
|||
|
return _debugInternalActive || taskController.overrideDebugInternalActive;
|
|||
|
}
|
|||
|
Debug.Log(NodeLogger("debugInternalActive", $"taskController is null, regenerate the nodes!"));
|
|||
|
return _debugInternalActive;
|
|||
|
}
|
|||
|
set {
|
|||
|
_debugInternalActive = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool hasChildren {
|
|||
|
get {
|
|||
|
return ((children != null) && (children.Count > 0));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool parentFolded {
|
|||
|
get {
|
|||
|
if (parentNode != null) {
|
|||
|
#if UNITY_EDITOR
|
|||
|
return parentNode.parentFolded || parentNode.isFolded;
|
|||
|
#else
|
|||
|
return false;
|
|||
|
#endif
|
|||
|
}
|
|||
|
// top node cannot be folded
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public enum TaskNodeType {
|
|||
|
TaskNode,
|
|||
|
TreeNode,
|
|||
|
CompositeNode,
|
|||
|
DecoratorNode,
|
|||
|
ActionNode,
|
|||
|
RunTreeNode,
|
|||
|
CommentNode
|
|||
|
}
|
|||
|
|
|||
|
#region node and task labels, icons, description and colors
|
|||
|
public static readonly Dictionary<Type, TaskNodeType> NodeTypeDict = new Dictionary<Type, TaskNodeType> {
|
|||
|
{typeof(Node), TaskNodeType.TaskNode},
|
|||
|
{typeof(TreeNode),TaskNodeType.TaskNode},
|
|||
|
{typeof(CompositeNode), TaskNodeType.CompositeNode},
|
|||
|
{typeof(DecoratorNode), TaskNodeType.DecoratorNode},
|
|||
|
{typeof(ActionNode), TaskNodeType.ActionNode},
|
|||
|
{typeof(RunTreeNode), TaskNodeType.RunTreeNode},
|
|||
|
};
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
public virtual void Init(int _nodeDepth, Token[] _tokens) { //, int[] _tokenIndexes) {
|
|||
|
taskController = null;
|
|||
|
tokens = _tokens;
|
|||
|
nodeDepth = _nodeDepth;
|
|||
|
}
|
|||
|
|
|||
|
public void SetTaskController (TaskController _taskController) {
|
|||
|
taskController = _taskController;
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool Add(Node n) {
|
|||
|
if (children == null) {
|
|||
|
children = new NodeList();
|
|||
|
}
|
|||
|
|
|||
|
// can only have 1 child node
|
|||
|
if ((this.GetType() == typeof(DecoratorNode)) || (this.GetType() == typeof(TreeNode))) {
|
|||
|
if (children.Count > 0) {
|
|||
|
// Debug.LogError($"Node.Add: Error in '{codeLine}' {lineNumber}:{colNumber}\n{this.GetType().Name} can only have 1 sub-element cannot add {n.codeLine}");
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// add new child to this
|
|||
|
children.Add(n);
|
|||
|
|
|||
|
// set parent in child
|
|||
|
n.parentNode = this;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// reset the whole behaviour tree structure
|
|||
|
/// </summary>
|
|||
|
public virtual void Destroy() {
|
|||
|
if (debugInternalActive)
|
|||
|
Debug.Log($"Node.Destroy cl:{codeLine} pr:{parentNode} cc:{children.Count} tl:{tokens?.Length} {(tokens!=null?(tokens.Length>0?tokens[0].location.line:-1):0)}");
|
|||
|
if (debugInternalActive) {
|
|||
|
Debug.Log(NodeLogger("Reset", ""));
|
|||
|
}
|
|||
|
tokens = null;
|
|||
|
closingTokens = null;
|
|||
|
parentNode = null;
|
|||
|
children = new NodeList();
|
|||
|
|
|||
|
for (int i= nodeRuntimeDataList.Count-1; i>-1; i--) {
|
|||
|
nodeRuntimeDataList[i].Destroy();
|
|||
|
}
|
|||
|
nodeRuntimeDataList = new List<NodeRuntimeData>();
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<Node> getAllNodesRecursively(bool traverseRunTree) {
|
|||
|
// Return the parent before its children
|
|||
|
yield return this;
|
|||
|
|
|||
|
// on a standard node, run the childrens
|
|||
|
if (!(this is RunTreeNode)) {
|
|||
|
// Debug.Log("Node.getAllNodesRecursively: Node");
|
|||
|
foreach (Node node in this.children) {
|
|||
|
foreach (Node n in node.getAllNodesRecursively(traverseRunTree)) {
|
|||
|
yield return n;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// on runtree nodes
|
|||
|
else {
|
|||
|
// Debug.Log("Node.getAllNodesRecursively: RunTreeNode");
|
|||
|
// run
|
|||
|
if (traverseRunTree) {
|
|||
|
RunTreeNode node = this as RunTreeNode;
|
|||
|
if (node.runThisNode != null) {
|
|||
|
foreach (Node n in node.runThisNode.getAllNodesRecursively(traverseRunTree)) {
|
|||
|
yield return n;
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
if (debugInternalActive) Debug.LogError(NodeLogger("Node.getAllNodesRecursively", "cannot traverse RunTreeNode, not bound"));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
// othervise ignore
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public TreeNode GetParentTreeNode () {
|
|||
|
Node currentNode = this;
|
|||
|
while (currentNode.GetType() != typeof(TreeNode)) {
|
|||
|
if (currentNode.parentNode != null) {
|
|||
|
currentNode = currentNode.parentNode;
|
|||
|
}
|
|||
|
}
|
|||
|
return currentNode as TreeNode;
|
|||
|
}
|
|||
|
|
|||
|
public int GetNodeTreeDepth () {
|
|||
|
TreeNode currentNode = GetParentTreeNode();
|
|||
|
return currentNode.nodeDepth;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// return wether the state has changed
|
|||
|
/// </summary>
|
|||
|
public virtual bool PreTick (NodeExecute nodeExecute, NodeRuntimeData nodeRuntimeData) {
|
|||
|
NodeState oldNodeState = nodeRuntimeData.nodeState;
|
|||
|
switch (nodeExecute) {
|
|||
|
case NodeExecute.Run:
|
|||
|
switch (nodeRuntimeData.nodeState) {
|
|||
|
case NodeState.NotRunning:
|
|||
|
nodeRuntimeData.nodeState = NodeState.FirstRun;
|
|||
|
nodeRuntimeData.nodeResult = NodeResult.Continue;
|
|||
|
break;
|
|||
|
case NodeState.FirstRun:
|
|||
|
nodeRuntimeData.nodeState = NodeState.Running;
|
|||
|
nodeRuntimeData.nodeResult = NodeResult.Continue;
|
|||
|
break;
|
|||
|
case NodeState.Running:
|
|||
|
nodeRuntimeData.nodeState = NodeState.Running;
|
|||
|
nodeRuntimeData.nodeResult = NodeResult.Continue;
|
|||
|
break;
|
|||
|
case NodeState.Aborting:
|
|||
|
nodeRuntimeData.nodeState = NodeState.Aborting;
|
|||
|
nodeRuntimeData.nodeResult = NodeResult.Undefined;
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
case NodeExecute.Abort:
|
|||
|
nodeRuntimeData.nodeState = NodeState.Aborting;
|
|||
|
nodeRuntimeData.nodeResult = NodeResult.Undefined;
|
|||
|
break;
|
|||
|
}
|
|||
|
return (nodeRuntimeData.nodeState != oldNodeState);
|
|||
|
}
|
|||
|
|
|||
|
public virtual IEnumerable<NodeResult> Tick(NodeRuntimeData parentNodeRuntimeData, int recursions) {
|
|||
|
yield break;
|
|||
|
}
|
|||
|
|
|||
|
public virtual void Execute(NodeRuntimeData myNodeRuntimeData, NodeExecute nodeExecute) {
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// return wether the state has changed
|
|||
|
/// </summary>
|
|||
|
public virtual bool PostTick(NodeExecute nodeExecute, NodeRuntimeData nodeRuntimeData) {
|
|||
|
NodeState oldNodeState = nodeRuntimeData.nodeState;
|
|||
|
NodeResult oldNodeResult = nodeRuntimeData.nodeResult;
|
|||
|
switch (nodeExecute) {
|
|||
|
case NodeExecute.Run:
|
|||
|
switch (nodeRuntimeData.nodeResult) {
|
|||
|
case NodeResult.Undefined:
|
|||
|
Debug.Log(NodeLogger("PostTick", "in undefined result state"));
|
|||
|
break;
|
|||
|
case NodeResult.Continue:
|
|||
|
nodeRuntimeData.nodeState = NodeState.Running;
|
|||
|
break;
|
|||
|
case NodeResult.Succeeded:
|
|||
|
nodeRuntimeData.nodeState = NodeState.NotRunning;
|
|||
|
break;
|
|||
|
case NodeResult.Failed:
|
|||
|
nodeRuntimeData.nodeState = NodeState.NotRunning;
|
|||
|
break;
|
|||
|
case NodeResult.Error:
|
|||
|
nodeRuntimeData.nodeState = NodeState.Aborting;
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
case NodeExecute.Abort:
|
|||
|
nodeRuntimeData.nodeState = NodeState.NotRunning;
|
|||
|
nodeRuntimeData.nodeResult = NodeResult.Undefined;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// copy result to lastResults
|
|||
|
if ((nodeExecute != NodeExecute.Abort) && (lastResults != null) && (nodeRuntimeDataList != null)) {
|
|||
|
// clear contents
|
|||
|
if (lastResultsTime != TaskController.currentUiTime) {
|
|||
|
foreach (List<NodeResult> nrl in lastResults) {
|
|||
|
nrl.Clear();
|
|||
|
}
|
|||
|
lastResultsTime = TaskController.currentUiTime;
|
|||
|
}
|
|||
|
int myIndex = nodeRuntimeDataList.IndexOf(nodeRuntimeData);
|
|||
|
if (debugChangesActive) Debug.Log(NodeLogger("PostTick", $"frame {Time.time} index {myIndex} result {nodeRuntimeData.nodeResult}"));
|
|||
|
if (myIndex != -1) {
|
|||
|
while (lastResults.Count < myIndex+1) { //nodeRuntimeDataList.Count) {
|
|||
|
lastResults.Add(new List<NodeResult>());
|
|||
|
}
|
|||
|
lastResults[myIndex].Add(nodeRuntimeDataList[myIndex].nodeResult);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return (nodeRuntimeData.nodeState != oldNodeState) || (nodeRuntimeData.nodeResult != oldNodeResult);
|
|||
|
}
|
|||
|
|
|||
|
public void DetachChildren() {
|
|||
|
if (children != null) {
|
|||
|
foreach (Node child in children) {
|
|||
|
child.DetachChildren();
|
|||
|
child.parentNode = null;
|
|||
|
}
|
|||
|
children = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string NodeLogger(string functionName, string appendix, NodeRuntimeData runtimeData=null, bool includeDetails =false) {
|
|||
|
StringBuilder sb = new StringBuilder();
|
|||
|
|
|||
|
sb.Append($"{this.GetType().Name.ToString().Trim()}.");
|
|||
|
if (!string.IsNullOrEmpty(functionName.Trim())) {
|
|||
|
sb.Append($"{functionName.Trim()} ");
|
|||
|
}
|
|||
|
sb.Append($"({this.lineNumber}:{ this.colNumber}): ");
|
|||
|
|
|||
|
RecursionIndention(ref sb, nodeDepth);
|
|||
|
|
|||
|
sb.Append($"{appendix} ");
|
|||
|
|
|||
|
if (includeDetails && (runtimeData != null)) {
|
|||
|
sb.Append($"State/Result: {MyBtResources.NodeStateLabels[runtimeData.nodeState]}, {MyBtResources.NodeResultLabels[runtimeData.nodeResult]} ");
|
|||
|
}
|
|||
|
|
|||
|
if ((runtimeData is ActionNodeRuntimeData) && (runtimeData != null)) {
|
|||
|
ActionNodeRuntimeData actionNodeRuntimeData = (ActionNodeRuntimeData)runtimeData;
|
|||
|
if (includeDetails) {
|
|||
|
sb.Append($"(Task: {MyBtResources.NodeStateLabels[actionNodeRuntimeData.nodeState]}, {MyBtResources.NodeResultLabels[actionNodeRuntimeData.nodeResult]}) ");
|
|||
|
|
|||
|
if (this is ActionNode)
|
|||
|
sb.Append($"(bnd: {((ActionNode)this).isBound} int: {debugInternalActive} chg: {debugChangesActive}) ");
|
|||
|
}
|
|||
|
|
|||
|
string debugDataString = (actionNodeRuntimeData.logString!=null)? actionNodeRuntimeData.logString.ToString():"undefined";
|
|||
|
// string debugDataString = (an.debugData!=null)?an.debugData.ToString():"undefined";
|
|||
|
sb.Append($"dbgDt: {debugDataString} ");
|
|||
|
|
|||
|
string userDataString = (actionNodeRuntimeData.userData != null) ? actionNodeRuntimeData.userData.ToString() : "undefined";
|
|||
|
sb.Append($"usrDt: {userDataString} ");
|
|||
|
}
|
|||
|
|
|||
|
return sb.ToString();
|
|||
|
}
|
|||
|
|
|||
|
public void RecursionIndention(ref StringBuilder sb, int recursion) {
|
|||
|
if (recursion != -1)
|
|||
|
sb.Insert(0, $"{string.Concat(Enumerable.Repeat("|--", recursion+1))} ");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private int _lineNumberCache;
|
|||
|
[SerializeField]
|
|||
|
private int _colNumberCache;
|
|||
|
[SerializeField]
|
|||
|
private int _lastLineNumberCache;
|
|||
|
[SerializeField]
|
|||
|
private string _codeLineCache;
|
|||
|
[SerializeField]
|
|||
|
private string _spacedTextCache;
|
|||
|
[SerializeField]
|
|||
|
private string _nodeStringCache;
|
|||
|
|
|||
|
public void GenerateNodeDataCaches () {
|
|||
|
if ((tokens != null) && (tokens.Length > 0) && (tokens[0] != null) && (tokens[0].location != null)) {
|
|||
|
_lineNumberCache = tokens[0].location.line;
|
|||
|
}
|
|||
|
else {
|
|||
|
// USING THE NODELOGGER WILL CAUSE A RECURSIVE ERROR IN HERE!!!
|
|||
|
//Debug.LogError(NodeLogger("lineNumber", $"tokens.Length: {(tokens != null ? tokens.Length : 0)}"));
|
|||
|
Debug.LogError("Error in lineNumber");
|
|||
|
}
|
|||
|
|
|||
|
if ((tokens != null) && (tokens.Length > 0) && (tokens[0] != null) && (tokens[0].location != null)) {
|
|||
|
_colNumberCache = tokens[0].location.col;
|
|||
|
}
|
|||
|
else {
|
|||
|
Debug.LogError("Error in colNumber");
|
|||
|
//Debug.LogError(NodeLogger("colNumber", $"tokens.Length: {(tokens != null ? tokens.Length : 0)}"));
|
|||
|
}
|
|||
|
|
|||
|
if ((closingTokens != null) && (closingTokens.Length > 0) && (closingTokens[0] != null) && (closingTokens[0].location != null)) {
|
|||
|
_lastLineNumberCache = closingTokens[0].location.line;
|
|||
|
}
|
|||
|
else {
|
|||
|
if (debugChangesActive) Debug.LogError(NodeLogger("lastLineNumber", $"tokens.Length: {(closingTokens != null ? closingTokens.Length : 0)}"));
|
|||
|
}
|
|||
|
|
|||
|
StringBuilder sb1 = new StringBuilder();
|
|||
|
if ((tokens != null) && (tokens.Length > 0)) {
|
|||
|
for (int i = 0; i < tokens.Length; i++) {
|
|||
|
if ((tokens[i] != null) && (tokens[i].location != null)) {
|
|||
|
sb1.Append(tokens[i].location.code);
|
|||
|
}
|
|||
|
else {
|
|||
|
if (tokens[i] == null) {
|
|||
|
Debug.LogError("Token undefined!");
|
|||
|
}
|
|||
|
else if (tokens[i].location != null) {
|
|||
|
Debug.LogError("Token.location undefined!");
|
|||
|
}
|
|||
|
_codeLineCache = "undefined";
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
Debug.LogError("Error in codeLine");
|
|||
|
//Debug.LogError(NodeLogger("codeLine", $"{(tokens != null ? tokens.Length : 0)}"));
|
|||
|
}
|
|||
|
_codeLineCache = sb1.ToString();
|
|||
|
|
|||
|
StringBuilder sb2 = new StringBuilder();
|
|||
|
// string tokenString = "";
|
|||
|
if ((tokens != null) && (tokens.Length > 0)) {
|
|||
|
for (int i = 0; i < tokens.Length; i++) {
|
|||
|
// tokenString += " " + Regex.Replace(tokens[i].location.code, @"\t|\n|\r", " ");
|
|||
|
sb2.Append($" {Regex.Replace(tokens[i].location.code, @"\t|\n|\r", " ")}");
|
|||
|
}
|
|||
|
}
|
|||
|
// return tokenString;
|
|||
|
_spacedTextCache = sb2.ToString();
|
|||
|
|
|||
|
_nodeStringCache = $"{this.GetType().Name.ToString().Trim()} ({this.lineNumber}:{this.colNumber})";
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public int lineNumber {
|
|||
|
get {
|
|||
|
return _lineNumberCache;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public int colNumber {
|
|||
|
get {
|
|||
|
return _colNumberCache;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public int lastLineNumber {
|
|||
|
get {
|
|||
|
return _lastLineNumberCache;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string codeLine {
|
|||
|
get {
|
|||
|
return _codeLineCache;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string spacedText {
|
|||
|
get {
|
|||
|
return _spacedTextCache;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string nodeString {
|
|||
|
get {
|
|||
|
return _nodeStringCache;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string DebugTree(int curDepth = 0) {
|
|||
|
string output = new string('-', curDepth) + ToString() + " " + codeLine + "\n";
|
|||
|
if (children != null) {
|
|||
|
foreach (Node taskNode in children) {
|
|||
|
output += taskNode.DebugTree(curDepth + 1);
|
|||
|
}
|
|||
|
}
|
|||
|
return output;
|
|||
|
}
|
|||
|
|
|||
|
public override string ToString () {
|
|||
|
return nodeString;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// TODO!!!
|
|||
|
/// </summary>
|
|||
|
public Node DuplicateRecursive (int recursion = 0, Node parent=null) {
|
|||
|
Debug.LogWarning($"DuplicateRecursive {this}");
|
|||
|
// TreeNode newRootNode = rootNode.Duplicate();
|
|||
|
Node newCurrentNode = Duplicate();
|
|||
|
if (parent != null) {
|
|||
|
parent.Add(newCurrentNode);
|
|||
|
}
|
|||
|
|
|||
|
if (recursion > 10) {
|
|||
|
Debug.LogError("Abort Recursion");
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
foreach (Node child in children) {
|
|||
|
Node newChild = child.DuplicateRecursive(recursion + 1, newCurrentNode);
|
|||
|
// if (parent != null) {
|
|||
|
// newCurrentNode.Add(newChild);
|
|||
|
// }
|
|||
|
}
|
|||
|
return newCurrentNode;
|
|||
|
}
|
|||
|
|
|||
|
public virtual Node Duplicate () {
|
|||
|
Debug.LogError("not implemented");
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public void BaseDuplicate (Node n) {
|
|||
|
n.parentNode = parentNode;
|
|||
|
|
|||
|
// cannot be copyed, is generated by recursion!
|
|||
|
// n.children = new NodeList();
|
|||
|
|
|||
|
n.tokens = tokens;
|
|||
|
|
|||
|
if (closingTokens != null) {
|
|||
|
n.closingTokens = closingTokens;
|
|||
|
} else {
|
|||
|
// Debug.LogWarning("BaseDuplicate closingTokens is null!");
|
|||
|
}
|
|||
|
|
|||
|
n.nodeDepth = nodeDepth;
|
|||
|
//n.nodeResult = nodeResult;
|
|||
|
|
|||
|
n.logStringDisplay = false;
|
|||
|
//n.logString = logString;
|
|||
|
//n.nodeState = nodeState;
|
|||
|
|
|||
|
n.debugInternalActive = false;
|
|||
|
n.debugChangesActive = false;
|
|||
|
// return n;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|