2023-08-02 06:08:03 +00:00
|
|
|
|
// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved.
|
|
|
|
|
// This code can only be used under the standard Unity Asset Store End User License Agreement
|
|
|
|
|
// A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using Doozy.Runtime.Common.Utils;
|
|
|
|
|
using Doozy.Runtime.UIManager.ScriptableObjects;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.Events;
|
|
|
|
|
|
|
|
|
|
namespace Doozy.Runtime.Nody
|
|
|
|
|
{
|
|
|
|
|
/// <summary> Base class for all nodes in Nody system </summary>
|
|
|
|
|
[Serializable]
|
|
|
|
|
public abstract class FlowNode : ScriptableObject
|
|
|
|
|
{
|
|
|
|
|
/// <summary> True is Multiplayer Mode is enabled </summary>
|
|
|
|
|
public bool multiplayerMode => UIManagerInputSettings.instance.multiplayerMode & flowGraph.controller.hasMultiplayerInfo;
|
|
|
|
|
/// <summary> Default player index value (used for global user) </summary>
|
|
|
|
|
public int playerIndex => flowGraph.controller.playerIndex;
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
[SerializeField] private string FlowGraphId;
|
|
|
|
|
/// <summary> Flow Graph Id this node belongs to </summary>
|
|
|
|
|
public string flowGraphId
|
|
|
|
|
{
|
|
|
|
|
get => FlowGraphId;
|
|
|
|
|
internal set => FlowGraphId = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Reference to the flow graph this node belongs to </summary>
|
|
|
|
|
public FlowGraph flowGraph { get; internal set; }
|
|
|
|
|
|
|
|
|
|
[SerializeField] private string NodeId;
|
|
|
|
|
/// <summary> Node Id </summary>
|
|
|
|
|
public string nodeId
|
|
|
|
|
{
|
|
|
|
|
get => NodeId;
|
|
|
|
|
internal set => NodeId = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] private NodeType NodeType;
|
|
|
|
|
/// <summary> Type of node </summary>
|
|
|
|
|
public NodeType nodeType => NodeType;
|
|
|
|
|
|
|
|
|
|
[SerializeField] private string NodeName;
|
|
|
|
|
/// <summary> Name of this node </summary>
|
|
|
|
|
public string nodeName
|
|
|
|
|
{
|
|
|
|
|
get => NodeName;
|
|
|
|
|
internal set => NodeName = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] private string NodeDescription;
|
|
|
|
|
/// <summary> Description for this node </summary>
|
|
|
|
|
public string nodeDescription
|
|
|
|
|
{
|
|
|
|
|
get => NodeDescription;
|
|
|
|
|
internal set => NodeDescription = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] private List<FlowPort> InputPorts;
|
|
|
|
|
/// <summary> All the input ports this node has </summary>
|
|
|
|
|
public List<FlowPort> inputPorts
|
|
|
|
|
{
|
|
|
|
|
get => InputPorts;
|
|
|
|
|
internal set => InputPorts = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Get the first input port. If one does not exit, this returns null </summary>
|
|
|
|
|
public FlowPort firstInputPort => inputPorts.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
/// <summary> Get the last input port. If one does not exit, this returns null </summary>
|
|
|
|
|
public FlowPort lastInputPort => inputPorts.Last();
|
|
|
|
|
|
|
|
|
|
/// <summary> List of all the input port ids connected to this node </summary>
|
|
|
|
|
public List<string> inputConnections
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var list = new List<string>();
|
|
|
|
|
foreach (FlowPort port in inputPorts)
|
|
|
|
|
list.AddRange(port.connections);
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] private List<FlowPort> OutputPorts;
|
|
|
|
|
/// <summary> All the output ports this node has </summary>
|
|
|
|
|
public List<FlowPort> outputPorts
|
|
|
|
|
{
|
|
|
|
|
get => OutputPorts;
|
|
|
|
|
internal set => OutputPorts = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Get the first output port. If one does not exit, this returns null </summary>
|
|
|
|
|
public FlowPort firstOutputPort => outputPorts.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
/// <summary> Get the last output port. If one does not exit, this returns null </summary>
|
|
|
|
|
public FlowPort lastOutputPort => outputPorts.Last();
|
|
|
|
|
|
|
|
|
|
/// <summary> List of all the output port ids connected to this node </summary>
|
|
|
|
|
public List<string> outputConnections
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var list = new List<string>();
|
|
|
|
|
foreach (FlowPort port in outputPorts)
|
|
|
|
|
list.AddRange(port.connections);
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> All the ports this node had (input and output) </summary>
|
|
|
|
|
public List<FlowPort> ports
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var list = new List<FlowPort>();
|
|
|
|
|
list.AddRange(inputPorts);
|
|
|
|
|
list.AddRange(outputPorts);
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> List of all the port ids connected to this node (input and output) </summary>
|
|
|
|
|
public List<string> connections
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var list = new List<string>();
|
|
|
|
|
list.AddRange(inputConnections);
|
|
|
|
|
list.AddRange(outputConnections);
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Minimum number of input ports this node can have.
|
|
|
|
|
/// Value checked when deleting ports to prevent deletion of important ports
|
|
|
|
|
/// </summary>
|
|
|
|
|
public virtual int minNumberOfInputPorts => 0;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Minimum number of output ports this node can have.
|
|
|
|
|
/// Value checked when deleting ports to prevent deletion of important ports
|
|
|
|
|
/// </summary>
|
|
|
|
|
public virtual int minNumberOfOutputPorts => 0;
|
|
|
|
|
|
|
|
|
|
[SerializeField] private bool RunUpdate;
|
|
|
|
|
/// <summary> Run Update when the node is active (default: false) </summary>
|
|
|
|
|
public bool runUpdate
|
|
|
|
|
{
|
|
|
|
|
get => RunUpdate;
|
|
|
|
|
set => RunUpdate = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] private bool RunFixedUpdate;
|
|
|
|
|
/// <summary> Run FixedUpdate when the node is active (default: false) </summary>
|
|
|
|
|
public bool runFixedUpdate
|
|
|
|
|
{
|
|
|
|
|
get => RunFixedUpdate;
|
|
|
|
|
set => RunFixedUpdate = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] private bool RunLateUpdate;
|
|
|
|
|
/// <summary> Run LateUpdate when the node is active (default: false) </summary>
|
|
|
|
|
public bool runLateUpdate
|
|
|
|
|
{
|
|
|
|
|
get => RunLateUpdate;
|
|
|
|
|
set => RunLateUpdate = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] protected NodeState NodeState = NodeState.Idle;
|
|
|
|
|
/// <summary> Current node state </summary>
|
|
|
|
|
public NodeState nodeState
|
|
|
|
|
{
|
|
|
|
|
get => NodeState;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
NodeState = value;
|
|
|
|
|
onStateChanged?.Invoke(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Triggered every time the node changes its state </summary>
|
|
|
|
|
public UnityAction<NodeState> onStateChanged { get; set; }
|
|
|
|
|
|
|
|
|
|
[SerializeField] private bool CanBeDeleted;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// [Editor] True if this node can be deleted (default: true)
|
|
|
|
|
/// Used to prevent special nodes from being deleted in the editor
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool canBeDeleted
|
|
|
|
|
{
|
|
|
|
|
get => CanBeDeleted;
|
|
|
|
|
internal set => CanBeDeleted = value;
|
|
|
|
|
}
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> Show Passthrough On/Off switch in the editor (default: false) </summary>
|
|
|
|
|
public virtual bool showPassthroughInEditor => false;
|
|
|
|
|
[SerializeField] private bool Passthrough;
|
|
|
|
|
/// <summary> Allow the graph to bypass this node when going back (default: true) </summary>
|
|
|
|
|
public bool passthrough
|
|
|
|
|
{
|
|
|
|
|
get => Passthrough;
|
|
|
|
|
set => Passthrough = value;
|
|
|
|
|
}
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> Show ClearGraphHistory On/Off switch in the editor (default: false) </summary>
|
|
|
|
|
public virtual bool showClearGraphHistoryInEditor => false;
|
|
|
|
|
[SerializeField] private bool ClearGraphHistory;
|
|
|
|
|
/// <summary> OnEnter, clear graph history and remove the possibility of being able to go back to previously active nodes (default: false) </summary>
|
|
|
|
|
public bool clearGraphHistory
|
|
|
|
|
{
|
|
|
|
|
get => ClearGraphHistory;
|
|
|
|
|
set => ClearGraphHistory = value;
|
|
|
|
|
}
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
[SerializeField] private Vector2 Position = Vector2.zero;
|
|
|
|
|
/// <summary> [Editor] Position of the node in the graph </summary>
|
|
|
|
|
public Vector2 position
|
|
|
|
|
{
|
|
|
|
|
get => Position;
|
|
|
|
|
internal set => Position = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> [Editor] Ping this node </summary>
|
|
|
|
|
public UnityAction<FlowDirection> ping { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary> [Editor] Refresh this node's editor </summary>
|
|
|
|
|
public UnityAction refreshNodeEditor { get; set; }
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> [Editor] Refresh this node's view </summary>
|
|
|
|
|
public UnityAction refreshNodeView { get; set; }
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> [Editor] Called when OnEnter is triggered </summary>
|
|
|
|
|
public UnityAction onEnter { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary> [Editor] Called when OnExit is triggered </summary>
|
|
|
|
|
public UnityAction onExit { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary> [Editor] Called when Start is triggered </summary>
|
|
|
|
|
public UnityAction onStart { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary> [Editor] Called when Stop is triggered </summary>
|
|
|
|
|
public UnityAction onStop { get; set; }
|
|
|
|
|
|
2023-12-05 06:20:20 +00:00
|
|
|
|
/// <summary> [Editor] Called when the node is paused </summary>
|
|
|
|
|
public UnityAction onPaused { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary> [Editor] Called when the node is unpaused </summary>
|
|
|
|
|
public UnityAction onUnPaused { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary> Check if this node is idle </summary>
|
|
|
|
|
public bool isIdle => nodeState == NodeState.Idle;
|
|
|
|
|
|
|
|
|
|
/// <summary> Check if this node is running. This means that this is probably a global node. </summary>
|
|
|
|
|
public bool isRunning => nodeState == NodeState.Running;
|
|
|
|
|
|
|
|
|
|
/// <summary> Check if this node is active </summary>
|
|
|
|
|
public bool isActive => nodeState == NodeState.Active;
|
|
|
|
|
|
|
|
|
|
/// <summary> Check if this node is paused </summary>
|
|
|
|
|
public bool isPaused => nodeState == NodeState.Paused;
|
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
protected FlowNode(NodeType type)
|
|
|
|
|
{
|
|
|
|
|
FlowGraphId = string.Empty;
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
NodeId = Guid.NewGuid().ToString();
|
|
|
|
|
NodeType = type;
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
NodeName = ObjectNames.NicifyVariableName(GetType().Name.Replace("Node", ""));
|
|
|
|
|
NodeDescription = string.Empty;
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
InputPorts = new List<FlowPort>();
|
|
|
|
|
OutputPorts = new List<FlowPort>();
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
RunUpdate = false;
|
|
|
|
|
RunFixedUpdate = false;
|
|
|
|
|
RunLateUpdate = false;
|
|
|
|
|
|
|
|
|
|
CanBeDeleted = true;
|
|
|
|
|
Passthrough = true;
|
|
|
|
|
ClearGraphHistory = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Called when the parent graph started and this is global node </summary>
|
|
|
|
|
public virtual void Start()
|
|
|
|
|
{
|
|
|
|
|
nodeState = NodeState.Running;
|
|
|
|
|
onStart?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Called when the parent graph stopped and this is a global node </summary>
|
|
|
|
|
public virtual void Stop()
|
|
|
|
|
{
|
|
|
|
|
nodeState = NodeState.Idle;
|
|
|
|
|
onStop?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Called on the frame when this node becomes active,
|
|
|
|
|
/// just before any of the node's Update methods are called for the first time
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="previousNode"> Source Node </param>
|
|
|
|
|
/// <param name="previousPort"> Source Port </param>
|
|
|
|
|
public virtual void OnEnter(FlowNode previousNode = null, FlowPort previousPort = null)
|
|
|
|
|
{
|
|
|
|
|
nodeState = NodeState.Active;
|
|
|
|
|
onEnter?.Invoke();
|
|
|
|
|
if (clearGraphHistory) flowGraph.ClearHistory();
|
2023-12-05 06:20:20 +00:00
|
|
|
|
}
|
2023-08-02 06:08:03 +00:00
|
|
|
|
|
|
|
|
|
/// <summary> Called just before this node becomes idle </summary>
|
|
|
|
|
public virtual void OnExit()
|
|
|
|
|
{
|
|
|
|
|
nodeState = NodeState.Idle;
|
|
|
|
|
onExit?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-05 06:20:20 +00:00
|
|
|
|
/// <summary> Called when the node is paused </summary>
|
|
|
|
|
public virtual void OnPaused()
|
|
|
|
|
{
|
|
|
|
|
if (!isActive) return;
|
|
|
|
|
nodeState = NodeState.Paused;
|
|
|
|
|
onPaused?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Called when the node is unpaused </summary>
|
|
|
|
|
public virtual void OnUnPaused()
|
|
|
|
|
{
|
|
|
|
|
if (!isPaused) return;
|
|
|
|
|
nodeState = NodeState.Active;
|
|
|
|
|
onUnPaused?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> Called every frame, if the node is enabled and active (Update Method) </summary>
|
|
|
|
|
public virtual void Update() {}
|
|
|
|
|
|
|
|
|
|
/// <summary> Called every frame, if the node is enabled and active (FixedUpdate Method) </summary>
|
|
|
|
|
public virtual void FixedUpdate() {}
|
|
|
|
|
|
|
|
|
|
/// <summary> Called every frame, if the node is enabled and active (LateUpdate Method) </summary>
|
|
|
|
|
public virtual void LateUpdate() {}
|
|
|
|
|
|
|
|
|
|
/// <summary> Clone this node </summary>
|
|
|
|
|
public virtual FlowNode Clone() =>
|
|
|
|
|
Instantiate(this);
|
|
|
|
|
|
|
|
|
|
/// <summary> Go to the next node, by accessing the first connection of the given output port </summary>
|
|
|
|
|
/// <param name="outputPort"> Output port to activate </param>
|
|
|
|
|
protected virtual void GoToNextNode(FlowPort outputPort)
|
|
|
|
|
{
|
|
|
|
|
if (flowGraph == null) return; //graph null check
|
|
|
|
|
FlowPort fromPort = outputPort; //get from port -> this node's target output port
|
|
|
|
|
FlowPort toPort = flowGraph.GetPortById(fromPort.firstConnection); //get to port -> other node's target input port
|
|
|
|
|
if (toPort == null) return; //next port null check
|
|
|
|
|
if (toPort.node == null) return; //next node null check
|
|
|
|
|
flowGraph.SetActiveNode(toPort.node, fromPort); //activate next node
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual void ResetNode()
|
|
|
|
|
{
|
|
|
|
|
nodeState = NodeState.Idle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Add a new port to this node </summary>
|
|
|
|
|
/// <param name="direction"> Port direction (Input/Output) - the port's connections direction </param>
|
|
|
|
|
/// <param name="capacity"> Port capacity (Single/Multi) - the number of connections this port can have </param>
|
|
|
|
|
public virtual FlowPort AddPort(PortDirection direction, PortCapacity capacity)
|
|
|
|
|
{
|
|
|
|
|
FlowPort port = new FlowPort().SetNodeId(nodeId).SetDirection(direction).SetCapacity(capacity);
|
|
|
|
|
switch (direction)
|
|
|
|
|
{
|
|
|
|
|
case PortDirection.Input:
|
|
|
|
|
inputPorts.Add(port);
|
|
|
|
|
break;
|
|
|
|
|
case PortDirection.Output:
|
|
|
|
|
outputPorts.Add(port);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
|
|
|
|
|
}
|
|
|
|
|
return port;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Add a new input port to this node </summary>
|
|
|
|
|
/// <param name="capacity"> Port capacity (Single/Multi) - the number of connections this port can have </param>
|
|
|
|
|
public virtual FlowPort AddInputPort(PortCapacity capacity = PortCapacity.Multi) =>
|
|
|
|
|
AddPort(PortDirection.Input, capacity);
|
|
|
|
|
|
|
|
|
|
/// <summary> Add a new output port to this node </summary>
|
|
|
|
|
/// <param name="capacity"> Port capacity (Single/Multi) - the number of connections this port can have </param>
|
|
|
|
|
public virtual FlowPort AddOutputPort(PortCapacity capacity = PortCapacity.Single) =>
|
|
|
|
|
AddPort(PortDirection.Output, capacity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
|
|
|
|
public static class FlowNodeExtensions
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the input port with the given port id.
|
|
|
|
|
/// If one is not found, this method returns null
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="portId"> Port id to search for </param>
|
|
|
|
|
public static FlowPort GetInputPortFromId<T>(this T target, string portId) where T : FlowNode =>
|
|
|
|
|
target.inputPorts.FirstOrDefault(port => port.portId.Equals(portId));
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the output port with the given port id.
|
|
|
|
|
/// If one is not found, this method returns null
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="portId"> Port id to search for </param>
|
|
|
|
|
public static FlowPort GetOutputPortFromId<T>(this T target, string portId) where T : FlowNode =>
|
|
|
|
|
target.outputPorts.FirstOrDefault(port => port.portId.Equals(portId));
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the port with the given port id. It can be either an input or an output port.
|
|
|
|
|
/// If one is not found, this method returns null
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="portId"> Port id to search for </param>
|
|
|
|
|
public static FlowPort GetPortFromId<T>(this T target, string portId) where T : FlowNode =>
|
|
|
|
|
target.ports.FirstOrDefault(port => port.portId.Equals(portId));
|
|
|
|
|
|
|
|
|
|
/// <summary> True if this node has at least one connected port </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
public static bool IsConnected<T>(this T target) where T : FlowNode =>
|
|
|
|
|
target.inputPorts.Any(p => p.isConnected) || target.outputPorts.Any(p => p.isConnected);
|
|
|
|
|
|
|
|
|
|
/// <summary> True if this node is connected to a node that has a port with the given port id </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="portId"> Port id to search for </param>
|
|
|
|
|
public static bool IsConnectedToPort<T>(this T target, string portId) where T : FlowNode =>
|
|
|
|
|
target.inputPorts.Any(port => port.IsConnectedToPort(portId)) || target.outputPorts.Any(port => port.IsConnectedToPort(portId));
|
|
|
|
|
|
|
|
|
|
/// <summary> True if the given port can be deleted from this node </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="portId"> Port id to search for </param>
|
|
|
|
|
public static bool CanDeletePort<T>(this T target, string portId) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
FlowPort port = target.GetPortFromId(portId);
|
|
|
|
|
if (port == null) return false; //port node found -> false
|
|
|
|
|
if (!port.canBeDeleted) return false; //port marked as do not delete -> false
|
|
|
|
|
switch (port.direction)
|
|
|
|
|
{
|
|
|
|
|
case PortDirection.Input:
|
|
|
|
|
if (target.inputPorts.Count <= target.minNumberOfInputPorts) return false; //deleting the port would invalidate the min number of ports -> false
|
|
|
|
|
break;
|
|
|
|
|
case PortDirection.Output:
|
|
|
|
|
if (target.outputPorts.Count <= target.minNumberOfOutputPorts) return false; //deleting the port would invalidate the min number of ports -> false
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> True if the port was deleted from this node </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="portId"> Port id to search for </param>
|
|
|
|
|
public static bool DeletePort<T>(this T target, string portId) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
if (!target.CanDeletePort(portId)) return false;
|
|
|
|
|
FlowPort port = target.GetPortFromId(portId);
|
|
|
|
|
switch (port.direction)
|
|
|
|
|
{
|
|
|
|
|
case PortDirection.Input:
|
|
|
|
|
target.inputPorts.Remove(port);
|
|
|
|
|
break;
|
|
|
|
|
case PortDirection.Output:
|
|
|
|
|
target.outputPorts.Remove(port);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Set the flow graph for this node (also updates the node's flowGraphId) </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="flowGraph"> Target flow graph </param>
|
|
|
|
|
public static T SetFlowGraph<T>(this T target, FlowGraph flowGraph) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
target.flowGraph = flowGraph;
|
|
|
|
|
target.flowGraphId = flowGraph != null ? flowGraph.id : string.Empty;
|
|
|
|
|
target.ports.ForEach(port => port.node = target);
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Set a new name for this node </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="nodeName"> New node name </param>
|
|
|
|
|
public static T SetNodeName<T>(this T target, string nodeName) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
target.nodeName = nodeName;
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Set a new description for this node </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="nodeDescription"> New node description </param>
|
|
|
|
|
public static T SetNodeDescription<T>(this T target, string nodeDescription) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
target.nodeDescription = nodeDescription;
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> [Editor] Set the position of the node in the graph </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="position"> Position in graph </param>
|
|
|
|
|
public static T SetPosition<T>(this T target, Vector2 position) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
target.position = position;
|
|
|
|
|
return target;
|
|
|
|
|
}
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> [Editor] Ping node </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
/// <param name="flowDirection"> Flow direction (back flow is when returning to the previous node) (</param>
|
|
|
|
|
public static T Ping<T>(this T target, FlowDirection flowDirection) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
target.ping?.Invoke(flowDirection);
|
|
|
|
|
return target;
|
|
|
|
|
}
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> [Editor] Refresh node's editor </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
public static T RefreshNodeEditor<T>(this T target) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
target.refreshNodeEditor?.Invoke();
|
|
|
|
|
return target;
|
|
|
|
|
}
|
2023-12-05 06:20:20 +00:00
|
|
|
|
|
2023-08-02 06:08:03 +00:00
|
|
|
|
/// <summary> [Editor] Refresh node's view </summary>
|
|
|
|
|
/// <param name="target"> Target node </param>
|
|
|
|
|
public static T RefreshNodeView<T>(this T target) where T : FlowNode
|
|
|
|
|
{
|
|
|
|
|
target.refreshNodeView?.Invoke();
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|