Firstborn/Assets/RPG Creation Kit/Scripts/BehaviourTrees/Nodes/Control Flow/RandomSelectorNode.cs

172 lines
5.2 KiB
C#
Raw Normal View History

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<int> possibilities = new List<int>();
// The random child index execution order.
private Stack<int> orderStack = new Stack<int>();
public BTNode nodeExecuting;
// Use this for initialization
protected override void Init()
{
base.Init();
}
[ContextMenu("Work")]
public override void ReorderChild()
{
List<NodePort> endPorts = GetOutputPort("outputs").GetConnections();
List<BTNode> nodes = new List<BTNode>();
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<NodePort> 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<NodePort> 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;
}
}
}
}