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.
541 lines
22 KiB
C#
541 lines
22 KiB
C#
using MalbersAnimations.Scriptables;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using MalbersAnimations.Events;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditorInternal;
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace MalbersAnimations.Controller
|
|
{
|
|
[AddComponentMenu("Malbers/Animal Controller/Combo Manager")]
|
|
public class ComboManager : MonoBehaviour
|
|
{
|
|
[RequiredField] public MAnimal animal;
|
|
|
|
public int Branch = 0;
|
|
public List<Combo> combos;
|
|
|
|
public IntReference ActiveComboIndex = new IntReference(0);
|
|
|
|
public Combo ActiveCombo { get; internal set; }
|
|
|
|
public int ActiveComboSequenceIndex { get; internal set; }
|
|
|
|
public ComboSequence ActiveComboSequence => ActiveCombo.CurrentSequence;
|
|
|
|
/// <summary> Is the manager playing a combo? </summary>
|
|
public bool PlayingCombo { get; internal set; }
|
|
|
|
public bool debug;
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (!animal) animal = this.FindComponent<MAnimal>();
|
|
|
|
ActiveCombo = combos[ActiveComboIndex];
|
|
|
|
animal.OnModeEnd.AddListener(OnModeEnd);
|
|
Restart();
|
|
}
|
|
private void OnDisable() { animal.OnModeEnd.RemoveListener(OnModeEnd); }
|
|
|
|
private void OnModeEnd(int modeID, int CurrentExitAbility)
|
|
{
|
|
if (PlayingCombo)
|
|
{
|
|
if (ActiveComboSequence.Finisher)
|
|
{
|
|
ActiveCombo.OnComboFinished.Invoke(ActiveComboSequenceIndex);
|
|
MDebug($"Combo Finished. <b>[{ActiveComboSequenceIndex}]</b> Branch:<b>[{Branch}]</b>. [Restarting]");
|
|
Restart();
|
|
}
|
|
else if (CurrentExitAbility == ActiveComboSequence.Ability ) //Are we exiting the Current Secuence or just the Old one??? A new Secuence is playing
|
|
{
|
|
StartCoroutine(ComboInterrupted());
|
|
}
|
|
}
|
|
}
|
|
|
|
protected IEnumerator ComboInterrupted()
|
|
{
|
|
yield return null;
|
|
yield return null; //Wait 2 frames
|
|
|
|
if (!animal.IsPlayingMode) // if is no longer playing a Mode then means it was interruptedd
|
|
{
|
|
MDebug($"Incomplete <b>[{ActiveComboSequenceIndex}]</b> Branch: <b>[{Branch}]</b>. [Restarting]");
|
|
ActiveCombo.OnComboInterrupted.Invoke(ActiveComboSequenceIndex);
|
|
Restart();//meaning it got to the end of the combo
|
|
}
|
|
yield return null;
|
|
}
|
|
|
|
|
|
public virtual void SetActiveCombo(int index)
|
|
{
|
|
ActiveComboIndex = index;
|
|
ActiveCombo = combos[ActiveComboIndex % combos.Count];
|
|
}
|
|
|
|
public virtual void SetActiveCombo(IntVar index) => SetActiveCombo(index.Value);
|
|
|
|
public virtual void SetActiveCombo(string ComboName)
|
|
{
|
|
int index = combos.FindIndex(x => x.Name == ComboName);
|
|
if (index != -1) SetActiveCombo(index);
|
|
|
|
}
|
|
|
|
public virtual void Play(int branch)
|
|
{
|
|
if (animal.Sleep) return;
|
|
if (!enabled) return;
|
|
if (!animal.IsPlayingMode && !animal.IsPreparingMode) Restart(); //Means is not Playing any mode so Restart
|
|
|
|
Branch = branch;
|
|
if (ActiveCombo != null) ActiveCombo.Play(this);
|
|
}
|
|
|
|
public virtual void PlayCombo(int branch) => Play(branch);
|
|
|
|
|
|
public virtual void Restart()
|
|
{
|
|
ActiveComboSequenceIndex = 0;
|
|
PlayingCombo = false;
|
|
|
|
if (ActiveCombo != null)
|
|
{
|
|
ActiveCombo.CurrentSequence = null; //Clean the current combo secuence
|
|
ActiveCombo.ActiveSequenceIndex = -1; //Clean the current combo secuence
|
|
foreach (var seq in ActiveCombo.Sequence) seq.Used = false; //Set that the secuenced is used to
|
|
}
|
|
|
|
MDebug("Restart");
|
|
}
|
|
|
|
|
|
|
|
internal void MDebug(string value)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (debug) Debug.Log($"<b><color=orange>[{animal.name}] - [Combo]</color></b> - {value}",this);
|
|
#endif
|
|
}
|
|
|
|
[HideInInspector] public int selectedCombo = -1;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class Combo
|
|
{
|
|
public ModeID Mode;
|
|
public string Name = "Combo1";
|
|
|
|
public List<ComboSequence> Sequence = new List<ComboSequence>();
|
|
public ComboSequence CurrentSequence { get; internal set; }
|
|
|
|
/// <summary> Current Index on the list to search combos. This is used to avoid searching already used Sequences on the list</summary>
|
|
public int ActiveSequenceIndex{ get; internal set; }
|
|
|
|
public IntEvent OnComboFinished = new IntEvent();
|
|
public IntEvent OnComboInterrupted = new IntEvent();
|
|
|
|
public void Play(ComboManager M)
|
|
{
|
|
var animal = M.animal;
|
|
|
|
if (!animal.IsPlayingMode) //Means is starting the combo
|
|
{
|
|
for (int i = 0; i < Sequence.Count; i++)
|
|
{
|
|
var Starter = Sequence[i];
|
|
|
|
if (!Starter.Used && Starter.Branch == M.Branch && Starter.PreviewsAbility == 0) //Only Start with Started Abilities
|
|
{
|
|
if (animal.Mode_TryActivate(Mode, Starter.Ability))
|
|
{
|
|
M.PlayingCombo = true;
|
|
PlaySequence(M, Starter);
|
|
ActiveSequenceIndex = i; //Finding which is the active secuence index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var aMode = animal.ActiveMode; //Get the Animal Active Mode
|
|
|
|
for (int i = ActiveSequenceIndex + 1; i < Sequence.Count; i++) //Search from the next one
|
|
{
|
|
var s = Sequence[i];
|
|
|
|
if (!s.Used && s.Branch == M.Branch && s.PreviewsAbility == aMode.AbilityIndex && s.Activation.IsInRange(animal.ModeTime))
|
|
{
|
|
if (animal.Mode_ForceActivate(Mode, s.Ability)) //Play the nex animation on the sequence
|
|
{
|
|
PlaySequence(M, s);
|
|
ActiveSequenceIndex = i; //Finding which is the active secuence index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PlaySequence(ComboManager M, ComboSequence sequence)
|
|
{
|
|
M.ActiveComboSequenceIndex = Mode.ID * 1000 + sequence.Ability;
|
|
CurrentSequence = sequence; //Store the current sequence
|
|
CurrentSequence.Used = true;
|
|
CurrentSequence.OnSequencePlay.Invoke(M.ActiveComboSequenceIndex);
|
|
|
|
M.MDebug($"Sequence: <b>[{M.ActiveComboSequenceIndex}]</b> - Branch:<b>[{M.Branch}]</b>. Time: {Time.time:F2}");
|
|
}
|
|
}
|
|
|
|
|
|
[System.Serializable]
|
|
public class ComboSequence
|
|
{
|
|
[MinMaxRange(0, 1)]
|
|
public RangedFloat Activation = new RangedFloat(0.3f, 0.6f);
|
|
public int PreviewsAbility = 0;
|
|
/// <summary> Ability needed to activate</summary>
|
|
public int Ability = 0;
|
|
/// <summary> Branch used on the combo sequence</summary>
|
|
public int Branch = 0;
|
|
public bool Used;
|
|
/// <summary> Is this Secuence a Finisher Combo? </summary>
|
|
public bool Finisher;
|
|
public IntEvent OnSequencePlay = new IntEvent();
|
|
}
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomEditor(typeof(ComboManager))]
|
|
|
|
public class ComboEditor : Editor
|
|
{
|
|
public static GUIStyle StyleGray => MTools.Style(new Color(0.5f, 0.5f, 0.5f, 0.3f));
|
|
public static GUIStyle StyleBlue => MTools.Style(new Color(0, 0.5f, 1f, 0.3f));
|
|
|
|
private int branch, prev, current;
|
|
|
|
SerializedProperty Branch, combos, animal, selectedCombo, debug, ActiveComboIndex;
|
|
private Dictionary<string, ReorderableList> SequenceReordable = new Dictionary<string, ReorderableList>();
|
|
private ReorderableList CombosReor;
|
|
|
|
private ComboManager M;
|
|
private int abiliIndex;
|
|
|
|
private void OnEnable()
|
|
{
|
|
M= (ComboManager )target;
|
|
|
|
animal = serializedObject.FindProperty("animal");
|
|
combos = serializedObject.FindProperty("combos");
|
|
Branch = serializedObject.FindProperty("Branch");
|
|
selectedCombo = serializedObject.FindProperty("selectedCombo");
|
|
debug = serializedObject.FindProperty("debug");
|
|
ActiveComboIndex = serializedObject.FindProperty("ActiveComboIndex");
|
|
|
|
CombosReor = new ReorderableList(serializedObject, combos, true, true, true, true)
|
|
{
|
|
drawElementCallback = Draw_Element_Combo,
|
|
drawHeaderCallback = Draw_Header_Combo,
|
|
onSelectCallback = Selected_ComboCB,
|
|
onRemoveCallback = OnRemoveCallback_Mode
|
|
};
|
|
}
|
|
|
|
private void Selected_ComboCB(ReorderableList list)
|
|
{
|
|
selectedCombo.intValue = list.index;
|
|
}
|
|
|
|
private void OnRemoveCallback_Mode(ReorderableList list)
|
|
{
|
|
// The reference value must be null in order for the element to be removed from the SerializedProperty array.
|
|
combos.DeleteArrayElementAtIndex(list.index);
|
|
list.index -= 1;
|
|
|
|
if (list.index == -1 && combos.arraySize > 0) list.index = 0; //In Case you remove the first one
|
|
|
|
selectedCombo.intValue--;
|
|
|
|
list.index = Mathf.Clamp(list.index, 0, list.index - 1);
|
|
|
|
EditorUtility.SetDirty(target);
|
|
}
|
|
|
|
private void Draw_Header_Combo(Rect rect)
|
|
{
|
|
float half = rect.width / 2;
|
|
var IDIndex = new Rect(rect.x, rect.y, 45, EditorGUIUtility.singleLineHeight);
|
|
var IDName = new Rect(rect.x + 45, rect.y, half - 15 - 45, EditorGUIUtility.singleLineHeight);
|
|
var IDRect = new Rect(rect.x + half + 10, rect.y, half - 10, EditorGUIUtility.singleLineHeight);
|
|
|
|
EditorGUI.LabelField(IDIndex, "Index");
|
|
EditorGUI.LabelField(IDName, " Name");
|
|
EditorGUI.LabelField(IDRect, " Mode");
|
|
}
|
|
|
|
private void Draw_Element_Combo(Rect rect, int index, bool isActive, bool isFocused)
|
|
{
|
|
var element = combos.GetArrayElementAtIndex(index);
|
|
var Mode = element.FindPropertyRelative("Mode");
|
|
var Name = element.FindPropertyRelative("Name");
|
|
rect.y += 2;
|
|
|
|
float half = rect.width / 2;
|
|
|
|
var IDIndex = new Rect(rect.x, rect.y, 25, EditorGUIUtility.singleLineHeight);
|
|
var IDName = new Rect(rect.x + 25, rect.y, half - 15 - 25, EditorGUIUtility.singleLineHeight);
|
|
var IDRect = new Rect(rect.x + half + 10, rect.y, half - 10, EditorGUIUtility.singleLineHeight);
|
|
|
|
var oldColor = GUI.contentColor;
|
|
|
|
if (index == M.ActiveComboIndex)
|
|
{
|
|
GUI.contentColor = Color.yellow;
|
|
}
|
|
|
|
|
|
EditorGUI.LabelField(IDIndex, "(" + index.ToString() + ")");
|
|
EditorGUI.PropertyField(IDName, Name, GUIContent.none);
|
|
EditorGUI.PropertyField(IDRect, Mode, GUIContent.none);
|
|
|
|
GUI.contentColor = oldColor;
|
|
|
|
}
|
|
|
|
private void DrawSequence(int ModeIndex, SerializedProperty combo, SerializedProperty sequence)
|
|
{
|
|
ReorderableList Reo_AbilityList;
|
|
string listKey = combo.propertyPath;
|
|
|
|
if (SequenceReordable.ContainsKey(listKey))
|
|
{
|
|
Reo_AbilityList = SequenceReordable[listKey]; // fetch the reorderable list in dict
|
|
}
|
|
else
|
|
{
|
|
Reo_AbilityList = new ReorderableList(combo.serializedObject, sequence, true, true, true, true)
|
|
{
|
|
drawElementCallback = (rect, index, isActive, isFocused) =>
|
|
{
|
|
rect.y += 2;
|
|
|
|
var Height = EditorGUIUtility.singleLineHeight;
|
|
var element = sequence.GetArrayElementAtIndex(index);
|
|
|
|
//var Activation = element.FindPropertyRelative("Activation");
|
|
var PreviewsAbility = element.FindPropertyRelative("PreviewsAbility");
|
|
var Ability = element.FindPropertyRelative("Ability");
|
|
var Branch = element.FindPropertyRelative("Branch");
|
|
var useD = element.FindPropertyRelative("Used");
|
|
var finisher = element.FindPropertyRelative("Finisher");
|
|
|
|
var IDRect = new Rect(rect) { height = Height };
|
|
|
|
float wid = rect.width / 3;
|
|
|
|
var IRWidth = 30f;
|
|
var Sep = -10f;
|
|
var Offset = 40f;
|
|
|
|
float xx = IRWidth + Offset;
|
|
|
|
var IndexRect = new Rect(IDRect) { width = IRWidth };
|
|
var BranchRect = new Rect(IDRect) { x = xx, width = wid - 15};
|
|
var PrevARect = new Rect(IDRect) { x = wid + xx+ Sep, width = wid - 15 - Sep };
|
|
var AbilityRect = new Rect(IDRect) { x = wid * 2 + xx+ Sep, width = wid - 15 - 20 };
|
|
var FinisherRect = new Rect(IDRect) { x = IDRect.width +30, width = 20 };
|
|
|
|
var style = new GUIStyle(EditorStyles.label);
|
|
|
|
if (!useD.boolValue && Application.isPlaying)style.normal.textColor = Color.green; //If the Combo is not used turn the combos to Green
|
|
|
|
|
|
EditorGUI.LabelField(IndexRect, "(" + index.ToString() + ")", style);
|
|
var oldCColor = GUI.contentColor;
|
|
var oldColor = GUI.color;
|
|
|
|
if (PreviewsAbility.intValue <= 0)
|
|
{
|
|
GUI.contentColor = Color.green;
|
|
}
|
|
|
|
if (Application.isPlaying)
|
|
{
|
|
if (M.ActiveCombo != null)
|
|
{
|
|
var Index = M.ActiveCombo.ActiveSequenceIndex;
|
|
|
|
if (Index == index) //Paint Active Index
|
|
{
|
|
GUI.contentColor =
|
|
GUI.color = Color.yellow;
|
|
|
|
if (M.ActiveComboSequence.Finisher) //Paint finisher
|
|
{
|
|
GUI.contentColor =
|
|
GUI.color = (Color.red + Color.yellow) / 2;
|
|
}
|
|
}
|
|
else if (Index > index) //Paint Used Index
|
|
{
|
|
GUI.contentColor =
|
|
GUI.color = Color.gray;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
EditorGUI.PropertyField(BranchRect, Branch, GUIContent.none);
|
|
EditorGUI.PropertyField(PrevARect, PreviewsAbility, GUIContent.none);
|
|
EditorGUI.PropertyField(AbilityRect, Ability, GUIContent.none);
|
|
EditorGUI.PropertyField(FinisherRect, finisher, GUIContent.none);
|
|
GUI.contentColor = oldCColor;
|
|
GUI.color = oldColor;
|
|
|
|
if (index == abiliIndex)
|
|
{
|
|
branch = Branch.intValue;
|
|
prev = PreviewsAbility.intValue;
|
|
current = Ability.intValue;
|
|
}
|
|
},
|
|
|
|
drawHeaderCallback = rect =>
|
|
{
|
|
var Height = EditorGUIUtility.singleLineHeight;
|
|
var IDRect = new Rect(rect) { height = Height };
|
|
|
|
float wid = rect.width / 3;
|
|
var IRWidth = 30f;
|
|
var Sep = -10f;
|
|
var Offset = 40f;
|
|
|
|
float xx = IRWidth + Offset;
|
|
|
|
var IndexRect = new Rect(IDRect) { width = IRWidth +5};
|
|
var BranchRect = new Rect(IDRect) { x = xx, width = wid - 15 };
|
|
var PrevARect = new Rect(IDRect) { x = wid + xx + Sep, width = wid - 15 };
|
|
var AbilityRect = new Rect(IDRect) { x = wid * 2 + xx + Sep - 10, width = wid - 80};
|
|
var FinisherRect = new Rect(IDRect) { x = IDRect.width-15, width = 45 };
|
|
|
|
EditorGUI.LabelField(IndexRect, "Index");
|
|
EditorGUI.LabelField(BranchRect, " Branch");
|
|
EditorGUI.LabelField(PrevARect, new GUIContent("Activation Ability" ,"Current Mode Ability [Index] Playing on the Animal needed to activate a sequence"));
|
|
EditorGUI.LabelField(AbilityRect, new GUIContent("Next Ability", "Next Mode Ability [Index] to Play on the Animal if the Active Mode Animation is withing the Activation Range limit "));
|
|
EditorGUI.LabelField(FinisherRect, new GUIContent("Finisher", "Combo Finisher"));
|
|
},
|
|
|
|
//elementHeightCallback = (index) =>
|
|
//{
|
|
// Repaint();
|
|
|
|
// if (index == abiliIndex)
|
|
|
|
// return EditorGUIUtility.singleLineHeight * 3;
|
|
// else
|
|
// return EditorGUIUtility.singleLineHeight + 5;
|
|
//}
|
|
};
|
|
|
|
SequenceReordable.Add(listKey, Reo_AbilityList); //Store it on the Editor
|
|
}
|
|
|
|
Reo_AbilityList.DoLayoutList();
|
|
|
|
abiliIndex = Reo_AbilityList.index;
|
|
|
|
if (abiliIndex != -1)
|
|
{
|
|
var element = sequence.GetArrayElementAtIndex(abiliIndex);
|
|
|
|
var Activation = element.FindPropertyRelative("Activation");
|
|
var OnSequencePlay = element.FindPropertyRelative("OnSequencePlay");
|
|
|
|
var lbl = "B[" + branch + "] AA[" + prev + "] NA[" + current + "]";
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
|
|
EditorGUILayout.LabelField("Sequence Properties - " + lbl);
|
|
EditorGUILayout.PropertyField(Activation, new GUIContent("Activation", "Range of the Preview Animation the Sequence can be activate"));
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.PropertyField(OnSequencePlay, new GUIContent("Sequence Play - " + lbl));
|
|
}
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
MalbersEditor.DrawDescription("Use Modes to create combo sequences. Active Combos using ComboManager.Play(int Branch)\nBranches are the different Inputs Values");
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
|
|
EditorGUILayout.PropertyField(animal );
|
|
EditorGUILayout.BeginHorizontal();
|
|
{
|
|
EditorGUILayout.PropertyField(ActiveComboIndex, new GUIContent("Active Combo Index", "Active Combo"));
|
|
|
|
MalbersEditor.DrawDebugIcon(debug);
|
|
// debug.boolValue = GUILayout.Toggle(debug.boolValue,new GUIContent("D","Debug"), EditorStyles.miniButton, GUILayout.Width(23));
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space(2f);
|
|
|
|
EditorGUILayout.PropertyField(Branch, new GUIContent("Branch",
|
|
"Current Branch ID for the Combo Sequence, if this value change then the combo will play different sequences"));
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
CombosReor.DoLayoutList();
|
|
|
|
CombosReor.index = selectedCombo.intValue;
|
|
int IndexCombo = CombosReor.index;
|
|
|
|
if (IndexCombo != -1)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
var combo = combos.GetArrayElementAtIndex(IndexCombo);
|
|
|
|
if (combo != null)
|
|
{
|
|
var name = combo.FindPropertyRelative("Name");
|
|
EditorGUILayout.LabelField(name.stringValue, EditorStyles.boldLabel);
|
|
// var active = combo.FindPropertyRelative("m_Active");
|
|
var OnComboFinished = combo.FindPropertyRelative("OnComboFinished");
|
|
var OnComboInterrupted = combo.FindPropertyRelative("OnComboInterrupted");
|
|
// EditorGUILayout.PropertyField(active, new GUIContent("Active", "is the Combo Active?"));
|
|
EditorGUILayout.HelpBox("Green Sequences are starters combos", MessageType.None);
|
|
EditorGUILayout.LabelField("Combo Sequence List", EditorStyles.boldLabel);
|
|
var sequence = combo.FindPropertyRelative("Sequence");
|
|
DrawSequence(IndexCombo, combo, sequence);
|
|
EditorGUILayout.PropertyField(OnComboFinished);
|
|
EditorGUILayout.PropertyField(OnComboInterrupted);
|
|
}
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
}
|
|
#endif
|
|
} |