/// <summary> /// Draws in the title area of the window /// </summary> public override void DrawTitle() { if (Editor.Canvas.SelectedNode != null && Editor.Canvas.SelectedNode.Content != null) { string lTitle = ""; SpellAction lAction = Editor.Canvas.SelectedNode.Content as SpellAction; if (lAction != null) { lTitle = lAction.Name; } if (lTitle.Length == 0) { lTitle = BaseNameAttribute.GetName(Editor.Canvas.SelectedNode.Content.GetType()); } GUI.Label(new Rect(12f, 8f, Position.width - 12f, 20f), lTitle, PanelTitleStyle); if (GUI.Button(new Rect(Position.width - 31f, 10f, 16f, 16f), new GUIContent(" ", "Remove Spell Action"), NodeEditorStyle.ButtonX)) { Editor.Canvas.SelectedNode.DestroyContent(); } } else { GUI.Label(new Rect(12f, 8f, Position.width - 12f, 20f), Title, PanelTitleStyle); } }
/// <summary> /// Simple test to determine if the link can be traversed /// </summary> /// <param name="rUserData">Optional data to help with the test</param> /// <returns>Determines if the link can be traversed</returns> public override bool TestActivate(object rUserData = null) { if (_Link == null || _Link.StartNode == null || _Link.StartNode._Content == null) { return(false); } SpellAction lSpellAction = _Link.StartNode._Content as SpellAction; if (lSpellAction == null) { return(false); } if (lSpellAction._Spell == null) { return(false); } if (lSpellAction._Spell.State == State) { return(true); } return(false); }
/// <summary> /// Deactivate the node contents and remove the node from the active list /// </summary> /// <param name="rNode">Node to activate</param> public void DeactivateNode(Node rNode) { if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].DeactivateNode() - Node: {1}", Name, rNode.Content.GetType().Name)); } // If we're dealing with a spell action, activate SpellAction lAction = rNode.Content as SpellAction; if (lAction != null) { if (lAction.State == EnumSpellActionState.ACTIVE) { lAction.Deactivate(); } } // Remove the node from the active list mActiveNodes.Remove(rNode); // Add the action to the shuttind down list. This way it can // run and we can remove it when needed if (lAction.IsShuttingDown) { if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].DeactivateNode() - Added to expiring nodes: {1}", Name, rNode.Content.GetType().Name)); } mExpiringActions.Add(lAction); } // Since we are instantiating the nodes, we now destroy them else // if (!rNode.IsImmediate) { if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].DeactivateNode() - Destroyed: {1}", Name, rNode.Content.GetType().Name)); } ScriptableObject.Destroy(rNode.Content); ScriptableObject.Destroy(rNode); } }
/// <summary> /// Draws in the content area of the window /// </summary> public override void DrawContent() { if (Editor.Canvas.SelectedNode == null) { return; } bool lIsDirty = false; if (Editor.Canvas.SelectedNode.Content != null) { string lDescription = BaseDescriptionAttribute.GetDescription(Editor.Canvas.SelectedNode.Content.GetType()); if (lDescription != null && lDescription.Length > 0) { NodeEditorStyle.DrawInspectorDescription(lDescription, MessageType.None); } } GUILayout.BeginHorizontal(); if (EditorHelper.BoolField("Is Start Node", "Determines if the node is used as a starting node.", Editor.Canvas.SelectedNode.IsStartNode, Editor.RootAsset)) { lIsDirty = true; Editor.Canvas.SelectedNode.IsStartNode = EditorHelper.FieldBoolValue; Spell lSpell = ((SpellEditor)Editor).Spell; if (lSpell != null) { if (lSpell.StartNodes == null) { lSpell.StartNodes = new System.Collections.Generic.List <Node>(); } if (lSpell.StartNodes.Contains(Editor.Canvas.SelectedNode)) { if (!Editor.Canvas.SelectedNode.IsStartNode) { lSpell.StartNodes.Remove(Editor.Canvas.SelectedNode); UnityEditor.EditorUtility.SetDirty(lSpell); } } else if (Editor.Canvas.SelectedNode.IsStartNode) { lSpell.StartNodes.Add(Editor.Canvas.SelectedNode); UnityEditor.EditorUtility.SetDirty(lSpell); } } } EditorHelper.LabelField("End Node", "Determines if the node is used as an ending node.", 60f); if (EditorHelper.BoolField(Editor.Canvas.SelectedNode.IsEndNode, "End Node", Editor.RootAsset, 16f)) { lIsDirty = true; Editor.Canvas.SelectedNode.IsEndNode = EditorHelper.FieldBoolValue; Spell lSpell = ((SpellEditor)Editor).Spell; if (lSpell != null) { if (lSpell.EndNodes == null) { lSpell.EndNodes = new System.Collections.Generic.List <Node>(); } if (lSpell.EndNodes.Contains(Editor.Canvas.SelectedNode)) { if (!Editor.Canvas.SelectedNode.IsEndNode) { lSpell.EndNodes.Remove(Editor.Canvas.SelectedNode); UnityEditor.EditorUtility.SetDirty(lSpell); } } else if (Editor.Canvas.SelectedNode.IsEndNode) { lSpell.EndNodes.Add(Editor.Canvas.SelectedNode); UnityEditor.EditorUtility.SetDirty(lSpell); } } } GUILayout.EndHorizontal(); if (Editor.Canvas.SelectedNode.Content == null) { EditorGUILayout.HelpBox(" Select an action for this node to take. Actions will be used to drive how the spell works.", MessageType.None); if (GUILayout.Button(new GUIContent("Select"))) { TypeSelectWindow lWindow = EditorWindow.GetWindow(typeof(TypeSelectWindow), false, "Select", true) as TypeSelectWindow; lWindow.BaseType = typeof(SpellAction); lWindow.SelectedEvent = OnTypeSelected; } } else { SpellAction lAction = Editor.Canvas.SelectedNode.Content as SpellAction; if (lAction != null) { bool lIsActionDirty = lAction.OnInspectorGUI(Editor.RootAsset); if (lIsActionDirty) { lIsDirty = true; } } } // If the node is dirty, we need to report it if (lIsDirty) { UnityEditor.EditorUtility.SetDirty(Editor.Canvas.SelectedNode); if (Editor.Canvas.SelectedNode._Content != null) { UnityEditor.EditorUtility.SetDirty(Editor.Canvas.SelectedNode._Content); } Editor.SetDirty(); } }
/// <summary> /// Activate the node contents and flag the node as working /// </summary> /// <param name="rNode">Node to activate</param> public void ActivateNode(Node rNode, object rData = null) { Node lNode = rNode; // If the node isn't immediate, we'll create an instance of it so we can // loop over and over as needed. This instance is a shallow copy. So, we're not // creating new instances of children. //if (!lNode.IsImmediate) { // Create an instance of the node. lNode = ScriptableObject.Instantiate <Node>(rNode); lNode.ID = rNode.ID; // Tell all the node links that this is the start node. We shallow copy them // as well so the StartNode can be different each instance for (int i = 0; i < lNode.Links.Count; i++) { NodeLink lLink = ScriptableObject.Instantiate <NodeLink>(lNode.Links[i]); lLink.StartNode = lNode; // Creates instances for each of the actions if (lLink.Actions != null && lLink.Actions.Count > 0) { for (int j = 0; j < lLink.Actions.Count; j++) { NodeLinkAction lLinkAction = ScriptableObject.Instantiate <NodeLinkAction>(lLink.Actions[j]); lLinkAction._Link = lLink; lLink.Actions[j] = lLinkAction; } } lNode.Links[i] = lLink; } // We do want a deep copy of the content. lNode.Content = ScriptableObjectPool.DeepCopy(lNode.Content, true); if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].ActivateNode() - Instance created for", Name, lNode.Content.GetType().Name)); } } // If we're dealing with a spell action, activate SpellAction lAction = lNode.Content as SpellAction; if (lAction != null) { lNode.State = EnumNodeState.WORKING; lAction.Spell = this; lAction.Node = lNode; lAction.Activate(0, rData); if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].ActivateNode() - Activated: {1}", Name, lAction.GetType().Name)); } // If the action takes time, add it to your queue if (lNode.State == EnumNodeState.WORKING) { if (!mActiveNodes.Contains(lNode)) { mActiveNodes.Add(lNode); if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].ActivateNode() - Added to active nodes: {1}", Name, lAction.GetType().Name)); } } } // if it's an instant action, we want to move to the next node else { if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].ActivateNode() - Testing links: {1}", Name, lAction.GetType().Name)); } // Process the node links to see if they should be activated for (int i = 0; i < lNode.Links.Count; i++) { bool lActivate = lNode.Links[i].TestActivate(); if (lActivate) { ActivateLink(lNode.Links[i], lNode.Data); } } } } // We may still have links that need to process else { lNode.State = EnumNodeState.SUCCEEDED; if (ShowDebug) { Utilities.Debug.Log.FileWrite(string.Format("Spell[{0}].ActivateNode() - No action, testing links: {1}", Name, lAction.GetType().Name)); } // Process the node links to see if they should be activated for (int i = 0; i < lNode.Links.Count; i++) { bool lActivate = lNode.Links[i].TestActivate(); if (lActivate) { ActivateLink(lNode.Links[i], lNode.Data); } } } }
/// <summary> /// Run as needed to process the "instance" data. This spell itself should only /// contain template level data. /// </summary> /// <param name="rDeltaTime">Time since the last frame update</param> /// <param name="rData">Instance level data for the spell.</param> public virtual void Update() { int lActiveNodes = mActiveNodes.Count; // Allow any expiring actions to finish shutting down. Then, // remove them when they are done. for (int i = mExpiringActions.Count - 1; i >= 0; i--) { SpellAction lAction = mExpiringActions[i]; lAction.Update(); if (!lAction.IsShuttingDown) { mExpiringActions.RemoveAt(i); // Since the node was probably listening for the action // to finish, now we can stop //if (!lAction.Node.IsImmediate) { Node lNode = lAction.Node; ScriptableObject.Destroy(lNode.Content); ScriptableObject.Destroy(lNode); } } } // Go through all the active nodes and update. Since we // may append new nodes, we don't want to go past the original // active node count for (int i = 0; i < lActiveNodes; i++) { Node lNode = mActiveNodes[i]; // Process the node content SpellAction lAction = lNode.Content as SpellAction; if (lAction != null) { lAction.Update(); } // If we're not shutting down, process the node links to see if they should be activated if (!mIsCancelling) { for (int j = 0; j < lNode.Links.Count; j++) { bool lActivate = lNode.Links[j].TestActivate(); if (lActivate) { ActivateLink(lNode.Links[j], lNode.Data); } } } } // If we've cancelled, clear any active nodes if (mIsCancelling && !mEndNodesLoaded) { mActiveNodes.Clear(); } // Deactivate any active nodes that have completed. This // may simply move them to the expiring queue else { for (int i = mActiveNodes.Count - 1; i >= 0; i--) { Node lNode = mActiveNodes[i]; if (lNode.State == EnumNodeState.SUCCEEDED || lNode.State == EnumNodeState.FAILED) { DeactivateNode(lNode); } } } // Flag the spell as completed if no actions are active if (State != EnumSpellState.READY && mExpiringActions.Count == 0 && mActiveNodes.Count == 0) { // Since no end nodes required, end if (mEndNodesLoaded || EndNodes.Count == 0) { State = EnumSpellState.COMPLETED; } // Start all the end nodes else { for (int i = 0; i < EndNodes.Count; i++) { ActivateNode(EndNodes[i]); } mIsCancelling = false; mEndNodesLoaded = true; } } }