using System; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; namespace RogueUtils.Data { public abstract class BaseVariableReference : SubBaseVariableReference where TVariable : BaseVariable { // [Multiline] [UnityEngine.Serialization.FormerlySerializedAs("description")] public string m_Description = ""; [SerializeField] [HideInInspector] protected TVariable m_Variable; #if UNITY_EDITOR // // This is what appear in the inspector as the variable. // It is separate so that Inspector changes can go through the this.Value = new variable instance // code path. // //[ShowInInspector] //[OnValueChanged("BaseHandleEditorVariableChange")] protected TVariable m_InspectorVariable; #endif /// /// The reason for the m_ZeroOnExitPlayMode is that it is an Unity Editor only feature and /// resets the Variable value so that Git does not see a bunch of changes to runtime /// ScriptableObject Variables /// [SerializeField] protected bool m_DefaultOnExitPlayMode = false; private event EventHandler InternalOnVariableChange; protected event EventHandler.OnChangeEventArgs> InternalOnReferencedValueChange; protected event EventHandler.OnChangeEventArgs> InternalOnValueChange; public event EventHandler OnVariableChange { // Not thread safe. // Will add tracking of subscribers add { InternalOnVariableChange += value; } remove { InternalOnVariableChange -= value; } } public event EventHandler.OnChangeEventArgs> OnReferencedValueChange { // Not thread safe. // Will add tracking of subscribers add { InternalOnReferencedValueChange += value; } remove { InternalOnReferencedValueChange -= value; } } public event EventHandler.OnChangeEventArgs> OnValueChange { // Not thread safe. // Will add tracking of subscribers add { InternalOnValueChange += value; } remove { InternalOnValueChange -= value; } } protected virtual TVariable DefaultVariable { get => default; } public virtual TVariable Variable { get { return m_Variable; } set { if (m_Variable != null) { // // Remove the listeners from the old variable // m_Variable.OnChange -= HandleValueChange; } m_Variable = value; #if UNITY_EDITOR m_InspectorVariable = m_Variable; #endif if (m_Variable != null) { m_Variable.OnChange += HandleValueChange; } TriggerVariableListeners(); // // This is also a value change // if (m_Variable != null) { HandleValueChange(m_Variable, new BaseVariable.OnChangeEventArgs(m_Variable.Value)); } else { HandleValueChange(m_Variable, new BaseVariable.OnChangeEventArgs(default)); } } } public virtual TValue Value { get { return (m_Variable != null ? m_Variable.Value : default); } set { if (m_Variable != null) { m_Variable.Value = value; } } } protected virtual void HandleValueChange(object sender, BaseVariable.OnChangeEventArgs onChangeEventArgs) { SubBaseTriggerValueListeners(); if (m_Variable != null) { InternalOnValueChange?.Invoke(m_Variable, onChangeEventArgs); } InternalOnReferencedValueChange?.Invoke(this, onChangeEventArgs); } protected virtual void OnEnable() { #if UNITY_EDITOR EditorApplication.playModeStateChanged += HandlePlayModeStateChange; #endif // // Listen on the variable value changing. // if (m_Variable != null) { m_Variable.OnChange += HandleValueChange; } } protected virtual void OnDisable() { #if UNITY_EDITOR EditorApplication.playModeStateChanged -= HandlePlayModeStateChange; #endif // // Stop listening on the variable value changing. // if (m_Variable != null) { m_Variable.OnChange -= HandleValueChange; } /*#if UNITY_EDITOR if (m_TestRuntimeVariable != null) { RogueUtils.Utils.NiceDestroy(m_TestRuntimeVariable); m_TestRuntimeVariable = null; } #endif*/ } #if UNITY_EDITOR private void HandlePlayModeStateChange(PlayModeStateChange stateChange) { bool doDefaultVariable = false; switch (stateChange) { case PlayModeStateChange.EnteredEditMode: break; case PlayModeStateChange.ExitingEditMode: break; case PlayModeStateChange.EnteredPlayMode: break; case PlayModeStateChange.ExitingPlayMode: doDefaultVariable = m_DefaultOnExitPlayMode; break; } if (doDefaultVariable) { var defaultVariable = DefaultVariable; //Undo.RecordObject(this, $"{name}: Clearing list"); m_Variable = defaultVariable; // // Marking this dirty so Unity sees the value is its default value // EditorUtility.SetDirty(this); } } #endif public override SubBaseVariable GetVariableUntyped() { return Variable; } public override void SetVariableUntyped(SubBaseVariable variable) { if (variable is TVariable theVariable) { Variable = theVariable; } } public override T GetValue() { var variable = Variable; if (variable != null) { var value = variable.Value; if (value is T theValue) { return theValue; } } return default; } public override object GetValueUntyped() { var variable = Variable; if (variable != null) { return variable.Value; } return default; } public override void SetValueUntyped(object value) { var variable = Variable; if (variable != null) { if (value is TValue theValue) { variable.Value = theValue; } } } public override void TriggerVariableListeners() { InternalOnVariableChange?.Invoke(this, EventArgs.Empty); base.SubBaseTriggerVariableListeners(); } #if UNITY_EDITOR [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Odin")] private void BaseHandleEditorVariableChange() { Variable = m_InspectorVariable; } //[FoldoutGroup("Debug", Order = 200)] //[BoxGroup("Debug/Runtime")] //[ShowInInspector] //[ReadOnly] private TVariable m_TestRuntimeVariable; //[BoxGroup("Debug/Runtime")] //[Button("Set to runtime variable instance")] //[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Odin")] private void SetToRuntimeInstance() { if (m_TestRuntimeVariable == null) { m_TestRuntimeVariable = ScriptableObject.CreateInstance(); } Variable = m_TestRuntimeVariable; } #endif } }