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

567 lines
18 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 Gameplay.SubSystems;
using NLDPB;
using PhxhSDK;
using System;
using System.Collections.Generic;
namespace Gameplay.Guide
{
/// <summary>
/// 新手引导管理器
/// </summary>
public class GuideManager : Singlenton<GuideManager>, IInitable, IUpdatable
{
/// <summary>
/// 引导组第一步的id
/// </summary>
private const int GUIDE_GROUP_FIRST_STEP_ID = 1;
/// <summary>
/// 是否初始化数据成功
/// </summary>
private bool isInitDataSuccess;
/// <summary>
/// 特殊pv引导id
/// </summary>
private const int PV_GUIDE_ID = 1000;
#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, List<DataGuideCfg>> dicGuideCfg;
/// <summary>
/// 未完成的引导组,注册的开始条件记录在这,key:reactionId,value:引导组id
/// </summary>
private Dictionary<int, int> dicReaction;
/// <summary>
/// 当前的引导组
/// </summary>
private GuideGroup curGroup;
/// <summary>
/// 是否引导中
/// </summary>
public bool IsGuiding
{
get { return curGroup != null && curGroup.IsGuiding; }
}
#region 引导特殊控制参数
/// <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;
#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;
}
/// <summary>
/// 启动引导
/// </summary>
public void StartGuide()
{
#if GUIDE
if (!isInitDataSuccess)
{
DebugUtil.LogError("启动引导失败,数据没有初始化成功");
return;
}
foreach (KeyValuePair<int, List<DataGuideCfg>> 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
}
/// <summary>
/// 完成指定引导步骤
/// </summary>
/// <param name="step"></param>
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($"<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 List<DataGuideCfg> listCfg))
{
curGroup = new(nextGroupId, listCfg, true);
curGroup.BeginGuide();
}
else
EndGuide(false);
}
else
{
if (curGroup.CurStep.Cfg.IsComplete)
DebugUtil.Log($"<color=#00FA9A>提前完成引导组组id:{curGroup.GroupId}</color>");
curGroup.BeginNextStep();
}
}
/// <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.ExitLevelChangeMainFunc = () =>
{
GameStateManager.Instance.ChangeState(new GameStateMainScene());
return false;
};
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 List<DataGuideCfg> listCfg))
{
DebugUtil.LogError("要完成的引导组不存在");
return;
}
NetHelper.SaveGuideProgress((uint)listCfg[^1].Id); // 跳过引导步骤,保存服务器
DebugUtil.Log($"<color=#00FA9A>跳过引导组服务器保存引导id:{listCfg[^1].Id}</color>");
EndGuide(false);
StartGuide();
}
/// <summary>
/// 跳过所有引导
/// </summary>
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);
}
/// <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)
{
int curCompleteStepId = (int)Actor.Instance.Logic.GetCount((uint)LogicID.NewGuideProgress);
return curCompleteStepId >= guideId;
}
#region 私有方法
/// <summary>
/// 初始化数据
/// </summary>
private void InitData()
{
dicGuideCfg = new();
foreach (DataGuideCfg cfg in TableManager.Instance.Tables.GuideConfig.DataList)
{
if (!dicGuideCfg.TryGetValue(cfg.GroupId, out List<DataGuideCfg> listCfg))
{
listCfg = new List<DataGuideCfg>();
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<DataGuideCfg> 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;
}
/// <summary>
/// 检查引导组是否完成
/// </summary>
/// <returns></returns>
private bool CheckGuideGroupComplete(int guideGroupId)
{
// 通过服务器数据判断该组引导是否完成
return false;
}
/// <summary>
/// 是否是引导组的第一步
/// </summary>
/// <returns></returns>
private bool IsFirstStep(DataGuideCfg dataGuideCfg)
{
return GUIDE_GROUP_FIRST_STEP_ID == dataGuideCfg.Id;
}
/// <summary>
/// 获取应该引导的引导组id
/// </summary>
/// <returns></returns>
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($"<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)
{
if (0 == dataGuideCfg.NextId) // 下一组id为0表示最后一组
return 0;
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;
}
/// <summary>
/// 通过条件触发开始引导
/// </summary>
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<DataGuideCfg> listCfg))
return;
curGroup = new GuideGroup(guideGroupId, listCfg);
SetGuideState();
curGroup.BeginGuide();
}
catch (Exception e)
{
DebugUtil.LogError($"跳过引导,错误信息:{e}");
RecoverState();
}
}
/// <summary>
/// 引导过程中的任意点击事件
/// </summary>
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
}
}