コード例 #1
0
    /// <summary>
    /// restores the int block list onto all states by name, by
    /// fetching block lists in string-list format from the
    /// backup dictionary and converting them back to int-lists
    /// to be set on the components
    /// </summary>
    static void RestoreBlockLists(vp_AIComponent component)
    {
        foreach (string s in m_BlockListBackups.Keys)
        {
            vp_AIState blocker = GetState(component, s);
            if (blocker == null)
            {
                continue;
            }

            List <string> stringBlockList;
            if (!m_BlockListBackups.TryGetValue(s, out stringBlockList))
            {
                continue;
            }

            List <int> intBlockList = new List <int>();
            foreach (string b in stringBlockList)
            {
                int blockee = GetStateId(component, b);
                if (blockee != -1)
                {
                    intBlockList.Add(GetStateId(component, b));
                }
            }

            blocker.StatesToBlock = intBlockList;
        }
    }
コード例 #2
0
 /// <summary>
 /// draws a foldout with buttons to load and save a preset
 /// </summary>
 public static bool PresetFoldout(bool foldout, vp_AIComponent component)
 {
     foldout = EditorGUILayout.Foldout(foldout, "Preset");
     if (foldout)
     {
         GUI.enabled = true;
         EditorGUILayout.BeginHorizontal();
         if (component.DefaultState.TextAsset != null)
         {
             GUI.enabled = false;
         }
         if (GUILayout.Button("Load"))
         {
             ShowLoadDialog(component);
         }
         GUI.enabled = true;
         if (GUILayout.Button("Save"))
         {
             ShowSaveDialog(component, false);
         }
         if (!Application.isPlaying)
         {
             GUI.enabled = false;
         }
         if (GUILayout.Button("Save Tweaks"))
         {
             ShowSaveDifferenceDialog(component);
         }
         GUI.enabled = true;
         EditorGUILayout.EndHorizontal();
     }
     return(foldout);
 }
コード例 #3
0
    /// <summary>
    /// opens a dialog for saving presets
    /// </summary>
    static private void ShowSaveDifferenceDialog(vp_AIComponent component)
    {
        string path = Application.dataPath;

        // LORE: in order not to overwrite existing values in a disk
        // preset, we'll load the disk preset before saving over it.
        // since the file dialog system will execute its callback
        // twice in case of an already existing file (and delete the
        // target file in the process) we need to store the preset
        // in memory outside of the callback and skip loading it on
        // the second iteration
        vp_AIComponentPreset diskPreset = null;

        vp_FileDialog.Create(vp_FileDialog.Mode.Save, "Save Tweaks", path, delegate(string filename)
        {
            // only attempt to load the disk preset the first time
            // the callback is executed
            if (diskPreset == null)
            {
                diskPreset = new vp_AIComponentPreset();
                // attempt to load target preset into memory, ignoring
                // load errors in the process
                bool logErrorState             = vp_AIComponentPreset.LogErrors;
                vp_AIComponentPreset.LogErrors = false;
                diskPreset.LoadTextStream(filename);
                vp_AIComponentPreset.LogErrors = logErrorState;
            }

            vp_FileDialog.Result = vp_AIComponentPreset.SaveDifference(component.InitialState.Preset, component, filename, diskPreset);
        }, ".txt");
    }
コード例 #4
0
 /// <summary>
 ///
 /// </summary>
 public static string GetStateName(vp_AIComponent component, int id)
 {
     if (id == -1)
     {
         return(null);
     }
     return(component.States[id].Name);
 }
コード例 #5
0
 /// <summary>
 ///
 /// </summary>
 public static vp_AIState GetState(vp_AIComponent component, string state)
 {
     foreach (vp_AIState s in component.States)
     {
         if (s.Name == state)
         {
             return(s);
         }
     }
     return(null);
 }
コード例 #6
0
 /// <summary>
 ///
 /// </summary>
 public static int GetStateId(vp_AIComponent component, string state)
 {
     for (int v = 0; v < component.States.Count; v++)
     {
         if (component.States[v].Name == state)
         {
             return(v);
         }
     }
     return(-1);
 }
