using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; // For Task using Base; using Data; using EventWorkClass; using UnityEngine; using UnityEngine.SceneManagement; using Utils; namespace Managers { /// /// 事件管理器,负责事件的加载、注册和执行。 /// 遵循单例模式,并在启动流程中扮演一个管理器角色。 /// internal class EventManager : Singleton, ILaunchManager, ITick // 实现 ITick 接口 { /// /// 存储当前正在播放的所有故事实例。 /// private readonly List _activeStoryPlayers = new(); /// /// 存储所有已加载的事件定义,键为事件名称,值为对应的事件工作类实例。 /// public Dictionary EventDefs { get; private set; } /// /// 存储所有已加载的故事定义,键为故事名称,值为对应的故事定义实例。 /// public Dictionary StoryDefs { get; private set; } public bool HasStoryDisplay => _activeStoryPlayers.Any(); /// /// 指示管理器是否已完成初始化。 /// public bool Completed { get; set; } /// /// 获取当前加载步骤的描述,用于启动流程的进度显示。 /// public string StepDescription => "正在载入事件和故事"; /// /// 初始化事件管理器,从定义管理器中加载所有事件定义和故事定义。 /// public async Task Init() // 接口变更:返回 Task { // 如果事件和故事定义已经加载,则直接返回,避免重复初始化。 // 使用 EventDefs != null 作为内部数据是否已加载的判据。 if (EventDefs != null && StoryDefs != null) return; // 从定义管理器查询并加载所有事件定义。(不进行单例空检查) var eventDefs = DefineManager.Instance.QueryDefinesByType(); EventDefs = new Dictionary(); if (eventDefs != null) // 检查 eventDefs 是否为 null,以避免 foreach 遍历 null 集合 { foreach (var def in eventDefs) { if (EventDefs.ContainsKey(def.defName)) { Debug.LogWarning($"警告:事件名称重复,已跳过加载名称为 '{def.defName}' 的事件定义。"); continue; } try { var eventWorker = StringUtils.GetAndInstantiateEventWorker(def.workClass); if (eventWorker == null) { Debug.LogWarning( $"警告:未能找到或实例化名称为 '{def.workClass}' 的事件工作类(可能是类型不存在或构造函数问题),已跳过加载名称为 '{def.defName}' 的事件定义。"); continue; } eventWorker.Init(def.parameter); EventDefs.Add(def.defName, eventWorker); } catch (Exception ex) { Debug.LogError( $"错误:加载或实例化事件工作类 '{def.workClass}' (事件 '{def.defName}') 时发生异常:{ex.Message}\n{ex.StackTrace}"); } } } // 从定义管理器查询并加载所有故事定义。(不进行单例空检查) var storyDefs = DefineManager.Instance.QueryDefinesByType(); StoryDefs = new Dictionary(); if (storyDefs != null) // 检查 storyDefs 是否为 null { foreach (var storyDef in storyDefs) { if (StoryDefs.ContainsKey(storyDef.defName)) { Debug.LogWarning($"警告:故事名称重复,已跳过加载名称为 '{storyDef.defName}' 的故事定义。"); continue; } StoryDefs.Add(storyDef.defName, storyDef); } } // 将自身注册到时钟系统,以便每帧更新故事播放。(不进行单例空检查) Clock.AddTick(this); SceneManager.sceneLoaded += OnSceneLoaded; Completed = true; // 接口变更:设置 Completed 为 true } /// /// 清理事件管理器,释放所有已加载的事件和故事定义。 /// public void Clear() { // 从时钟系统移除自身,停止接收Tick更新。(不进行单例空检查) Clock.RemoveTick(this); // 清理所有正在播放的故事实例。 _activeStoryPlayers.Clear(); // 释放事件定义字典。 EventDefs = null; // 释放故事定义字典。 StoryDefs = null; SceneManager.sceneLoaded -= OnSceneLoaded; Completed = false; // 清理后应将 Completed 置为 false } /// /// 每帧更新,用于驱动所有活跃故事的播放逻辑。 /// 作为 接口的实现,由时钟系统调用。 /// public void Tick() { if (_activeStoryPlayers.Count == 0) return; // 获取自上一帧以来的时间增量。 var deltaTime = Time.deltaTime; // 遍历所有正在播放的故事实例,并更新它们的状态。 // 为了避免在迭代过程中修改列表,先收集要移除的,再统一移除。 var playersToRemove = new List(); foreach (var player in _activeStoryPlayers) { player.Update(deltaTime); if (player.IsFinished) playersToRemove.Add(player); } // 移除所有已完成的故事实例。 foreach (var player in playersToRemove) _activeStoryPlayers.Remove(player); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { // 如果 Clock.AddTick 内部没有去重逻辑,这里可能会重复添加。 // 但根据要求,不修改原有逻辑,保持原样。 Clock.AddTick(this); // (不进行单例空检查) } /// /// 执行指定名称的事件。 /// /// 要执行的事件的名称。 /// 事件执行的维度ID,如果为null,将使用当前焦点的维度ID。 public void Action(string eventName, string dimensionID = null) { // 依赖于 EventDefs 集合是否已初始化,而不是 Completed 状态, // 因为 Completed 仅表示 ILaunchManager 的初始化完成, // 可能会有异步操作导致 EventDefs 尚未完全填充。 if (EventDefs == null) { Debug.LogError($"错误:事件管理器尚未初始化或已被清理。无法执行事件 '{eventName}'。"); return; } if (!EventDefs.TryGetValue(eventName, out var eventWorker)) { Debug.LogWarning($"警告:未能找到名称为 '{eventName}' 的事件定义,已跳过执行该事件。"); return; } // 假设 Program.Instance 存在且可访问。(不进行单例空检查) // 如果 dimensionID 为 null,则使用当前焦点维度ID。 dimensionID ??= Program.Instance.FocusedDimensionId; eventWorker.Run(dimensionID); } public void Action(string eventName, Entity.Entity initiator) { if (EventDefs == null) { Debug.LogError($"错误:事件管理器尚未初始化或已被清理。无法执行事件 '{eventName}'。"); return; } if (!EventDefs.TryGetValue(eventName, out var eventWorker)) { Debug.LogWarning($"警告:未能找到名称为 '{eventName}' 的事件定义,已跳过执行该事件。"); return; } eventWorker.Run(initiator.currentDimensionId, initiator); } public void Action(EventDef eventDef, Entity.Entity initiator) { Action(eventDef.defName, initiator); } /// /// 播放指定名称的故事。 /// /// 要播放的故事的名称。 /// 故事执行的维度ID,如果为null,将使用当前焦点的维度ID。 public void PlayStory(string storyName, string dimensionID = null) { // 依赖于 StoryDefs 集合是否已初始化。 if (StoryDefs == null || StoryDefs.Count == 0) { Debug.LogError($"错误:故事定义尚未加载或已被清理。无法播放故事 '{storyName}'。"); return; } if (!StoryDefs.TryGetValue(storyName, out var storyDef)) { Debug.LogWarning($"警告:未能找到名称为 '{storyName}' 的故事定义,已跳过播放该故事。"); return; } if (storyDef.storyStage == null || storyDef.storyStage.Length == 0) { Debug.LogWarning($"警告:故事 '{storyDef.defName}' 没有定义任何阶段,已跳过播放。"); return; } // 确保 dimensionID 有效,如果为 null 则使用当前焦点维度ID。(不进行单例空检查) dimensionID ??= Program.Instance.FocusedDimensionId; // 创建一个新的 StoryPlayer 实例并添加到活跃列表中。 var player = new StoryPlayer(storyDef, dimensionID, this); _activeStoryPlayers.Add(player); } /// /// 停止所有指定名称的活跃故事。 /// /// 要停止的故事的名称。 /// 停止的故事数量。 public int StopStory(string storyName) { if (string.IsNullOrEmpty(storyName)) { Debug.LogWarning("停止故事:提供的故事名称为空或null。"); return 0; } // 使用 RemoveAll 高效移除所有匹配的故事实例 var stoppedCount = _activeStoryPlayers.RemoveAll(player => player.StoryDef.defName == storyName); if (stoppedCount > 0) { Debug.Log($"停止了 {stoppedCount} 个名称为 '{storyName}' 的故事。"); } return stoppedCount; } /// /// 停止指定名称和维度ID的活跃故事。 /// /// 要停止的故事的名称。 /// 故事所处的维度ID。 /// 停止的故事数量。 public int StopStory(string storyName, string dimensionID) { if (string.IsNullOrEmpty(storyName) || string.IsNullOrEmpty(dimensionID)) { Debug.LogWarning("停止故事:提供的故事名称或维度ID为空或null。"); return 0; } // 使用 RemoveAll 高效移除所有名称和维度ID都匹配的故事实例 var stoppedCount = _activeStoryPlayers.RemoveAll(player => player.StoryDef.defName == storyName && player.DimensionID == dimensionID); if (stoppedCount > 0) { Debug.Log($"停止了 {stoppedCount} 个名称为 '{storyName}' 且维度ID为 '{dimensionID}' 的故事。"); } return stoppedCount; } /// /// 停止所有当前活跃的故事。 /// /// 停止的故事数量。 public int StopAllStories() { var count = _activeStoryPlayers.Count; if (count > 0) { _activeStoryPlayers.Clear(); // 清空列表 Debug.Log($"停止了所有 {count} 个活跃故事。"); } return count; } /// /// 将字符串颜色值解析为 对象。 /// 支持十六进制颜色(如 "#RRGGBB" 或 "#AARRGGBB")或标准颜色名称。 /// /// 颜色字符串。 /// 解析后的 对象。如果解析失败,返回白色。 private Color ParseColor(string colorString) { if (ColorUtility.TryParseHtmlString(colorString, out var color)) return color; Debug.LogWarning($"警告:无法解析颜色字符串 '{colorString}'。使用默认白色。"); return Color.white; // 默认返回白色 } /// /// 表示一个正在播放的故事实例的状态机。 /// private class StoryPlayer { // 事件管理器实例,用于调用 Action 和 ParseColor 方法。 private readonly EventManager _eventManager; // 当前故事阶段的索引。 private int _currentStageIndex; // 当前故事播放器所处的状态。 private State _currentState; // 当前阶段的等待计时器。 private float _currentWaitTimer; /// /// 初始化 类的新实例。 /// /// 要播放的故事定义。 /// 故事播放所在的维度ID。 /// 事件管理器实例,用于执行事件和解析颜色。 public StoryPlayer(StoryDef storyDef, string dimensionId, EventManager eventManager) { StoryDef = storyDef; DimensionID = dimensionId; _eventManager = eventManager; _currentStageIndex = 0; _currentWaitTimer = 0f; _currentState = State.Initializing; } /// 获取当前正在播放的故事定义。 public StoryDef StoryDef { get; } /// 获取故事播放所在的维度ID。 public string DimensionID { get; } /// 获取一个值,表示故事是否已播放完毕。 public bool IsFinished { get; private set; } /// /// 每帧更新故事播放器的状态。 /// /// 自上一帧以来的时间增量。 public void Update(float deltaTime) { if (IsFinished) return; // 如果故事阶段定义为null或为空,则立即标记故事完成。 if (StoryDef.storyStage == null || StoryDef.storyStage.Length == 0) { Debug.LogWarning($"警告:故事 '{StoryDef.defName}' 没有定义任何阶段,StoryPlayer 将立即完成。"); IsFinished = true; return; } var currentStage = StoryDef.storyStage[_currentStageIndex]; switch (_currentState) { case State.Initializing: // 从当前阶段的 lastWaitTime 开始等待,如果 lastWaitTime 小于等于 0 则直接跳过等待执行阶段。 if (currentStage.lastWaitTime > 0) { _currentState = State.WaitingLastTime; _currentWaitTimer = 0f; } else { // 没有 lastWaitTime,直接执行阶段。 _currentState = State.ExecutingStage; } break; case State.WaitingLastTime: _currentWaitTimer += deltaTime; if (_currentWaitTimer >= currentStage.lastWaitTime) { // 等待时间已到,切换到执行阶段。 _currentWaitTimer = 0f; _currentState = State.ExecutingStage; } break; case State.ExecutingStage: // 处理事件 if (currentStage.eventDef != null) { if (!string.IsNullOrEmpty(currentStage.eventDef.defName)) _eventManager.Action(currentStage.eventDef.defName, DimensionID); else Debug.LogWarning( $"警告:故事 '{StoryDef.defName}' 阶段 {_currentStageIndex} 包含一个没有定义名称的事件,已跳过。"); } // 处理消息 if (currentStage.messageDef != null) { // MessageManager.Instance 不进行空检查,假定单例始终可用 var messageColor = _eventManager.ParseColor(currentStage.messageDef.color); MessageManager.Instance.DisplayMessage(currentStage.messageDef.text, currentStage.messageDef.type, messageColor); } // 决定下一个状态 if (currentStage.nextWaitTime > 0) { _currentState = State.WaitingNextTime; _currentWaitTimer = 0f; } else { // 没有 nextWaitTime,直接推进到下一个阶段。 AdvanceToNextStage(); } break; case State.WaitingNextTime: _currentWaitTimer += deltaTime; if (_currentWaitTimer >= currentStage.nextWaitTime) { // 等待时间已到,推进到下一个阶段。 _currentWaitTimer = 0f; AdvanceToNextStage(); } break; case State.Finished: // 故事已经完成,不再更新。 break; } } /// /// 推进到下一个故事阶段,或标记故事完成。 /// private void AdvanceToNextStage() { _currentStageIndex++; if (_currentStageIndex < StoryDef.storyStage.Length) { // 还有后续阶段,重置状态以处理下一个阶段的 lastWaitTime。 _currentState = State.Initializing; } else { // 所有阶段播放完毕,标记故事完成。 _currentState = State.Finished; IsFinished = true; } } /// 故事播放阶段的状态枚举。 private enum State { Initializing, // 刚开始,准备处理第一个阶段的 lastWaitTime WaitingLastTime, // 等待当前阶段的 lastWaitTime ExecutingStage, // 执行当前阶段的事件/消息 WaitingNextTime, // 等待当前阶段的 nextWaitTime Finished // 故事播放完毕 } } } }