Example #1
0
    public void OnEnable()
    {
        // The actionScriptableObject instance being edited by this inspector
        ActionScriptableObject actionScriptableObject = (ActionScriptableObject)target;
        // Retrieves the action instance from the scriptable object being edited. This is a data container for the action's properties
        Action actionInfo = actionScriptableObject.action;

        // The foldout for the events performed at the same time this action is performed
        onStartEventsFoldout = new EventsFoldout("Events On Start", actionInfo.onStartEvents);

        showHitOptionsFoldouts = new bool[actionInfo.hitBoxes.Length];
        // The GameObjects which serve as templates for the hit boxes.
        // One is supplied for each hit box in this action. If a Gameobject
        // is dragged and dropped, its values are copactionInfoEditoried to the hit box
        templateHitBoxes = new GameObject[actionInfo.hitBoxes.Length];
        // The foldouts which edit the events which happen on collision for each hit box.
        hitSelfEventsFoldouts      = new EventsFoldout[actionInfo.hitBoxes.Length];
        hitAdversaryEventsFoldouts = new EventsFoldout[actionInfo.hitBoxes.Length];
        for (int i = 0; i < actionInfo.hitBoxes.Length; i++)
        {
            hitSelfEventsFoldouts[i]      = new EventsFoldout("Events [Self]", actionInfo.hitBoxes[i].hitInfo.selfEvents);
            hitAdversaryEventsFoldouts[i] = new EventsFoldout("Events [Adversary]", actionInfo.hitBoxes[i].hitInfo.adversaryEvents);
        }


        // "Forces" foldouts
        showStartTimeFoldouts       = new bool[actionInfo.forces.Length];
        showDurationFoldouts        = new bool[actionInfo.forces.Length];
        showOnCompleteEventFoldouts = new bool[actionInfo.forces.Length];
    }
Example #2
0
    public override void OnStart()
    {
        // Casts the inspector object into an ActionScriptableObject. This is the action the character will perform
        actionToPerform = (ActionScriptableObject)(actionScriptableObject.Value);

        // Perform the action
        character.CharacterControl.PerformAction(actionToPerform);
    }
Example #3
0
    /// <summary>
    /// Performs the given action. The action is given in the form of an ActionScriptableObject, which
    /// is a serializable container for an action. When the character is created, his ActionSet creates
    /// an Action instance for each ActionScriptableObject he is given in the inspector. Therefore, a
    /// search must be done to find the Action instance corresponding to the given scriptable object
    /// before actually performing the action.
    /// </summary>
    public void PerformAction(ActionScriptableObject actionScriptableObject)
    {
        // Stores the action corresponding to the given scriptable object
        Action action = actionSet.FindAction(actionScriptableObject);

        // Perform the action
        PerformAction(action);
    }
Example #4
0
    public void OnDisable()
    {
        // Save the changes made to this action
        ActionScriptableObject actionScriptableObject = (ActionScriptableObject)target;

        AssetDatabase.Refresh();
        EditorUtility.SetDirty(actionScriptableObject);
        AssetDatabase.SaveAssets();
    }
Example #5
0
        // Creates an Event with the same properties as the given event
        public Event(Event other)
        {
            type                 = other.type;
            actionToPerform      = other.actionToPerform;
            basicActionToPerform = other.basicActionToPerform;
            soundEffect          = other.soundEffect;

            cameraMovement = new CameraMovement(other.cameraMovement);
            slowMotion     = other.slowMotion;
            particleEvent  = other.particleEvent;
            forceEvent     = new Force(other.forceEvent, false);
            colorFlash     = other.colorFlash;
            ghostEffect    = other.ghostEffect;
            screenShake    = other.screenShake;
            tweenEvent     = other.tweenEvent;

            startTime = other.startTime;
            duration  = other.duration;
        }
