using System; using System.Text; using System.Collections.Generic; using Golems.Attributes; #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; using UnityEngine.Serialization; namespace RogueUtils.Data { public abstract class BaseVariable : SubBaseVariable, IValueMonitor #if UNITY_EDITOR , ISerializationCallbackReceiver #endif { // // NOTE: you cannot have Editor only serialised fields in Generics, it will crash a build // see https://issuetracker.unity3d.com/issues/serialization-layout-error-thrown-in-build-when-using-generic-classes-with-fields-that-only-exist-in-the-editor // [Multiline] public string m_Description = ""; [SerializeField] protected T m_Value; // DW: Lets re-implement this down the line /*#if UNITY_EDITOR [NonSerialized] [ShowInInspector] [OnValueChanged("BaseHandleEditorValueChange")] [LabelText("Value")] // // This is an inspector editable version of the value // protected T m_EditorValue; #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 InternalOnChange; public event EventHandler OnChange { add { InternalOnChange += value; } remove { InternalOnChange -= value; } } public class OnChangeEventArgs : EventArgs { public T OldValue { get; private set; } public T NewValue { get; private set; } public OnChangeEventArgs(T value) { OldValue = value; NewValue = value; } public OnChangeEventArgs(T oldValue, T newValue) { OldValue = oldValue; NewValue = newValue; } } protected virtual T DefaultValue { get => default; } public virtual T Value { get { return m_Value; } set { T oldValue = m_Value; m_Value = value; /*#if UNITY_EDITOR m_EditorValue = m_Value; #endif*/ BaseHandleValueChange(); InternalOnChange?.Invoke(this, new OnChangeEventArgs(oldValue, m_Value)); } } [Button] public void ForceOnChange() { InternalOnChange?.Invoke(this, new OnChangeEventArgs(m_Value)); } public override TValue GetValue() { var value = Value; if (value is TValue theValue) { return theValue; } return default; } public override object GetValueUntyped() { return Value; } public override void SetValueUntyped(object value) { if (value is T theValue) { Value = theValue; } } public void MonitoredValueChanged(T value, T _) { Value = value; } protected virtual void OnEnable() { #if UNITY_EDITOR EditorApplication.playModeStateChanged += HandlePlayModeStateChange; #endif } protected virtual void OnDisable() { #if UNITY_EDITOR EditorApplication.playModeStateChanged -= HandlePlayModeStateChange; #endif } #if UNITY_EDITOR private void HandlePlayModeStateChange(PlayModeStateChange stateChange) { bool doDefaultValue = false; switch (stateChange) { case PlayModeStateChange.EnteredEditMode: break; case PlayModeStateChange.ExitingEditMode: break; case PlayModeStateChange.EnteredPlayMode: break; case PlayModeStateChange.ExitingPlayMode: doDefaultValue = m_DefaultOnExitPlayMode; break; } if (doDefaultValue) { var defaultValue = DefaultValue; //Undo.RecordObject(this, $"{name}: Clearing list"); m_Value = defaultValue; #if UNITY_EDITOR // m_EditorValue = m_Value; #endif // // Marking this dirty so Unity sees the value is its default value // JJA: Doing this means the user has to manually save otherwise the Dirty flag carries over to the next time // Play is hit, causing it to save with the value it has after entering play mode (ie the runtime, not default value). // Bottom line: There is no need to set this dirty, as play mode changes aren't saved and by changing it back to default on exit we already avoid any chance of a runtime value being saved later // EditorUtility.SetDirty(this); } AdditionalHandlePlayModeStateChange(stateChange); } /// /// Do any additional changes needed when changing PlayMode. /// NOTE: any override must be surrounded by #if UNITY_EDITOR as it is Editor only. /// /// protected virtual void AdditionalHandlePlayModeStateChange(PlayModeStateChange stateChange) { } #endif #if UNITY_EDITOR [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Odin")] private void BaseHandleEditorValueChange() { HandleEditorValueChange(); // // Marking this dirty so Unity sees the value change // EditorUtility.SetDirty(this); } protected virtual void HandleEditorValueChange() { //Value = m_EditorValue; } // [BoxGroup("Debug/Listeners")] // [HideIf("@this.m_InspectorValueListeners == null || this.m_InspectorValueListeners.Count == 0")] // [Button("Log Listeners")] private void LogListListeners() { var listeners = InternalOnChange?.GetInvocationList(); if (listeners == null) { return; } StringBuilder builder = new StringBuilder(); foreach (var listener in listeners) { builder.AppendLine(string.Format("{0}: listener {1}", name, listener)); } Debug.Log(builder.ToString()); } #endif public override string ToString() { if (m_Value == null) { return name + " (null)"; } return name + " (" + m_Value.ToString() + ")"; } #if UNITY_EDITOR public void OnBeforeSerialize() { } public void OnAfterDeserialize() { //m_EditorValue = m_Value; } #endif } }