using System; using System.Collections.Generic; using System.Linq; using Activity.Condition; using Activity.EventSubscriber; using asap.core; using cfg; using TimeManager; using UnityEngine; namespace Activity { /// /// 活动管理器 - 负责监听事件、判断条件、调用Sentry执行开关 /// public class ActivityManager : IDisposable { public ActivityManager(Tables tables,ActivityScopeManager scopeManager) { _tables = tables; _scopeManager = scopeManager; } private readonly Tables _tables; private readonly ActivityScopeManager _scopeManager; private readonly Dictionary<(int EventType,int EventSubType), IActivitySentry> _sentryMap = new(); private readonly Dictionary> _dataTypeIndex = new(); #region ManagerInit private void InitAllManage() { // 注册 EventDataRepository 为单例 GContext.container.Register().AsSingleton(); Debug.Log("[ActivityManager] EventDataRepository registered as singleton."); } #endregion #region Sentry 初始化 private void InitializeAllSentries() { // 按 (Type, SubType) 分组,每个类型只创建一次 Sentry var eventGroups = _tables.TbFishingEvent.DataList .GroupBy(e => (e.Type, e.SubType)); foreach (var group in eventGroups) { var events = group.ToList(); var sentry = GetOrCreateSentry(events); if (sentry != null) BuildDataTypeIndexForGroup(events); else _scopeManager.DestroyActivityScope((events[0].Type, events[0].SubType)); } Debug.Log($"[ActivityManager] Initialized {_sentryMap.Count} sentries, indexed {_dataTypeIndex.Count} data types."); } private IActivitySentry GetOrCreateSentry(List events) { if (events == null || events.Count == 0) return null; var typeKey = (events[0].Type, events[0].SubType); // 如果已经创建过,直接返回 if (_sentryMap.TryGetValue(typeKey, out var createSentry)) { return createSentry; } var sentryType = ActivitySentryManager.GetActivitySentryType(events[0]); if (sentryType == null) { #if UNITY_EDITOR Debug.LogWarning($"[ActivityManager] No sentry for event {events[0].ID}"); #endif return null; } var scope = _scopeManager.GetActivityScope(typeKey); var instance = scope.GetInstance((typeof(IActivitySentry), sentryType.Name)); if (instance is IActivitySentry sentry) { //注入依赖 sentry.InjectDependencies(_scopeManager); sentry.CurrentEventConfig = null; sentry.RelatedEvents = events; _sentryMap[typeKey] = sentry; Debug.Log($"[ActivityManager] Created sentry {sentryType.Name} for Type:{events[0].Type} SubType:{events[0].SubType}"); return sentry; } Debug.LogError($"[ActivityManager] Failed to create {sentryType.Name}"); return null; } /// /// 为一组活动构建数据类型索引(同一类型的活动共享相同的检查器) /// private void BuildDataTypeIndexForGroup(List allEvents) { if (allEvents == null || allEvents.Count == 0) return; var dataTypeToEvents = new Dictionary>(); foreach (var evt in allEvents) { var checkers = evt.GetConditionCheckers(); if (checkers == null) continue; foreach (var checker in checkers) { if (checker.CareDataChangeWithType == null) continue; foreach (var dataType in checker.CareDataChangeWithType) { if (!dataTypeToEvents.ContainsKey(dataType)) dataTypeToEvents[dataType] = new HashSet(); dataTypeToEvents[dataType].Add(evt); } } } foreach (var (dataType, events) in dataTypeToEvents) { if (!_dataTypeIndex.ContainsKey(dataType)) _dataTypeIndex[dataType] = new HashSet(); _dataTypeIndex[dataType].UnionWith(events); } } #endregion #region 全局事件订阅 private void SubscribeGlobalEvents() { foreach (var subscriber in ActivitySubscriberManager.ActivityEventSubscribers) subscriber.Subscribe(this); Debug.Log("[ActivityManager] Global events subscribed."); } /// /// 将活动代币余额变更转发给各 Sentry(由 订阅总线后调用)。 /// public void DispatchActivityTokenBalanceChanged(int eventId) { foreach (var sentry in _sentryMap.Values) sentry.OnActivityTokenBalanceChanged(eventId); } #endregion #region 事件处理 /// /// 当数据变化时调用 - 通用处理方法 /// 直接通过数据类型找到所有需要检查的活动,并从活动中获取该数据类型的检查器 /// public void OnDataChanged(T data) { // 查找关心该数据类型的所有活动 if (!_dataTypeIndex.TryGetValue(typeof(T), out var events)) return; foreach (var fishingEvent in events) OnActivitySingleEvent(fishingEvent); } /// /// 当活动特定事件发生时调用(收集奖励、购买礼包等) /// public void OnActivitySingleEvent(FishingEvent fishingEvent) { if (fishingEvent == null) return; var typeKey = (fishingEvent.Type, fishingEvent.SubType); CheckActivityStatus(typeKey); } public void OnActivityEvents(List fishingEvents) { foreach (var fishingEvent in fishingEvents) { OnActivitySingleEvent(fishingEvent); } } #endregion #region 活动状态检查 /// /// 检查所有活动的状态 /// public void CheckAllActivities() { foreach (var typeKey in _sentryMap.Keys) { CheckActivityStatus(typeKey); } } /// /// 检查指定类型的活动状态 /// private void CheckActivityStatus((int, int) typeKey) { if (!_sentryMap.TryGetValue(typeKey, out var sentry)) return; // 从 Sentry 中获取该类型的所有活动 var relevantEvents = sentry.RelatedEvents; if (relevantEvents == null || relevantEvents.Count == 0) return; foreach (var fishingEvent in relevantEvents) { IsOpenActivity(fishingEvent, sentry ); } } private void IsOpenActivity( FishingEvent fishingEvent, IActivitySentry sentry) { if(fishingEvent==null) return; // 判断该活动是否已经开启 var isAlreadyOpen = sentry.CurrentEventConfig?.ID == fishingEvent.ID; var isOpenActivity = ShouldActivityOpen(fishingEvent, isAlreadyOpen); if (isOpenActivity && !isAlreadyOpen) // 如果应该开启且未开启,则开启 OpenActivity(sentry, fishingEvent); else if (!isOpenActivity && isAlreadyOpen) // 只有应该关闭且已经开启时才关闭 CloseActivity(sentry, fishingEvent); } /// /// 判断活动是否应该开启 /// /// 活动配置 /// 活动是否已经开启 private bool ShouldActivityOpen(FishingEvent fishingEvent, bool isAlreadyOpen) { var checkers = fishingEvent.GetConditionCheckers(); if (checkers == null) return false; // 如果活动已经开启,只检查需要持续监听的条件(单向条件无需重复检查) if (isAlreadyOpen) { return checkers .Where(checker => checker.NeedContinueListenAfterSatisfied) .All(checker => checker.CanOpen(fishingEvent)); } // 活动未开启,检查所有条件 return checkers.All(checker => checker.CanOpen(fishingEvent)); } #endregion #region 活动开关执行 /// /// 开启活动 /// private void OpenActivity(IActivitySentry sentry, FishingEvent fishingEvent) { // 开启新活动 sentry.Open(fishingEvent); } /// /// 关闭活动 /// private void CloseActivity(IActivitySentry sentry, FishingEvent fishingEvent) { sentry.Close(fishingEvent); } #endregion #region 公共API public void Dispose() { // 释放所有 Sentry foreach (var sentry in _sentryMap.Values) sentry?.Dispose(); // 取消所有事件订阅 if(ActivitySubscriberManager.ActivityEventSubscribers!=null) foreach (var subscription in ActivitySubscriberManager.ActivityEventSubscribers) subscription?.Unsubscribe(); _sentryMap.Clear(); _dataTypeIndex.Clear(); _scopeManager?.Dispose(); } public void Init() { // 显式拉起 TimeRefreshManager(订阅 MinuteTick、跨天检测);须在本 Init 内任意入口订阅 SecondTick 之前执行 GContext.container.Resolve(); ConditionCheckerFactory.Initialize(); InitAllManage(); InitializeAllSentries(); SubscribeGlobalEvents(); CheckAllActivities(); } /// /// 根据类型键获取对应的 Sentry 实例 /// public IActivitySentry GetSentry((int EventType, int EventSubType) typeKey) { return _sentryMap.GetValueOrDefault(typeKey); } public IActivitySentry GetSentry(FishingEvent fishingEvent) { if (fishingEvent == null) return null; return GetSentry((fishingEvent.Type, fishingEvent.SubType)); } public TM Resolve() where TM : AEventDataManager { if (!_scopeManager.IsRegistered()) return null; // 从 _scopeManager 获取 typeKey var typeKey = _scopeManager.GetRegisteredTypeKey(); if (typeKey == null) return null; if (!_scopeManager.HasActiveScope(typeKey.Value)) return null; return _scopeManager.GetOrRegisterInstance(typeKey.Value); } #endregion #region 活动代币查询 /// /// 通过配置表反向查询物品对应的活动代币数量 /// 注意: 配置表中多个 FishingEventCycleItem 使用同一个 ItemId /// 活动切换时 RedirectID(eventId)会变化 /// /// 代币数量,如果活动未开启或未找到返回 null public int? GetTokenCountByItemId(FishingEventCycleItem cycleItem) { if (cycleItem == null) return null; // 1. 获取活动哨兵 var sentry = GetSentry((cycleItem.Type, cycleItem.SubType)); if (sentry?.CurrentEventConfig == null) return null; // 2. 查询代币数量 var tokenService = GContext.container.Resolve(); return tokenService?.GetTokenCount(sentry.CurrentEventConfig.ID); } #endregion } }