using System.Collections; using System.Collections.Generic; using UnityEngine; using XNode; using RPGCreationKit.BehaviourTree; using UnityEditor; namespace RPGCreationKit.BehaviourTree { [CreateNodeMenu("RPGCK_BehaviourTree/Random Selector", order = 1)] [System.Serializable] public class RandomSelectorNode : BTNode { [Output(ShowBackingValue.Never, ConnectionType.Multiple, TypeConstraint.None)] public BTNode outputs; // A list of indexes of every child task. This list is used by the Fischer-Yates shuffle algorithm. private List possibilities = new List(); // The random child index execution order. private Stack orderStack = new Stack(); public BTNode nodeExecuting; // Use this for initialization protected override void Init() { base.Init(); } [ContextMenu("Work")] public override void ReorderChild() { List endPorts = GetOutputPort("outputs").GetConnections(); List nodes = new List(); for (int i = 0; i < endPorts.Count; i++) nodes.Add((endPorts[i].node as BTNode)); nodes.Sort(SortInGraphYPos); for (int i = 0; i < nodes.Count; i++) { BTNode btNode = nodes[i] as BTNode; btNode.indexInSequence = i; } } // Return the correct value of an output port when requested public override object GetValue(NodePort port) { return null; // Replace this } public override NodeState Execute() { if (m_NodeState == NodeState.Success || m_NodeState == NodeState.Failure) if (hasEvaluated == true) return m_NodeState; if (!STARTED) OnStart(); List endPorts = GetOutputPort("outputs").GetConnections(); if (nodeExecuting != null && nodeExecuting.m_NodeState != NodeState.Failure && nodeExecuting.m_NodeState != NodeState.Success) { switch (nodeExecuting.Execute()) { case NodeState.Failure: break; case NodeState.Success: m_NodeState = NodeState.Success; hasEvaluated = true; return m_NodeState; case NodeState.Running: m_NodeState = NodeState.Running; return m_NodeState; default: break; } } else if(nodeExecuting != null && nodeExecuting.m_NodeState == NodeState.Success) { m_NodeState = NodeState.Success; hasEvaluated = true; return m_NodeState; } // Pop the top index from the stack and set the execution status. while (orderStack.Count > 0) { nodeExecuting = (endPorts[orderStack.Pop()].node as BTNode); return NodeState.Running; } // If we reach this far we've failed m_NodeState = NodeState.Failure; hasEvaluated = true; return m_NodeState; } public override void ReEvaluate() { if (m_NodeState != NodeState.Running) { base.ReEvaluate(); hasEvaluated = false; nodeExecuting = null; OnStart(); List endPorts = GetOutputPort("outputs").GetConnections(); for (int i = 0; i < endPorts.Count; i++) { (endPorts[i].node as BTNode).ReEvaluate(); } } } public override void OnCreateConnection(NodePort from, NodePort to) { base.OnCreateConnection(from, to); #if UNITY_EDITOR EditorApplication.delayCall += ReorderChild; #endif } public override void OnRemoveConnection(NodePort port) { base.OnRemoveConnection(port); if (port.fieldName == "input") indexInSequence = -1; else if (port.fieldName == "outputs") { #if UNITY_EDITOR EditorApplication.delayCall += ReorderChild; #endif } } public override void OnStart() { possibilities.Clear(); for (int i = 0; i < GetOutputPort("outputs").GetConnections().Count; ++i) possibilities.Add(i); // Randomize the indecies RandomizeIndicies(); STARTED = true; } private void RandomizeIndicies() { orderStack.Clear(); // Use Fischer-Yates shuffle to randomize the child index order. for (int i = possibilities.Count; i > 0; --i) { int j = Random.Range(0, i); int index = possibilities[j]; orderStack.Push(index); possibilities[j] = possibilities[i - 1]; possibilities[i - 1] = index; } } } }