688 lines
22 KiB
C#
688 lines
22 KiB
C#
using System;
|
|
using PhxhSDK;
|
|
using UnityEngine;
|
|
using System.Linq;
|
|
using PhxhSDK.Phxh;
|
|
using LC.Newtonsoft.Json;
|
|
using Framework.Constants;
|
|
using Sirenix.OdinInspector;
|
|
using Cysharp.Threading.Tasks;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.AddressableAssets;
|
|
|
|
namespace Framework.Manager
|
|
{
|
|
/// <summary>
|
|
/// 解锁类型
|
|
/// </summary>
|
|
public enum UnlockType
|
|
{
|
|
[LabelText("按挂点解锁")] ForGroup,
|
|
|
|
[LabelText("按主题解锁")] ForThematic,
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解锁条件类型
|
|
/// </summary>
|
|
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<UnlockInfo> unlockInfos;
|
|
[LabelText("挂点数据")] public List<BuildNode> 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<string> Options;
|
|
|
|
[HideInInspector] public string IconPath;
|
|
|
|
[JsonConstructor]
|
|
public BuildNode(string name)
|
|
{
|
|
this.Name = name;
|
|
this.Options = new List<string>();
|
|
this.IconPath = string.Empty;
|
|
}
|
|
|
|
public BuildNode()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 节点信息类
|
|
/// </summary>
|
|
public class NodeInfo
|
|
{
|
|
public string Name;
|
|
|
|
public Dictionary<string, OptionInfo> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 节点选项类
|
|
/// </summary>
|
|
public class OptionInfo
|
|
{
|
|
public string Name;
|
|
|
|
public string IconPath;
|
|
|
|
//按主题解锁条件
|
|
public int Condition;
|
|
|
|
//前置主题
|
|
public string PreThematic;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 每个主题节点保存信息
|
|
/// </summary>
|
|
public class BuildInfo
|
|
{
|
|
public string BuildSceneID;
|
|
|
|
public Dictionary<string, string> ChooseNodeInfo;
|
|
|
|
//当前节点
|
|
public bool Complete;
|
|
|
|
public BuildInfo()
|
|
{
|
|
ChooseNodeInfo = new Dictionary<string, string>();
|
|
}
|
|
|
|
public BuildInfo(string buildId)
|
|
{
|
|
BuildSceneID = buildId;
|
|
ChooseNodeInfo = new Dictionary<string, string>();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 玩家养成建造存盘数据
|
|
/// </summary>
|
|
public class UserBuildInfo
|
|
{
|
|
public int GuideGroup;
|
|
public string CurBuildId;
|
|
public Dictionary<string, BuildInfo> AllChooseNodeInfo;
|
|
|
|
public UserBuildInfo()
|
|
{
|
|
AllChooseNodeInfo = new Dictionary<string, BuildInfo>();
|
|
}
|
|
|
|
public UserBuildInfo(string curBuildId)
|
|
{
|
|
CurBuildId = curBuildId;
|
|
AllChooseNodeInfo = new Dictionary<string, BuildInfo>();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 玩家存盘选择信息
|
|
/// </summary>
|
|
public UserBuildInfo UserBuildInfo
|
|
{
|
|
get => _userBuildInfo;
|
|
private set => _userBuildInfo = value;
|
|
}
|
|
|
|
private UserBuildInfo _userBuildInfo;
|
|
|
|
/// <summary>
|
|
/// 当前玩家选择主题
|
|
/// </summary>
|
|
public BuildInfo CurBuildInfo
|
|
{
|
|
get => _curBuildInfo;
|
|
private set => _curBuildInfo = value;
|
|
}
|
|
|
|
private BuildInfo _curBuildInfo;
|
|
|
|
/// <summary>
|
|
/// 更换场景
|
|
/// </summary>
|
|
public bool ChangeBuildSceneID;
|
|
|
|
/// <summary>
|
|
/// 已达到的条件
|
|
/// </summary>
|
|
public int ReachCondition { get; private set; } = -1;
|
|
|
|
/// <summary>
|
|
/// 当前场景蓝图
|
|
/// </summary>
|
|
public Sprite CurBlueprint;
|
|
|
|
/// <summary>
|
|
/// 当前场景顶栏图片
|
|
/// </summary>
|
|
public Sprite CurTopBuildUI;
|
|
|
|
/// <summary>
|
|
/// Build场景相机
|
|
/// </summary>
|
|
public Camera CurBuildCamera;
|
|
|
|
/// <summary>
|
|
/// 当前场景节点信息
|
|
/// </summary>
|
|
private Dictionary<string, NodeInfo> _nodeInfos;
|
|
|
|
/// <summary>
|
|
///动态加载的图标
|
|
/// </summary>
|
|
private Dictionary<string, Sprite> _iconSprites;
|
|
|
|
private BuildData _curBuildData;
|
|
private bool _isInit;
|
|
public bool BuildInGame;
|
|
|
|
public async UniTask LoadBuild(string buildID, int reachCondition)
|
|
{
|
|
InitForStorage();
|
|
|
|
var lastBuildID = _curBuildInfo;
|
|
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<BuildData>(buildConfig.BuildData);
|
|
|
|
await Init(buildData, true);
|
|
await Addressables.LoadSceneAsync(buildConfig.Path).ToUniTask();
|
|
InitBlueprint();
|
|
if (lastBuildID is { Complete: true } && !_curBuildInfo.Complete)
|
|
{
|
|
_userBuildInfo.CurBuildId = buildConfigId;
|
|
StorageManager.Instance.SaveWithoutUpdate();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取存盘信息
|
|
/// </summary>
|
|
private void InitForStorage()
|
|
{
|
|
if (_isInit) return;
|
|
_userBuildInfo = StorageManager.Instance.GetStorage<UserBuildInfo>(NormalConstants.UserBuildSaveKey);
|
|
_userBuildInfo ??= new UserBuildInfo();
|
|
if (string.IsNullOrEmpty(_userBuildInfo.CurBuildId))
|
|
{
|
|
_userBuildInfo.CurBuildId = TableManager.Instance.Tables.BuildConfig.DataList[0].ID;
|
|
}
|
|
}
|
|
|
|
public async UniTask Init(BuildData buildData, bool inGame = false)
|
|
{
|
|
if (_isInit && !ChangeBuildSceneID) return;
|
|
|
|
BuildInGame = inGame;
|
|
_curBuildData = buildData;
|
|
_nodeInfos = new Dictionary<string, NodeInfo>();
|
|
_iconSprites = new Dictionary<string, Sprite>();
|
|
|
|
InitNodesInfo();
|
|
InitUserBuildInfo();
|
|
InitCondition();
|
|
await InitIcon();
|
|
|
|
|
|
if (inGame)
|
|
CurBuildCamera = CameraManager.Instance.UICamera;
|
|
|
|
_isInit = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 初始化该Build场景的节点信息
|
|
/// </summary>
|
|
private void InitNodesInfo()
|
|
{
|
|
try
|
|
{
|
|
foreach (var node in _curBuildData.NodeInfos)
|
|
{
|
|
var nodeInfo = new NodeInfo
|
|
{
|
|
Name = node.Name,
|
|
Options = new Dictionary<string, OptionInfo>(),
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 初始化选择信息
|
|
/// </summary>
|
|
private void InitUserBuildInfo()
|
|
{
|
|
try
|
|
{
|
|
if (!BuildInGame)
|
|
{
|
|
StorageManager.Instance.Init();
|
|
_userBuildInfo = StorageManager.Instance.GetStorage<UserBuildInfo>(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, "");
|
|
}
|
|
|
|
_curBuildInfo = _userBuildInfo.AllChooseNodeInfo[curBuildID];
|
|
|
|
//DebugUserChooseNode();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DebugUtil.LogError("BuildManager.InitUserBuildInfo 初始玩家信息错误 :{0}", e);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 初始化条件
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 异步加载该建造物品图标
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 初始化加载选项图标
|
|
/// </summary>
|
|
private async UniTask<string> 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<Sprite>(assetPath);
|
|
if (_iconSprites.TryAdd(path, sprite))
|
|
return path;
|
|
return null;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DebugUtil.LogError("BuildManager.InitOptionIcon 加载选项图标错误, 路径: {0}, Error: {1}", iconsPath, e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 加载当前场景蓝图和对应UI
|
|
/// </summary>
|
|
private async void InitBlueprint()
|
|
{
|
|
try
|
|
{
|
|
var bluePath = string.Format(PathConstants.BuildBlueprint, _curBuildData.buildID);
|
|
AssetManager.Instance.Unload(bluePath);
|
|
CurBlueprint = await AssetManager.Instance.LoadAssetAsync<Sprite>(bluePath);
|
|
var buildUIPath = string.Format(PathConstants.BuildTopUIPath, _curBuildData.buildID);
|
|
AssetManager.Instance.Unload(buildUIPath);
|
|
CurTopBuildUI = await AssetManager.Instance.LoadAssetAsync<Sprite>(buildUIPath);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DebugUtil.LogError("BuildManager.InitBlueprint 加载蓝图错误 :{0}", e);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获得选项Icon图标
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据节点获得解锁条件
|
|
/// </summary>
|
|
public int GetCondition(string nodeName)
|
|
{
|
|
var condition = 0;
|
|
if (_nodeInfos.TryGetValue(nodeName, out var nodeInfo))
|
|
{
|
|
condition = nodeInfo.Condition;
|
|
}
|
|
|
|
return condition;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存节点
|
|
/// </summary>
|
|
public void SaveNodeInfo(string node, string option)
|
|
{
|
|
if (_curBuildInfo.ChooseNodeInfo.TryGetValue(node, out var oldOption))
|
|
{
|
|
_curBuildInfo.ChooseNodeInfo[node] = option;
|
|
DebugUtil.LogY($"场景{_curBuildInfo.BuildSceneID}中的节点{node}保存了{option}选择");
|
|
}
|
|
else
|
|
{
|
|
DebugUtil.LogWarning("玩家Build存档信息没有 {0} 节点信息,请检查初始化", node);
|
|
_curBuildInfo.ChooseNodeInfo.Add(node, option);
|
|
}
|
|
|
|
var complete = true;
|
|
foreach (var chooseNode in _curBuildInfo.ChooseNodeInfo)
|
|
{
|
|
if (string.IsNullOrEmpty(chooseNode.Value))
|
|
{
|
|
complete = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (complete)
|
|
_curBuildInfo.Complete = true;
|
|
|
|
StorageManager.Instance.SaveWithoutUpdate();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取下一个解锁节点
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新已经达到的条件
|
|
/// </summary>
|
|
public void UpdateReachCondition(int condition)
|
|
{
|
|
//TODO 分场景 、解锁类型、解锁条件
|
|
ReachCondition = condition;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新本地节点选择
|
|
/// </summary>
|
|
public void SetBuildUserInfo(int guideGroupID)
|
|
{
|
|
_userBuildInfo.GuideGroup = guideGroupID;
|
|
StorageManager.Instance.SyncForce = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 播放音效
|
|
/// </summary>
|
|
public void PlaySound()
|
|
{
|
|
if (BuildInGame)
|
|
AudioManager.Instance.PlaySound(AudioType.SOUND, "S_Btn",
|
|
new UnityAudio(false));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Debug 清楚玩家所有选择
|
|
/// </summary>
|
|
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 CurBuildInfo.ChooseNodeInfo)
|
|
{
|
|
DebugUtil.LogError("节点 {0} 选择的的是 {1}", infos.Key, infos.Value);
|
|
}
|
|
}
|
|
|
|
public void CurBuildRelease()
|
|
{
|
|
foreach (var icon in _iconSprites)
|
|
{
|
|
AssetManager.Instance.Unload(icon.Key);
|
|
}
|
|
|
|
CurBlueprint = null;
|
|
CurTopBuildUI = null;
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
CurBuildRelease();
|
|
}
|
|
}
|
|
} |