コード例 #7
0
    /// <summary>
    /// draws a button showing if a state is on or off, allowing
    /// the user to toggle states at runtime. will also show
    /// a text saying if the state is currently disallowed
    /// </summary>
    public static void RunTimeStateField(vp_AIComponent component, vp_AIState state, List <vp_AIState> stateList)
    {
        EditorGUILayout.BeginHorizontal();

        GUI.color = m_ColorTransparentWhite;
        if (!state.Enabled)
        {
            GUILayout.Space(20);
            GUI.enabled = true;
            GUILayout.Label((stateList.Count - stateList.IndexOf(state) - 1).ToString() + ":", vp_EditorGUIUtility.RightAlignedPathStyle, GUILayout.MinWidth(20), GUILayout.MaxWidth(20));
            GUILayout.BeginHorizontal();
            if (GUILayout.Button(state.Name, vp_EditorGUIUtility.CenteredBoxStyle, GUILayout.MinWidth(90), GUILayout.MaxWidth(90)))
            {
                vp_AIComponent[] compos = component.gameObject.GetComponentsInChildren <vp_AIComponent>();
                foreach (vp_AIComponent c in compos)
                {
                    c.StateManager.SetState(state.Name, true);
                    c.Refresh();
                }
            }
            GUILayout.EndHorizontal();
            GUI.color = m_ColorTransparentWhite;
        }
        else
        {
            GUILayout.Space(20);
            GUILayout.Label((stateList.Count - stateList.IndexOf(state) - 1).ToString() + ":", vp_EditorGUIUtility.RightAlignedPathStyle, GUILayout.MinWidth(20), GUILayout.MaxWidth(20));
            if (GUILayout.Button(state.Name,
                                 ((!state.Blocked) ? vp_EditorGUIUtility.CenteredBoxStyleBold : vp_EditorGUIUtility.CenteredStyleBold)
                                 , GUILayout.MinWidth(90), GUILayout.MaxWidth(90)))
            {
                vp_AIComponent[] compos = component.gameObject.GetComponentsInChildren <vp_AIComponent>();
                foreach (vp_AIComponent c in compos)
                {
                    c.StateManager.SetState(state.Name, false);
                    c.Refresh();
                }
            }
        }
        if (state.Name != "Default")
        {
            if (state.Blocked)
            {
                GUILayout.TextField("(Blocked on " + component.GetType().ToString() + "(" + state.BlockCount + "))", vp_EditorGUIUtility.NoteStyle, GUILayout.MinWidth(100));
            }

            if ((state.TextAsset == null) && (!state.Blocked))
            {
                GUILayout.TextField("(No preset)", vp_EditorGUIUtility.NoteStyle, GUILayout.MinWidth(100));
            }
        }

        EditorGUILayout.EndHorizontal();
    }
コード例 #8
0
    static private void ShowSaveDialog(vp_AIComponent component, bool showLoadDialogAfterwards)
    {
        string path = Application.dataPath;

        vp_FileDialog.Create(vp_FileDialog.Mode.Save, "Save Preset", path, delegate(string filename)
        {
            vp_FileDialog.Result = vp_AIComponentPreset.Save(component, filename);

            if (showLoadDialogAfterwards)
            {
                ShowLoadDialog(component);
            }
        }, ".txt");
    }
コード例 #9
0
    private int m_TargetId  = 0;        // id of the last modified state (for triggering enable / disable callbacks)


    /// <summary>
    /// manages a list of states and corresponding presets for a
    /// component. loads the presets into memory on startup, and
    /// allows applying them from memory to the component.
    /// states are enabled in a layered manner, that is: the
    /// default state is always alive, and any enabled states are
    /// applied on top of it in the order of which they were enabled.
    ///
    /// this class doesn't store any preset data between sessions.
    /// it is merely used to manipulate the component using a list
    /// of states that is sent along at startup. it is very silent
    /// and forgiving; it won't complain if a state isn't found
    /// (since providing a certain state should not be considered
    /// mandatory for a component, and states can be set recursively).
    /// it will also ignore empty presets
    /// </summary>
    public vp_AIStateManager(vp_AIComponent component, List <vp_AIState> states)
    {
        m_States    = states;
        m_Component = component;

        // create default state and add it to the list
        m_Component.RefreshDefaultState();

        // refresh the initial state, needed for being able to save
        // partial presets in the editor
#if UNITY_EDITOR
        m_Component.RefreshInitialState();
#endif

        // load states and initialize the state id dictionary
        m_StateIds = new Dictionary <string, int>(System.StringComparer.CurrentCulture);
        foreach (vp_AIState s in m_States)
        {
            s.StateManager = this;

            // store the name and list index of each state in a dictionary for
            // fast lookup. IMPORTANT: the state list (m_States) must never be
            // modified at runtime (i.e. have states reordered, added, renamed
            // or removed) or the dictionary (m_StateIds) will be out of date
            if (!m_StateIds.ContainsKey(s.Name))
            {
                m_StateIds.Add(s.Name, m_States.IndexOf(s));
            }
            else
            {
                Debug.LogWarning("Warning: " + m_Component.GetType() + " on '" + m_Component.name + "' has more than one state named: '" + s.Name + "'. Only the topmost one will be used.");
                m_States[m_DefaultId].StatesToBlock.Add(m_States.IndexOf(s));
            }

            // load up the preset of each user assigned state and
            if (s.Preset == null)
            {
                s.Preset = new vp_AIComponentPreset();
            }
            if (s.TextAsset != null)
            {
                s.Preset.LoadFromTextAsset(s.TextAsset);
            }
        }

        // the default state of a component is always the last one in the list
        m_DefaultId = m_States.Count - 1;
    }
