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; for (int i = 0; i < instances.Count; i++) if (instances[i].IsActive) count++; return count; } } public void SetMaxActiveSlots(int slots) { maxActiveSlots = slots; } public ModifierInstance Add(ModifierDefinitionSO 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; bool 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() { bool changed = false; for (int 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) { activeCacheDirty = true; OnActiveModifiersChanged?.Invoke(Active); OnChanged?.Invoke(); } } public List GetSaveData() { var entries = new List(); for (int i = 0; i < instances.Count; i++) { var inst = instances[i]; 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, ModifierCatalogSO catalog) { instances.Clear(); activeCacheDirty = true; if (entries == null) return; for (int i = 0; i < entries.Count; i++) { var entry = entries[i]; 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(); } private void RebuildActiveCache() { activeCache.Clear(); for (int i = 0; i < instances.Count; i++) { if (instances[i].IsActive) activeCache.Add(instances[i]); } activeCacheDirty = false; } } }