Example #6
0
    /// <summary>
    /// Returns the Action instance corresponding to the given ActionScriptableObject. An ActionScriptableObject
    /// is a serializable container for an action. When an ActionSet is created, it creates a new Action instance
    /// for each ActionScriptableObject it is given in the inspector. Therefore, a search must be done to find the
    /// Action instance corresponding to the given scriptable object.
    /// Note: Runs in O(n) time, where n is the number of combat actions assigned to this action set
    /// </summary>
    public Action FindAction(ActionScriptableObject actionScriptableObject)
    {
        // TODO: Create a dictionary which maps ActionScriptableObjects to Actions for quicker searching.
        // Cycle through each scriptableObject for the character's combat actions. The given scriptable object should be
        // contained in this list, if the character was assigned this action in the inspector
        for (int i = 0; i < combatActionScriptableObjects.Length; i++)
        {
            // If the element being iterated through is equal to the given actionScriptableObject, the correct action
            // has been found in the character's action-set
            if (combatActionScriptableObjects[i] == actionScriptableObject)
            {
                // Return the corresponding combatAction, which is stored in the same index as the
                return(combatActions[i]);
            }
        }

        // If this statement is reached, no actions corresponding to the given scriptable object could be found. Return null
        return(null);
    }
Example #7
0
    public override void OnInspectorGUI()
    {
        // The actionScriptableObject instance being edited by this inspector
        ActionScriptableObject actionScriptableObject = (ActionScriptableObject)target;
        // Retrieves the action instance from the scriptable object being edited. This is a data container for the action's properties
        Action actionInfo = actionScriptableObject.action;

        // Add undo support to the actionInfo instance.
        Undo.RecordObject(actionScriptableObject, "Action Info Undo");

        EditorGUILayout.BeginVertical();
        {
            actionInfo.name = EditorGUILayout.TextField("Name:", actionInfo.name);

            /*********************
             * ANIMATION FOLDOUT *
             **********************/
            EditorGUI.indentLevel = 0;
            showAnimationFoldout  = AnimationFoldout(actionInfo, showAnimationFoldout);

            /*****************
            * INPUT FOLDOUT *
            *****************/
            EditorGUI.indentLevel = 0;

            // Display the animation foldout
            showInputFoldout = EditorGUILayout.Foldout(showInputFoldout, "Input");

            if (showInputFoldout)
            {
                EditorGUI.indentLevel = 1;
                // Input Foldout contents
                EditorGUILayout.BeginVertical();
                {
                    // Can the action be activated by user input?
                    EditorGUILayout.BeginHorizontal();
                    {
                        EditorGUILayout.LabelField("Activate using input?", GUILayout.Width(150));
                        actionInfo.listensToInput = EditorGUILayout.Toggle("", actionInfo.listensToInput);
                    }
                    EditorGUILayout.EndHorizontal();


                    // Only show input options if the action requires user input
                    if (actionInfo.listensToInput)
                    {
                        actionInfo.inputType = (InputType)EditorGUILayout.EnumPopup("Input Type:", actionInfo.inputType);
                        if (actionInfo.inputType == InputType.Swipe)
                        {
                            actionInfo.swipeDirection = (SwipeDirection)EditorGUILayout.EnumPopup("Swipe Direction:",
                                                                                                  actionInfo.swipeDirection);
                        }
                        actionInfo.inputRegion = (InputRegion)EditorGUILayout.EnumPopup("Input Region:", actionInfo.inputRegion);
                    }
                }
                EditorGUILayout.EndVertical();
            }

            /*******************
            * HIT BOX FOLDOUT *
            *******************/

            EditorGUI.indentLevel = 0;

            showHitBoxFoldout = EditorGUILayout.Foldout(showHitBoxFoldout, "Hit Boxes (" + actionInfo.hitBoxes.Length + ")");

            if (showHitBoxFoldout)
            {
                EditorGUILayout.BeginVertical();
                {
                    EditorGUI.indentLevel = 1;

                    // Cycle through each hit box associated with this action
                    for (int i = 0; i < actionInfo.hitBoxes.Length; i++)
                    {
                        HitBox hitBox = actionInfo.hitBoxes[i];

                        // Select a hit box type
                        hitBox.hitBoxType = (HitBoxType)EditorGUILayout.EnumPopup("Hit box type:", hitBox.hitBoxType);

                        // If the hit box is a standard box collider attached to a bone
                        if (hitBox.hitBoxType == HitBoxType.Standard)
                        {
                            // If a template GameObject is provided, copy its values to the hit box
                            templateHitBoxes[i] = (GameObject)EditorGUILayout.ObjectField("Copy Values From:", templateHitBoxes[i], typeof(GameObject), true);

                            // If a template object is given, copy its values to the hit box data instance
                            if (templateHitBoxes[i] != null)
                            {
                                // Copy the values from the template object
                                BoneFollower boneFollowerTemplate = templateHitBoxes[i].GetComponent <BoneFollower>();
                                hitBox.boneName = boneFollowerTemplate.boneName;

                                BoxCollider2D colliderTemplate = templateHitBoxes[i].GetComponent <BoxCollider2D>();
                                hitBox.offset = colliderTemplate.offset;
                                hitBox.size   = colliderTemplate.size;
                            }

                            // Edit HitBox properties
                            hitBox.boneName = EditorGUILayout.TextField("Bone Name:", hitBox.boneName);
                            hitBox.offset   = EditorGUILayout.Vector2Field("Offset:", hitBox.offset);
                            hitBox.size.x   = EditorGUILayout.FloatField("Width:", hitBox.size.x);
                            hitBox.size.y   = EditorGUILayout.FloatField("Height:", hitBox.size.y);
                        }
                        // Else, if the hit box automatically hits its target at a specified time
                        else if (hitBox.hitBoxType == HitBoxType.ForceHit)
                        {
                            EditorGUILayout.LabelField("Frame Select:");

                            EditorGUI.indentLevel = 2;

                            // Cycle through each hit frame, where the j-th hit frame corresponds to the j-th animation sequence
                            for (int j = 0; j < hitBox.hitFrames.Length; j++)
                            {
                                EditorGUILayout.BeginHorizontal();
                                {
                                    // Choose the frame where the hit box hits its target for the j-th animation sequence
                                    EditorGUILayout.LabelField("Animation sequence " + j + ", frame:");
                                    hitBox.hitFrames[j] = EditorGUILayout.IntField("", hitBox.hitFrames[j], GUILayout.Width(100));
                                }
                                EditorGUILayout.EndHorizontal();
                            }
                        }

                        EditorGUI.indentLevel = 1;

                        // "Hit Options" foldout
                        showHitOptionsFoldouts[i] = EditorGUILayout.Foldout(showHitOptionsFoldouts[i], "Hit Options");

                        if (showHitOptionsFoldouts[i])
                        {
                            // Change what happens when a hit box hits an opposing hit box
                            hitBox.hitInfo.baseDamage = EditorGUILayout.FloatField("Base Damage:", hitBox.hitInfo.baseDamage);
                            // hitBox.hitInfo.hitStrength = (HitStrength)EditorGUILayout.EnumPopup ("Strength:", hitBox.hitInfo.hitStrength);
                            hitBox.hitInfo.knockbackVelocity = EditorGUILayout.Vector2Field("Knockback velocity:", hitBox.hitInfo.knockbackVelocity);
                            hitBox.hitInfo.knockbackTime     = EditorGUILayout.FloatField("Knockback time:", hitBox.hitInfo.knockbackTime);
                            hitBox.hitInfo.freezeFrames      = EditorGUILayout.IntField("Freeze time: (frames):", hitBox.hitInfo.freezeFrames);

                            hitBox.hitInfo.selfEvents      = hitSelfEventsFoldouts[i].Display();
                            hitBox.hitInfo.adversaryEvents = hitAdversaryEventsFoldouts[i].Display();
                        }

                        // Delete button
                        EditorGUILayout.BeginHorizontal();
                        {
                            // Left padding
                            EditorGUILayout.LabelField("");

                            // Delete hit box
                            if (GUILayout.Button("Delete", GUILayout.Width(60)))
                            {
                                actionInfo.hitBoxes        = ArrayUtils.Remove <HitBox>(actionInfo.hitBoxes, hitBox);
                                showHitOptionsFoldouts     = ArrayUtils.RemoveAt(showHitOptionsFoldouts, i);
                                templateHitBoxes           = ArrayUtils.Remove <GameObject>(templateHitBoxes, templateHitBoxes[i]);
                                hitSelfEventsFoldouts      = ArrayUtils.RemoveAt(hitSelfEventsFoldouts, i);
                                hitAdversaryEventsFoldouts = ArrayUtils.RemoveAt(hitAdversaryEventsFoldouts, i);
                            }
                        }
                        EditorGUILayout.EndHorizontal();

                        EditorGUILayout.Space();
                    }

                    // "New Hit Box" Button
                    if (GUILayout.Button("New Hit Box"))
                    {
                        HitBox newHitBox = new HitBox();
                        actionInfo.hitBoxes = ArrayUtils.Add <HitBox>(actionInfo.hitBoxes, newHitBox);
                        // Create the 'hitFrames' array to match the number of animation sequences for the action
                        actionInfo.hitBoxes[actionInfo.hitBoxes.Length - 1].hitFrames = new int[actionInfo.animationSequences.Length];
                        showHitOptionsFoldouts     = ArrayUtils.Add <bool>(showHitOptionsFoldouts, false);
                        templateHitBoxes           = ArrayUtils.Add <GameObject>(templateHitBoxes, null);
                        hitSelfEventsFoldouts      = ArrayUtils.Add <EventsFoldout> (hitSelfEventsFoldouts, new EventsFoldout("Events [Self]", newHitBox.hitInfo.selfEvents));
                        hitAdversaryEventsFoldouts = ArrayUtils.Add <EventsFoldout> (hitAdversaryEventsFoldouts, new EventsFoldout("Events [Adversary]", newHitBox.hitInfo.adversaryEvents));
                    }
                }
                EditorGUILayout.EndVertical();
            }             // End "Hit Box" foldout

            /******************
            * FORCES FOLDOUT *
            ******************/

            EditorGUI.indentLevel = 0;

            showForcesFoldout = EditorGUILayout.Foldout(showForcesFoldout, "Forces (" + actionInfo.forces.Length + ")");

            if (showForcesFoldout)
            {
                EditorGUILayout.BeginVertical();
                {
                    EditorGUI.indentLevel = 1;

                    // Display each force for the action
                    for (int i = 0; i < actionInfo.forces.Length; i++)
                    {
                        Force force = actionInfo.forces[i];

                        // Select a force type
                        force.forceType = (ForceType)EditorGUILayout.EnumPopup("Force Type:", force.forceType);
                        switch (force.forceType)
                        {
                        case ForceType.Velocity:
                            force.velocity = EditorGUILayout.Vector2Field("Velocity:", force.velocity);
                            force.relativeToFacingDirection = EditorGUILayout.Toggle("Relative to facing direction?", force.relativeToFacingDirection);
                            break;

                        case ForceType.Position:
                            force.target = (TargetPosition)EditorGUILayout.EnumPopup("Target Position:", force.target);
                            if (force.target == TargetPosition.CustomPosition)
                            {
                                force.customTargetPosition = EditorGUILayout.Vector2Field("Custom Position:", force.customTargetPosition);
                            }
                            force.faceTarget = EditorGUILayout.Toggle("Face target?", force.faceTarget);
                            break;
                        }

                        EditorGUI.indentLevel = 1;

                        // "Starting Time" foldout
                        showStartTimeFoldouts[i] = EditorGUILayout.Foldout(showStartTimeFoldouts[i], "Starting Time");

                        if (showStartTimeFoldouts[i])
                        {
                            EditorGUI.indentLevel = 2;

                            // Specify the starting time of this force
                            EditorGUILayout.BeginHorizontal();
                            {
                                force.startTime.type = (DurationType)EditorGUILayout.EnumPopup("Start at:", force.startTime.type);
                                switch (force.startTime.type)
                                {
                                case DurationType.WaitForAnimationComplete:
                                    force.startTime.animationToWaitFor = EditorGUILayout.IntField("", force.startTime.animationToWaitFor, GUILayout.Width(80));
                                    break;

                                case DurationType.Frame:
                                    force.startTime.nFrames = EditorGUILayout.IntField("", force.startTime.nFrames, GUILayout.Width(80));
                                    break;
                                }
                            }
                            EditorGUILayout.EndHorizontal();
                        }

                        EditorGUI.indentLevel = 1;

                        // "Duration" foldout
                        showDurationFoldouts[i] = EditorGUILayout.Foldout(showDurationFoldouts[i], "Duration");

                        if (showDurationFoldouts[i])
                        {
                            EditorGUI.indentLevel = 2;

                            //Define the duration of the force
                            force.duration.type = (DurationType)EditorGUILayout.EnumPopup("Duration type:", force.duration.type);

                            switch (force.duration.type)
                            {
                            case DurationType.WaitForAnimationComplete:
                                force.duration.animationToWaitFor = EditorGUILayout.IntField("Stop at animation:", force.duration.animationToWaitFor);
                                break;

                            case DurationType.Frame:
                                force.duration.nFrames = EditorGUILayout.IntField("Number of frames:", force.duration.nFrames);
                                break;
                            }
                        }

                        EditorGUI.indentLevel = 1;

                        // "On Complete" foldout
                        showOnCompleteEventFoldouts[i] = EditorGUILayout.Foldout(showOnCompleteEventFoldouts[i], "On Complete");

                        if (showOnCompleteEventFoldouts[i])
                        {
                            // Select what to perform when the force is done being applied
                            force.onCompleteEvent.type = (Brawler.EventType)EditorGUILayout.EnumPopup("Event type:", force.onCompleteEvent.type);

                            if (force.onCompleteEvent.type == Brawler.EventType.PerformAction)
                            {
                                // Select an action
                                force.onCompleteEvent.actionToPerform = (ActionScriptableObject)EditorGUILayout.ObjectField("Action:",
                                                                                                                            force.onCompleteEvent.actionToPerform,
                                                                                                                            typeof(ActionScriptableObject), false);
                            }
                            else if (force.onCompleteEvent.type == Brawler.EventType.PerformBasicAction)
                            {
                                // Select a basic action
                                force.onCompleteEvent.basicActionToPerform = (BasicActionType)EditorGUILayout.EnumPopup("Basic action:",
                                                                                                                        force.onCompleteEvent.basicActionToPerform);
                            }
                        }

                        // Delete a force
                        EditorGUILayout.BeginHorizontal();
                        {
                            EditorGUILayout.LabelField("");
                            // Delete a force
                            if (GUILayout.Button("Delete", GUILayout.Width(60)))
                            {
                                actionInfo.forces = ArrayUtils.Remove <Force>(actionInfo.forces, force);

                                // Update boolean arrays for starting time/duration foldouts
                                showStartTimeFoldouts       = ArrayUtils.RemoveAt(showStartTimeFoldouts, i);
                                showDurationFoldouts        = ArrayUtils.RemoveAt(showDurationFoldouts, i);
                                showOnCompleteEventFoldouts = ArrayUtils.RemoveAt(showOnCompleteEventFoldouts, i);
                            }
                        }
                        EditorGUILayout.EndVertical();

                        EditorGUILayout.Space();
                    }

                    // "New force" button
                    if (GUILayout.Button("New Force"))
                    {
                        actionInfo.forces = ArrayUtils.Add <Force>(actionInfo.forces, new Force());

                        // Update boolean arrays for starting time/duration foldouts
                        showStartTimeFoldouts       = ArrayUtils.Add <bool>(showStartTimeFoldouts, false);
                        showDurationFoldouts        = ArrayUtils.Add <bool>(showDurationFoldouts, false);
                        showOnCompleteEventFoldouts = ArrayUtils.Add <bool>(showOnCompleteEventFoldouts, false);
                    }
                }
                EditorGUILayout.EndVertical();
            }             // End "Forces" foldout

            /*********************************
            * LINKABLE COMBAT MOVES FOLDOUT *
            *********************************/

            EditorGUI.indentLevel = 0;

            showLinkableActionsFoldout = EditorGUILayout.Foldout(showLinkableActionsFoldout, "Linkable Combat Actions (" +
                                                                 actionInfo.linkableCombatActionScriptableObjects.Length + ")");

            if (showLinkableActionsFoldout)
            {
                EditorGUI.indentLevel = 1;

                for (int i = 0; i < actionInfo.linkableCombatActionScriptableObjects.Length; i++)
                {
                    EditorGUILayout.BeginHorizontal();
                    {
                        // Choose a linkable combat action
                        actionInfo.linkableCombatActionScriptableObjects[i] = (ActionScriptableObject)EditorGUILayout.ObjectField(
                            actionInfo.linkableCombatActionScriptableObjects[i],
                            typeof(ActionScriptableObject), false);

                        // Delete a linkable combat action
                        if (GUILayout.Button("X", GUILayout.Width(40)))
                        {
                            actionInfo.linkableCombatActionScriptableObjects = ArrayUtils.RemoveAt(
                                actionInfo.linkableCombatActionScriptableObjects, i);
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                }

                // New linkable combat action ("+") button
                EditorGUILayout.BeginHorizontal();
                {
                    EditorGUILayout.LabelField("");

                    if (GUILayout.Button("+", GUILayout.Width(40)))
                    {
                        actionInfo.linkableCombatActionScriptableObjects = ArrayUtils.Add <ActionScriptableObject>(
                            actionInfo.linkableCombatActionScriptableObjects, null);
                    }
                }
                EditorGUILayout.EndHorizontal();


                EditorGUILayout.HelpBox("The combat actions which can cancel this action and be performed instead. Useful for creating combos. " +
                                        "(Note that any combat action can be performed after this action. However, if a move is in this list, " +
                                        "it has higher priority, and will be chosen first over another one which satisfies the same input.",
                                        MessageType.Info);
            }

            /***************************
             * EVENTS ON START FOLDOUT *
             **************************/
            EditorGUI.indentLevel = 0;

            actionInfo.onStartEvents = onStartEventsFoldout.Display();

            /******************
            * SOUNDS FOLDOUT *
            ******************/

            EditorGUI.indentLevel = 0;

            showSoundsFoldout = EditorGUILayout.Foldout(showSoundsFoldout, "Sounds");

            if (showSoundsFoldout)
            {
                EditorGUILayout.BeginVertical();
                {
                    /** Start sounds foldout */
                    EditorGUI.indentLevel = 1;

                    showStartSoundsFoldout = EditorGUILayout.Foldout(showStartSoundsFoldout, "On Start (" + actionInfo.startSounds.Length + ")");

                    if (showStartSoundsFoldout)
                    {
                        EditorGUI.indentLevel = 2;

                        // Display each possible start sound (one is chosen at random when move is performed)
                        for (int i = 0; i < actionInfo.startSounds.Length; i++)
                        {
                            // Animation text field
                            EditorGUILayout.BeginHorizontal();
                            {
                                actionInfo.startSounds[i] = (AudioClip)EditorGUILayout.ObjectField(actionInfo.startSounds[i], typeof(AudioClip), false);

                                // Delete animation string from animation sequence
                                if (GUILayout.Button("X", GUILayout.Width(40)))
                                {
                                    actionInfo.startSounds = ArrayUtils.Remove <AudioClip>(actionInfo.startSounds, actionInfo.startSounds[i]);
                                }
                            }
                            EditorGUILayout.EndHorizontal();
                        }

                        // Add start sound ("+") button
                        EditorGUILayout.BeginHorizontal();
                        {
                            EditorGUILayout.LabelField("");
                            // Add sound
                            if (GUILayout.Button("+", GUILayout.Width(40)))
                            {
                                actionInfo.startSounds = ArrayUtils.Add <AudioClip>(actionInfo.startSounds, null);
                            }
                        }
                        EditorGUILayout.EndHorizontal();

                        // Display help box if multiple sounds provided
                        if (actionInfo.startSounds.Length > 1)
                        {
                            EditorGUILayout.HelpBox("One sound is chosen at random when the action is performed", MessageType.Info);
                        }
                    }

                    /** Impact sounds foldout */
                    EditorGUI.indentLevel = 1;

                    showImpactSoundsFoldout = EditorGUILayout.Foldout(showImpactSoundsFoldout, "On Impact (" + actionInfo.impactSounds.Length + ")");

                    if (showImpactSoundsFoldout)
                    {
                        EditorGUI.indentLevel = 2;

                        // Display each possible start sound (one is chosen at random when move is performed)
                        for (int i = 0; i < actionInfo.impactSounds.Length; i++)
                        {
                            // Impact sound
                            EditorGUILayout.BeginHorizontal();
                            {
                                actionInfo.impactSounds[i] = (AudioClip)EditorGUILayout.ObjectField(actionInfo.impactSounds[i], typeof(AudioClip), false);

                                // Delete animation string from animation sequence
                                if (GUILayout.Button("X", GUILayout.Width(40)))
                                {
                                    actionInfo.impactSounds = ArrayUtils.Remove <AudioClip>(actionInfo.impactSounds, actionInfo.impactSounds[i]);
                                }
                            }
                            EditorGUILayout.EndHorizontal();
                        }

                        // Add impact sound ("+") button
                        EditorGUILayout.BeginHorizontal();
                        {
                            EditorGUILayout.LabelField("");
                            // Add new impact sound
                            if (GUILayout.Button("+", GUILayout.Width(40)))
                            {
                                actionInfo.impactSounds = ArrayUtils.Add <AudioClip>(actionInfo.impactSounds, null);
                            }
                        }
                        EditorGUILayout.EndHorizontal();

                        // Display help box if multiple sounds provided
                        if (actionInfo.impactSounds.Length > 1)
                        {
                            EditorGUILayout.HelpBox("One sound is chosen at random when the action is performed", MessageType.Info);
                        }
                    }
                }
                EditorGUILayout.EndVertical();
            }             // End "Hit Box" foldout

            /*******************
            * OPTIONS FOLDOUT *
            *******************/

            EditorGUI.indentLevel = 0;

            showOptionsFoldout = EditorGUILayout.Foldout(showHitBoxFoldout, "Options");

            if (showOptionsFoldout)
            {
                EditorGUILayout.BeginVertical();
                {
                    EditorGUI.indentLevel = 1;

                    // "Cancelable?" checkbox
                    actionInfo.cancelable = EditorGUILayout.Toggle("Cancelable?", actionInfo.cancelable);
                    // "Can cancel any move?" checkbox
                    actionInfo.overrideCancelable = EditorGUILayout.Toggle("Can cancel current move?", actionInfo.overrideCancelable);
                }
                EditorGUILayout.EndVertical();
            }             // End "Options" foldout
        }
        EditorGUILayout.EndVertical();
    }