コード例 #10
0
    /// <summary>
    /// opens a dialog for loading presets
    /// </summary>
    static private void ShowLoadDialog(vp_AIComponent component)
    {
        string path = Application.dataPath.Replace("\\", "/");

        vp_FileDialog.Create(vp_FileDialog.Mode.Open, "Load Preset", path, delegate(string filename)
        {
            if (!vp_AIComponentPreset.Load(component, filename))
            {
                vp_FileDialog.Result = "Failed to load preset '" + vp_FileDialog.ExtractFilenameFromPath(filename) + "'.\n\nIs it the correct component type? (" + component.GetType().ToString() + ")";
            }
            else
            {
                EditorUtility.SetDirty(component);
            }
        }, ".txt");
    }
コード例 #11
0
    /// <summary>
    /// fills a dictionary with all the states as string keys
    /// (instead of objects) and their block lists as string-list
    /// (instead of int-list) values
    /// </summary>
    static void BackupBlockLists(vp_AIComponent component)
    {
        m_BlockListBackups = new Dictionary <string, List <string> >();
        foreach (vp_AIState blocker in component.States)
        {
            List <string> blockees = new List <string>();
            foreach (int i in blocker.StatesToBlock)
            {
                string blockee = GetStateName(component, i);
                if (blockee != null)
                {
                    blockees.Add(blockee);
                }
            }

            if (!m_BlockListBackups.ContainsKey(blocker.Name))
            {
                m_BlockListBackups.Add(blocker.Name, blockees);
            }
        }
    }
