using System; using DependencyInjection.ScriptableObjects; using UnityEngine; namespace DependencyInjection.ScriptableObjects { /// /// A ScriptableObject that acts as a typed, persistent anchor for a runtime reference. /// Serves as the bridge between the SO architecture and the DI system — inject the /// anchor itself so consumers always hold a stable reference and react to value changes. /// public abstract class RuntimeAnchor : ScriptableObject where T : class { [Tooltip("Optional fallback value used when no runtime instance is registered.")] [SerializeField] T _fallback; T _runtimeValue; bool _isSet; public T Value => _isSet ? _runtimeValue : _fallback; public bool IsSet => _isSet; public event Action OnValueChanged; /// Register an instance into this anchor. public void Register(T instance) { if (instance == null) { Debug.LogWarning($"[RuntimeAnchor<{typeof(T).Name}>] Attempted to register a null instance."); return; } var previous = Value; _runtimeValue = instance; _isSet = true; // only invoke if the value actually changed if (previous != _runtimeValue) { OnValueChanged?.Invoke(previous, _runtimeValue); } } /// Unregister the current instance (e.g., on object destroy). public void Deregister(T instance) { if (!_isSet || _runtimeValue != instance) return; var previous = Value; _runtimeValue = null; _isSet = false; OnValueChanged?.Invoke(previous, Value); // Value now returns _fallback } /// Force-clear without reference matching — use with care. public void Clear() { var previous = Value; _runtimeValue = null; _isSet = false; OnValueChanged?.Invoke(previous, Value); } void OnDisable() { _runtimeValue = null; _isSet = false; } } }