using System; using PhxhSDK; using UnityEngine; using System.Linq; using LC.Newtonsoft.Json; using Framework.Constants; using Sirenix.OdinInspector; using Cysharp.Threading.Tasks; using System.Collections.Generic; using cfg.Build; using PhxhSDK.Phxh; using UnityEngine.AddressableAssets; namespace Framework.Manager { /// /// 解锁类型 /// public enum UnlockType { [LabelText("按挂点解锁")] ForGroup, [LabelText("按主题解锁")] ForThematic, } /// /// 解锁条件类型 /// public enum UnlockConditionType { [LabelText("关卡解锁")] Level, [LabelText("金币解锁")] Coin, } [Serializable] public class BuildData { [ReadOnly] public string buildID; [LabelText("挂点数量")] public int nodeCount; [LabelText("主题数量")] public int thematicCount; [LabelText("解锁类型")] [SerializeField] public UnlockType unlockType; [LabelText("解锁条件类型")] [SerializeField] public UnlockConditionType unlockConditionType; [LabelText("解锁条件")] public List unlockInfos; [LabelText("挂点数据")] public List NodeInfos; [Serializable] public class UnlockInfo { [LabelText("条件")] public int condition; //主题 or 挂点 [LabelText("对应组")] public int conditionGroup; [LabelText("前置组")] public int preGroup; } [Serializable] public class BuildNode { [LabelText("挂点名称")] [ReadOnly] public string Name; [HideInInspector] public List Options; [HideInInspector] public string IconPath; [JsonConstructor] public BuildNode(string name) { this.Name = name; this.Options = new List(); this.IconPath = string.Empty; } public BuildNode() { } } } /// /// 节点信息类 /// public class NodeInfo { public string Name; public Dictionary Options; //按挂点解锁条件 public int Condition; //前置解锁组 public string PreGroup; public string IconPath; public OptionInfo GetOptionInfo(string optionID = null) { if (optionID == null) optionID = "Option1"; return Options.GetValueOrDefault(optionID); } } /// /// 节点选项类 /// public class OptionInfo { public string Name; public string IconPath; //按主题解锁条件 public int Condition; //前置主题 public string PreThematic; } /// /// 每个主题节点保存信息 /// public class BuildInfo { public string BuildSceneID; public Dictionary ChooseNodeInfo; public BuildInfo() { ChooseNodeInfo = new Dictionary(); } public BuildInfo(string buildId) { BuildSceneID = buildId; ChooseNodeInfo = new Dictionary(); } } /// /// 玩家养成建造存盘数据 /// public class UserBuildInfo { public int GuideGroup; public string CurBuildId; public Dictionary AllChooseNodeInfo; public UserBuildInfo() { AllChooseNodeInfo = new Dictionary(); } public UserBuildInfo(string curBuildId) { CurBuildId = curBuildId; AllChooseNodeInfo = new Dictionary(); } } public class BuildManager { private const string UserBuildSaveKey = "UserBuildInfo"; private const string NodeName = "Node{0}"; private const string OptionName = "Option{0}"; private static BuildManager _instance; public static BuildManager Instance { get { if (_instance == null) { _instance = new BuildManager(); } return _instance; } } /// /// 玩家存盘选择信息 /// public UserBuildInfo UserBuildInfo { get => _userBuildInfo; private set => _userBuildInfo = value; } private UserBuildInfo _userBuildInfo; /// /// 当前主题玩家选择情况 /// public BuildInfo BuildInfo { get => _curBuildInfo; private set => _curBuildInfo = value; } private BuildInfo _curBuildInfo; /// /// 更换场景 /// public bool ChangeBuildSceneID; /// /// 已达到的条件 /// public int ReachCondition { get; private set; } = -1; /// /// 当前场景蓝图 /// public Sprite CurBlueprint; /// /// 当前场景顶栏图片 /// public Sprite CurTopBuildUI; /// /// Build场景相机 /// public Camera CurBuildCamera; /// /// 当前场景节点信息 /// private Dictionary _nodeInfos; /// ///动态加载的图标 /// private Dictionary _iconSprites; /// /// 已经加载的资源 /// private readonly List _loadAssets = new List(); private BuildData _curBuildData; private bool _isInit; public bool BuildInGame; public async UniTask LoadBuild(string buildID, int reachCondition) { InitForStorage(); var buildConfigId = !string.IsNullOrEmpty(buildID) ? buildID : _userBuildInfo.CurBuildId; if (!TableManager.Instance.Tables.BuildConfig.DataMap.TryGetValue(buildConfigId, out var buildConfig)) { DebugUtil.LogError("没有{0}的建造配置", buildConfigId); return; } if (GuideMananger.Instance.IsGuiding && !string.IsNullOrEmpty(GuideMananger.Instance.PassLevelID)) { var guideCondition = int.Parse(GuideMananger.Instance.PassLevelID.Substring("level".Length)); Instance.UpdateReachCondition(guideCondition); } else { UpdateReachCondition(reachCondition); } var buildData = await JsonHelper.LoadFromAddressable(buildConfig.BuildData); await Init(buildData, true); await Addressables.LoadSceneAsync(buildConfig.Path).ToUniTask(); _userBuildInfo.CurBuildId = buildConfigId; StorageManager.Instance.SaveWithoutUpdate(); } /// /// 获取存盘信息 /// private void InitForStorage() { if (_isInit) return; _userBuildInfo = StorageManager.Instance.GetStorage(NormalConstants.UserBuildSaveKey); _userBuildInfo ??= new UserBuildInfo(); if (string.IsNullOrEmpty(_userBuildInfo.CurBuildId)) { _userBuildInfo.CurBuildId = TableManager.Instance.Tables.BuildConfig.DataList[0].ID; DebugUtil.LogError("无存盘信息, 读默认表:{0}", _userBuildInfo.CurBuildId); } } public async UniTask Init(BuildData buildData, bool inGame = false) { if (_isInit && !ChangeBuildSceneID) return; DebugUtil.LogError("当前建造ID:{0}", buildData.buildID); BuildInGame = inGame; _curBuildData = buildData; _nodeInfos = new Dictionary(); _iconSprites = new Dictionary(); InitNodesInfo(); InitUserBuildInfo(); InitCondition(); await InitIcon(); InitBlueprint(); if (inGame) CurBuildCamera = CameraManager.Instance.UICamera; _isInit = true; } /// /// 初始化该Build场景的节点信息 /// private void InitNodesInfo() { try { foreach (var node in _curBuildData.NodeInfos) { var nodeInfo = new NodeInfo { Name = node.Name, Options = new Dictionary(), IconPath = node.IconPath }; foreach (var option in node.Options) { var optionInfo = new OptionInfo() { Name = option }; nodeInfo.Options.Add(option, optionInfo); } _nodeInfos.Add(nodeInfo.Name, nodeInfo); } } catch (Exception e) { DebugUtil.LogError("BuildManager.InitNodesInfo 初始化节点错误 :{0}", e); } } /// /// 初始化选择信息 /// private void InitUserBuildInfo() { try { if (!BuildInGame) { StorageManager.Instance.Init(); _userBuildInfo = StorageManager.Instance.GetStorage(UserBuildSaveKey); } if (_userBuildInfo == null) { var buildId = _curBuildData.buildID; _userBuildInfo = new UserBuildInfo(buildId); } var curBuildID = _curBuildData.buildID; //获取节点存盘信息 if (!_userBuildInfo.AllChooseNodeInfo.TryGetValue(curBuildID, out var buildInfo)) { _userBuildInfo.AllChooseNodeInfo.TryAdd(curBuildID, new BuildInfo(curBuildID)); } foreach (var nodeInfo in _nodeInfos) { if (_userBuildInfo.AllChooseNodeInfo[curBuildID].ChooseNodeInfo .TryGetValue(nodeInfo.Key, out var option)) { continue; } _userBuildInfo.AllChooseNodeInfo[curBuildID].ChooseNodeInfo.TryAdd(nodeInfo.Key, ""); DebugUtil.LogError("当前场景:{0},添加了信息节点:{1},选项:{2}", curBuildID, nodeInfo.Key, ""); } _curBuildInfo = _userBuildInfo.AllChooseNodeInfo[curBuildID]; //DebugUserChooseNode(); } catch (Exception e) { DebugUtil.LogError("BuildManager.InitUserBuildInfo 初始玩家信息错误 :{0}", e); } } /// /// 初始化条件 /// private void InitCondition() { try { switch (_curBuildData.unlockType) { case UnlockType.ForGroup: { foreach (var unlockInfo in _curBuildData.unlockInfos) { var nodeName = string.Format(NodeName, unlockInfo.conditionGroup); if (_nodeInfos.TryGetValue(nodeName, out var nodeInfo)) { nodeInfo.Condition = unlockInfo.condition; var nextNode = string.Format(NodeName, unlockInfo.preGroup); nodeInfo.PreGroup = nextNode; //DebugUtil.LogError("挂点解锁:节点{0}的解锁条件是: {1}, 前置解锁组是: {2}", nodeInfo.Name, unlockInfo.condition, nextNode); } } break; } case UnlockType.ForThematic: { foreach (var unlockInfo in _curBuildData.unlockInfos) { var optionName = string.Format(OptionName, unlockInfo.conditionGroup); foreach (var nodeInfo in _nodeInfos.Values) { if (nodeInfo.Options.TryGetValue(optionName, out var optionInfo)) { optionInfo.Condition = unlockInfo.condition; var nextOption = string.Format(OptionName, unlockInfo.preGroup); optionInfo.PreThematic = nextOption; DebugUtil.LogError("主题解锁:节点{0}的选项{1}的解锁条件是:{2}, 前置解锁主题是: {3}", nodeInfo.Name, optionInfo.Name, unlockInfo.condition, nextOption); } } } break; } } } catch (Exception e) { DebugUtil.LogError("BuildManager.InitCondition 初始化条件错误 :{0}", e); } } /// /// 异步加载该建造物品图标 /// private async UniTask InitIcon() { try { foreach (var nodeInfo in _nodeInfos.Values) { foreach (var optionInfo in nodeInfo.Options.Values) { optionInfo.IconPath = await InitOptionIcon(nodeInfo.IconPath, optionInfo.Name); } } } catch (Exception e) { DebugUtil.LogError("BuildManager.InitIcon 初始化图标数据错误 :{0}", e); } } /// /// 初始化加载选项图标 /// private async UniTask InitOptionIcon(string iconsPath, string optionName) { try { var path = string.Format(iconsPath, optionName); var assetPath = path.Replace(Application.dataPath, "").Replace('\\', '/'); var sprite = await AssetManager.Instance.LoadAssetAsync(assetPath); if (_iconSprites.TryAdd(path, sprite)) return path; return null; } catch (Exception e) { DebugUtil.LogError("BuildManager.InitOptionIcon 加载选项图标错误, 路径: {0}, Error: {1}", iconsPath, e); return null; } } /// /// 加载当前场景蓝图和对应UI /// private async void InitBlueprint() { try { var bluePath = string.Format(PathConstants.BuildBlueprint, _curBuildData.buildID); AssetManager.Instance.Unload(bluePath); CurBlueprint = await AssetManager.Instance.LoadAssetAsync(bluePath); var buildUIPath = string.Format(PathConstants.BuildTopUIPath, _curBuildData.buildID); AssetManager.Instance.Unload(buildUIPath); CurTopBuildUI = await AssetManager.Instance.LoadAssetAsync(buildUIPath); } catch (Exception e) { DebugUtil.LogError("BuildManager.InitBlueprint 加载蓝图错误 :{0}", e); } } /// /// 获得选项Icon图标 /// public Sprite GetOptionIcon(string nodeName, string optionName) { if (_nodeInfos.TryGetValue(nodeName, out var nodeInfo)) { if (nodeInfo.Options.TryGetValue(optionName, out var optionInfo)) { if (_iconSprites.TryGetValue(optionInfo.IconPath, out var sprite)) return sprite; } } return null; } /// /// 根据节点获得解锁条件 /// public int GetCondition(string nodeName) { var condition = 0; if (_nodeInfos.TryGetValue(nodeName, out var nodeInfo)) { condition = nodeInfo.Condition; } return condition; } /// /// 保存节点 /// public void SaveNodeInfo(string node, string option) { if (_curBuildInfo.ChooseNodeInfo.TryGetValue(node, out var oldOption)) { _curBuildInfo.ChooseNodeInfo[node] = option; DebugUtil.LogY($"场景{_userBuildInfo.CurBuildId}中的节点{node}保存了{option}选择"); } else { DebugUtil.LogWarning("玩家Build存档信息没有 {0} 节点信息,请检查初始化", node); _curBuildInfo.ChooseNodeInfo.Add(node, option); } StorageManager.Instance.SaveWithoutUpdate(); } /// /// 获取下一个解锁节点 /// public string GetNextLockNode() { string nodeName = null; switch (_curBuildData.unlockType) { case UnlockType.ForGroup: { foreach (var node in _nodeInfos) { if (_curBuildInfo.ChooseNodeInfo.TryGetValue(node.Key, out var curNode) && _curBuildInfo.ChooseNodeInfo.TryGetValue(node.Value.PreGroup, out var preNode)) { //当前节点位选择且前置节点已选择 if (string.IsNullOrEmpty(curNode) && !string.IsNullOrEmpty(preNode)) { //DebugUtil.LogError("下一个节点是{0}", node.Key); return node.Key; } } } var firstNode = _nodeInfos .OrderBy(kv => kv.Value.Condition) .FirstOrDefault(); return firstNode.Key; } //TODO 按主题解锁 default: break; } //DebugUtil.LogError("得到最小的节点是{0}", nodeName); return nodeName; } /// /// 更新已经达到的条件 /// public void UpdateReachCondition(int condition) { //TODO 分场景 、解锁类型、解锁条件 ReachCondition = condition; } /// /// 更新本地节点选择 /// public void SetBuildUserInfo(int guideGroupID) { _userBuildInfo.GuideGroup = guideGroupID; StorageManager.Instance.SyncForce = true; } /// /// 播放音效 /// public void PlaySound() { if (BuildInGame) AudioManager.Instance.PlaySound(AudioType.SOUND, "S_Btn", new UnityAudio(false)); } /// /// Debug 清楚玩家所有选择 /// public void ClearOption() { foreach (var node in _nodeInfos) { if (_curBuildInfo.ChooseNodeInfo.TryGetValue(node.Key, out var option)) _curBuildInfo.ChooseNodeInfo[node.Key] = ""; } DebugUserChooseNode(); StorageManager.Instance.SyncForce = true; } private void DebugUserChooseNode() { foreach (var infos in BuildInfo.ChooseNodeInfo) { DebugUtil.LogError("节点 {0} 选择的的是 {1}", infos.Key, infos.Value); } } public void Release() { foreach (var icon in _iconSprites) { AssetManager.Instance.Unload(icon.Key); } CurBlueprint = null; CurTopBuildUI = null; } } }