using cfg.GuideCfg; using Framework; using Framework.Condition; using Gameplay.Area; using Gameplay.Level; using Gameplay.Net; using Gameplay.Skill; using NLDPB; using PhxhSDK; using System; using System.Collections.Generic; using UnityEngine; namespace Gameplay.Guide { /// /// 新手引导管理器 /// public class GuideManager : Singlenton, IInitable, IUpdatable { #region 兜底强制跳过引导机制 /// /// 2秒内点击n次,弹出强制跳过引导 /// private readonly Queue queueTime = new(); /// /// 强制跳过引导检测时间(秒) /// private const double SKIP_GUIDE_CHECK_TIME = 2d; #endregion /// /// 新手引导配置字典,key:引导组,Value:引导步骤配置链表 /// private Dictionary> dicGuideCfg; /// /// 当前的引导组 /// private GuideGroup curGroup; /// /// 触发条件id /// private int reactionID = ConditionReactor.INVALID_ALLOC_ID; /// /// 是否引导中 /// public bool IsGuiding { get { return curGroup != null && curGroup.IsGuiding; } } /// /// 控制地板点击 /// public bool IsControlFloor = false; /// /// 是否改名 /// public bool IsChangeName = true; /// /// 指定地板索引 /// public int FloorIndex = -1; /// /// 是否编队引导 /// public bool IsEditTeamGuide = false; public void Init() { InputManager.Instance.OnAnyClick += OnAnyClick; EventManager.Instance.Register(EventManager.EventName.AllConfigLoad, InitData); } public void Update(float deltaTime) { if (!Instance.IsGuiding) return; if (!LevelManager.Instance.IsInLevel) return; if (null == LevelManager.Instance.CurrentLevel) return; if (!LevelManager.Instance.CurrentLevel.isInBattle) return; if (!LevelManager.Instance.CurrentLevel.IsPause()) return; AreaManager.instance.LogicUpdate(deltaTime); SkillManager.instance.playerSkillManager.LogicUpdate(deltaTime); } public void Release() { EventManager.Instance.Unregister(EventManager.EventName.AllConfigLoad, InitData); if (null != InputManager.Instance) InputManager.Instance.OnAnyClick -= OnAnyClick; } /// /// 开始引导 /// public void BeginGuide(int completeStepId = 0) { #if GUIDE try { if (IsGuiding) return; if (ConditionReactor.INVALID_ALLOC_ID != reactionID) ConditionReactor.Instance.RemoveReaction(reactionID); if (completeStepId == 0) completeStepId = (int)Actor.Instance.Logic.GetCount((uint)LogicID.NewGuideProgress); int groupId = GetGuideGroupId(completeStepId); if (!dicGuideCfg.TryGetValue(groupId, out LinkedList linkCfg)) return; curGroup = new GuideGroup(groupId, linkCfg); if (curGroup.CurStep.Cfg.BeginCondition.Length <= 0) { OnBeginGuide(true, null, ConditionReactor.INVALID_ALLOC_ID); return; } reactionID = ConditionReactor.Instance.AddReaction(curGroup.CurStep.Cfg.BeginCondition, OnBeginGuide); } catch (Exception e) { DebugUtil.LogError($"引导启动异常:{e}"); } #endif } /// /// 完成指定引导步骤 /// /// public void CompleteStep(GuideStepBase step) { if (null == curGroup) return; if (step != curGroup.CurStep) { DebugUtil.LogError("要完成的引导步骤不是当前步骤"); EndGuide(true); return; } NetHelper.SaveGuideProgress((uint)curGroup.CurStep.Cfg.Id); // 完成引导步骤,保存服务器 DebugUtil.Log($"完成引导步骤,服务器保存引导id:{curGroup.CurStep.Cfg.Id}"); queueTime.Clear(); if (curGroup.IsComplete) { DebugUtil.Log($"完成引导组,组id:{curGroup.GroupId}"); int nextGroupId = curGroup.NextGroupId; if (dicGuideCfg.TryGetValue(nextGroupId, out LinkedList linkCfg)) { curGroup = new(nextGroupId, linkCfg, true); curGroup.BeginGuide(); } else EndGuide(false); } else { if (curGroup.CurStep.Cfg.IsComplete) DebugUtil.Log($"提前完成引导组,组id:{curGroup.GroupId}"); curGroup.BeginNextStep(); } } /// /// 回到指定步骤 /// /// public void ReturnStep(int stepId) { curGroup.EndGuide(); curGroup.BeginGuideById(stepId); } /// /// 结束引导 /// public void EndGuide(bool isReEnterMain) { if (IsGuiding) curGroup.EndGuide(); RecoverState(); curGroup = null; UIManager.Instance.CloseWindow(UINameConst.UI_Guide); UIManager.Instance.CloseWindow(UINameConst.UI_GuideTip); if (isReEnterMain) GameStateManager.Instance.ChangeState(new GameStateMainScene()); } /// /// 跳过当前引导组 /// /// public void SkipCurGuideGroup() { if (null == curGroup) return; if (!dicGuideCfg.TryGetValue(curGroup.GroupId, out LinkedList linkedCfg)) { DebugUtil.LogError("要完成的引导组不存在"); return; } NetHelper.SaveGuideProgress((uint)linkedCfg.Last.Value.Id); // 跳过引导步骤,保存服务器 DebugUtil.Log($"跳过引导组,服务器保存引导id:{linkedCfg.Last.Value.Id}"); EndGuide(false); BeginGuide(linkedCfg.Last.Value.Id); } /// /// 跳过所有引导 /// public void SkipAllGuide() { DebugUtil.Log("跳过所有引导"); GuideConfig guideConfig = TableManager.Instance.Tables.GuideConfig; DataGuideCfg cfg = guideConfig.DataList[^1]; NetHelper.SaveGuideProgress((uint)cfg.Id); EndGuide(true); } /// /// 显示跳过所有引导提示 /// public void ShowSkipAllGuide() { UITipsManager.ShowConfirmTwo(CommonUtils.GetLocalizeText("Guide_SkipAllTitle"), // 脱离卡死 CommonUtils.GetLocalizeText("Guide_SkipAllContent"), // 是否强制跳过新手引导?(选择【跳过】便不再触发新手引导) CommonUtils.GetLocalizeText("Skip"), // 跳过 CommonUtils.GetLocalizeText("Not"), // 否 () => { SkipAllGuide(); }); } /// /// 判断引导步骤是否完成 /// /// public bool CheckComplete(int guideId) { int curCompleteStepId = (int)Actor.Instance.Logic.GetCount((uint)LogicID.NewGuideProgress); return curCompleteStepId >= guideId; } /// /// 初始化数据 /// private void InitData() { dicGuideCfg = new(); foreach (DataGuideCfg cfg in TableManager.Instance.Tables.GuideConfig.DataList) { if (dicGuideCfg.ContainsKey(cfg.GroupId) && !IsFirstStep(cfg)) continue; DataGuideCfg addCfg = cfg; // 记录要添加进链表的引导步骤配置 int groudId = cfg.GroupId; LinkedList linkCfg = new(); dicGuideCfg[groudId] = linkCfg; while (null != addCfg && addCfg.GroupId == groudId) { linkCfg.AddLast(addCfg); if (0 == addCfg.NextId) addCfg = null; else if (addCfg.Id >= addCfg.NextId) throw new Exception($"{addCfg.Id}引导的下一步id配置错误,下一步id小于等于当前id可能导致死循环"); else addCfg = TableManager.Instance.Tables.GuideConfig.Get(addCfg.NextId); } } #region 检验每个引导组的步骤顺序是否正常 foreach (LinkedList linkCfg in dicGuideCfg.Values) { DataGuideCfg preCfg = null; foreach (DataGuideCfg cfg in linkCfg) { if (null == preCfg) { preCfg = cfg; continue; } if (preCfg.NextId != cfg.Id) { Debug.LogError($"新手引导配置出错,组id:{cfg.GroupId}, 当前步骤id:{preCfg.Id}, 实际下一步步骤id:{preCfg.NextId}, 当前下一步步骤id:{cfg.Id}"); return; } else preCfg = cfg; } } #endregion } /// /// 是否是引导组的第一步 /// /// private bool IsFirstStep(DataGuideCfg dataGuideCfg) { return dataGuideCfg.Id == dataGuideCfg.GroupId; } /// /// 获取应该引导的引导组id /// /// private int GetGuideGroupId(int completeStepId) { GuideConfig guideConfig = TableManager.Instance.Tables.GuideConfig; if (guideConfig.DataList.Count <= 0) return 0; if (completeStepId >= guideConfig.DataList[^1].Id) // 大于等于最后一步的id return 0; // 跳过引导 DataGuideCfg completeCfg = guideConfig.Get(completeStepId); // 已经完成的引导步骤配置 if (null == completeCfg) // 没有获取到,说明不是存在的配置id return guideConfig.DataList.Count > 0 ? guideConfig.DataList[0].GroupId : 0; bool isGroupComplete = false; // 当前步骤所在引导组是否完成 DataGuideCfg nextCfg = guideConfig.Get(completeCfg.NextId); if (null == nextCfg) return 0; // 没有下一步了 else if (nextCfg.GroupId != completeCfg.GroupId) // 该步骤是该组最后一步,表示整组完成 isGroupComplete = true; else { foreach (DataGuideCfg cfg in TableManager.Instance.Tables.GuideConfig.DataList) { if (cfg.GroupId != completeCfg.GroupId) continue; if (cfg.IsComplete && cfg.Id <= completeCfg.Id) { DebugUtil.Log($"引导步骤{completeStepId}所在组{cfg.GroupId}的{cfg.Id}步骤完成,该组完成"); isGroupComplete = true; break; } } } if (isGroupComplete) // 该组完成,取下一组 { DataGuideCfg dataGuideCfg = guideConfig.Get(completeCfg.NextId); while (null != dataGuideCfg) { if (dataGuideCfg.GroupId == completeCfg.GroupId) { dataGuideCfg = guideConfig.Get(dataGuideCfg.NextId); continue; } return dataGuideCfg.GroupId; } return 0; } else return completeCfg.GroupId; // 该组没有完成,重来 } /// /// 设置引导状态 /// private void SetGuideState() { UIManager.Instance.IsEnableBack = false; } /// /// 还原状态 /// private void RecoverState() { UIManager.Instance.IsEnableBack = true; ConditionReactor.Instance.RemoveReaction(reactionID); reactionID = ConditionReactor.INVALID_ALLOC_ID; } /// /// 开始引导 /// private void OnBeginGuide(bool conditionResult, object callBackParam, int reactionID) { if (!conditionResult) return; ConditionReactor.Instance.RemoveReaction(reactionID); this.reactionID = ConditionReactor.INVALID_ALLOC_ID; if (IsGuiding) return; try { SetGuideState(); int completeStepId = (int)Actor.Instance.Logic.GetCount((uint)LogicID.NewGuideProgress); DebugUtil.Log($"已完成的引导步骤id:{completeStepId},开始引导组id:{curGroup.GroupId}"); curGroup.BeginGuide(); } catch (Exception e) { DebugUtil.LogError($"跳过引导,错误信息:{e}"); RecoverState(); } } /// /// 引导过程中的任意点击事件 /// private void OnAnyClick() { if (!IsGuiding) return; DateTime curTime = CommonUtils.GetCurTime(CommonUtils.E_TimeType.Local); queueTime.Enqueue(curTime); while (queueTime.TryPeek(out DateTime firstTime)) { TimeSpan timeSpan = curTime - firstTime; if (timeSpan.TotalSeconds < SKIP_GUIDE_CHECK_TIME) break; queueTime.Dequeue(); } if (TableManager.Instance.Tables.GlobalConfig.SkipGuideClickCount <= queueTime.Count) { DebugUtil.Log($"{SKIP_GUIDE_CHECK_TIME}秒内连续点击{TableManager.Instance.Tables.GlobalConfig.SkipGuideClickCount}次,弹出强制跳过新手引导提示"); queueTime.Clear(); ShowSkipAllGuide(); } else DebugUtil.Log($"[引导]记录两秒内的点击次数{queueTime.Count}"); } } }