using System; using System.Collections.Generic; using UnityEngine; using YachtDice.Modifiers.Definition; namespace YachtDice.Modifiers.Runtime { public class ModifierRegistry { private readonly List _instances = new(); private readonly List _activeCache = new(); private int _maxActiveSlots; private bool _activeCacheDirty = true; public event Action OnChanged; public event Action> OnActiveModifiersChanged; public IReadOnlyList All => _instances; public int MaxActiveSlots => _maxActiveSlots; public ModifierRegistry(int maxActiveSlots = 5) { this._maxActiveSlots = maxActiveSlots; } public IReadOnlyList Active { get { if (_activeCacheDirty) RebuildActiveCache(); return _activeCache; } } public int ActiveCount { get { int count = 0; foreach (var t in _instances) if (t.IsActive) count++; return count; } } public void SetMaxActiveSlots(int slots) { _maxActiveSlots = slots; } public ModifierInstance Add(ModifierDefinition definition) { var instance = new ModifierInstance(definition); _instances.Add(instance); _activeCacheDirty = true; OnChanged?.Invoke(); return instance; } public void Remove(ModifierInstance instance) { if (!_instances.Contains(instance)) return; var wasActive = instance.IsActive; instance.IsActive = false; _instances.Remove(instance); _activeCacheDirty = true; if (wasActive) OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); } public bool TryActivate(ModifierInstance instance) { if (instance.IsActive) return false; if (!_instances.Contains(instance)) return false; if (ActiveCount >= _maxActiveSlots) return false; instance.IsActive = true; _activeCacheDirty = true; OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); return true; } public void Deactivate(ModifierInstance instance) { if (!instance.IsActive) return; instance.IsActive = false; _activeCacheDirty = true; OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); } public void ConsumeChargesOnActive() { var changed = false; for (var i = _instances.Count - 1; i >= 0; i--) { var inst = _instances[i]; if (!inst.IsActive) continue; if (!inst.Definition.HasLimitedUses) continue; inst.ConsumeCharge(); if (inst.IsExpired) { _instances.RemoveAt(i); changed = true; } } if (!changed) return; _activeCacheDirty = true; OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); } public List GetSaveData() { var entries = new List(); foreach (var inst in _instances) { var entry = new ModifierSaveEntry { modifierId = inst.Definition.Id, isActive = inst.IsActive, remainingUses = inst.RemainingUses, stacks = inst.Stacks, }; foreach (var kvp in inst.CustomState) { entry.customState.Add(new CustomStateEntry { key = kvp.Key, value = kvp.Value, }); } entries.Add(entry); } return entries; } public void LoadSaveData(List entries, ModifierCatalog catalog) { _instances.Clear(); _activeCacheDirty = true; if (entries == null) return; foreach (var entry in entries) { var definition = catalog.FindById(entry.modifierId); if (definition == null) { Debug.LogWarning($"Modifier '{entry.modifierId}' not found in catalog, skipping."); continue; } var instance = new ModifierInstance(definition) { IsActive = entry.isActive, RemainingUses = entry.remainingUses, Stacks = entry.stacks, }; if (entry.customState != null) { foreach (var cs in entry.customState) instance.CustomState[cs.key] = cs.value; } _instances.Add(instance); } OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); } public void Clear() { _instances.Clear(); _activeCacheDirty = true; OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); } public void RemoveExpired() { var changed = false; var activeChanged = false; for (var i = _instances.Count - 1; i >= 0; i--) { var instance = _instances[i]; if (!instance.IsExpired) continue; if (instance.IsActive) activeChanged = true; instance.IsActive = false; _instances.RemoveAt(i); changed = true; } if (!changed) return; _activeCacheDirty = true; if (activeChanged) OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); } private void RebuildActiveCache() { _activeCache.Clear(); for (int i = 0; i < _instances.Count; i++) { if (_instances[i].IsActive) _activeCache.Add(_instances[i]); } _activeCacheDirty = false; } } }