NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/Guide/GuideManager.cs

518 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
{
/// <summary>
/// 新手引导管理器
/// </summary>
public class GuideManager : Singlenton<GuideManager>, IInitable, IUpdatable
{
#region 兜底强制跳过引导机制
/// <summary>
/// 2秒内点击n次弹出强制跳过引导
/// </summary>
private readonly Queue<DateTime> queueTime = new();
/// <summary>
/// 强制跳过引导检测时间(秒)
/// </summary>
private const double SKIP_GUIDE_CHECK_TIME = 2d;
#endregion
/// <summary>
/// 新手引导配置字典key引导组Value引导步骤配置链表
/// </summary>
private Dictionary<int, LinkedList<DataGuideCfg>> dicGuideCfg;
/// <summary>
/// 当前的引导组
/// </summary>
private GuideGroup curGroup;
/// <summary>
/// 触发条件id
/// </summary>
private int reactionID = ConditionReactor.INVALID_ALLOC_ID;
/// <summary>
/// 当前完成的引导id防止服务器没保存成功
/// </summary>
private int curCompleteId = 0;
/// <summary>
/// 特殊pv引导id
/// </summary>
private int pvGuideID = 1000;
/// <summary>
/// 是否引导中
/// </summary>
public bool IsGuiding
{
get { return curGroup != null && curGroup.IsGuiding; }
}
/// <summary>
/// 控制地板点击
/// </summary>
public bool IsControlFloor = false;
/// <summary>
/// 是否改名
/// </summary>
public bool IsChangeName = true;
/// <summary>
/// 指定地板索引
/// </summary>
public int FloorIndex = -1;
/// <summary>
/// 是否编队引导
/// </summary>
public bool IsEditTeamGuide = false;
/// <summary>
/// 使用技能成功
/// </summary>
public bool IsUseSkillSuccess = true;
/// <summary>
/// 是否播放过PV视频
/// </summary>
public bool IsPlayPVVideo;
/// <summary>
/// 是否响应任意点击
/// </summary>
public bool IsEnableAnyClick = true;
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;
}
/// <summary>
/// 开始引导
/// </summary>
public void BeginGuide(int completeStepId = 0)
{
#if GUIDE
try
{
if (IsGuiding)
return;
//if (ConditionReactor.INVALID_ALLOC_ID != reactionID)
//ConditionReactor.Instance.RemoveReaction(reactionID);
if (0 == completeStepId)
{
if (0 == curCompleteId)
curCompleteId = (int)Actor.Instance.Logic.GetCount((uint)LogicID.NewGuideProgress);
}
else
curCompleteId = completeStepId;
int groupId = GetGuideGroupId(curCompleteId);
if (!dicGuideCfg.TryGetValue(groupId, out LinkedList<DataGuideCfg> 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
}
/// <summary>
/// 完成指定引导步骤
/// </summary>
/// <param name="step"></param>
public void CompleteStep(GuideStepBase step)
{
if (null == curGroup)
return;
if (step != curGroup.CurStep)
{
DebugUtil.LogError("要完成的引导步骤不是当前步骤");
EndGuide(true);
return;
}
curCompleteId = curGroup.CurStep.Cfg.Id;
NetHelper.SaveGuideProgress((uint)curGroup.CurStep.Cfg.Id); // 完成引导步骤,保存服务器
DebugUtil.Log($"<color=#00FA9A>完成引导步骤服务器保存引导id:{curGroup.CurStep.Cfg.Id}</color>");
queueTime.Clear();
if (curGroup.IsComplete)
{
DebugUtil.Log($"<color=#00FA9A>完成引导组组id:{curGroup.GroupId}</color>");
int nextGroupId = curGroup.NextGroupId;
if (dicGuideCfg.TryGetValue(nextGroupId, out LinkedList<DataGuideCfg> linkCfg))
{
curGroup = new(nextGroupId, linkCfg, true);
curGroup.BeginGuide();
}
else
EndGuide(false);
}
else
{
if (curGroup.CurStep.Cfg.IsComplete)
DebugUtil.Log($"<color=#00FA9A>提前完成引导组组id:{curGroup.GroupId}</color>");
curGroup.BeginNextStep();
}
}
/// <summary>
/// 回到指定步骤
/// </summary>
/// <param name="step"></param>
public void ReturnStep(int stepId)
{
curGroup.EndGuide();
curGroup.BeginGuideById(stepId);
}
/// <summary>
/// 结束引导
/// </summary>
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.TryExitLevel(false, null, true);
else
GameStateManager.Instance.ChangeState(new GameStateMainScene());
}
}
/// <summary>
/// 跳过当前引导组
/// </summary>
/// <param name="isReEnterMain"></param>
public void SkipCurGuideGroup()
{
if (null == curGroup)
return;
if (!dicGuideCfg.TryGetValue(curGroup.GroupId, out LinkedList<DataGuideCfg> linkedCfg))
{
DebugUtil.LogError("要完成的引导组不存在");
return;
}
curCompleteId = linkedCfg.Last.Value.Id;
NetHelper.SaveGuideProgress((uint)linkedCfg.Last.Value.Id); // 跳过引导步骤,保存服务器
DebugUtil.Log($"<color=#00FA9A>跳过引导组服务器保存引导id:{linkedCfg.Last.Value.Id}</color>");
EndGuide(false);
BeginGuide(linkedCfg.Last.Value.Id);
}
/// <summary>
/// 跳过所有引导
/// </summary>
public async void SkipAllGuide(bool isEditor = false)
{
DebugUtil.Log("跳过所有引导");
GuideConfig guideConfig = TableManager.Instance.Tables.GuideConfig;
DataGuideCfg cfg = guideConfig.DataList[^1];
curCompleteId = cfg.Id;
NetHelper.SaveGuideProgress((uint)cfg.Id);
if (!isEditor &&
!IsPlayPVVideo &&
null != curGroup &&
curGroup.CurStep.Cfg.Id <= pvGuideID) // 特殊处理 播放过pv视频则不播放没播放过则播放
{
EndGuide(false);
await UI_PVAnimaPlayController.Open("Assets/Video/LevelIntro/LevelIntro_PV.mp4", () => { EndGuide(true); });
}
else
EndGuide(true);
}
/// <summary>
/// 显示跳过所有引导提示
/// </summary>
public void ShowSkipAllGuide()
{
UITipsManager.ShowConfirmTwo(CommonUtils.GetLocalizeText("Guide_SkipAllTitle"), // 脱离卡死
CommonUtils.GetLocalizeText("Guide_SkipAllContent"), // 是否强制跳过新手引导?(选择【跳过】便不再触发新手引导)
CommonUtils.GetLocalizeText("Skip"), // 跳过
CommonUtils.GetLocalizeText("Not"), // 否
() => { SkipAllGuide(); });
}
/// <summary>
/// 判断引导步骤是否完成
/// </summary>
/// <returns></returns>
public bool CheckComplete(int guideId)
{
if (0 == curCompleteId)
{
int curCompleteStepId = (int)Actor.Instance.Logic.GetCount((uint)LogicID.NewGuideProgress);
return curCompleteStepId >= guideId;
}
return curCompleteId >= guideId;
}
/// <summary>
/// 初始化数据
/// </summary>
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<DataGuideCfg> 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<DataGuideCfg> 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
}
/// <summary>
/// 是否是引导组的第一步
/// </summary>
/// <returns></returns>
private bool IsFirstStep(DataGuideCfg dataGuideCfg)
{
return dataGuideCfg.Id == dataGuideCfg.GroupId;
}
/// <summary>
/// 获取应该引导的引导组id
/// </summary>
/// <returns></returns>
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可能修改过id
{
foreach (DataGuideCfg guideCfg in TableManager.Instance.Tables.GuideConfig.DataList)
{
if (completeStepId > guideCfg.Id)
completeCfg = guideCfg;
}
}
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($"<color=#00FA9A>引导步骤{completeStepId}所在组{cfg.GroupId}的{cfg.Id}步骤完成,该组完成</color>");
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; // 该组没有完成,重来
}
/// <summary>
/// 设置引导状态
/// </summary>
private void SetGuideState()
{
UIManager.Instance.IsEnableBack = false;
}
/// <summary>
/// 还原状态
/// </summary>
private void RecoverState()
{
UIManager.Instance.IsEnableBack = true;
ConditionReactor.Instance.RemoveReaction(reactionID);
reactionID = ConditionReactor.INVALID_ALLOC_ID;
}
/// <summary>
/// 开始引导
/// </summary>
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();
curGroup.BeginGuide();
}
catch (Exception e)
{
DebugUtil.LogError($"跳过引导,错误信息:{e}");
RecoverState();
}
}
/// <summary>
/// 引导过程中的任意点击事件
/// </summary>
private void OnAnyClick()
{
if (!IsGuiding)
return;
if (!IsEnableAnyClick)
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}");
}
}
}