コード例 #12
0
    /// <summary>
    /// draws a row displaying a preset state name, a path and
    /// buttons for browsing the path + deleting the state
    /// </summary>
    public static void StateField(vp_AIState state, List <vp_AIState> stateList, vp_AIComponent component)
    {
        GUI.enabled = !Application.isPlaying;           // only allow preset field interaction in 'stopped' mode

        EditorGUILayout.BeginHorizontal();

        string orig = state.Name;

        if (state.Name == "Default")
        {
            GUI.enabled = false;
            EditorGUILayout.TextField(state.Name, GUILayout.MinWidth(90), GUILayout.MaxWidth(90));
            GUI.enabled = true;
        }
        else
        {
            if (m_ShowBlockListFor != null)
            {
                if (!component.States.Contains(m_ShowBlockListFor))
                {
                    m_ShowBlockListFor = null;
                }
                else if (m_ShowBlockListFor.StatesToBlock.Contains(component.States.IndexOf(state)))
                {
                    GUI.color = m_ColorGrayYellow;
                }
            }
            state.Name = EditorGUILayout.TextField(state.Name, GUILayout.MinWidth(90), GUILayout.MaxWidth(90));
            GUI.color  = Color.white;
        }

        if (orig != state.Name)
        {
            int collisions = -1;
            foreach (vp_AIState s in stateList)
            {
                if (s.Name == state.Name)
                {
                    collisions++;
                }
            }

            if (state.Name == "Default")
            {
                vp_MessageBox.Create(vp_MessageBox.Mode.OK, "Error", "'Default' is a reserved state name.");
                state.Name = orig;
            }
            else if (state.Name.Length == 0)
            {
                vp_MessageBox.Create(vp_MessageBox.Mode.OK, "Error", "State name can't be empty.");
                state.Name = orig;
            }
            else if (collisions > 0)
            {
                vp_MessageBox.Create(vp_MessageBox.Mode.OK, "Error", "There is already a state named '" + state.Name + "'.\nTIP: If you need a similar state name, begin by adding numbers at the end.");
                state.Name = orig;
            }
            else
            {
                EditorUtility.SetDirty(component);
            }
        }

        PresetField(state);

        if (state.Name == "Default")
        {
            if (state.TextAsset == null)
            {
                GUI.enabled = false;
                GUILayout.TextField("(Inspector)", vp_EditorGUIUtility.NoteStyle, GUILayout.MinWidth(60));
            }
            else
            {
                GUI.enabled = true;
                if (GUILayout.Button("Unlock", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(30), GUILayout.MinHeight(15)))
                {
                    state.TextAsset = null;
                    EditorUtility.SetDirty(component);
                }
            }
        }
        else
        {
            if (stateList.IndexOf(state) == 0)
            {
                GUI.enabled = false;
            }

            GUI.SetNextControlName("state");
            if (GUILayout.Button("^", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(15), GUILayout.MaxWidth(15), GUILayout.MinHeight(16)))
            {
                BackupBlockLists(component);
                int i = stateList.IndexOf(state);
                if (i != 0)
                {
                    stateList.Remove(state);
                    stateList.Insert(i - 1, state);
                }
                RestoreBlockLists(component);

                // focus this button to get rid of possible textfield focus,
                // or the textfields won't update properly when moving state
                GUI.FocusControl("state");
                EditorUtility.SetDirty(component);
            }

            GUI.enabled = true;

            if (GUILayout.Button(new GUIContent(state.IsAnimationState ? m_AnimationTexture : m_AnimationOffTexture, "Enable/Disable Animation State for " + state.Name), SmallButtonStyle, GUILayout.MinWidth(15), GUILayout.MaxWidth(15), GUILayout.MinHeight(11)))
            {
                state.IsAnimationState = !state.IsAnimationState;
            }

            GUI.color = state.IsAudioState ? m_ColorYellow : Color.white;
            if (UnityEditorInternal.InternalEditorUtility.HasPro())
            {
                GUI.color = state.IsAudioState ? m_ColorYellowPro : Color.white;
            }
            if (GUILayout.Button(new GUIContent(m_SoundTexture, "Enable/Disable Audio State for " + state.Name), SmallButtonStyle, GUILayout.MinWidth(15), GUILayout.MaxWidth(15), GUILayout.MinHeight(11)))
            {
                state.IsAudioState = !state.IsAudioState;
            }
            GUI.color = Color.white;

            if (state.StatesToBlock != null)
            {
                if ((state.StatesToBlock.Count > 0) && ((m_ShowBlockListFor == null) || (!component.States.Contains(m_ShowBlockListFor)) || m_ShowBlockListFor == state))
                {
                    GUI.color = m_ColorGrayYellow;
                }
            }

            GUI.enabled = (component.States.Count > 2);
            if (GUILayout.Button("B", SmallButtonStyle, GUILayout.MinWidth(15), GUILayout.MaxWidth(15), GUILayout.MinHeight(16)))
            {
                if (m_ShowBlockListFor == state)
                {
                    m_ShowBlockListFor = null;
                }
                else
                {
                    m_ShowBlockListFor = state;
                }
                EditorUtility.SetDirty(component);
            }
            GUI.enabled = true;
            GUI.color   = Color.white;

            if (GUILayout.Button("X", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(15), GUILayout.MaxWidth(15), GUILayout.MinHeight(16)))
            {
                vp_MessageBox.Create(vp_MessageBox.Mode.YesNo, "Confirm", "Are you sure you want to delete the state '" + state.Name + "'?", delegate(vp_MessageBox.Answer answer)
                {
                    if (answer == vp_MessageBox.Answer.Yes)
                    {
                        BackupBlockLists(component);
                        stateList.Remove(state);
                        RestoreBlockLists(component);
                        EditorUtility.SetDirty(component);
                    }
                });

                EditorUtility.SetDirty(component);
            }
        }

        GUI.enabled = true;

        EditorGUILayout.EndHorizontal();
    }
