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
}
}