An easy to use Unity 3D library for creating illustrated Interactive Fiction games and more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
12 KiB

// This code is part of the Fungus library (http://fungusgames.com) maintained by Chris Gregan (http://twitter.com/gofungus).
// It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE)
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using Rotorz.ReorderableList;
using System.Linq;
using System.Reflection;
namespace Fungus.EditorUtils
{
[CustomEditor (typeof(Flowchart))]
public class FlowchartEditor : Editor
{
protected class AddVariableInfo
{
public Flowchart flowchart;
public System.Type variableType;
}
protected SerializedProperty descriptionProp;
protected SerializedProperty colorCommandsProp;
protected SerializedProperty hideComponentsProp;
protected SerializedProperty stepPauseProp;
protected SerializedProperty saveSelectionProp;
protected SerializedProperty localizationIdProp;
protected SerializedProperty variablesProp;
protected SerializedProperty showLineNumbersProp;
protected SerializedProperty hideCommandsProp;
protected SerializedProperty luaEnvironmentProp;
protected SerializedProperty luaBindingNameProp;
protected Texture2D addTexture;
protected virtual void OnEnable()
{
if (NullTargetCheck()) // Check for an orphaned editor instance
return;
descriptionProp = serializedObject.FindProperty("description");
colorCommandsProp = serializedObject.FindProperty("colorCommands");
hideComponentsProp = serializedObject.FindProperty("hideComponents");
stepPauseProp = serializedObject.FindProperty("stepPause");
saveSelectionProp = serializedObject.FindProperty("saveSelection");
localizationIdProp = serializedObject.FindProperty("localizationId");
variablesProp = serializedObject.FindProperty("variables");
showLineNumbersProp = serializedObject.FindProperty("showLineNumbers");
hideCommandsProp = serializedObject.FindProperty("hideCommands");
luaEnvironmentProp = serializedObject.FindProperty("luaEnvironment");
luaBindingNameProp = serializedObject.FindProperty("luaBindingName");
addTexture = Resources.Load("Icons/add_small") as Texture2D;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
var flowchart = target as Flowchart;
flowchart.UpdateHideFlags();
EditorGUILayout.PropertyField(descriptionProp);
EditorGUILayout.PropertyField(colorCommandsProp);
EditorGUILayout.PropertyField(hideComponentsProp);
EditorGUILayout.PropertyField(stepPauseProp);
EditorGUILayout.PropertyField(saveSelectionProp);
EditorGUILayout.PropertyField(localizationIdProp);
EditorGUILayout.PropertyField(showLineNumbersProp);
EditorGUILayout.PropertyField(luaEnvironmentProp);
EditorGUILayout.PropertyField(luaBindingNameProp);
// Show list of commands to hide in Add Command menu
ReorderableListGUI.Title(new GUIContent(hideCommandsProp.displayName, hideCommandsProp.tooltip));
ReorderableListGUI.ListField(hideCommandsProp);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Open Flowchart Window", "Opens the Flowchart Window")))
{
EditorWindow.GetWindow(typeof(FlowchartWindow), false, "Flowchart");
}
GUILayout.Space(10);
if (GUILayout.Button(new GUIContent("Center View", "Centers the window view at the center of all blocks in the Flowchart")))
{
// Reset the zoom so we don't have adjust the center position depending on zoom
flowchart.ScrollPos = flowchart.CenterPosition;
flowchart.Zoom = FlowchartWindow.maxZoomValue;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
serializedObject.ApplyModifiedProperties();
}
public virtual void DrawVariablesGUI()
{
serializedObject.Update();
var t = target as Flowchart;
if (t.Variables.Count == 0)
{
t.VariablesExpanded = true;
}
if (!t.VariablesExpanded)
{
if (GUILayout.Button ("Variables (" + t.Variables.Count + ")", GUILayout.Height(24)))
{
t.VariablesExpanded = true;
}
// Draw disclosure triangle
Rect lastRect = GUILayoutUtility.GetLastRect();
lastRect.x += 5;
lastRect.y += 5;
EditorGUI.Foldout(lastRect, false, "");
}
else
{
Rect listRect = new Rect();
if (t.Variables.Count > 0)
{
// Remove any null variables from the list
// Can sometimes happen when upgrading to a new version of Fungus (if .meta GUID changes for a variable class)
for (int i = t.Variables.Count - 1; i >= 0; i--)
{
if (t.Variables[i] == null)
{
t.Variables.RemoveAt(i);
}
}
ReorderableListGUI.Title("Variables");
VariableListAdaptor adaptor = new VariableListAdaptor(variablesProp, 0);
ReorderableListFlags flags = ReorderableListFlags.DisableContextMenu | ReorderableListFlags.HideAddButton;
ReorderableListControl.DrawControlFromState(adaptor, null, flags);
listRect = GUILayoutUtility.GetLastRect();
}
else
{
GUILayoutUtility.GetRect(300, 24);
listRect = GUILayoutUtility.GetLastRect();
listRect.y += 20;
}
float plusWidth = 32;
float plusHeight = 24;
Rect buttonRect = listRect;
float buttonHeight = 24;
buttonRect.x = 4;
buttonRect.y -= buttonHeight - 1;
buttonRect.height = buttonHeight;
if (!Application.isPlaying)
{
buttonRect.width -= 30;
}
if (GUI.Button (buttonRect, "Variables"))
{
t.VariablesExpanded = false;
}
// Draw disclosure triangle
Rect lastRect = buttonRect;
lastRect.x += 5;
lastRect.y += 5;
EditorGUI.Foldout(lastRect, true, "");
Rect plusRect = listRect;
plusRect.x += plusRect.width - plusWidth;
plusRect.y -= plusHeight - 1;
plusRect.width = plusWidth;
plusRect.height = plusHeight;
if (!Application.isPlaying &&
GUI.Button(plusRect, addTexture))
{
GenericMenu menu = new GenericMenu ();
List<System.Type> types = FindAllDerivedTypes<Variable>();
// Add variable types without a category
foreach (var type in types)
{
VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type);
if (variableInfo == null ||
variableInfo.Category != "")
{
continue;
}
AddVariableInfo addVariableInfo = new AddVariableInfo();
addVariableInfo.flowchart = t;
addVariableInfo.variableType = type;
GUIContent typeName = new GUIContent(variableInfo.VariableType);
menu.AddItem(typeName, false, AddVariable, addVariableInfo);
}
// Add types with a category
foreach (var type in types)
{
VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type);
if (variableInfo == null ||
variableInfo.Category == "")
{
continue;
}
AddVariableInfo info = new AddVariableInfo();
info.flowchart = t;
info.variableType = type;
GUIContent typeName = new GUIContent(variableInfo.Category + "/" + variableInfo.VariableType);
menu.AddItem(typeName, false, AddVariable, info);
}
menu.ShowAsContext ();
}
}
serializedObject.ApplyModifiedProperties();
}
protected virtual void AddVariable(object obj)
{
AddVariableInfo addVariableInfo = obj as AddVariableInfo;
if (addVariableInfo == null)
{
return;
}
var flowchart = addVariableInfo.flowchart;
System.Type variableType = addVariableInfo.variableType;
Undo.RecordObject(flowchart, "Add Variable");
Variable newVariable = flowchart.gameObject.AddComponent(variableType) as Variable;
newVariable.Key = flowchart.GetUniqueVariableKey("");
flowchart.Variables.Add(newVariable);
// Because this is an async call, we need to force prefab instances to record changes
PrefabUtility.RecordPrefabInstancePropertyModifications(flowchart);
}
public static List<System.Type> FindAllDerivedTypes<T>()
{
return FindAllDerivedTypes<T>(Assembly.GetAssembly(typeof(T)));
}
public static List<System.Type> FindAllDerivedTypes<T>(Assembly assembly)
{
var derivedType = typeof(T);
return assembly
.GetTypes()
.Where(t =>
t != derivedType &&
derivedType.IsAssignableFrom(t)
).ToList();
}
/// <summary>
/// When modifying custom editor code you can occasionally end up with orphaned editor instances.
/// When this happens, you'll get a null exception error every time the scene serializes / deserialized.
/// Once this situation occurs, the only way to fix it is to restart the Unity editor.
/// As a workaround, this function detects if this editor is an orphan and deletes it.
/// </summary>
protected virtual bool NullTargetCheck()
{
try
{
// The serializedObject accessor creates a new SerializedObject if needed.
// However, this will fail with a null exception if the target object no longer exists.
#pragma warning disable 0219
SerializedObject so = serializedObject;
}
catch (System.NullReferenceException)
{
DestroyImmediate(this);
return true;
}
return false;
}
}
}