コード例 #13
0
    /// <summary>
    ///
    /// </summary>
    static void StateBlockList(vp_AIComponent component, vp_AIState blocker)
    {
        GUILayout.BeginHorizontal();
        GUILayout.Space(20);
        GUILayout.BeginVertical();

        string componentName = component.GetType().ToString();

        if (componentName.Contains("vp_"))
        {
            componentName = componentName.Substring(3);
        }

        EditorGUILayout.HelpBox("'" + blocker.Name + "' blocks " + ((blocker.StatesToBlock.Count > 0) ? blocker.StatesToBlock.Count.ToString() : "no") + " state" + ((blocker.StatesToBlock.Count == 1) ? "" : "s") + " on this " + componentName + ".", MessageType.None);

        GUILayout.BeginVertical();
        int e = 0;

        foreach (vp_AIState blockee in component.States)
        {
            if (blockee == blocker)
            {
                continue;
            }

            if (blockee.Name == "Default")
            {
                continue;
            }

            int i = component.States.IndexOf(blockee);

            if (component.States[i].StatesToBlock.Contains(component.States.IndexOf(blocker)))
            {
                GUI.enabled = false;
            }

            if (e % 2 == 0)
            {
                GUILayout.BeginHorizontal();
            }
            GUILayout.Space(20);
            bool before = blocker.StatesToBlock.Contains(i);
            bool after  = before;

            after = GUILayout.Toggle(after, blockee.Name);

            if (before != after)
            {
                if (!before)
                {
                    blocker.StatesToBlock.Add(i);
                }
                else
                {
                    blocker.StatesToBlock.Remove(i);
                }

                EditorUtility.SetDirty(component);
            }
            if (e % 2 == 1)
            {
                GUILayout.Space(10);
                GUILayout.EndHorizontal();
            }

            e++;
            GUI.enabled = true;
        }
        if (e % 2 == 1)
        {
            GUILayout.EndHorizontal();
        }

        GUILayout.EndVertical();

        GUILayout.BeginHorizontal();
        EditorGUILayout.HelpBox("Select states to be disallowed on this " + componentName + " while the '" + blocker.Name + "' state is enabled. A state can not block itself, a state that blocks it or the Default state.", MessageType.Info);
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        if (GUILayout.Button("Close", GUILayout.MinWidth(100), GUILayout.MaxWidth(50)))
        {
            m_ShowBlockListFor = null;
            EditorUtility.SetDirty(component);
        }

        GUILayout.EndVertical();
        GUILayout.EndHorizontal();
        GUILayout.Space(10);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);
    }
コード例 #14
0
    /// <summary>
    /// draws a field allowing the user to create, reorganize,
    /// name, assign presets to and delete states on a component
    /// </summary>
    public static bool StateFoldout(bool foldout, vp_AIComponent component, List <vp_AIState> stateList, vp_AIComponentPersister persister = null)
    {
        bool before = foldout;

        foldout = EditorGUILayout.Foldout(foldout,
                                          (foldout && !Application.isPlaying) ? "State             Preset" : "States"
                                          );

        if (foldout != before)
        {
            m_ShowBlockListFor = null;
            component.RefreshDefaultState();
        }

        if (foldout)
        {
            if (m_ShowBlockListFor != null)
            {
                if (!stateList.Contains(m_ShowBlockListFor))
                {
                    foldout = false;
                }
            }
        }

        if (foldout)
        {
            for (int v = 0; v < stateList.Count; v++)
            {
                vp_AIState s = stateList[v];
                if (!Application.isPlaying)
                {
                    vp_AIPresetEditorGUIUtility.StateField(s, stateList, component);
                    if ((m_ShowBlockListFor != null) && m_ShowBlockListFor == s)
                    {
                        StateBlockList(component, s);
                    }
                }
                else
                {
                    vp_AIPresetEditorGUIUtility.RunTimeStateField(component, s, stateList);
                }
            }

            GUILayout.BeginHorizontal();
            if (!Application.isPlaying)
            {
                if (GUILayout.Button("Add State", GUILayout.MinWidth(90), GUILayout.MaxWidth(90)))
                {
                    m_ShowBlockListFor = null;
                    string newStateName = "Untitled";
                    int    n            = 1;
                    while (GetStateId(component, newStateName) != -1)
                    {
                        n++;
                        newStateName = newStateName.Substring(0, 8) + (n < 10?"0":"") + n.ToString();
                    }

                    stateList.Add(new vp_AIState(component.GetType().Name, newStateName, ""));
                    component.RefreshDefaultState();
                    EditorUtility.SetDirty(component);
                }
            }
            else
            {
                GUI.color = Color.clear;
                GUILayout.Button("", GUILayout.MinWidth(36), GUILayout.MaxWidth(36));
                GUI.color = Color.white;
            }
            if (!Application.isPlaying)
            {
                GUILayout.EndHorizontal();
            }
            if (persister != null)
            {
                vp_AIPresetEditorGUIUtility.PersistToggle(persister);
            }
            if (Application.isPlaying)
            {
                GUILayout.EndHorizontal();
            }

            vp_EditorGUIUtility.Separator();
        }

        return(foldout);
    }
コード例 #15
0
 /// <summary>
 /// opens a dialog for saving presets
 /// </summary>
 static private void ShowSaveDialog(vp_AIComponent component)
 {
     ShowSaveDialog(component, false);
 }