You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

927 lines
35 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using System;
using MalbersAnimations.Scriptables;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using UnityEditorInternal;
using UnityEditor;
#endif
namespace MalbersAnimations.Controller.AI
{
[CreateAssetMenu(menuName = "Malbers Animations/Pluggable AI/AI State", order = -100, fileName = "New AI State")]
public class MAIState : ScriptableObject
{
[Tooltip("ID of the AI State. This is used on the AI Brain On AIStateChanged Event")]
public IntReference ID = new IntReference();
//[Tooltip("Creates the Decisions and Tasks inside this AI State")]
//public bool internalData = true;
[FormerlySerializedAs("actions")] public MTask[] tasks;
public MAITransition[] transitions;
public Color GizmoStateColor = Color.gray;
[HideInInspector] public bool CreateTaskAsset = true;
[HideInInspector] public bool CreateDecisionAsset = true;
public virtual void Play(MAnimalBrain brain) => brain?.StartNewState(this);
internal void Update_State(MAnimalBrain brain)
{
Update_Tasks(brain);
Update_Transitions(brain);
}
private void Update_Transitions(MAnimalBrain brain)
{
for (int i = 0; i < transitions.Length; i++)
{
if (this != brain.currentState) return; //BUG BUG BUG FIXed
var transition = transitions[i];
var decision = transition.decision;
if (decision == null) return; //Ignore Code
//If the Decision is waiting for a task to be done and the tasks is NOT done, then ignore this decision
if (decision.waitForTask != -1 && !brain.IsTasksDone(decision.waitForTask)) continue;
//If the Decision is waiting for ALL tasks to be done tasks are NOT done then ignore this decision
if (decision.WaitForAllTasks && !brain.AllTasksDone()) continue;
if (decision.interval > 0)
{
if (brain.CheckIfDecisionsCountDownElapsed(decision.interval, i))
{
brain.ResetDecisionTime(i);
Decide(brain, i, transition);
}
}
else
{
Decide(brain, i, transition);
}
}
}
private void Decide(MAnimalBrain brain, int Index, MAITransition transition)
{
if (!transition.decision.active) return; //Skip if the Decision is not active
bool decisionSucceded = brain.DecisionResult[Index] = transition.decision.Decide(brain, Index);
// Debug.Log($"{brain.Animal.name} decisionSucceded "+ decisionSucceded);
brain.TransitionToState(decisionSucceded ? transition.trueState : transition.falseState, decisionSucceded,
transition.decision, Index);
}
/// <summary>When a new State starts this method is called for each Tasks</summary>
internal void Start_AIState(MAnimalBrain brain)
{
for (int i = 0; i < tasks.Length; i++) StartTaks(brain, i);
}
internal void StartTaks(MAnimalBrain brain, int i)
{
if (tasks[i] == null)
{
Debug.LogError($"The {name} AI State has an Empty Task. Please check all your AI States Tasks. {brain.Animal.name} brain is Disabled", this);
brain.enabled = false;
return;
};
if (brain.TasksStarted[i]) return; //DO NOT START AN ALREADY STARTED TASK
if (i == 0 || !tasks[i].WaitForPreviousTask)
{
// Debug.Log($"<B>{brain.Animal.name}:</B> Start Task: [{name}] [{i}]-[{tasks[i].name }]");
//Prepare the Task
brain.TasksStarted[i] = true;
brain.SetTaskStartTime(i);
tasks[i].StartTask(brain, i); //Start the Task after it has being prepared.
if (tasks[i].MessageID != 0)
brain.OnTaskStarted.Invoke(tasks[i].MessageID); //Send Events after the Task has started
}
}
internal void StartWaitforPreviusTask(MAnimalBrain brain, int i)
{
if (tasks[i] == null)
{
Debug.LogError($"The {name} AI State has an Empty Task. Please check all your AI States Tasks. {brain.Animal.name} brain is Disabled", this);
brain.enabled = false;
return;
};
if (brain.TasksStarted[i]) return; //DO NOT START AN ALREADY STARTED TASK
if (tasks[i].WaitForPreviousTask)
{
//Prepare the Task
brain.TasksStarted[i] = true;
brain.SetTaskStartTime(i);
tasks[i].StartTask(brain, i); //Start the Task after it has being prepared.
if (tasks[i].MessageID != 0)
brain.OnTaskStarted.Invoke(tasks[i].MessageID); //Send Events after the Task has started
}
}
/// <summary>When a new State starts this method is called for each Decisions</summary>
internal void Prepare_Decisions(MAnimalBrain brain)
{
if (transitions != null)
for (int i = 0; i < transitions.Length; i++)
{
transitions[i].decision.PrepareDecision(brain, i);
}
}
internal void Update_Tasks(MAnimalBrain brain)
{
for (int i = 0; i < tasks.Length; i++)
{
if (brain.TasksStarted[i] && !brain.TasksDone[i] && tasks[i].active)
{
tasks[i].InternalUpdateTask(brain, i);
}
}
}
internal void Finish_Tasks(MAnimalBrain brain)
{
for (int i = 0; i < tasks.Length; i++)
if (tasks[i].active) tasks[i].ExitAIState(brain, i);
}
#region Target Event Listeners for Tasks
/// <summary> Notify the Tasks that the Animal has arrived to the Current assigned target</summary>
internal void OnTargetArrived(MAnimalBrain brain, Transform target)
{
for (int i = 0; i < tasks.Length; i++)
{
if (tasks[i] != null) tasks[i].OnTargetArrived(brain, target, i);
}
}
#endregion
#region Self Animal Listen Events
internal void OnAnimalStateEnter(MAnimalBrain brain, State state)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i]?.OnAnimalStateEnter(brain, state, i);
}
internal void OnAnimalStateExit(MAnimalBrain brain, State state)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i]?.OnAnimalStateExit(brain, state, i);
}
internal void OnAnimalStanceChange(MAnimalBrain brain, int Stance)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i]?.OnAnimalStanceChange(brain, Stance, i);
}
internal void OnAnimalModeStart(MAnimalBrain brain, Mode mode)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i]?.OnAnimalModeStart(brain, mode, i);
}
internal void OnAnimalModeEnd(MAnimalBrain brain, Mode mode)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i]?.OnAnimalModeEnd(brain, mode, i);
}
#endregion
#region Target Animal Listen Events
public void OnTargetAnimalStateEnter(MAnimalBrain brain, State state)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i].OnTargetAnimalStateEnter(brain, state, i);
}
public void OnTargetAnimalStateExit(MAnimalBrain brain, State state)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i].OnTargetAnimalStateExit(brain, state, i);
}
public void OnTargetAnimalStanceChange(MAnimalBrain brain, int Stance)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i].OnTargetAnimalStanceChange(brain, Stance, i);
}
public void OnTargetAnimalModeStart(MAnimalBrain brain, Mode mode)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i].OnTargetAnimalModeStart(brain, mode, i);
}
public void OnTargetAnimalModeEnd(MAnimalBrain brain, Mode mode)
{
for (int i = 0; i < tasks.Length; i++)
tasks[i].OnTargetAnimalModeEnd(brain, mode, i);
}
#endregion
}
[System.Serializable]
public class MAITransition
{
public MAIDecision decision;
public MAIState trueState;
public MAIState falseState;
}
#if UNITY_EDITOR
[CustomEditor(typeof(MAIState))]
public class MAIStateEditor : Editor
{
MAIState m;
private SerializedProperty tasks, transitions, GizmoStateColor, ID;
private ReorderableList Reo_List_Tasks, Reo_List_Transitions;
private List<Type> TasksType;
private List<Type> DecisionType;
private GUIContent plus;
private void OnEnable()
{
if (plus == null) plus = EditorGUIUtility.IconContent("d_Toolbar Plus");
m = (MAIState)target;
tasks = serializedObject.FindProperty("tasks");
transitions = serializedObject.FindProperty("transitions");
GizmoStateColor = serializedObject.FindProperty("GizmoStateColor");
ID = serializedObject.FindProperty("ID");
GizmoStateColor = serializedObject.FindProperty("GizmoStateColor");
TasksType = MTools.GetAllTypes<MTask>();
DecisionType = MTools.GetAllTypes<MAIDecision>();
TasksList();
TransitionList();
}
private void OnAddCallback_Task(ReorderableList list)
{
var addMenu = new GenericMenu();
ResizeTasks();
for (int i = 0; i < TasksType.Count; i++)
{
Type st = TasksType[i];
//Fast Ugly get the name of the Asset thing
MTask t = (MTask)CreateInstance(st);
var name = t.DisplayName;
DestroyImmediate(t);
//var Rname = Regex.Replace(st.Name, @"([a-z])([A-Z])", "$1 $2");
addMenu.AddItem(new GUIContent(name), false, () => AddTask(st, m.tasks.Length - 1));
}
addMenu.AddSeparator("");
addMenu.AddItem(new GUIContent("Empty"), false, () => { });
addMenu.ShowAsContext();
}
private void AddTask(Type NewTask, int index)
{
MTask task = (MTask)CreateInstance(NewTask);
task.hideFlags = HideFlags.None;
task.name = "T_" + NewTask.Name;
AssetDatabase.AddObjectToAsset(task, AssetDatabase.GetAssetPath(target));
m.tasks[index] = task; //the other way was not working
Reo_List_Tasks.index = index;
Reo_List_Transitions.index = -1;
EditorUtility.SetDirty(task);
EditorUtility.SetDirty(target);
serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
}
private void TasksList()
{
Reo_List_Tasks = new ReorderableList(serializedObject, tasks, true, true, true, true)
{
onSelectCallback = (index) =>
{
Reo_List_Transitions.index = -1; //Do not select the Tasks when selecting the transitions
},
drawElementCallback = (rect, index, isActive, isFocused) =>
{
var element = tasks.GetArrayElementAtIndex(index);
var indexR = 36;
var r = new Rect(rect) { x = rect.x + indexR, y = rect.y + 2, height = EditorGUIUtility.singleLineHeight, width = rect.width - indexR };
var indRect = new Rect(rect) { y = rect.y + 2, height = EditorGUIUtility.singleLineHeight, width = indexR };
bool isWaiting = element.objectReferenceValue != null && (element.objectReferenceValue as MTask).WaitForPreviousTask;
if (isWaiting) r.width -= 20;
EditorGUI.LabelField(indRect, $"[{index}]");
string Path = AssetDatabase.GetAssetPath(element.objectReferenceValue);
var dC = GUI.contentColor;
if (element.objectReferenceValue != null & Path != AssetDatabase.GetAssetPath(target)) //mean it was created inside the AI STATE
{
GUI.contentColor = new Color(0.7f,0.8f,1);
}
var activeRect = new Rect(rect);
activeRect.width -= 20;
activeRect.x += 20;
if (element.objectReferenceValue != null)
{
MTask task = element.objectReferenceValue as MTask;
using (var cc = new EditorGUI.ChangeCheckScope())
{
task.active = EditorGUI.Toggle(activeRect, GUIContent.none, task.active);
if (cc.changed)
{
Reo_List_Transitions.index = -1;
element.serializedObject.ApplyModifiedProperties();
}
}
}
else
{
EditorGUI.Toggle(activeRect, GUIContent.none, false);
}
EditorGUI.PropertyField(r, element, GUIContent.none);
GUI.contentColor = dC;
var st = new GUIStyle(EditorStyles.whiteLabel)
{
fontSize = 18
};
st.normal.textColor = Color.green;
if (isWaiting)
{
var waitinR = new Rect(r)
{
width = 20,
height = r.height + 2,
y = r.y - 2,
x = rect.width + 25
};
EditorGUI.LabelField(waitinR, new GUIContent("⤴", "This task is waiting for the previous task to finish "), st);
}
},
drawHeaderCallback = rect =>
{
var r = new Rect(rect);
var ColorRect = new Rect(rect)
{
x = rect.width - 40,
width = 60,
height = rect.height - 2,
y = rect.y + 1,
};
EditorGUI.LabelField(rect, new GUIContent(" Tasks", "Tasks for the state"));
EditorGUI.PropertyField(ColorRect, GizmoStateColor, GUIContent.none);
},
onAddCallback = list => OnAddCallback_Task(list),
onRemoveCallback = list =>
{
var task = tasks.GetArrayElementAtIndex(list.index);
if (task != null && task.objectReferenceValue != null)
{
if (EditorUtility.DisplayDialog("Remove Task", "Deleting a Task cannot be undone. Are you sure you want to delete it?", "Yes", "No"))
{
string Path = AssetDatabase.GetAssetPath(task.objectReferenceValue);
if (Path == AssetDatabase.GetAssetPath(target)) //mean it was created inside the AI STATE
{
DestroyImmediate(task.objectReferenceValue, true); //Delete the internal asset!
task.objectReferenceValue = null;
tasks.DeleteArrayElementAtIndex(list.index); //Double Hack
AssetDatabase.SaveAssets();
}
else // is an Outside Element
{
tasks.DeleteArrayElementAtIndex(list.index);
}
}
}
else
{
tasks.DeleteArrayElementAtIndex(list.index);
}
tasks.serializedObject.ApplyModifiedProperties();
//Reset the selection on Remove
Reo_List_Transitions.index = -1;
Reo_List_Tasks.index = -1;
EditorUtility.SetDirty(target);
GUIUtility.ExitGUI();
}
};
}
private void TransitionList()
{
Reo_List_Transitions = new ReorderableList(serializedObject, transitions, true, true, true, true)
{
onSelectCallback = (index) =>
{
Reo_List_Tasks.index = -1; //Do not select the Tasks when selecting the transitions
},
drawElementCallback = (rect, index, isActive, isFocused) =>
{
var r1 = new Rect(rect) { y = rect.y + 2, height = EditorGUIUtility.singleLineHeight };
var r2 = new Rect(r1);
var r3 = new Rect(r2);
bool empty = false;
var element = transitions.GetArrayElementAtIndex(index);
var decision = element.FindPropertyRelative("decision");
var TrueState = element.FindPropertyRelative("trueState");
var FalseState = element.FindPropertyRelative("falseState");
string Path = AssetDatabase.GetAssetPath(decision.objectReferenceValue);
var dC = GUI.contentColor;
if (decision.objectReferenceValue != null && Path != AssetDatabase.GetAssetPath(target)) //mean it was created inside the AI STATE
{
GUI.contentColor = Color.yellow+Color.gray;
}
var activeRect = new Rect(r1);
activeRect.width = 25;
activeRect.x += 20;
if (decision.objectReferenceValue != null)
{
MAIDecision des = decision.objectReferenceValue as MAIDecision;
using (var cc = new EditorGUI.ChangeCheckScope())
{
des.active = EditorGUI.Toggle(activeRect, GUIContent.none, des.active);
if (cc.changed)
{
Reo_List_Tasks.index = -1;
element.serializedObject.ApplyModifiedProperties();
}
}
}
else
{
EditorGUI.Toggle(activeRect, GUIContent.none, false);
}
EditorGUI.PropertyField(r1, decision,
new GUIContent("[" + index + "] Decision",
"If the Decision is true it will go to the True State, else it will go to the False state"));
GUI.contentColor = dC;
empty = TrueState.objectReferenceValue == null;
if (empty) r2.width -= 28;
r2.y += EditorGUIUtility.singleLineHeight + 3;
EditorGUI.PropertyField(r2, TrueState,
new GUIContent("True" + (empty ? " (Do Nothing)" : ""),
"If the Decision is TRUE, It will execute this state. if is Empty, it will do nothing"));
if (empty)
{
var AddButtonRect = new Rect(r2)
{ x = rect.width + 18, width = 20, height = EditorGUIUtility.singleLineHeight };
if (GUI.Button(AddButtonRect, plus, EditorStyles.helpBox))
MTools.CreateScriptableAsset(TrueState, typeof(MAIState),
MTools.GetSelectedPathOrFallback());
}
empty = FalseState.objectReferenceValue == null;
if (empty) r3.width -= 28;
r3.y += (EditorGUIUtility.singleLineHeight + 3) * 2;
EditorGUI.PropertyField(r3, FalseState,
new GUIContent("False" + (empty ? " (Do Nothing)" : ""),
"If the Decision is FALSE, It will execute this state. if is Empty, it will do nothing"));
if (empty)
{
var AddButtonRect = new Rect(r3)
{ x = rect.width + 18, width = 20, height = EditorGUIUtility.singleLineHeight };
if (GUI.Button(AddButtonRect, plus, EditorStyles.helpBox))
MTools.CreateScriptableAsset(FalseState, typeof(MAIState),
MTools.GetSelectedPathOrFallback());
}
},
drawHeaderCallback = rect =>
EditorGUI.LabelField(rect, new GUIContent(" Decisions", "Transitions for other States")),
onAddCallback = list => OnAddCallback_Decision(),
onRemoveCallback = list =>
{
var decision = m.transitions[list.index].decision;
if (decision != null)
{
if (EditorUtility.DisplayDialog("Remove Decision", "Deleting a Decision cannot be undone. Are you sure you want to delete it?", "Yes", "No"))
{
string Path = AssetDatabase.GetAssetPath(decision);
if (Path == AssetDatabase.GetAssetPath(target)) //mean it was created inside the AI STATE
{
DestroyImmediate(decision, true); //Delete the internal asset!
m.transitions[list.index].decision = null;
transitions.DeleteArrayElementAtIndex(list.index);
transitions.serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
}
else
{
transitions.DeleteArrayElementAtIndex(list.index);
transitions.serializedObject.ApplyModifiedProperties();
list.index = -1;
}
}
}
else
{
transitions.DeleteArrayElementAtIndex(list.index);
transitions.serializedObject.ApplyModifiedProperties();
}
//Reset the selection on Remove
Reo_List_Transitions.index = -1;
Reo_List_Tasks.index = -1;
EditorUtility.SetDirty(target);
GUIUtility.ExitGUI();
},
elementHeightCallback = (index) =>
{
var DefaultHeight = EditorGUIUtility.singleLineHeight + 5;
return DefaultHeight * 3;
},
};
}
private void OnAddCallback_Decision()
{
var addMenu = new GenericMenu();
ResizeTransitions();
for (int i = 0; i < DecisionType.Count; i++)
{
Type st = DecisionType[i];
//Fast Ugly get the name of the Asset thing
MAIDecision t = (MAIDecision)CreateInstance(st);
var Rname = t.DisplayName;
DestroyImmediate(t);
//var Rname = Regex.Replace(st.Name, @"([a-z])([A-Z])", "$1 $2");
addMenu.AddItem(new GUIContent(Rname), false, () => AddDecision(st, m.transitions.Length - 1));
}
addMenu.AddSeparator("");
addMenu.AddItem(new GUIContent("Empty"), false, () => { });
addMenu.ShowAsContext();
}
private void AddDecision(Type desicion, int index)
{
MAIDecision des = (MAIDecision)CreateInstance(desicion);
des.hideFlags = HideFlags.None;
des.name = "D_" + desicion.Name;
AssetDatabase.AddObjectToAsset(des, AssetDatabase.GetAssetPath(target));
AssetDatabase.SaveAssets();
m.transitions[index].decision = des; //the other way was not working
EditorUtility.SetDirty(des);
EditorUtility.SetDirty(target);
transitions.serializedObject.ApplyModifiedProperties();
serializedObject.ApplyModifiedProperties();
Reo_List_Transitions.index = index;
Reo_List_Tasks.index = -1;
}
private void ResizeTasks()
{
if (m.tasks == null)
{
m.tasks = new MTask[1];
}
else
{
Array.Resize<MTask>(ref m.tasks, m.tasks.Length + 1); //Hack for non UNITY OBJECTS ARRAYS
}
EditorUtility.SetDirty(target);
}
private void ResizeTransitions()
{
if (m.transitions == null)
{
m.transitions = new MAITransition[1];
}
else
{
Array.Resize<MAITransition>(ref m.transitions,
m.transitions.Length + 1); //Hack for non UNITY OBJECTS ARRAYS
}
EditorUtility.SetDirty(target);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.LabelField(target.name, EditorStyles.boldLabel);
EditorGUILayout.PropertyField(ID);
//EditorGUILayout.PropertyField(internalData);
EditorGUILayout.Space();
if (m.tasks != null)
foreach (var t in m.tasks)
{
if (t == null)
{
EditorGUILayout.HelpBox("The Brain cannot contain empty Tasks. Set the missing tasks", MessageType.Error);
break;
}
}
Reo_List_Tasks.DoLayoutList();
// if (Reo_List_Tasks.index != -1) Reo_List_Transitions.index = -1;
if (m.transitions != null)
foreach (var t in m.transitions)
{
if (t.decision == null)
{
EditorGUILayout.HelpBox("The Brain cannot contain empty Decisions. Set the missing decisions", MessageType.Error);
break;
}
}
Reo_List_Transitions.DoLayoutList();
//if (Reo_List_Transitions.index != -1) Reo_List_Tasks.index = -1;
var indexTasks = Reo_List_Tasks.index;
var indexTrans = Reo_List_Transitions.index;
if (indexTasks != -1)
{
var element = tasks.GetArrayElementAtIndex(indexTasks);
if (element != null && element.objectReferenceValue != null)
{
var asset = element.objectReferenceValue;
using (new GUILayout.VerticalScope(EditorStyles.helpBox))
{
EditorGUILayout.LabelField("Task: " + asset.name, EditorStyles.boldLabel);
string Path = AssetDatabase.GetAssetPath(element.objectReferenceValue);
if (Path != AssetDatabase.GetAssetPath(target)) //mean it was created inside the AI STATE
{
EditorGUILayout.HelpBox("This Task is shared with other AI States", MessageType.Info);
}
using (new GUILayout.HorizontalScope())
{
asset.name = EditorGUILayout.TextField("Name", asset.name);
element.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(asset);
if (GUILayout.Button(new GUIContent("R", "Update the Asset name"), GUILayout.Width(20)))
{
string taskPath = AssetDatabase.GetAssetPath(asset);
string targetPath = AssetDatabase.GetAssetPath(target);
// Check if the asset itself is external or internal to the target
if (taskPath != targetPath)
AssetDatabase.RenameAsset(taskPath, asset.name);
AssetDatabase.SaveAssets();
EditorGUIUtility.PingObject(asset); //Final way of changing the name of the asset... dirty but it works
}
if (GUILayout.Button(new GUIContent("E", "Extract the task into its own file"),
GUILayout.Width(20)))
{
ExtractTaskFromList(asset, element, indexTasks);
GUIUtility.ExitGUI();
}
}
MTools.DrawObjectReferenceInspector(element);
}
}
}
if (indexTrans != -1)
{
var element = transitions.GetArrayElementAtIndex(indexTrans);
var decision = element.FindPropertyRelative("decision");
if (decision != null && decision.objectReferenceValue != null)
{
using (new GUILayout.VerticalScope(EditorStyles.helpBox))
{
EditorGUILayout.LabelField("Decision: " + decision.objectReferenceValue.name, EditorStyles.boldLabel);
string Path = AssetDatabase.GetAssetPath(decision.objectReferenceValue);
if (Path != AssetDatabase.GetAssetPath(target)) //mean it was created inside the AI STATE
{
EditorGUILayout.HelpBox("This Decision is shared with other AI States", MessageType.Info);
}
var asset = decision.objectReferenceValue;
using (new GUILayout.HorizontalScope())
{
asset.name = EditorGUILayout.TextField("Name", asset.name);
decision.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(asset);
if (GUILayout.Button(new GUIContent("R", "Update the Asset name"), GUILayout.Width(20)))
{
string taskPath = AssetDatabase.GetAssetPath(asset);
string targetPath = AssetDatabase.GetAssetPath(target);
// Check if the asset itself is external or internal to the target
if (taskPath != targetPath)
AssetDatabase.RenameAsset(taskPath, asset.name);
AssetDatabase.SaveAssets();
EditorGUIUtility
.PingObject(
asset); //Final way of changing the name of the asset... dirty but it works
}
if (GUILayout.Button(new GUIContent("E", "Extract the decision into its own file"),
GUILayout.Width(20)))
{
ExtractDecisionFromList(asset, element, indexTrans);
}
}
MTools.DrawObjectReferenceInspector(decision);
}
}
}
serializedObject.ApplyModifiedProperties();
}
private void ExtractTaskFromList(Object asset, SerializedProperty element, int index)
{
string taskPath = AssetDatabase.GetAssetPath(asset);
string targetPath = AssetDatabase.GetAssetPath(target);
if (taskPath == targetPath)
{
Object clone = MTools.ExtractObject(asset, index);
if (!clone) return;
DestroyImmediate(asset, true); //Remove all version
element.objectReferenceValue = clone; //Use the new extracted one
Reo_List_Tasks.index = index;
Reo_List_Transitions.index = -1;
EditorUtility.SetDirty(target);
serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
EditorGUIUtility.PingObject(clone);
}
else
{
// Checking this beforehand is quite in-efficient as the AssetDatabase.GetAssetPath() is slow
// If there is a better way to check whether it's an internal or external asset then that could be used
Debug.LogWarning("Cannot extract already extracted task");
}
}
private void ExtractDecisionFromList(Object asset, SerializedProperty element, int index)
{
string taskPath = AssetDatabase.GetAssetPath(asset);
string targetPath = AssetDatabase.GetAssetPath(target);
if (taskPath == targetPath)
{
Object clone = MTools.ExtractObject(asset, index);
if (!clone) return;
// Remove from list
DestroyImmediate(asset, true);
// Add as external decision
SerializedProperty decision = element.FindPropertyRelative("decision");
decision.objectReferenceValue = clone;
Reo_List_Tasks.index = -1;
Reo_List_Transitions.index = index;
EditorUtility.SetDirty(target);
serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
EditorGUIUtility.PingObject(clone);
}
else
{
// Checking this beforehand is quite in-efficient as the AssetDatabase.GetAssetPath() is slow
// If there is a better way to check whether it's an internal or external asset then that could be used
Debug.LogWarning("Cannot extract already extracted decision");
}
}
}
#endif
}