using cfg.GuideCfg; using Framework; using Framework.Condition; using Gameplay.Area; using Gameplay.Level; using Gameplay.Net; using Gameplay.Skill; using Gameplay.SubSystems; using NLDPB; using PhxhSDK; using System; using System.Collections.Generic; namespace Gameplay.Guide { /// /// 新手引导管理器 /// public class GuideManager : Singlenton, IInitable, IUpdatable { /// /// 引导组第一步的id /// private const int GUIDE_GROUP_FIRST_STEP_ID = 1; /// /// 是否初始化数据成功 /// private bool isInitDataSuccess; /// /// 特殊pv引导id /// private const int PV_GUIDE_ID = 1000; #region 兜底强制跳过引导机制 /// /// 2秒内点击n次,弹出强制跳过引导 /// private readonly Queue queueTime = new(); /// /// 强制跳过引导检测时间(秒) /// private const double SKIP_GUIDE_CHECK_TIME = 2d; #endregion /// /// 新手引导配置字典,key:引导组,Value:引导组引导步骤列表 /// private Dictionary> dicGuideCfg; /// /// 未完成的引导组,注册的开始条件记录在这,key:reactionId,value:引导组id /// private Dictionary dicReaction; /// /// 当前的引导组 /// private GuideGroup curGroup; /// /// 是否引导中 /// public bool IsGuiding { get { return curGroup != null && curGroup.IsGuiding; } } #region 引导特殊控制参数 /// /// 控制地板点击 /// public bool IsControlFloor = false; /// /// 是否改名 /// public bool IsChangeName = true; /// /// 指定地板索引 /// public int FloorIndex = -1; /// /// 是否编队引导 /// public bool IsEditTeamGuide = false; /// /// 使用技能成功 /// public bool IsUseSkillSuccess = true; /// /// 是否播放过PV视频 /// public bool IsPlayPVVideo; /// /// 是否响应任意点击 /// public bool IsEnableAnyClick = true; #endregion public int CurGuideStepId { get { if (IsGuiding) return curGroup.CurStep.Cfg.Id; return 0; } } public void Init() { isInitDataSuccess = false; 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 StartGuide() { #if GUIDE if (!isInitDataSuccess) { DebugUtil.LogError("启动引导失败,数据没有初始化成功"); return; } foreach (KeyValuePair> kv in dicGuideCfg) { if (CheckGuideGroupComplete(kv.Key)) // 已经完成的引导跳过 continue; if (kv.Value.Count <= 0) { DebugUtil.LogError($"引导组id:{kv.Key}没有引导步骤数据"); continue; } DataGuideCfg cfg = kv.Value[0]; if (!IsFirstStep(cfg)) { DebugUtil.LogError($"引导组id:{kv.Key}的第一个引导数据不是起始步骤,引导id:{cfg.Id}"); continue; } if (null == dicReaction) dicReaction = new(); dicReaction[ConditionReactor.Instance.AddReaction(cfg.BeginCondition, OnBeginGuideByCondition)] = cfg.GroupId; } #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 List listCfg)) { curGroup = new(nextGroupId, listCfg, true); curGroup.BeginGuide(); } else EndGuide(false); } else { if (curGroup.CurStep.Cfg.IsComplete) DebugUtil.Log($"提前完成引导组,组id:{curGroup.GroupId}"); curGroup.BeginNextStep(); } } /// /// 结束引导 /// 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) { if (LevelManager.Instance.IsInLevel) { LevelManager.Instance.ExitLevelChangeMainFunc = () => { GameStateManager.Instance.ChangeState(new GameStateMainScene()); return false; }; LevelManager.Instance.TryExitLevel(false, null, true); } else GameStateManager.Instance.ChangeState(new GameStateMainScene()); } } /// /// 跳过当前引导组 /// /// public void SkipCurGuideGroup() { if (null == curGroup) return; if (!dicGuideCfg.TryGetValue(curGroup.GroupId, out List listCfg)) { DebugUtil.LogError("要完成的引导组不存在"); return; } NetHelper.SaveGuideProgress((uint)listCfg[^1].Id); // 跳过引导步骤,保存服务器 DebugUtil.Log($"跳过引导组,服务器保存引导id:{listCfg[^1].Id}"); EndGuide(false); StartGuide(); } /// /// 跳过所有引导 /// public async void SkipAllGuide(bool isEditor = false) { DebugUtil.Log("跳过所有引导"); GuideConfig guideConfig = TableManager.Instance.Tables.GuideConfig; DataGuideCfg cfg = guideConfig.DataList[^1]; NetHelper.SaveGuideProgress((uint)cfg.Id); if (!isEditor && !IsPlayPVVideo && null != curGroup && curGroup.CurStep.Cfg.Id <= PV_GUIDE_ID) // 特殊处理 播放过pv视频则不播放,没播放过则播放 { EndGuide(false); if (LevelManager.Instance.IsInLevel) { LevelManager.Instance.ExitLevelChangeMainFunc = () => { GameStateManager.Instance.ChangeState(new GameStateMainScene()); return false; }; if (!LevelManager.Instance.CurrLevel.IsStory) LevelManager.Instance.TryExitLevel(false, null, true); } await UI_PVAnimaPlayController.Open("Assets/Video/LevelIntro/LevelIntro_PV.mp4", () => { if (!LevelManager.Instance.IsInLevel) { GameStateManager.Instance.ChangeState(new GameStateMainScene()); return; } if (LevelManager.Instance.CurrLevel.IsStory) LevelManager.Instance.TryExitLevel(false, null, true); else LevelManager.Instance.ExitLevelDirectly(); }); } else 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; } #region 私有方法 /// /// 初始化数据 /// private void InitData() { dicGuideCfg = new(); foreach (DataGuideCfg cfg in TableManager.Instance.Tables.GuideConfig.DataList) { if (!dicGuideCfg.TryGetValue(cfg.GroupId, out List listCfg)) { listCfg = new List(); dicGuideCfg[cfg.GroupId] = listCfg; } if(cfg.NextId<=cfg.Id) throw new Exception($"引导组id:{cfg.GroupId},的{cfg.Id}引导的下一步id配置错误,下一步id小于等于当前id会导致死循环"); listCfg.Add(cfg); } #region 检验每个引导组的步骤顺序是否正常 foreach (List listCfg in dicGuideCfg.Values) { DataGuideCfg preCfg = null; foreach (DataGuideCfg cfg in listCfg) { if (null == preCfg) { preCfg = cfg; continue; } if (preCfg.NextId != cfg.Id) throw new Exception($"新手引导配置出错,组id:{preCfg.GroupId}, 步骤id:{preCfg.Id}的下一步步骤id:{preCfg.NextId},当前配置是{cfg.Id}"); else preCfg = cfg; } } #endregion isInitDataSuccess = true; } /// /// 检查引导组是否完成 /// /// private bool CheckGuideGroupComplete(int guideGroupId) { // 通过服务器数据判断该组引导是否完成 return false; } /// /// 是否是引导组的第一步 /// /// private bool IsFirstStep(DataGuideCfg dataGuideCfg) { return GUIDE_GROUP_FIRST_STEP_ID == dataGuideCfg.Id; } /// /// 获取应该引导的引导组id /// /// private int GetGuideGroupId(int completeStepId) { return 0; /* GuideConfig guideConfig = TableManager.Instance.Tables.GuideConfig; if (guideConfig.DataList.Count <= 0) return 0; if (completeStepId >= guideConfig.DataList[^1].Id) // 大于等于最后一步的id return 0; // 跳过引导 DataGuideCfg completeCfg = null; if (0 != completeStepId && 1 != completeStepId) completeCfg = guideConfig.Get(completeStepId); // 已经完成的引导步骤配置 if (null == completeCfg) // 没有获取到,不是存在的配置id,可能修改过id { foreach (DataGuideCfg guideCfg in TableManager.Instance.Tables.GuideConfig.DataList) { if (guideCfg.Id >= completeStepId) { completeCfg = guideCfg; break; } } } 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) { if (0 == dataGuideCfg.NextId) // 下一组id为0表示最后一组 return 0; 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; } /// /// 通过条件触发开始引导 /// private void OnBeginGuideByCondition(bool conditionResult, object callBackParam, int reactionID) { if (!conditionResult) return; if (!dicReaction.TryGetValue(reactionID, out int guideGroupId)) return; ConditionReactor.Instance.RemoveReaction(reactionID); dicReaction.Remove(reactionID); try { if (!dicGuideCfg.TryGetValue(guideGroupId, out List listCfg)) return; curGroup = new GuideGroup(guideGroupId, listCfg); SetGuideState(); curGroup.BeginGuide(); } catch (Exception e) { DebugUtil.LogError($"跳过引导,错误信息:{e}"); RecoverState(); } } /// /// 引导过程中的任意点击事件 /// private void OnAnyClick() { if (!IsGuiding) return; if (!IsEnableAnyClick) return; if (LoadingManager.Instance.IsRuning) 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(); } } #endregion } }