// Copyright (c) 2024 Augie R. Maddox, Guavaman Enterprises. All rights reserved. #pragma warning disable 0649 namespace Rewired.Glyphs { using System; using System.Collections.Generic; using Rewired; /// /// Base class for a Controller Element Glyph component. /// public abstract class ControllerElementGlyphBase : UnityEngine.MonoBehaviour { [UnityEngine.Tooltip("If set, when glyph/text objects are created, they will be instantiated from this prefab. If left blank, the global default prefab will be used.")] [UnityEngine.SerializeField] private UnityEngine.GameObject _glyphOrTextPrefab; [UnityEngine.Tooltip("Determines what types of objects are allowed.")] [UnityEngine.SerializeField] private AllowedTypes _allowedTypes; [NonSerialized] private readonly List _entries = new List(); [NonSerialized] private List _tempGlyphs = new List(); [NonSerialized] private UnityEngine.GameObject _lastGlyphOrTextPrefab; /// /// If set, when glyph/text objects are created, they will be instantiated from this prefab. /// If left blank, the global default prefab will be used. /// public virtual UnityEngine.GameObject glyphOrTextPrefab { get { return _glyphOrTextPrefab; } set { _glyphOrTextPrefab = value; RequireRebuild(); } } /// /// Determines what types of objects are allowed. /// public virtual AllowedTypes allowedTypes { get { return _allowedTypes; } set { _allowedTypes = value; } } /// /// Gets the list of entries. /// protected List entries { get { return _entries; } } protected virtual void Awake() { } protected virtual void Start() { } protected virtual void OnDestroy() { #if UNITY_EDITOR ReInput.EditorRecompileEvent -= OnEditorRecompile; #endif } protected virtual void OnEnable() { #if UNITY_EDITOR ReInput.EditorRecompileEvent -= OnEditorRecompile; ReInput.EditorRecompileEvent += OnEditorRecompile; #endif } protected virtual void OnDisable() { } protected virtual void Update() { if (!ReInput.isReady) return; // Monitor for prefab changes to automatically rebuild if (_lastGlyphOrTextPrefab != GetGlyphOrTextPrefabOrDefault()) { _lastGlyphOrTextPrefab = GetGlyphOrTextPrefabOrDefault(); RequireRebuild(); } #if UNITY_EDITOR HandleInspectorValueChanges(); #endif } /// /// Clears all instantiated glyph / text objects so they can be recreated from the prefab. /// public virtual void RequireRebuild() { ClearObjects(); } /// /// Clears all instantiated glyph / text objects objects. /// protected virtual void ClearObjects() { for (int i = 0; i < _entries.Count; i++) { if (_entries[i] == null) continue; _entries[i].Destroy(); } _entries.Clear(); Hide(); } /// /// Hides glyph / text objects that are no longer active. /// protected virtual void EvaluateObjectVisibility() { // Disable all fields except those that are visible for (int i = 0; i < _entries.Count; i++) { if (_entries[i] == null) continue; _entries[i].HideIfIdle(); } } /// /// Hides all glyph / text objects that are no longer active. /// If the parent transform is on a different GameObject, the parent /// GameObject will be hidden if all of its glyphs / text objects /// are inactive. /// /// The parent transform of the objects. protected virtual void EvaluateObjectVisibility(UnityEngine.Transform transform) { EvaluateObjectVisibility(transform, _entries); } /// /// Hides glyph / text objects that are no longer active in the specified list. /// If the parent transform is on a different GameObject, the parent /// GameObject will be hidden if all of its glyphs / text objects /// are inactive. /// /// The parent transform of the objects. /// The list of entries. protected virtual void EvaluateObjectVisibility(UnityEngine.Transform transform, List entries) { if (transform == this.transform) return; // do not modify visibility of this GameObject bool isVisible = false; for (int i = 0; i < entries.Count; i++) { if (entries[i].isVisible) { isVisible = true; } } if (transform.gameObject.activeSelf != isVisible) { transform.gameObject.SetActive(isVisible); } } /// /// Displays glyphs or text for an Action Element Map and adds them to the specified entries list. /// /// The action element map. /// The parent transform of the objects. /// The list of entries. /// The number of glyphs or text objects that were displayed. protected virtual int ShowGlyphsOrText(ActionElementMap actionElementMap, UnityEngine.Transform parent, List entries) { _tempGlyphs.Clear(); int count = 0; if (IsAllowed(AllowedTypes.Glyphs) && GetGlyphs(actionElementMap, _tempGlyphs) > 0) { if (!CreateObjectsAsNeeded(parent, entries, _tempGlyphs.Count)) return 0; for (int i = 0; i < _tempGlyphs.Count; i++) { entries[i].ShowGlyph(_tempGlyphs[i]); } count += _tempGlyphs.Count; } else if (IsAllowed(AllowedTypes.Text) && actionElementMap != null) { // fall back to text if (!CreateObjectsAsNeeded(parent, entries, 1)) return 0; entries[0].ShowText(actionElementMap.elementIdentifierName); count += 1; } return count; } /// /// Displays glyphs or text for an Action Element Map. /// /// The action element map. /// The number of glyphs or text objects that were displayed. protected virtual int ShowGlyphsOrText(ActionElementMap actionElementMap) { return ShowGlyphsOrText(actionElementMap, transform, _entries); } /// /// Displays glyphs or text for an Action Element Map and adds them to the specified entries list. /// /// The element identifier. /// The parent transform of the objects. /// The list of entries. /// The number of glyphs or text objects that were displayed. protected virtual int ShowGlyphsOrText(ControllerElementIdentifier elementIdentifier, AxisRange axisRange, UnityEngine.Transform parent, List entries) { if (elementIdentifier == null) return 0; object glyph; int count = 0; if (IsAllowed(AllowedTypes.Glyphs) && (glyph = elementIdentifier.GetGlyph(axisRange)) != null) { if (!CreateObjectsAsNeeded(parent, entries, 1)) return 0; entries[0].ShowGlyph(glyph); count += 1; } else if (IsAllowed(AllowedTypes.Text)) { // fall back to text if (!CreateObjectsAsNeeded(parent, entries, 1)) return 0; entries[0].ShowText(elementIdentifier.GetDisplayName(axisRange)); count += 1; } return count; } /// /// Displays glyphs or text for an Action Element Map. /// /// The element identifier. /// The axis range. /// The number of glyphs or text objects that were displayed. protected virtual int ShowGlyphsOrText(ControllerElementIdentifier elementIdentifier, AxisRange axisRange) { return ShowGlyphsOrText(elementIdentifier, axisRange, transform, _entries); } /// /// Hides all glyph / text objects. /// protected virtual void Hide() { for (int i = 0; i < _entries.Count; i++) { if (_entries[i] == null) continue; _entries[i].Hide(); } } /// /// Gets the glyph or text prefab if set, otherwise the default prefab. /// /// Glyph or text prefab if set, otherwise the default prefab. protected virtual UnityEngine.GameObject GetGlyphOrTextPrefabOrDefault() { return _glyphOrTextPrefab != null ? _glyphOrTextPrefab : GetDefaultGlyphOrTextPrefab(); } /// /// Gets the default glyph or text prefab. /// /// The default glyph or text prefab. protected abstract UnityEngine.GameObject GetDefaultGlyphOrTextPrefab(); /// /// Creates glyph / text objects on-demand. /// /// The parent transform of the objects. /// The list of entries. /// The number of objects required. /// True if objects were created, false if an error occurred. protected virtual bool CreateObjectsAsNeeded(UnityEngine.Transform parent, List entries, int count) { if (count <= 0) return false; UnityEngine.GameObject prefab = GetGlyphOrTextPrefabOrDefault(); if (prefab == null) { UnityEngine.Debug.LogError("Rewired: Default prefab is null."); return false; } if (entries == null) return false; int existingCount = entries.Count; UnityEngine.GameObject instance; GlyphOrTextBase component; for (int i = existingCount; i < count; i++) { instance = Instantiate(prefab); instance.name = "Object"; instance.hideFlags = UnityEngine.HideFlags.DontSave; instance.transform.SetParent(parent, false); component = instance.GetComponent(); if (component == null) { UnityEngine.Debug.LogError("Rewired: Prefab does not contain a " + typeof(GlyphOrTextBase) + " component."); Destroy(instance); continue; } GlyphOrTextObject obj = new GlyphOrTextObject(component); entries.Add(obj); if (entries != _entries) { _entries.Add(obj); } } return true; } /// /// Is the type allowed? /// /// Allowed type. /// True if the type is allowed, False otherwise. protected virtual bool IsAllowed(AllowedTypes allowedType) { if (_allowedTypes == AllowedTypes.All) return true; return allowedType == _allowedTypes; } #if UNITY_EDITOR [NonSerialized] private Action _inspectorActions; protected virtual void OnValidate() { _inspectorActions = null; // prevent buffering of actions in edit mode CheckInspectorValues(ref _inspectorActions); } protected virtual void CheckInspectorValues(ref Action actions) { } protected virtual void HandleInspectorValueChanges() { Action actions = _inspectorActions; _inspectorActions = null; if (actions != null) actions(); } protected virtual void OnEditorRecompile() { ClearObjects(); } #endif // Static /// /// Gets all glyphs for an Action Element Map including modifier glyphs. /// If any glyphs are missing, for example, one modifier key has no glyph, no glyphs will be returned. /// If multiple glyphs are returned, the list will be ordered modifier key glyphs first, primary glyph last. /// /// The Action Element Map. /// The list of glyph results. /// The number of glyphs returned. protected static int GetGlyphs(ActionElementMap actionElementMap, List results) { if (actionElementMap == null) return 0; int count = 1; if (actionElementMap.hasModifiers) { if (actionElementMap.modifierKey1 != ModifierKey.None) count += 1; if (actionElementMap.modifierKey2 != ModifierKey.None) count += 1; if (actionElementMap.modifierKey3 != ModifierKey.None) count += 1; } if (actionElementMap.elementIdentifierGlyphCount != count) return 0; actionElementMap.GetElementIdentifierGlyphs(results); return count; } // Classes /// /// Represents a glyph / text object. /// protected class GlyphOrTextObject { private GlyphOrTextBase _glyphOrText; private int _frame; private bool _isVisible; /// /// Determines if the glyph / text is visible. /// public virtual bool isVisible { get { return _isVisible; } protected set { _isVisible = value; } } /// /// The glyph / text object. /// public GlyphOrTextBase glyphOrText { get { return _glyphOrText; } set { _glyphOrText = value; } } /// /// Creates an instance. /// /// The glyph / text object. public GlyphOrTextObject(GlyphOrTextBase glyphOrText) { _glyphOrText = glyphOrText; } /// /// Displays the specified glyph. /// /// Glyph to display. public virtual void ShowGlyph(object glyph) { if (_glyphOrText == null) return; _glyphOrText.ShowGlyph(glyph); _frame = UnityEngine.Time.frameCount; _isVisible = true; } /// /// Displays the specified text. /// /// The text string to display. public virtual void ShowText(string text) { if (_glyphOrText == null) return; _glyphOrText.ShowText(text); _frame = UnityEngine.Time.frameCount; _isVisible = true; } /// /// Hides the glyph / text. /// public virtual void Hide() { if (_glyphOrText == null) return; if (!_isVisible) return; _glyphOrText.Hide(); _isVisible = false; } /// /// Hides the glyph / text if it hasn't been updated this frame. /// public virtual void HideIfIdle() { if (_frame == UnityEngine.Time.frameCount) return; Hide(); } /// /// Destroys glyph / text object. /// public virtual void Destroy() { if (_glyphOrText == null) return; UnityEngine.Object.Destroy(_glyphOrText.gameObject); _glyphOrText = null; _isVisible = false; } } /// /// The type of objects that are allowed. /// public enum AllowedTypes { /// /// All types are allowed. Glyphs will be displayed, or text if no glyph is available. /// All = 0, /// /// Only glyphs will be displayed. /// Glyphs = 1, /// /// Only text will be displayed. /// Text = 2 } } }