UP-Viagg-io/Viagg-io/Assets/Packages/MyBT/BT/Node/Node.cs

606 lines
22 KiB
C#
Raw Normal View History

2024-01-19 16:30:05 +01:00
//============= 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;
}
}
}