using System; using System.Collections.Generic; using UnityEngine; namespace OM { /// /// A static event manager supporting both typed and named event subscriptions. /// Provides publish-subscribe functionality for decoupled communication. /// public static class OM_EventsManager { #if UNITY_EDITOR /// /// Invoked in the Editor whenever an event is published (typed or named). /// Used for debugging and visualization purposes. /// public static Action OnEventPublished; #endif private static readonly Dictionary TypedEvents = new(); private static readonly Dictionary SimpleNamedEvents = new(); private static readonly Dictionary GenericNamedEvents = new(); [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void ResetStatics() { TypedEvents.Clear(); SimpleNamedEvents.Clear(); GenericNamedEvents.Clear(); } /// /// Subscribes a callback to a typed event. /// public static void Subscribe(Action callback) { if (TypedEvents.TryGetValue(typeof(T), out var del)) TypedEvents[typeof(T)] = Delegate.Combine(del, callback); else TypedEvents[typeof(T)] = callback; } /// /// Unsubscribes a callback from a typed event. /// public static void Unsubscribe(Action callback) { if (TypedEvents.TryGetValue(typeof(T), out var del)) { var newDel = Delegate.Remove(del, callback); if (newDel == null) TypedEvents.Remove(typeof(T)); else TypedEvents[typeof(T)] = newDel; } } /// /// Subscribes a callback to a named event with no parameters. /// public static void Subscribe(string key, Action callback) { if (SimpleNamedEvents.TryGetValue(key, out var existing)) SimpleNamedEvents[key] = (Action)Delegate.Combine(existing, callback); else SimpleNamedEvents[key] = callback; } /// /// Subscribes a callback to a named event with a parameter. /// public static void Subscribe(string key, Action callback) { if (GenericNamedEvents.TryGetValue(key, out var existing)) { if (existing is not Action) throw new InvalidOperationException($"Key '{key}' is already registered with a different parameter type."); GenericNamedEvents[key] = Delegate.Combine(existing, callback); } else { GenericNamedEvents[key] = callback; } } /// /// Unsubscribes a callback from a named event with no parameters. /// public static void Unsubscribe(string key, Action callback) { if (SimpleNamedEvents.TryGetValue(key, out var existing)) { var updated = Delegate.Remove(existing, callback); if (updated == null) SimpleNamedEvents.Remove(key); else SimpleNamedEvents[key] = (Action)updated; } } /// /// Unsubscribes a callback from a named event with parameters. /// public static void Unsubscribe(string key, Action callback) { if (GenericNamedEvents.TryGetValue(key, out var existing)) { if (existing is not Action) throw new InvalidOperationException($"Key '{key}' is not registered with Action<{typeof(T).Name}>."); var updated = Delegate.Remove(existing, callback); if (updated == null) GenericNamedEvents.Remove(key); else GenericNamedEvents[key] = updated; } } /// /// Publishes a typed event. /// public static void Publish(T data) { if (TypedEvents.TryGetValue(typeof(T), out var del)) (del as Action)?.Invoke(data); #if UNITY_EDITOR OnEventPublished?.Invoke(typeof(T).Name); #endif } /// /// Publishes a named event with no parameters. /// public static void Publish(string key) { if (SimpleNamedEvents.TryGetValue(key, out var action)) action?.Invoke(); #if UNITY_EDITOR OnEventPublished?.Invoke(key); #endif } /// /// Publishes a named event with a parameter. Optionally also triggers any matching simple event. /// public static void Publish(string key, T arg, bool triggerSimpleEventsIfExists = true) { if (GenericNamedEvents.TryGetValue(key, out var del) && del is Action action) { action(arg); } if (triggerSimpleEventsIfExists && SimpleNamedEvents.TryGetValue(key, out var simpleAction)) { simpleAction?.Invoke(); } #if UNITY_EDITOR OnEventPublished?.Invoke(key); #endif } /// /// Clears all stored events of all types. /// public static void ClearAllEvents() { TypedEvents.Clear(); SimpleNamedEvents.Clear(); GenericNamedEvents.Clear(); } /// /// Removes all listeners associated with a named key (both simple and generic). /// public static void UnsubscribeAll(string key) { SimpleNamedEvents.Remove(key); GenericNamedEvents.Remove(key); } /// /// Clears a specific typed event by its type. /// public static void ClearTypedEvent() => TypedEvents.Remove(typeof(T)); /// /// Clears all named event listeners associated with the given key. /// public static void ClearNamedEvent(string key) { SimpleNamedEvents.Remove(key); GenericNamedEvents.Remove(key); } /// /// Checks if a typed event has listeners. /// public static bool HasTypedEvent() => TypedEvents.ContainsKey(typeof(T)); /// /// Checks if a named event has listeners. /// public static bool HasNamedEvent(string key) { return SimpleNamedEvents.ContainsKey(key) || GenericNamedEvents.ContainsKey(key); } /// /// Logs all subscribed events and their listener counts to the console. /// public static void LogAllEvents() { Debug.Log("=== EventManager Subscriptions ==="); Debug.Log($"[Typed] Count: {TypedEvents.Count}"); foreach (var e in TypedEvents) Debug.Log($" Type: {e.Key.Name} → Listeners: {e.Value?.GetInvocationList().Length}"); Debug.Log($"[Named] Count: {SimpleNamedEvents.Count}"); foreach (var e in SimpleNamedEvents) Debug.Log($" Key: \"{e.Key}\" → Listeners: {e.Value?.GetInvocationList().Length}"); Debug.Log($"[Generic Named] Count: {GenericNamedEvents.Count}"); foreach (var e in GenericNamedEvents) Debug.Log($" Key: \"{e.Key}\" → Listeners: {e.Value?.GetInvocationList().Length}"); } #if UNITY_EDITOR /// /// Returns all typed events for editor debugging. /// public static Dictionary Debug_GetTypedEvents() => TypedEvents; /// /// Returns all named events (simple + generic) for editor debugging. /// public static Dictionary Debug_GetNamedEvents() { var combined = new Dictionary(); foreach (var pair in SimpleNamedEvents) combined[pair.Key] = pair.Value; foreach (var pair in GenericNamedEvents) combined[pair.Key] = pair.Value; return combined; } /// /// Clears a specific typed event by its System.Type. /// public static void ClearTypedEventByType(Type type) => TypedEvents.Remove(type); #endif } }