#if UNITY_EDITOR using FIMSpace.FEditor; using UnityEditor; #endif using FIMSpace.Generating.Planner.Nodes; using FIMSpace.Graph; using System; using System.Collections.Generic; using UnityEngine; using FIMSpace.Generating.Rules.QuickSolutions; using FIMSpace.Generating.Checker; namespace FIMSpace.Generating.Planning.PlannerNodes { /// /// It's always sub-asset -> it's never project file asset /// public abstract partial class PlannerRuleBase : PGGPlanner_NodeBase { public static bool Debugging = false; /// Warning! Duplicate is refering to root project planner, In the nodes logics you can use CurrentExecutingPlanner for instance planner reference [HideInInspector] public FieldPlanner ParentPlanner; public FieldPlanner CurrentExecutingPlanner { get { return FieldPlanner.CurrentGraphExecutingPlanner; } } public bool IsGeneratingAsync { get { if (CurrentExecutingPlanner == null) return false; if (CurrentExecutingPlanner.ParentBuildPlanner == null) return false; return CurrentExecutingPlanner.ParentBuildPlanner.AsyncGenerating; } } [HideInInspector] public ScriptableObject ParentNodesContainer; /// By default the ParentNodesContainer is treated as LiteParentContainer, but other implementations like SubGraphs, which are not scriptable objects, are also LiteParentContainer and requires this variable for being detected [NonSerialized] public IPlanNodesContainer LiteParentContainer = null; public string DebuggingInfo { get; protected set; } public Action DebuggingGizmoEvent { get; protected set; } //public virtual string TitleName() { return GetType().Name; } public virtual string Tooltip() { string tooltipHelp = "(" + GetType().Name; return tooltipHelp + ")"; } public override Vector2 NodeSize { get { return new Vector2(232, 90); } } /// PlannerRuleBase by default is true public override bool DrawInputConnector { get { return true; } } public List GetPlannersFromPort(PGGPlannerPort port, bool newListInstance = false, bool callRead = true) { return PGGPlannerPort.GetPlannersFromPort(port, newListInstance, callRead); } /// Getting connected planner, if there is no planner, it will return self planner public FieldPlanner GetPlannerFromPortAlways(PGGPlannerPort port, bool callRead = true) { FieldPlanner planner = PGGPlannerPort.GetPlannerFromPort(port, callRead); if (planner == null) planner = CurrentExecutingPlanner; return planner; } public FieldPlanner GetPlannerFromPort(PGGPlannerPort port, bool callRead = true) { return PGGPlannerPort.GetPlannerFromPort(port, callRead); } public CheckerField3D GetCheckerFromPort(PGGPlannerPort source, bool callRead = true) { return PGGPlannerPort.GetCheckerFromPort(source, callRead); } public FieldCell GetCellFromInputPort(IFGraphPort port, bool callRead = false) { NodePortBase nodePort = port as NodePortBase; if (nodePort == null) return null; if (callRead) nodePort.TriggerReadPort(true); if (nodePort.IsConnected) { if (nodePort.BaseConnection.PortReference is PGGCellPort) { PGGCellPort cellPrt = nodePort.BaseConnection.PortReference as PGGCellPort; if (FGenerators.NotNull(cellPrt.GetInputCellValue)) return cellPrt.GetInputCellValue; } } return null; } public FieldPlanner GetPlannerByID(int plannerId, int duplicateId = -1, int subId = -1) { return PGGPlannerPort.GetPlannerByID(plannerId, duplicateId, subId); } public static FieldPlanner GetFieldPlannerByID(int plannerId, int duplicateId = -1, int subFieldID = -1, bool selfOnUndefined = true) { return PGGPlannerPort.GetFieldPlannerByID(plannerId, duplicateId, subFieldID, selfOnUndefined); } /// [Base Calls OnCustomPrepare] This method can contain preparation code which not need to be async public virtual void PreGeneratePrepare() { OnCustomPrepare(); } /// [Base is empty] .Called by PreGeneratePrepare. For function nodes to refresh some variables when re-executing node. Should be Async friendly. public virtual void OnCustomPrepare() { } /// [Base is not empty] Preparing initial debug message public virtual void Prepare(PlanGenerationPrint print) { #if UNITY_EDITOR DebuggingInfo = "Debug Info not Assigned"; #endif } /// [Base is empty] public virtual void Execute(PlanGenerationPrint print, PlannerResult newResult) { // Node Procedures Code } protected void CallOtherExecution(FGraph_NodeBase otherNode, PlanGenerationPrint print) { if (otherNode == null) return; FieldPlanner callerParent = ParentPlanner; if (callerParent == null) if (ParentNodesContainer is PlannerFunctionNode) callerParent = CurrentExecutingPlanner; if (callerParent == null) { if (print == null) { if (otherNode is PlannerRuleBase) { PlannerRuleBase oPlannerRule = otherNode as PlannerRuleBase; if (oPlannerRule.ParentPlanner == null) { if (MG_ModGraph != null) MG_ModGraph.CallExecution(otherNode as PlannerRuleBase); } else { callerParent.CallExecution(otherNode as PlannerRuleBase, print); } } } return; } if (otherNode is PlannerRuleBase) { callerParent.CallExecution(otherNode as PlannerRuleBase, print); } } protected void CallOtherExecutionWithConnector(int altId, PlanGenerationPrint print) { for (int c = 0; c < OutputConnections.Count; c++) { if (OutputConnections[c].ConnectionFrom_AlternativeID == altId) { CallOtherExecution(OutputConnections[c].GetOther(this), print); } } } public static void EnsurePlannerRulesOwner(List nodes, object owner) { for (int i = 0; i < nodes.Count; i++) { PlannerRuleBase rule = nodes[i] as PlannerRuleBase; if (rule == null) continue; if (owner is ScriptableObject) rule.ParentNodesContainer = owner as ScriptableObject; else if (owner is IPlanNodesContainer) rule.LiteParentContainer = owner as IPlanNodesContainer; } } #region Editor related #if UNITY_EDITOR public virtual void OnGUIModify() { } [HideInInspector] public bool _editor_drawRule = true; protected UnityEditor.SerializedObject inspectorViewSO = null; protected virtual void DrawGUIHeader(int i) { if (inspectorViewSO == null) inspectorViewSO = new UnityEditor.SerializedObject(this); EditorGUILayout.BeginHorizontal(FGUI_Resources.BGInBoxLightStyle, GUILayout.Height(20)); // 1 Enabled = EditorGUILayout.Toggle(Enabled, GUILayout.Width(24)); string foldout = FGUI_Resources.GetFoldSimbol(_editor_drawRule); string tip = Tooltip(); if (GUILayout.Button(new GUIContent(foldout + " " + GetDisplayName() + " " + foldout, tip), FGUI_Resources.HeaderStyle)) { bool rmb = false; if (rmb == false) _editor_drawRule = !_editor_drawRule; } int hh = 18; if (i > 0) if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_ArrowUp), FGUI_Resources.ButtonStyle, GUILayout.Width(18), GUILayout.Height(hh))) { FGenerators.SwapElements(ParentPlanner.FProcedures, i, i - 1); return; } if (i < ParentPlanner.FProcedures.Count - 1) if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_ArrowDown), FGUI_Resources.ButtonStyle, GUILayout.Width(18), GUILayout.Height(hh))) { FGenerators.SwapElements(ParentPlanner.FProcedures, i, i + 1); return; } if (GUILayout.Button("X", FGUI_Resources.ButtonStyle, GUILayout.Width(24), GUILayout.Height(hh))) { ParentPlanner.RemoveRuleFromPlanner(this); return; } EditorGUILayout.EndHorizontal(); // 1 } protected virtual void DrawGUIFooter() { EditorGUILayout.EndVertical(); if (inspectorViewSO.ApplyModifiedProperties()) { OnStartReadingNode(); } } //public void DrawGUIStack(int i) //{ // DrawGUIHeader(i); // Color preColor = GUI.color; // if (inspectorViewSO != null) // if (inspectorViewSO.targetObject != null) // if (_editor_drawRule) // { // EditorGUILayout.BeginVertical(FGUI_Resources.BGInBoxStyle); // if (Enabled == false) GUI.color = new Color(0.9f, 0.9f, 0.9f, 0.7f); // inspectorViewSO.Update(); // DrawGUIBody(); // DrawGUIFooter(); // } // GUI.color = preColor; //} /// /// Returns true if something changed in GUI - using EditorGUI.BeginChangeCheck(); /// //protected virtual void DrawGUIBody(/*int i*/) //{ // UnityEditor.SerializedProperty sp = inspectorViewSO.GetIterator(); // sp.Next(true); // sp.NextVisible(false); // bool can = sp.NextVisible(false); // if (can) // { // do // { // bool cont = false; // if (cont) continue; // UnityEditor.EditorGUILayout.PropertyField(sp); // } // while (sp.NextVisible(false) == true); // } // // EditorGUILayout.EndVertical(); // // so.ApplyModifiedProperties(); // //} //} #endif #endregion #region Mod Graph Related /// Current executing mod graph (for field modification graph) public SR_ModGraph MG_ModGraph { get { return SR_ModGraph.Graph_ModGraph; } } /// Current executing field mod (for field modification graph) public FieldSpawner MG_Spawner { get { return SR_ModGraph.Graph_Spawner; } } /// Current executing field modificator's spawner public FieldModification MG_Mod { get { return SR_ModGraph.Graph_Mod; } } /// Current executing mod spawner's spawn (for field modification graph) public SpawnData MG_Spawn { get { return SR_ModGraph.Graph_SpawnData; } } /// Current executing field setup preset (for field modification graph) public FieldSetup MG_Preset { get { return SR_ModGraph.Graph_Preset; } } /// Current executing field grid cell (for field modification graph) public FieldCell MG_Cell { get { return SR_ModGraph.Graph_Cell; } } /// Current executing field gridd (for field modification graph) public FGenGraph MG_Grid { get { return SR_ModGraph.Graph_Grid; } } public List MG_GridInstructions { get { return SR_ModGraph.Graph_Instructions; } } ///// Current executing field mod (for field modification graph) //public Vector3? Graph_RestrictDir { get { return SR_ModGraph.Graph_Mod; } } public ModificatorsPack MGGetParentPack() { SR_ModGraph owner = ParentNodesContainer as SR_ModGraph; if (owner == null) return null; return owner.TryGetParentModPack(); } public UnityEngine.Object MGGetFieldSetup() { SR_ModGraph owner = ParentNodesContainer as SR_ModGraph; if (owner == null) return null; var fs = owner.TryGetParentFieldSetup(); if (fs == null) return null; //if (fs) //{ // if (fs.InstantiatedOutOf) return fs.InstantiatedOutOf; //} return fs; } protected List MGGetVariables(UnityEngine.Object tgt) { if (tgt == null) return null; FieldSetup fs = tgt as FieldSetup; if (fs) { return fs.Variables; } ModificatorsPack mp = tgt as ModificatorsPack; if (mp) { return mp.Variables; } return null; } protected FieldVariable MGGetVariable(UnityEngine.Object tgt, int index) { var variables = MGGetVariables(tgt); if (variables == null) return null; if (variables.ContainsIndex(index)) return variables[index]; return null; } #endregion #region Basic Utilities protected GUIContent GetTryRefreshGUIC { get { return new GUIContent("error - try refresh", "Try refreshing preview re-open graph or try recompilling scripts."); } } #endregion } }