From 31df656c557ea342d6855d97a4589b9a23556093 Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期三, 24 十二月 2025 10:00:15 +0800
Subject: [PATCH] 382 武将宿缘-客户端

---
 Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs                                    |   31 
 Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs                                  |   35 
 Main/Config/Configs/HeroFatesQualityLVConfig.cs                                         |   50 +
 Main/Config/ConfigManager.cs                                                            |    6 
 Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs                |   31 
 Main/System/HeroFates/HeroFatesUpgradeWin.cs                                            |  252 +++++++
 Main/System/HeroFates/HeroFatesPutItem.cs                                               |   42 +
 Main/System/HeroFates/HeroFatesUpgradeWin.cs.meta                                       |   11 
 Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs.meta                               |   11 
 Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs                                       |   43 +
 Main/Config/Configs/HeroFatesQualityLVConfig.cs.meta                                    |   11 
 Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs.meta                                  |   11 
 Main/System/Redpoint/MainRedDot.cs                                                      |    3 
 Main/System/HeroFates/HeroFatesPutCell.cs                                               |   31 
 Main/System/HeroFates/HeroFatesManager.cs                                               |  732 ++++++++++++++++++++
 Main/System/HeroFates/HeroFatesPutWin.cs                                                |  100 ++
 Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs             |   24 
 Main/System/HeroFates/HeroFatesPutCell.cs.meta                                          |   11 
 Main/System/HeroFates/HeroFatesPutItem.cs.meta                                          |   11 
 Main/Main.cs                                                                            |    2 
 Main/System/HeroFates/HeroFatesIHItem.cs.meta                                           |   11 
 Main/Config/Configs/HeroFatesConfig.cs.meta                                             |   11 
 Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs.meta                                |   11 
 Main/Utility/EnumHelper.cs                                                              |    1 
 Main/System/Hero/UIHeroController.cs                                                    |   15 
 Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs.meta                             |   11 
 Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs      |   12 
 Main/System/HeroFates/HeroFatesIHItem.cs                                                |   41 +
 Main/System/HeroFates/HeroFatesPutWin.cs.meta                                           |   11 
 Main/System/Main/FightPowerManager.cs                                                   |    4 
 Main/System/HeroUI/HeroBaseWin.cs                                                       |    5 
 Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs                                       |   19 
 Main/Config/Configs/HeroFatesConfig.cs                                                  |   92 ++
 Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs.meta        |   11 
 Main/System/HeroFates.meta                                                              |    8 
 Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs                                     |   65 +
 Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs.meta           |   11 
 Main/System/HeroFates/HeroFatesWin.cs.meta                                              |   11 
 Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs                                    |    1 
 Main/System/HeroFates/HeroFatesCell.cs.meta                                             |   11 
 Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs.meta |   11 
 Main/System/HeroFates/HeroFatesManager.cs.meta                                          |   11 
 Main/System/HeroFates/HeroFatesWin.cs                                                   |   90 ++
 Main/System/HeroFates/HeroFatesCell.cs                                                  |  175 ++++
 Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs.meta                                  |   11 
 Main/System/HeroUI/HeroHeadBaseCell.cs                                                  |   13 
 46 files changed, 2,108 insertions(+), 13 deletions(-)

diff --git a/Main/Config/ConfigManager.cs b/Main/Config/ConfigManager.cs
index 97d68ae..f895a55 100644
--- a/Main/Config/ConfigManager.cs
+++ b/Main/Config/ConfigManager.cs
@@ -54,6 +54,8 @@
             typeof(GoldRushCampConfig),
             typeof(GoldRushItemConfig),
             typeof(GoldRushWorkerConfig),
+            typeof(HeroFatesConfig),
+            typeof(HeroFatesQualityLVConfig),
             typeof(HeroLineupHaloConfig),
             typeof(HeroQualityLVConfig),
             typeof(HorseClassConfig),
@@ -266,6 +268,10 @@
         ClearConfigDictionary<GoldRushItemConfig>();
         // 娓呯┖ GoldRushWorkerConfig 瀛楀吀
         ClearConfigDictionary<GoldRushWorkerConfig>();
+        // 娓呯┖ HeroFatesConfig 瀛楀吀
+        ClearConfigDictionary<HeroFatesConfig>();
+        // 娓呯┖ HeroFatesQualityLVConfig 瀛楀吀
+        ClearConfigDictionary<HeroFatesQualityLVConfig>();
         // 娓呯┖ HeroLineupHaloConfig 瀛楀吀
         ClearConfigDictionary<HeroLineupHaloConfig>();
         // 娓呯┖ HeroQualityLVConfig 瀛楀吀
diff --git a/Main/Config/Configs/HeroFatesConfig.cs b/Main/Config/Configs/HeroFatesConfig.cs
new file mode 100644
index 0000000..da8736b
--- /dev/null
+++ b/Main/Config/Configs/HeroFatesConfig.cs
@@ -0,0 +1,92 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           2025骞�12鏈�10鏃�
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class HeroFatesConfig : ConfigBase<int, HeroFatesConfig>
+{
+    static HeroFatesConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int FatesID;
+	public string FatesName;
+	public int FatesQuality;
+	public int[] HeroIDList;
+	public int[][] AwardItemList;
+	public int[] AttrIDList;
+	public int[] LVAttrValueList;
+
+    public override int LoadKey(string _key)
+    {
+        int key = GetKey(_key);
+        return key;
+    }
+
+    public override void LoadConfig(string input)
+    {
+        try {
+        string[] tables = input.Split('\t');
+        int.TryParse(tables[0],out FatesID); 
+
+			FatesName = tables[1];
+
+			int.TryParse(tables[2],out FatesQuality); 
+
+			if (tables[3].Contains("["))
+			{
+				HeroIDList = JsonMapper.ToObject<int[]>(tables[3]);
+			}
+			else
+			{
+				string[] HeroIDListStringArray = tables[3].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+				HeroIDList = new int[HeroIDListStringArray.Length];
+				for (int i=0;i<HeroIDListStringArray.Length;i++)
+				{
+					 int.TryParse(HeroIDListStringArray[i],out HeroIDList[i]);
+				}
+			}
+
+			AwardItemList = JsonMapper.ToObject<int[][]>(tables[4].Replace("(", "[").Replace(")", "]")); 
+
+			if (tables[5].Contains("["))
+			{
+				AttrIDList = JsonMapper.ToObject<int[]>(tables[5]);
+			}
+			else
+			{
+				string[] AttrIDListStringArray = tables[5].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+				AttrIDList = new int[AttrIDListStringArray.Length];
+				for (int i=0;i<AttrIDListStringArray.Length;i++)
+				{
+					 int.TryParse(AttrIDListStringArray[i],out AttrIDList[i]);
+				}
+			}
+
+			if (tables[6].Contains("["))
+			{
+				LVAttrValueList = JsonMapper.ToObject<int[]>(tables[6]);
+			}
+			else
+			{
+				string[] LVAttrValueListStringArray = tables[6].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+				LVAttrValueList = new int[LVAttrValueListStringArray.Length];
+				for (int i=0;i<LVAttrValueListStringArray.Length;i++)
+				{
+					 int.TryParse(LVAttrValueListStringArray[i],out LVAttrValueList[i]);
+				}
+			}
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/Config/Configs/HeroFatesConfig.cs.meta b/Main/Config/Configs/HeroFatesConfig.cs.meta
new file mode 100644
index 0000000..f622c7e
--- /dev/null
+++ b/Main/Config/Configs/HeroFatesConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: de1bf25d84fa4414a9c579dfa3b636fc
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/Configs/HeroFatesQualityLVConfig.cs b/Main/Config/Configs/HeroFatesQualityLVConfig.cs
new file mode 100644
index 0000000..2a0f1b5
--- /dev/null
+++ b/Main/Config/Configs/HeroFatesQualityLVConfig.cs
@@ -0,0 +1,50 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           2025骞�12鏈�10鏃�
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class HeroFatesQualityLVConfig : ConfigBase<int, HeroFatesQualityLVConfig>
+{
+    static HeroFatesQualityLVConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int Id;
+	public int FatesQuality;
+	public int FatesLV;
+	public int NeedStarTotal;
+	public int NeedHeroCnt;
+
+    public override int LoadKey(string _key)
+    {
+        int key = GetKey(_key);
+        return key;
+    }
+
+    public override void LoadConfig(string input)
+    {
+        try {
+        string[] tables = input.Split('\t');
+        int.TryParse(tables[0],out Id); 
+
+			int.TryParse(tables[1],out FatesQuality); 
+
+			int.TryParse(tables[2],out FatesLV); 
+
+			int.TryParse(tables[3],out NeedStarTotal); 
+
+			int.TryParse(tables[4],out NeedHeroCnt); 
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/Config/Configs/HeroFatesQualityLVConfig.cs.meta b/Main/Config/Configs/HeroFatesQualityLVConfig.cs.meta
new file mode 100644
index 0000000..b9f5717
--- /dev/null
+++ b/Main/Config/Configs/HeroFatesQualityLVConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04ded98ba28951c4db9c8281c43ae86b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs b/Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs
new file mode 100644
index 0000000..fb36a69
--- /dev/null
+++ b/Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+
+public partial class HeroFatesQualityLVConfig : ConfigBase<int, HeroFatesQualityLVConfig>
+{
+    // <瀹跨紭鍝佽川,<瀹跨紭绛夌骇锛屽敮涓�ID>
+    static Dictionary<int, Dictionary<int, int>> idDict = new Dictionary<int, Dictionary<int, int>>();
+    protected override void OnConfigParseCompleted()
+    {
+        if (!idDict.ContainsKey(FatesQuality))
+        {
+            idDict[FatesQuality] = new Dictionary<int, int>();
+        }
+        idDict[FatesQuality][FatesLV] = Id;
+    }
+
+    public static bool TryGetDictByFatesQuality(int fatesQuality, out Dictionary<int, int> dict)
+    {
+        return idDict.TryGetValue(fatesQuality, out dict);
+    }
+
+    public static bool TryGetId(int fatesQuality, int fatesLV, out int id)
+    {
+        id = 0;
+        return idDict.TryGetValue(fatesQuality, out var dict) && dict.TryGetValue(fatesLV, out id);
+    }
+
+    public static bool TryGetHeroFatesQualityLVConfig(int fatesQuality, int fatesLV, out HeroFatesQualityLVConfig config)
+    {
+        config = null;
+        if (!TryGetId(fatesQuality, fatesLV, out int id) || !HasKey(id))
+            return false;
+        config = Get(id);
+        return true;
+    }
+}
diff --git a/Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs.meta b/Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs.meta
new file mode 100644
index 0000000..318509f
--- /dev/null
+++ b/Main/Config/PartialConfigs/HeroFatesQualityLVConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 892ed1407bdeb8a42b61f323ff60f3a6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs b/Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs
new file mode 100644
index 0000000..5bafa40
--- /dev/null
+++ b/Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs
@@ -0,0 +1,24 @@
+using UnityEngine;
+using System.Collections;
+
+// B2 41 姝﹀皢瀹跨紭 #tagCSHeroFates
+
+public class CB241_tagCSHeroFates : GameNetPackBasic {
+    public byte FatesID;    // 瀹跨紭ID
+    public byte OPType;    // 0-婵�娲婚濂栵紱1-鍗囩骇
+    public byte IndexCnt;
+    public  ushort[] ItemIndexList;    // 鍗囩骇鏃舵秷鑰楃殑鏉愭枡鍗″湪姝﹀皢鑳屽寘绱㈠紩鍒楄〃锛屽崌绾ф椂鎵嶅彂
+
+    public CB241_tagCSHeroFates () {
+        combineCmd = (ushort)0x03FE;
+        _cmd = (ushort)0xB241;
+    }
+
+    public override void WriteToBytes () {
+        WriteBytes (FatesID, NetDataType.BYTE);
+        WriteBytes (OPType, NetDataType.BYTE);
+        WriteBytes (IndexCnt, NetDataType.BYTE);
+        WriteBytes (ItemIndexList, NetDataType.WORD, IndexCnt);
+    }
+
+}
diff --git a/Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs.meta b/Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs.meta
new file mode 100644
index 0000000..6bb9846
--- /dev/null
+++ b/Main/Core/NetworkPackage/ClientPack/CB2_NewFunction/CB241_tagCSHeroFates.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 109782f5f047e6b40aa4acf5bee3caa3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs
new file mode 100644
index 0000000..8253829
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs
@@ -0,0 +1,12 @@
+using UnityEngine;
+using System.Collections;
+
+// B1 31 瀹跨紭淇℃伅 #tagSCHeroFatesInfo
+
+public class DTCB131_tagSCHeroFatesInfo : DtcBasic {
+    public override void Done(GameNetPackBasic vNetPack) {
+        base.Done(vNetPack);
+        HB131_tagSCHeroFatesInfo vNetData = vNetPack as HB131_tagSCHeroFatesInfo;
+        HeroFatesManager.Instance.OnUpdateHeroFatesInfo(vNetData);
+    }
+}
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs.meta b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs.meta
new file mode 100644
index 0000000..d6a73d8
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB131_tagSCHeroFatesInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f32287c05ce489141b839fa73fb39327
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs b/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
index b3f7191..4e90c99 100644
--- a/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
+++ b/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -135,6 +135,7 @@
         Register(typeof(HA319_tagMCPackDownloadRecord), typeof(DTCA319_tagMCPackDownloadRecord));
         Register(typeof(HB129_tagSCLineupRecommendInfo), typeof(DTCB129_tagSCLineupRecommendInfo));
         Register(typeof(HAB05_tagSCOSACelebrationInfo), typeof(DTCAB05_tagSCOSACelebrationInfo));
+        Register(typeof(HB131_tagSCHeroFatesInfo), typeof(DTCB131_tagSCHeroFatesInfo));
     }
 
     //涓诲伐绋嬫敞鍐屽皝鍖�
diff --git a/Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs b/Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs
new file mode 100644
index 0000000..7b4c8b8
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs
@@ -0,0 +1,31 @@
+using UnityEngine;
+using System.Collections;
+
+// B1 31 瀹跨紭淇℃伅 #tagSCHeroFatesInfo
+
+public class HB131_tagSCHeroFatesInfo : GameNetPackBasic {
+    public byte Count;
+    public  tagSCHeroFates[] FatesList;
+
+    public HB131_tagSCHeroFatesInfo () {
+        _cmd = (ushort)0xB131;
+    }
+
+    public override void ReadFromBytes (byte[] vBytes) {
+        TransBytes (out Count, vBytes, NetDataType.BYTE);
+        FatesList = new tagSCHeroFates[Count];
+        for (int i = 0; i < Count; i ++) {
+            FatesList[i] = new tagSCHeroFates();
+            TransBytes (out FatesList[i].FatesID, vBytes, NetDataType.BYTE);
+            TransBytes (out FatesList[i].State, vBytes, NetDataType.BYTE);
+            TransBytes (out FatesList[i].FatesLV, vBytes, NetDataType.BYTE);
+        }
+    }
+
+    public class tagSCHeroFates {
+        public byte FatesID;        // 瀹跨紭ID
+        public byte State;        // 瀹跨紭鐘舵�侊細0-鏈縺娲伙紱1-宸叉縺娲诲凡棰嗗
+        public byte FatesLV;        // 瀹跨紭绛夌骇锛屾縺娲绘椂涓�0绾э紝鍗囩骇鍚庢湁鍗囩骇灞炴��
+    }
+
+}
diff --git a/Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs.meta b/Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs.meta
new file mode 100644
index 0000000..8b36b29
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HB1_Role/HB131_tagSCHeroFatesInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f9ee46f7d4246af45a7fcdca58ae94db
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Main.cs b/Main/Main.cs
index 8d58b09..0874c51 100644
--- a/Main/Main.cs
+++ b/Main/Main.cs
@@ -94,7 +94,7 @@
         managers.Add(GuildBossManager.Instance);
         managers.Add(LineupRecommendManager.Instance);
         managers.Add(OSActivityManager.Instance);
-
+        managers.Add(HeroFatesManager.Instance);
         foreach (var manager in managers)
         {
             manager.Init();
diff --git a/Main/System/Hero/UIHeroController.cs b/Main/System/Hero/UIHeroController.cs
index 45b661f..6119e98 100644
--- a/Main/System/Hero/UIHeroController.cs
+++ b/Main/System/Hero/UIHeroController.cs
@@ -22,6 +22,7 @@
 
 			if (skeletonGraphic != null)
 			{
+				SetMaterialNone();
 				if (isLh)
 				{
 					var skinConfigTmp = HeroSkinConfig.Get(skinID);
@@ -128,6 +129,7 @@
 		skeletonGraphic.Initialize(true);
 
 		skeletonGraphic.enabled = true;
+		SetMaterialNone();
 		spineAnimationState = skeletonGraphic.AnimationState;
 		spineAnimationState.Data.DefaultMix = 0f;
 		if (motionName == "")
@@ -158,7 +160,7 @@
 	/// <param name="motionName">鍔ㄤ綔鍚�</param>
 	/// <param name="loop">寰幆</param>
 	/// <param name="replay">濡傛灉鐩稿悓鍔ㄤ綔鏄惁鍐嶆閲嶆挱锛屾瘮濡傝窇姝ラ噸鎾氨浼氳烦甯т笉椤烘粦</param>
-	public virtual void PlayAnimation(string motionName, bool loop = false, bool replay=true)
+	public virtual void PlayAnimation(string motionName, bool loop = false, bool replay = true)
 	{
 		if (spineAnimationState == null) return;
 
@@ -214,11 +216,20 @@
 	}
 
 	public void SetEnabled(bool isEnable)
-	{ 
+	{
 		if (skeletonGraphic == null)
 		{
 			return;
 		}
 		skeletonGraphic.enabled = isEnable;
 	}
+
+	public void SetGray()
+	{
+		skeletonGraphic.material = MaterialUtility.GetDefaultSpriteGrayMaterial();
+	}
+	public void SetMaterialNone()
+	{
+		skeletonGraphic.material = null;
+	}
 }
\ No newline at end of file
diff --git a/Main/System/HeroFates.meta b/Main/System/HeroFates.meta
new file mode 100644
index 0000000..f368ab2
--- /dev/null
+++ b/Main/System/HeroFates.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 87b403c587f7df14899f863d5427edac
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesCell.cs b/Main/System/HeroFates/HeroFatesCell.cs
new file mode 100644
index 0000000..0d41fb0
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesCell.cs
@@ -0,0 +1,175 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HeroFatesCell : MonoBehaviour
+{
+    [SerializeField] ImageEx imgBG;
+    [SerializeField] TextEx txtFateName;
+    [SerializeField] OutlineEx outlineFateName;
+    [SerializeField] TextEx txtFateLV;
+    [SerializeField] TextEx txtFateNeedHeroHasCnt;
+    [SerializeField] TextEx txtHasHeroTotalStar;
+
+    [SerializeField] ItemCell itemCell;
+
+    [SerializeField] ButtonEx btnHave;
+    [SerializeField] ImageEx imgHave;
+    [SerializeField] Image imgHaveRed;
+
+    [SerializeField] ButtonEx btnUpgrade;
+    [SerializeField] TextEx txtUpgrade;
+    [SerializeField] ImageEx imgUpgrade;
+    [SerializeField] Image imgUpgradeRed;
+
+    [SerializeField] HorizontalLayoutGroup horizontalLayoutGroup;
+    [SerializeField] List<HeroFatesIHItem> heroFatesIHItems;
+    int[] spacingArr = new int[5] { 150, 150, 99, 68, 37 };
+    [SerializeField] Transform transAttr;
+    [SerializeField] List<TextEx> attrs;
+    [SerializeField] Transform transItems;
+    [SerializeField] List<ItemCell> items;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+    public void Display(int index, List<int> showList)
+    {
+        if (showList.IsNullOrEmpty() || index >= showList.Count || index < 0)
+        {
+            return;
+        }
+
+        int fatesId = showList[index];
+        if (!manager.TryGetHeroFatesConfig(fatesId, out HeroFatesConfig config) || config.HeroIDList.IsNullOrEmpty())
+        {
+            return;
+        }
+
+        HeroFatesState state = manager.GetHeroFatesState(fatesId);
+        bool isNoActive = state == HeroFatesState.Locked || state == HeroFatesState.Activatable;
+        txtFateLV.SetActive(!isNoActive);
+        txtFateNeedHeroHasCnt.SetActive(isNoActive);
+        txtHasHeroTotalStar.SetActive(state == HeroFatesState.ActiveNotUpgradable || state == HeroFatesState.ActiveUpgradable);
+
+        btnHave.SetActive(isNoActive);
+        btnHave.interactable = state == HeroFatesState.Activatable;
+        imgHave.gray = state == HeroFatesState.Locked;
+        imgHaveRed.SetActive(state == HeroFatesState.Activatable);
+
+        btnUpgrade.SetActive(!isNoActive);
+        btnUpgrade.interactable = state != HeroFatesState.MaxLevel;
+        imgUpgrade.gray = state == HeroFatesState.MaxLevel;
+        txtUpgrade.text = Language.Get(state == HeroFatesState.MaxLevel ? "L1110" : "L1109");
+        imgUpgradeRed.SetActive(state == HeroFatesState.ActiveUpgradable);
+
+        imgBG.SetSprite(manager.GetCellBgByFatesQuality(config.FatesQuality));
+        txtFateName.text = config.FatesName;
+        txtFateName.color = UIHelper.GetUIColorByFunc(config.FatesQuality);
+        outlineFateName.OutlineColor = UIHelper.GetUIOutlineColor(config.FatesQuality);
+
+        int needHeroHasCnt = manager.GetFateNeedHeroHasCnt(fatesId);
+        int needHeroCnt = config.HeroIDList.Length;
+        txtFateNeedHeroHasCnt.text = Language.Get("BoneField09", needHeroHasCnt, needHeroCnt);
+
+        bool hasNowLVAndNextLVConfig = manager.TryGetNowLVAndNextLVConfig(fatesId, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig);
+        if (hasNowLVAndNextLVConfig)
+        {
+            int fatesNowStarCnt = manager.GetFatesNowStarCnt(fatesId);
+            int nextNeedStarCnt = nextLVConfig.NeedStarTotal;
+            txtHasHeroTotalStar.text = StringUtility.Concat(Language.Get("HeroFates05"), UIHelper.AppendColor(nextNeedStarCnt <= fatesNowStarCnt ? TextColType.LightGreen : TextColType.Red, Language.Get("HeroFates11", fatesNowStarCnt, nextNeedStarCnt)));
+        }
+
+        DisplayLH(config);
+
+        transAttr.SetActive(!isNoActive);
+        DisplayAttrs(fatesId);
+
+        transItems.SetActive(!config.AwardItemList.IsNullOrEmpty() && isNoActive);
+        DisplayAwardItems(config);
+
+        bool hasInfo = manager.TryGetHeroFates(fatesId, out HeroFates info);
+        txtFateLV.text = Language.Get("L1113", hasInfo ? info.FatesLV : 0);
+
+        btnHave.SetListener(() => manager.SendHeroFates(fatesId, 0));
+        btnUpgrade.SetListener(() =>
+        {
+            if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.HeroFatesUpgrade, true))
+                return;
+            manager.chooseHeroFatesId = fatesId;
+            manager.chooseHeroFatesQuality = config.FatesQuality;
+            UIManager.Instance.OpenWindow<HeroFatesUpgradeWin>();
+        });
+    }
+
+    void DisplayLH(HeroFatesConfig config)
+    {
+        if (manager.TryGetNowMaxStarHeroDict(out Dictionary<int, HeroInfo> nowMaxStarHeroDict) || !nowMaxStarHeroDict.IsNullOrEmpty())
+        {
+            for (int i = 0; i < heroFatesIHItems.Count; i++)
+            {
+                if (i < config.HeroIDList.Length)
+                {
+                    int heroID = config.HeroIDList[i];
+                    heroFatesIHItems[i].SetActive(true);
+                    heroFatesIHItems[i].Display(config.HeroIDList, i, nowMaxStarHeroDict);
+                }
+                else
+                {
+                    heroFatesIHItems[i].SetActive(false);
+                }
+            }
+
+            int index = Mathf.Max(config.HeroIDList.Length - 1, 0);
+            if (index >= spacingArr.Length)
+            {
+                index = spacingArr.Length - 1;
+            }
+            int spacing = spacingArr[index];
+            horizontalLayoutGroup.spacing = spacing;
+        }
+    }
+
+    void DisplayAttrs(int fatesId)
+    {
+        if (attrs.IsNullOrEmpty())
+            return;
+        bool hasConfig = manager.TryGetAttrIDListAndLVAttrValueList(fatesId, out int[] attrIDList, out int[] lvAttrValueList);
+        if (!hasConfig)
+            return;
+        for (int i = 0; i < attrs.Count; i++)
+        {
+            if (i < attrIDList.Length)
+            {
+                int attrID = attrIDList[i];
+                int attrValue = manager.GetNowAttrValue(fatesId, i);
+                attrs[i].text = PlayerPropertyConfig.GetFullDescription(attrID, attrValue);
+                attrs[i].SetActive(true);
+            }
+            else
+            {
+                attrs[i].SetActive(false);
+            }
+        }
+
+
+    }
+
+    void DisplayAwardItems(HeroFatesConfig config)
+    {
+        if (items.IsNullOrEmpty() || config.AwardItemList.IsNullOrEmpty())
+            return;
+        for (int i = 0; i < items.Count; i++)
+        {
+            if (i < config.AwardItemList.Length)
+            {
+                int itemID = config.AwardItemList[i][0];
+                int count = config.AwardItemList[i][1];
+                items[i].Init(new ItemCellModel(itemID, false, count));
+                items[i].button.SetListener(() => ItemTipUtility.Show(itemID));
+                items[i].SetActive(true);
+            }
+            else
+            {
+                items[i].SetActive(false);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesCell.cs.meta b/Main/System/HeroFates/HeroFatesCell.cs.meta
new file mode 100644
index 0000000..c7c857f
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a00cea9f743c4124381d8ae7426329d5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs b/Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs
new file mode 100644
index 0000000..871eb4f
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class HeroFatesFastPutPreviewCell : MonoBehaviour
+{
+    [SerializeField] List<HeroFatesUpgradeHeadCell> headCells;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+
+    public void Display(int rowIndex, List<HeroInfo> list)
+    {
+        if (list.IsNullOrEmpty())
+        {
+            return;
+        }
+
+        for (int i = 0; i < headCells.Count; i++)
+        {
+            int index = rowIndex * manager.rowCountMax + i;
+            if (index < list.Count)
+            {
+                headCells[i].SetActive(true);
+                headCells[i].Display(list[index]);
+            }
+            else
+            {
+                headCells[i].SetActive(false);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs.meta b/Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs.meta
new file mode 100644
index 0000000..7eaa338
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesFastPutPreviewCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cb39d470ad837b74fba100a954f52ce7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs b/Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs
new file mode 100644
index 0000000..0e99636
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class HeroFatesFastPutPreviewWin : UIBase
+{
+    [SerializeField] ScrollerController scroller;
+    [SerializeField] ButtonEx btnClose;
+    [SerializeField] ButtonEx btnOk;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+    protected override void InitComponent()
+    {
+        btnClose.SetListener(CloseWindow);
+        btnOk.SetListener(() =>
+        {
+            manager.FastAddList(putPreviewHeroList);
+            CloseWindow();
+        });
+    }
+
+    protected override void OnPreOpen()
+    {
+        scroller.OnRefreshCell += OnRefreshCell;
+        manager.OnUpdateHeroFatesInfoEvent += OnUpdateHeroFatesInfo;
+        CreateScoller();
+    }
+
+    protected override void OnPreClose()
+    {
+        scroller.OnRefreshCell -= OnRefreshCell;
+        manager.OnUpdateHeroFatesInfoEvent -= OnUpdateHeroFatesInfo;
+    }
+
+    private void OnUpdateHeroFatesInfo()
+    {
+        RefeshScoller();
+    }
+
+    List<HeroInfo> putPreviewHeroList = new List<HeroInfo>();
+    private void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<HeroFatesFastPutPreviewCell>();
+        _cell?.Display(cell.index, putPreviewHeroList);
+    }
+
+    void CreateScoller()
+    {
+        scroller.Refresh();
+        putPreviewHeroList = manager.GetPutPreviewHeroList(manager.chooseHeroFatesId);
+        if (!putPreviewHeroList.IsNullOrEmpty())
+        {
+            int rowCount = Mathf.CeilToInt((float)putPreviewHeroList.Count / manager.rowCountMax);
+            for (int i = 0; i < rowCount; i++)
+            {
+                scroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        scroller.Restart();
+    }
+
+    void RefeshScoller()
+    {
+        scroller.m_Scorller.RefreshActiveCellViews();
+    }
+
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs.meta b/Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs.meta
new file mode 100644
index 0000000..5718e5f
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesFastPutPreviewWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9641203a901f43a4d9ca3110fa8fec3c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesIHItem.cs b/Main/System/HeroFates/HeroFatesIHItem.cs
new file mode 100644
index 0000000..64f1b04
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesIHItem.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HeroFatesIHItem : MonoBehaviour
+{
+    [SerializeField] UIHeroController uiHeroController;
+    [SerializeField] List<Image> starImgList;
+    [SerializeField] ImageEx imgHeroNameBg;
+    [SerializeField] TextEx txtHeroName;
+    [SerializeField] float lhSize = 0.8f;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+    public void Display(int[] heroIDList, int index, Dictionary<int, HeroInfo> nowMaxStarHeroDict)
+    {
+        if (heroIDList.IsNullOrEmpty() || index >= heroIDList.Length || index < 0)
+            return;
+        int heroId = heroIDList[index];
+        if (!manager.TryGetHeroAndSkinConfigByHeroID(heroId, out HeroConfig heroConfig, out HeroSkinConfig heroSkinConfig))
+            return;
+
+
+
+        uiHeroController.Create(heroSkinConfig.SkinID, lhSize);
+        bool isHasHero = HeroManager.Instance.HasHero(heroId);
+        if (isHasHero)
+        {
+            uiHeroController.SetMaterialNone();
+
+            int starCnt = !nowMaxStarHeroDict.IsNullOrEmpty() && nowMaxStarHeroDict.ContainsKey(heroId) ? nowMaxStarHeroDict[heroId].heroStar : 0;
+            manager.DisplayStars(starImgList, starCnt);
+        }
+        else
+        {
+            uiHeroController.SetGray();
+            manager.DisplayStars(starImgList, -1);
+        }
+
+        imgHeroNameBg.SetSprite(manager.GetHeroFatesNameBGByFatesQuality(heroConfig.Quality));
+        txtHeroName.text = heroConfig.Name;
+    }
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesIHItem.cs.meta b/Main/System/HeroFates/HeroFatesIHItem.cs.meta
new file mode 100644
index 0000000..9b8edf7
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesIHItem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 35905f3d0d176724a91c3188027ed5fb
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesManager.cs b/Main/System/HeroFates/HeroFatesManager.cs
new file mode 100644
index 0000000..b5bba18
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesManager.cs
@@ -0,0 +1,732 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine.UI;
+
+public class HeroFatesManager : GameSystemManager<HeroFatesManager>
+{
+    public readonly int rowCountMax = 5;
+
+    public override void Init()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
+        HeroUIManager.Instance.OnHeroCollectEvent += OnHeroCollectEvent;
+        HeroManager.Instance.onHeroChangeEvent += OnHeroChangeEvent;
+        FuncOpen.Instance.OnFuncStateChangeEvent += OnFuncStateChangeEvent;
+    }
+
+    public override void Release()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk;
+        HeroUIManager.Instance.OnHeroCollectEvent -= OnHeroCollectEvent;
+        HeroManager.Instance.onHeroChangeEvent -= OnHeroChangeEvent;
+        FuncOpen.Instance.OnFuncStateChangeEvent -= OnFuncStateChangeEvent;
+    }
+
+    private void OnFuncStateChangeEvent(int obj)
+    {
+        if (obj == (int)FuncOpenEnum.Hero || obj == (int)FuncOpenEnum.HeroFatesUpgrade)
+        {
+            UpdateRedpoint();
+        }
+    }
+
+    private void OnBeforePlayerDataInitializeEventOnRelogin()
+    {
+        heroFatesInfos.Clear();
+    }
+
+    private void OnPlayerLoginOk()
+    {
+        UpdateRedpoint();
+        RefreshAttr();
+    }
+
+    private void OnHeroCollectEvent()
+    {
+        UpdateRedpoint();
+    }
+
+    private void OnHeroChangeEvent(HeroInfo info)
+    {
+        UpdateRedpoint();
+    }
+
+    List<int> allFatesHeroID = new List<int>();
+
+    public List<int> GetAllFatesHeroID()
+    {
+        if (allFatesHeroID.IsNullOrEmpty())
+        {
+            foreach (var info in HeroFatesConfig.GetValues())
+            {
+                if (info.HeroIDList.IsNullOrEmpty())
+                {
+                    continue;
+                }
+                foreach (var HeroID in info.HeroIDList)
+                {
+                    if (!allFatesHeroID.Contains(HeroID))
+                    {
+                        allFatesHeroID.Add(HeroID);
+                    }
+                }
+            }
+        }
+        return allFatesHeroID;
+    }
+
+    Redpoint redpoint = new Redpoint(MainRedDot.MainHerosRedpoint, MainRedDot.HeroFatesRepoint);
+    public void UpdateRedpoint()
+    {
+        redpoint.state = RedPointState.None;
+        //姝﹀皢娌″紑涓嶅埛绾㈢偣
+        if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.Hero))
+            return;
+        bool hasShowRedDotFates = HasShowRedDotFates(out int firstIndex);
+        if (hasShowRedDotFates)
+        {
+            redpoint.state = RedPointState.Simple;
+        }
+    }
+
+    //<瀹跨紭ID锛屽缂樹俊鎭�>
+    public Dictionary<int, HeroFates> heroFatesInfos = new Dictionary<int, HeroFates>();
+
+    public bool TryGetHeroFates(int fatesID, out HeroFates info)
+    {
+        return heroFatesInfos.TryGetValue(fatesID, out info);
+    }
+
+    public event Action OnUpdateHeroFatesInfoEvent;
+    public event Action OnUpgradeHeroFatesEvent;
+    public void OnUpdateHeroFatesInfo(HB131_tagSCHeroFatesInfo vNetData)
+    {
+        if (vNetData == null || vNetData.FatesList.IsNullOrEmpty())
+        {
+            return;
+        }
+        bool isUpgrade = false;
+
+        foreach (var vInfo in vNetData.FatesList)
+        {
+            if (!heroFatesInfos.ContainsKey(vInfo.FatesID))
+            {
+                heroFatesInfos[vInfo.FatesID] = new HeroFates();
+            }
+
+            heroFatesInfos[vInfo.FatesID].FatesID = vInfo.FatesID;
+            heroFatesInfos[vInfo.FatesID].State = vInfo.State;
+
+            if (heroFatesInfos[vInfo.FatesID].FatesLV != vInfo.FatesLV)
+            {
+                isUpgrade = true;
+            }
+            heroFatesInfos[vInfo.FatesID].FatesLV = vInfo.FatesLV;
+        }
+        RefreshAttr();
+        UpdateRedpoint();
+        OnUpdateHeroFatesInfoEvent?.Invoke();
+        if (isUpgrade)
+        {
+            OnUpgradeHeroFatesEvent?.Invoke();
+        }
+    }
+
+    public ushort[] GetItemIndexList(List<HeroInfo> heroInfos)
+    {
+        return heroInfos.Select(heroInfo => (ushort)heroInfo.itemHero.gridIndex).ToArray();
+    }
+
+    public void SendHeroFates(int fatesID, int opType, ushort[] itemIndexList = null)
+    {
+        CB241_tagCSHeroFates pack = new CB241_tagCSHeroFates();
+        pack.FatesID = (byte)fatesID;   // 瀹跨紭ID
+        pack.OPType = (byte)opType;     // 0-婵�娲婚濂栵紱1-鍗囩骇
+
+        // 鍗囩骇鏃舵秷鑰楃殑鏉愭枡鍗″湪姝﹀皢鑳屽寘绱㈠紩鍒楄〃锛屽崌绾ф椂鎵嶅彂
+        bool isNullOrEmpty = itemIndexList.IsNullOrEmpty();
+        pack.ItemIndexList = isNullOrEmpty ? null : itemIndexList;
+        pack.IndexCnt = isNullOrEmpty ? (byte)0 : (byte)itemIndexList.Length;
+
+        GameNetSystem.Instance.SendInfo(pack);
+    }
+
+    public HeroFatesState GetHeroFatesState(int fatesID)
+    {
+        //娌℃湁灏佸寘
+        if (!TryGetHeroFates(fatesID, out HeroFates info))
+        {
+            return IsHeroIDListAllActive(fatesID) ? HeroFatesState.Activatable : HeroFatesState.Locked;
+        }
+
+        // 灏佸寘瀹跨紭鐘舵�侊細0-鏈縺娲伙紱1-宸叉縺娲诲凡棰嗗
+        if (info.State == 0)
+        {
+            //姝﹀皢ID缁勫悎鍒楄〃涓殑姝﹀皢鐨勫浘閴存湁娌¤В閿佺殑?- 鏈В閿� 
+            return IsHeroIDListAllActive(fatesID) ? HeroFatesState.Activatable : HeroFatesState.Locked;
+        }
+        else if (info.State == 1)
+        {
+            // 瀹跨紭鍗囩骇鍔熻兘娌″紑鏃惰涓轰笉鍙崌绾�
+            if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.HeroFatesUpgrade))
+            {
+                return HeroFatesState.ActiveNotUpgradable;
+            }
+
+            if (IsHeroFatesStarMax(fatesID))
+            {
+                return HeroFatesState.MaxLevel;
+            }
+            return IsHeroFatesCanUp(fatesID, out int fromFatesLv, out int toFatesLv) ? HeroFatesState.ActiveUpgradable : HeroFatesState.ActiveNotUpgradable;
+        }
+        return HeroFatesState.Locked;
+    }
+
+    public bool IsHeroFatesCanUp(int fatesID, out int fromFatesLv, out int toFatesLv)
+    {
+        fromFatesLv = 0;
+        toFatesLv = 0;
+
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config))
+        {
+            return false;
+        }
+
+        if (!TryGetHeroFates(fatesID, out HeroFates info))
+        {
+            return false;
+        }
+
+        fromFatesLv = info.FatesLV;
+        toFatesLv = info.FatesLV + 1;
+
+        int fatesQuality = config.FatesQuality;
+        if (!HeroFatesQualityLVConfig.TryGetHeroFatesQualityLVConfig(fatesQuality, toFatesLv, out HeroFatesQualityLVConfig heroFatesQualityLVConfig))
+        {
+            return false;
+        }
+
+        // 姝﹀皢鎬绘槦绾ф槸鍚︽弧瓒冲崌绾ч渶姹�
+        int needStarCnt = heroFatesQualityLVConfig.NeedStarTotal;
+        int hasStarCnt = GetFatesNowStarCnt(fatesID);
+        if (hasStarCnt < needStarCnt)
+        {
+            return false;
+        }
+        // 娑堣�楁灏嗘暟閲忔槸鍚︽弧瓒抽渶姹�
+        List<HeroInfo> consumableHeroInfos = GetConsumableHeroInfoList(heroFatesQualityLVConfig.FatesQuality);
+        int hasHeroCnt = consumableHeroInfos == null ? 0 : consumableHeroInfos.Count;
+        int needHeroCnt = heroFatesQualityLVConfig.NeedHeroCnt;
+        if (hasHeroCnt < needHeroCnt)
+        {
+            return false;
+        }
+        return true;
+    }
+
+
+    // 鑾峰緱鑳屽寘涓殑鎵�鏈夋寚瀹氬搧璐ㄧ殑鍙秷鑰楁灏� 瀹跨紭鍝佽川灏辨槸姝﹀皢鍝佽川
+    public List<HeroInfo> GetConsumableHeroInfoList(int fatesQuality)
+    {
+        List<HeroInfo> result = new List<HeroInfo>();
+        List<HeroInfo> allHeroesInBag = HeroManager.Instance.GetHeroList();
+        foreach (var item in allHeroesInBag)
+        {
+            if (item == null)
+                continue;
+            //涓嶆槸鎵�闇�鍝佽川
+            if (item.Quality != fatesQuality)
+                continue;
+            // 鐢熸晥涓殑姝﹀皢
+            if (item.isAttrActive)
+                continue;
+            // 鍦ㄤ换浣曢樀瀹逛腑涓婇樀
+            bool isInAnyTeam = item.IsInAnyTeam();
+            if (isInAnyTeam)
+                continue;
+            // 閿佸畾涓�
+            if (item.isLock)
+                continue;
+            // 鍗囩骇杩�
+            if (item.heroLevel > 1)
+                continue;
+            // 鐢熸槦杩�
+            if (item.heroStar > 0)
+                continue;
+            // 瑙夐啋杩�
+            if (item.awakeLevel > 0)
+                continue;
+            result.Add(item);
+        }
+        result.Sort((a, b) => a.heroId.CompareTo(b.heroId));
+        return result;
+    }
+
+    // 褰撳墠瀹跨紭鐨勫綋鍓嶆渶楂樻�绘槦绾ф槸鍚﹀凡婊$骇
+    public bool IsHeroFatesStarMax(int fatesID)
+    {
+        if (!TryGetHeroFates(fatesID, out HeroFates info))
+            return false;
+        if (!TryGetMaxFatesLVConfigByFatesID(fatesID, out HeroFatesQualityLVConfig heroFatesQualityLVConfig))
+            return false;
+        int maxLv = heroFatesQualityLVConfig.FatesLV;
+        int nowLv = info.FatesLV;
+        return nowLv >= maxLv;
+    }
+
+    // 姝﹀皢ID缁勫悎鍒楄〃涓殑姝﹀皢鐨勫浘閴撮兘瑙i攣浜嗭紵
+    public bool IsHeroIDListAllActive(int fatesID)
+    {
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config) || config.HeroIDList.IsNullOrEmpty())
+            return false;
+        int activeHeroCnt = GetFateNeedHeroHasCnt(fatesID);
+        return config.HeroIDList.Length <= activeHeroCnt;
+    }
+
+    public int GetFateNeedHeroHasCnt(int fatesID)
+    {
+        int res = 0;
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config) || config.HeroIDList.IsNullOrEmpty())
+            return 0;
+        foreach (var heroID in config.HeroIDList)
+        {
+            if (!TryGetHeroConfig(heroID, out HeroConfig heroConfig))
+                continue;
+            // heroBookState: 0鏈幏寰椼��1鍙縺娲汇��2甯歌銆�3绐佺牬鍗囩骇銆�4鏄熷崌绾с��5宸叉弧绾�
+            int heroBookState = HeroUIManager.Instance.GetHeroBookState(heroID, heroConfig.Quality);
+            if (heroBookState > 0)
+                res += 1;
+        }
+        return res;
+    }
+
+    public bool TryGetNowMaxStarHeroDict(out Dictionary<int, HeroInfo> nowMaxStarHeroDict)
+    {
+        nowMaxStarHeroDict = new Dictionary<int, HeroInfo>();
+        List<HeroInfo> allHeroesInBag = HeroManager.Instance.GetHeroList();
+        if (allHeroesInBag.IsNullOrEmpty())
+            return false;
+        foreach (var item in allHeroesInBag)
+        {
+            if (item == null)
+                continue;
+            if (!nowMaxStarHeroDict.TryGetValue(item.heroId, out HeroInfo heroInfo))
+            {
+                nowMaxStarHeroDict[item.heroId] = item;
+            }
+            else
+            {
+                if (item.heroStar > heroInfo.heroStar)
+                    nowMaxStarHeroDict[item.heroId] = item;
+            }
+        }
+        return true;
+    }
+
+    public Dictionary<int, HeroInfo> GetFatesNowStarHeroInfoDict(int fatesID)
+    {
+        if (!IsHeroIDListAllActive(fatesID))
+            return null;
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config) || config.HeroIDList.IsNullOrEmpty())
+            return null;
+        if (!TryGetNowMaxStarHeroDict(out Dictionary<int, HeroInfo> nowMaxStarHeroDict) || nowMaxStarHeroDict.IsNullOrEmpty())
+            return null;
+        Dictionary<int, HeroInfo> heroInfoDict = new Dictionary<int, HeroInfo>();
+        foreach (var heroID in config.HeroIDList)
+        {
+            if (nowMaxStarHeroDict.ContainsKey(heroID))
+            {
+                heroInfoDict[heroID] = nowMaxStarHeroDict[heroID];
+            }
+        }
+        return heroInfoDict;
+    }
+
+    // 鑾峰彇褰撳墠瀹跨紭鐨勫綋鍓嶆渶楂樻�绘槦绾�
+    public int GetFatesNowStarCnt(int fatesID)
+    {
+        Dictionary<int, HeroInfo> dict = GetFatesNowStarHeroInfoDict(fatesID);
+        if (dict.IsNullOrEmpty())
+            return 0;
+        // 閬嶅巻瀹跨紭閰嶇疆涓墍闇�鐨勬灏咺D锛屼粠maxStarsInBag涓幏鍙栨渶楂樻槦绾у苟绱姞
+        int res = 0;
+        foreach (var item in dict.Values)
+        {
+            res += item.heroStar;
+        }
+        return res;
+    }
+
+
+    public void DisplayStars(List<Image> starImgList, int starCnt)
+    {
+        for (int i = 0; i < starImgList.Count; i++)
+        {
+            //涓嶆樉绀烘槦绾�
+            if (starCnt == -1)
+            {
+                starImgList[i].SetActive(false);
+            }
+            else if (starCnt == 0 && i == 0)
+            {
+                // 鏃犳槦绾� 鐗规畩澶勭悊
+                starImgList[i].SetActive(true);
+                starImgList[i].SetSprite("herostar" + starCnt);
+            }
+            else if ((starCnt - 1) % starImgList.Count >= i)
+            {
+                starImgList[i].SetActive(true);
+                starImgList[i].SetSprite("herostar" + (((starCnt - 1) / starImgList.Count) + 1) * starImgList.Count);
+            }
+            else
+            {
+                starImgList[i].SetActive(false);
+            }
+        }
+    }
+
+    public bool IsShowRedDot(int fatesID)
+    {
+        HeroFatesState state = GetHeroFatesState(fatesID);
+        return state == HeroFatesState.Activatable || state == HeroFatesState.ActiveUpgradable;
+    }
+
+    public bool HasShowRedDotFates(out int firstIndex)
+    {
+        firstIndex = 0;
+        List<int> list = GetFatesIDList();
+        if (list.IsNullOrEmpty())
+            return false;
+        for (int i = 0; i < list.Count; i++)
+        {
+            int fatesID = list[i];
+            if (IsShowRedDot(fatesID))
+            {
+                firstIndex = i;
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    public List<HeroInfo> GetPutPreviewHeroList(int fatesID)
+    {
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config))
+        {
+            return null;
+        }
+
+        int fatesQuality = config.FatesQuality;
+        if (!TryGetNowLVAndNextLVConfig(fatesID, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig))
+        {
+            return null;
+        }
+
+        int needHeroCnt = nextLVConfig.NeedHeroCnt;
+        List<HeroInfo> consumableHeroInfoList = GetConsumableHeroInfoList(fatesQuality);
+        if (consumableHeroInfoList.IsNullOrEmpty())
+        {
+            return null;
+        }
+        List<HeroInfo> putPreviewHeroList = new List<HeroInfo>();
+        int count = Math.Min(needHeroCnt, consumableHeroInfoList.Count);
+        for (int i = 0; i < count; i++)
+        {
+            putPreviewHeroList.Add(consumableHeroInfoList[i]);
+        }
+
+        return putPreviewHeroList;
+    }
+
+    #region 鐜╁閫変腑鍐呭
+    public int chooseHeroFatesId = 0;
+    public int chooseHeroFatesQuality = 0;
+    public HeroInfo chooseHeroInfo = null;
+
+    // 缁欑帺瀹舵搷浣滈�変腑鐨勫垪琛�
+    public List<HeroInfo> chooseCostHeroInfos = new List<HeroInfo>();
+    // 鐜╁瀹為檯閫変腑鐨勫垪琛�
+    public List<HeroInfo> realCostHeroInfos = new List<HeroInfo>();
+    public event Action OnRealChooseChangeEvent;
+    public event Action OnChooseChangeEvent;
+
+    public void InitChooseList()
+    {
+        chooseCostHeroInfos.Clear();
+        foreach (var heroInfo in realCostHeroInfos)
+        {
+            chooseCostHeroInfos.Add(heroInfo);
+        }
+        chooseHeroInfo = null;
+    }
+
+    public void ChooseAdd(HeroInfo heroInfo)
+    {
+        if (chooseCostHeroInfos.Contains(heroInfo))
+        {
+            chooseCostHeroInfos.Remove(heroInfo);
+            chooseHeroInfo = null;
+        }
+        else
+        {
+            chooseCostHeroInfos.Add(heroInfo);
+            chooseHeroInfo = heroInfo;
+        }
+        OnChooseChangeEvent?.Invoke();
+    }
+
+    public void ClearAllList()
+    {
+        chooseCostHeroInfos.Clear();
+        realCostHeroInfos.Clear();
+        chooseHeroInfo = null;
+        OnRealChooseChangeEvent?.Invoke();
+    }
+
+    public void PutList()
+    {
+        realCostHeroInfos.Clear();
+        foreach (var heroInfo in chooseCostHeroInfos)
+        {
+            realCostHeroInfos.Add(heroInfo);
+        }
+        OnRealChooseChangeEvent?.Invoke();
+    }
+
+    public void FastAddList(List<HeroInfo> heroInfos)
+    {
+        if (heroInfos.IsNullOrEmpty())
+            return;
+        chooseCostHeroInfos.Clear();
+        realCostHeroInfos.Clear();
+        foreach (var heroInfo in heroInfos)
+        {
+            chooseCostHeroInfos.Add(heroInfo);
+            realCostHeroInfos.Add(heroInfo);
+        }
+        OnRealChooseChangeEvent?.Invoke();
+    }
+
+    #endregion
+
+
+    #region 灞炴�ц绠�
+    public Dictionary<int, long> attrDic = new Dictionary<int, long>();
+
+    public Dictionary<int, long> GetTotalAttr()
+    {
+        return attrDic;
+    }
+
+    public long GetAttrValue(int attrID)
+    {
+        attrDic.TryGetValue(attrID, out long value);
+        return value;
+    }
+
+    public int GetAttrPer(int attrID)
+    {
+        if (PlayerPropertyConfig.baseAttr2perDict.ContainsKey(attrID))
+        {
+            var pertype = PlayerPropertyConfig.baseAttr2perDict[attrID];
+            attrDic.TryGetValue(pertype, out long value);
+            return (int)(value);
+        }
+        return 0;
+    }
+
+    public void RefreshAttr()
+    {
+        attrDic.Clear();
+        List<int> allIds = HeroFatesConfig.GetKeys();
+        foreach (var fatesID in allIds)
+        {
+            HeroFatesState state = GetHeroFatesState(fatesID);
+            if (!TryGetAttrIDListAndLVAttrValueList(fatesID, out int[] attrIDList, out int[] lvAttrValueList))
+                continue;
+            for (int i = 0; i < attrIDList.Length; i++)
+            {
+                if (!attrDic.ContainsKey(attrIDList[i]))
+                {
+                    attrDic[attrIDList[i]] = 0;
+                }
+                if (state == HeroFatesState.Locked || state == HeroFatesState.Activatable)
+                    continue;
+                int value = GetNowAttrValue(fatesID, i);
+                attrDic[attrIDList[i]] += value;
+            }
+        }
+    }
+
+    //鏈�缁堝睘鎬� = 绛夌骇 * 姣忕骇灞炴�у��
+    public int GetNowAttrValue(int fatesID, int attrIndex)
+    {
+        if (!TryGetAttrIDListAndLVAttrValueList(fatesID, out int[] attrIDList, out int[] lvAttrValueList))
+            return 0;
+        if (attrIndex < 0 || attrIndex > attrIDList.Length)
+            return 0;
+        if (!TryGetHeroFates(fatesID, out HeroFates info))
+            return 0;
+        int lv = info.FatesLV;
+        return lv * lvAttrValueList[attrIndex];
+    }
+
+    //涓嬩竴绾у睘鎬� = (绛夌骇 + 1) * 姣忕骇灞炴�у��
+    public int GetNextAttrValue(int fatesID, int attrIndex)
+    {
+        if (!TryGetAttrIDListAndLVAttrValueList(fatesID, out int[] attrIDList, out int[] lvAttrValueList))
+            return 0;
+        if (attrIndex < 0 || attrIndex > attrIDList.Length)
+            return 0;
+        if (!TryGetHeroFates(fatesID, out HeroFates info))
+            return 0;
+        int lv = info.FatesLV + 1;
+        return lv * lvAttrValueList[attrIndex];
+    }
+
+
+    #endregion
+
+    #region 璇昏〃
+    // 0绾ф椂锛屼笉杩斿洖nowLVConfig锛屽彧杩斿洖nowLVConfig
+    public bool TryGetNowLVAndNextLVConfig(int fatesID, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig)
+    {
+        nowLv = 0;
+        nowLVConfig = null;
+        nextLVConfig = null;
+        if (!TryGetHeroFates(fatesID, out HeroFates info))
+            return false;
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config))
+            return false;
+        int quality = config.FatesQuality;
+        nowLv = info.FatesLV;
+        if (nowLv != 0)
+        {
+            if (!HeroFatesQualityLVConfig.TryGetHeroFatesQualityLVConfig(quality, nowLv, out nowLVConfig))
+                return false;
+        }
+        int nextLv = nowLv + 1;
+        return HeroFatesQualityLVConfig.TryGetHeroFatesQualityLVConfig(quality, nextLv, out nextLVConfig);
+    }
+
+    public bool TryGetMaxFatesLVConfigByFatesID(int fatesID, out HeroFatesQualityLVConfig heroFatesQualityLVConfig)
+    {
+        heroFatesQualityLVConfig = null;
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config))
+            return false;
+        int fatesQuality = config.FatesQuality;
+        if (!HeroFatesQualityLVConfig.TryGetDictByFatesQuality(fatesQuality, out Dictionary<int, int> dict) || dict.IsNullOrEmpty())
+            return false;
+        int maxFatesLV = dict.Keys.Max();
+        if (!HeroFatesQualityLVConfig.TryGetHeroFatesQualityLVConfig(fatesQuality, maxFatesLV, out heroFatesQualityLVConfig))
+            return false;
+        return true;
+    }
+
+    public bool TryGetHeroFatesConfig(int fatesID, out HeroFatesConfig config)
+    {
+        config = null;
+        if (!HeroFatesConfig.HasKey(fatesID))
+            return false;
+        config = HeroFatesConfig.Get(fatesID);
+        return true;
+    }
+
+    public bool TryGetAttrIDListAndLVAttrValueList(int fatesID, out int[] attrIDList, out int[] lvAttrValueList)
+    {
+        attrIDList = null;
+        lvAttrValueList = null;
+        if (!TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config))
+            return false;
+        attrIDList = config.AttrIDList;
+        lvAttrValueList = config.LVAttrValueList;
+        if (attrIDList.IsNullOrEmpty() || lvAttrValueList.IsNullOrEmpty())
+            return false;
+        return attrIDList.Length == lvAttrValueList.Length;
+    }
+    public bool TryGetHeroConfig(int heroID, out HeroConfig config)
+    {
+        config = null;
+        if (!HeroConfig.HasKey(heroID))
+            return false;
+        config = HeroConfig.Get(heroID);
+        return true;
+    }
+    public bool TryGetHeroAndSkinConfigByHeroID(int heroID, out HeroConfig heroConfig, out HeroSkinConfig heroSkinConfig)
+    {
+        heroConfig = null;
+        heroSkinConfig = null;
+        if (!HeroConfig.HasKey(heroID))
+            return false;
+        heroConfig = HeroConfig.Get(heroID);
+        int[] skinIDList = heroConfig.SkinIDList;
+        if (skinIDList.IsNullOrEmpty())
+            return false;
+        int skinID = skinIDList[0];
+        if (!HeroSkinConfig.HasKey(skinID))
+            return false;
+        heroSkinConfig = HeroSkinConfig.Get(skinID);
+        return true;
+    }
+
+    List<int> fatesIDList = null;
+    public List<int> GetFatesIDList()
+    {
+        if (fatesIDList.IsNullOrEmpty())
+        {
+            fatesIDList = HeroFatesConfig.GetKeys();
+            // 鎸夊搧璐ㄥ崌搴忔帓搴忥紝鐩稿悓鍝佽川鎸塈D鍗囧簭鎺掑簭
+            fatesIDList.Sort((id1, id2) =>
+            {
+                if (!TryGetHeroFatesConfig(id1, out HeroFatesConfig config1) ||
+                    !TryGetHeroFatesConfig(id2, out HeroFatesConfig config2))
+                {
+                    return id1.CompareTo(id2); // 濡傛灉鑾峰彇閰嶇疆澶辫触锛屾寜ID鎺掑簭
+                }
+
+                // 鍏堟瘮杈冨搧璐�
+                int qualityCompare = config1.FatesQuality.CompareTo(config2.FatesQuality);
+                if (qualityCompare != 0)
+                {
+                    return qualityCompare; // 鍝佽川涓嶅悓锛屾寜鍝佽川鎺掑簭
+                }
+
+                // 鍝佽川鐩稿悓锛屾寜ID鎺掑簭
+                return id1.CompareTo(id2);
+            });
+        }
+        return fatesIDList;
+    }
+
+
+    public string GetCellBgByFatesQuality(int fatesQuality)
+    {
+        return StringUtility.Concat("HeroFatesQualityBG", fatesQuality.ToString());
+    }
+
+    public string GetHeroFatesNameBGByFatesQuality(int fatesQuality)
+    {
+        return StringUtility.Concat("HeroFatesNameBG", fatesQuality.ToString());
+    }
+    #endregion
+}
+public enum HeroFatesState
+{
+    Locked,             // 鏈縺娲�
+    Activatable,        // 鍙縺娲� (鏈縺娲讳絾鏉愭枡瓒冲)
+    ActiveNotUpgradable,// 宸叉縺娲讳絾涓嶅彲鍗囩骇 (鏉愭枡涓嶈冻鎴栭厤缃笉鏀寔)
+    ActiveUpgradable,   // 宸叉縺娲讳笖鍙崌绾� (鏉愭枡瓒冲鍗囩骇涓嬩竴妗�)
+    MaxLevel,           // 宸插崌鍒版弧绾�
+}
+public class HeroFates
+{
+    public int FatesID;        // 瀹跨紭ID
+    public int State;        // 瀹跨紭鐘舵�侊細0-鏈縺娲伙紱1-宸叉縺娲诲凡棰嗗
+    public int FatesLV;        // 瀹跨紭绛夌骇锛屾縺娲绘椂涓�0绾э紝鍗囩骇鍚庢湁鍗囩骇灞炴��
+}
+
+
diff --git a/Main/System/HeroFates/HeroFatesManager.cs.meta b/Main/System/HeroFates/HeroFatesManager.cs.meta
new file mode 100644
index 0000000..7b12fe1
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 43d2eef891a1efc45b86bf65cbc5a010
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesPutCell.cs b/Main/System/HeroFates/HeroFatesPutCell.cs
new file mode 100644
index 0000000..a1ad263
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesPutCell.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class HeroFatesPutCell : MonoBehaviour
+{
+    [SerializeField] List<HeroFatesPutItem> items = new List<HeroFatesPutItem>();
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+
+    public void Display(int rowIndex, List<HeroInfo> list)
+    {
+        if (list.IsNullOrEmpty())
+        {
+            return;
+        }
+
+        for (int i = 0; i < items.Count; i++)
+        {
+            int index = rowIndex * manager.rowCountMax + i;
+            if (index < list.Count)
+            {
+                items[i].SetActive(true);
+                items[i].Display(list[index]);
+            }
+            else
+            {
+                items[i].SetActive(false);
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesPutCell.cs.meta b/Main/System/HeroFates/HeroFatesPutCell.cs.meta
new file mode 100644
index 0000000..d811a3c
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesPutCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c9f56162b0d350048812d05a806629f4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesPutItem.cs b/Main/System/HeroFates/HeroFatesPutItem.cs
new file mode 100644
index 0000000..f8fda96
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesPutItem.cs
@@ -0,0 +1,42 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HeroFatesPutItem : MonoBehaviour
+{
+    [SerializeField] HeroHeadBaseCell heroHeadBaseCell;
+    [SerializeField] Image jobImg;
+    [SerializeField] Text nameText;
+    [SerializeField] Transform select;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+    HeroInfo hero;
+    public void Display(HeroInfo hero)
+    {
+        this.hero = hero;
+        heroHeadBaseCell.Init(hero.heroId, hero.SkinID, hero.heroStar, hero.awakeLevel, hero.heroLevel, OnClick);
+        nameText.text = hero.breakLevel == 0 ? hero.heroConfig.Name : Language.Get("herocardbreaklv", hero.heroConfig.Name, hero.breakLevel);
+        jobImg.SetSprite(HeroUIManager.Instance.GetJobIconName(hero.heroConfig.Class));
+        bool isChoose = manager.chooseCostHeroInfos.Contains(hero);
+        select?.SetActive(isChoose);
+    }
+
+    public void OnClick()
+    {
+        if (!manager.TryGetNowLVAndNextLVConfig(manager.chooseHeroFatesId, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig))
+        {
+            return;
+        }
+        bool isChoose = manager.chooseCostHeroInfos.Contains(hero);
+        if (!isChoose)
+        {
+            int nextCnt = manager.chooseCostHeroInfos.Count + 1;
+            // 褰撳墠閫変腑姝﹀皢宸茶揪涓婇檺
+            if (nextCnt > nextLVConfig.NeedHeroCnt)
+            {
+                SysNotifyMgr.Instance.ShowTip("HeroFates03");
+                return;
+            }
+        }
+        manager.ChooseAdd(hero);
+    }
+
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesPutItem.cs.meta b/Main/System/HeroFates/HeroFatesPutItem.cs.meta
new file mode 100644
index 0000000..5be126a
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesPutItem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 549bc7cdfe32a6648a40368a19551861
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesPutWin.cs b/Main/System/HeroFates/HeroFatesPutWin.cs
new file mode 100644
index 0000000..19d3e92
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesPutWin.cs
@@ -0,0 +1,100 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class HeroFatesPutWin : UIBase
+{
+    [SerializeField] Transform transChooseHero;
+    [SerializeField] HeroFatesPutItem chooseHero;
+    [SerializeField] GiftBaseCell[] giftBaseCells;
+
+    [SerializeField] Transform transNoChooseHero;
+
+    [SerializeField] ScrollerController scroller;
+    [SerializeField] Transform transNoCanChooseHero;
+    [SerializeField] ButtonEx btnPut;
+    [SerializeField] TextEx txtChooseCnt;
+
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+    protected override void InitComponent()
+    {
+        btnPut.SetListener(() =>
+        {
+            manager.PutList();
+            CloseWindow();
+        });
+    }
+
+
+    protected override void OnPreOpen()
+    {
+        scroller.OnRefreshCell += OnRefreshCell;
+        manager.OnChooseChangeEvent += OnChooseChangeEvent;
+
+        CreateScoller();
+        DisplayChooseHero();
+    }
+
+    protected override void OnPreClose()
+    {
+        scroller.OnRefreshCell -= OnRefreshCell;
+        manager.OnChooseChangeEvent -= OnChooseChangeEvent;
+    }
+
+    private void OnChooseChangeEvent()
+    {
+        RefeshScoller();
+        DisplayChooseHero();
+    }
+
+    List<HeroInfo> list = new List<HeroInfo>();
+    private void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<HeroFatesPutCell>();
+        _cell?.Display(cell.index, list);
+    }
+
+    void CreateScoller()
+    {
+        scroller.Refresh();
+        if (manager.TryGetHeroFatesConfig(manager.chooseHeroFatesId, out HeroFatesConfig config))
+        {
+            int fatesQuality = config.FatesQuality;
+            list = manager.GetConsumableHeroInfoList(fatesQuality);
+            transNoCanChooseHero.SetActive(list.IsNullOrEmpty());
+            if (!list.IsNullOrEmpty())
+            {
+                int rowCount = Mathf.CeilToInt((float)list.Count / manager.rowCountMax);
+                for (int i = 0; i < rowCount; i++)
+                {
+                    scroller.AddCell(ScrollerDataType.Header, i);
+                }
+            }
+        }
+        scroller.Restart();
+    }
+
+    void RefeshScoller()
+    {
+        scroller.m_Scorller.RefreshActiveCellViews();
+    }
+
+    void DisplayChooseHero()
+    {
+        HeroInfo heroInfo = manager.chooseHeroInfo;
+        transNoChooseHero.SetActive(heroInfo == null);
+        transChooseHero.SetActive(heroInfo != null);
+        if (heroInfo != null)
+        {
+            chooseHero.Display(heroInfo);
+            HeroUIManager.Instance.RefreshGiftCell(giftBaseCells, heroInfo);
+        }
+
+        if (!manager.TryGetNowLVAndNextLVConfig(manager.chooseHeroFatesId, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig))
+        {
+            return;
+        }
+        int needCnt = nextLVConfig.NeedHeroCnt;
+        int chooseCnt = manager.chooseCostHeroInfos.Count;
+        txtChooseCnt.text = Language.Get("HeroFates04", UIHelper.AppendColor(chooseCnt >= needCnt ? TextColType.DarkGreen : TextColType.Red, Language.Get("BoneField09", chooseCnt, needCnt)));
+    }
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesPutWin.cs.meta b/Main/System/HeroFates/HeroFatesPutWin.cs.meta
new file mode 100644
index 0000000..751ee7c
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesPutWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e38b8e8bec1da9945b8ba306696a828a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs b/Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs
new file mode 100644
index 0000000..7aba3aa
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs
@@ -0,0 +1,43 @@
+using UnityEngine;
+
+public class HeroFatesUpgradeAttrCell : MonoBehaviour
+{
+    [SerializeField] TextEx txtAttrName;
+    [SerializeField] TextEx txtAttrFromValue;
+    [SerializeField] TextEx txtAttrToValue;
+    [SerializeField] ImageEx imgAdd;
+    [SerializeField] UIEffectPlayer uIEffectPlayer1;
+    [SerializeField] UIEffectPlayer uIEffectPlayer2;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+
+    public void Display(int index, CellView cell, bool isPlayer = false)
+    {
+        uIEffectPlayer1.Stop();
+        uIEffectPlayer2.Stop();
+        int fatesID = cell.info.Value.infoInt1;
+
+        if (!manager.TryGetAttrIDListAndLVAttrValueList(fatesID, out int[] attrIDList, out int[] lvAttrValueList))
+            return;
+        if (index < 0 || index >= attrIDList.Length)
+            return;
+        int attrID = attrIDList[index];
+        long attrValue = lvAttrValueList[index];
+        if (!PlayerPropertyConfig.HasKey(attrID))
+            return;
+        PlayerPropertyConfig config = PlayerPropertyConfig.Get(attrID);
+        txtAttrName.text = config.ShowName;
+
+        long fromValue = manager.GetNowAttrValue(fatesID, index);
+        txtAttrFromValue.text = PlayerPropertyConfig.GetValueDescription(attrID, fromValue);
+        long toValue = manager.GetNextAttrValue(fatesID, index);
+        txtAttrToValue.text = PlayerPropertyConfig.GetValueDescription(attrID, toValue);
+
+        imgAdd.SetActive(toValue > fromValue);
+
+        if (isPlayer)
+        {
+            uIEffectPlayer1.Play();
+            uIEffectPlayer2.Play();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs.meta b/Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs.meta
new file mode 100644
index 0000000..ddc9ad6
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesUpgradeAttrCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3300269fab35f104bad377e0345e51ce
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs b/Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs
new file mode 100644
index 0000000..0168781
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs
@@ -0,0 +1,19 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HeroFatesUpgradeHeadCell : MonoBehaviour
+{
+    [SerializeField] HeroHeadBaseCell heroHeadBaseCell;
+    [SerializeField] Image jobImg;
+    [SerializeField] Text nameText;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+
+    public void Display(HeroInfo hero)
+    {
+        heroHeadBaseCell.Init(hero.heroId, hero.SkinID, hero.heroStar, hero.awakeLevel, hero.heroLevel);
+        nameText.text = hero.breakLevel == 0 ? hero.heroConfig.Name : Language.Get("herocardbreaklv", hero.heroConfig.Name, hero.breakLevel);
+        jobImg.SetSprite(HeroUIManager.Instance.GetJobIconName(hero.heroConfig.Class));
+    }
+    
+
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs.meta b/Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs.meta
new file mode 100644
index 0000000..58e1c99
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesUpgradeHeadCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e3e4cfdc83ca8f2429a55b307166a0a2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesUpgradeWin.cs b/Main/System/HeroFates/HeroFatesUpgradeWin.cs
new file mode 100644
index 0000000..bd79533
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesUpgradeWin.cs
@@ -0,0 +1,252 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HeroFatesUpgradeWin : UIBase
+{
+    [SerializeField] List<HeroFatesUpgradeHeadCell> headCells;
+    [SerializeField] TextEx txtTotalStarCnt;
+    [SerializeField] TextEx txtFromLv;
+    [SerializeField] TextEx txtToLv;
+    [SerializeField] ScrollerController scroller;
+    [SerializeField] ButtonEx btnFastPut;
+    [SerializeField] ButtonEx btnUpdate;
+
+    [SerializeField] ButtonEx btnPut;
+    [SerializeField] ImageEx imgQualityHeroBg;
+    [SerializeField] ImageEx imgQualityHeroIcon;
+    [SerializeField] ImageEx imgQualityHeroNull;
+    [SerializeField] Image imgPutRed;
+    [SerializeField] ButtonEx btnQualityHeroClear;
+    [SerializeField] TextEx txtQualityHeroName;
+    [SerializeField] TextEx txtNeedQualityHeroCnt;
+    int fatesID;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+    protected override void InitComponent()
+    {
+        btnPut.SetListener(() =>
+        {
+            manager.InitChooseList();
+            UIManager.Instance.OpenWindow<HeroFatesPutWin>();
+        });
+
+        btnQualityHeroClear.SetListener(() =>
+        {
+            manager.ClearAllList();
+            DisplayChoose(fatesID);
+        });
+
+        btnFastPut.SetListener(() =>
+        {
+            var list = manager.GetPutPreviewHeroList(manager.chooseHeroFatesId);
+            if (list.IsNullOrEmpty())
+            {
+                //绗﹀悎鍗囩骇鏉′欢鐨勬灏嗕笉瓒�
+                SysNotifyMgr.Instance.ShowTip("HeroFates02");
+                return;
+            }
+            UIManager.Instance.OpenWindow<HeroFatesFastPutPreviewWin>();
+        });
+        btnUpdate.SetListener(() =>
+        {
+            if (!manager.TryGetNowLVAndNextLVConfig(fatesID, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig))
+            {
+                return;
+            }
+
+            int fatesNowStarCnt = manager.GetFatesNowStarCnt(fatesID);
+            int nextNeedStarCnt = nextLVConfig.NeedStarTotal;
+            if (fatesNowStarCnt < nextNeedStarCnt)
+            {
+                //姝﹀皢鎬绘槦绾т笉瓒�
+                SysNotifyMgr.Instance.ShowTip("HeroFates01");
+                return;
+            }
+
+            ushort[] arr = manager.GetItemIndexList(manager.realCostHeroInfos);
+            //鏀惧叆鐨勬秷鑰楁灏嗘暟閲忎笉瓒�
+            if (arr.IsNullOrEmpty() || nextLVConfig.NeedHeroCnt > manager.realCostHeroInfos.Count)
+            {
+                SysNotifyMgr.Instance.ShowTip("HeroFates04");
+                return;
+            }
+            manager.SendHeroFates(manager.chooseHeroFatesId, 1, arr);
+            manager.ClearAllList();
+        });
+
+    }
+
+    protected override void OnPreOpen()
+    {
+        scroller.OnRefreshCell += OnRefreshCell;
+        manager.OnUpdateHeroFatesInfoEvent += OnUpdateHeroFatesInfo;
+        manager.OnRealChooseChangeEvent += OnRealChooseChangeEvent;
+
+        manager.ClearAllList();
+
+        fatesID = manager.chooseHeroFatesId;
+        DisplayHeadCells(fatesID);
+        DisplayTotalStarCnt(fatesID);
+        CreateScoller(fatesID);
+        DisplayLV(fatesID);
+        DisplayChoose(fatesID);
+    }
+
+    void DisplayTotalStarCnt(int fatesID)
+    {
+        bool hasNowLVAndNextLVConfig = manager.TryGetNowLVAndNextLVConfig(fatesID, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig);
+        if (hasNowLVAndNextLVConfig)
+        {
+            int fatesNowStarCnt = manager.GetFatesNowStarCnt(fatesID);
+            int nextNeedStarCnt = nextLVConfig.NeedStarTotal;
+            txtTotalStarCnt.text = UIHelper.AppendColor(nextNeedStarCnt <= fatesNowStarCnt ? TextColType.DarkGreen : TextColType.Red, StringUtility.Concat(Language.Get("HeroFates05"), Language.Get("HeroFates11", fatesNowStarCnt, nextNeedStarCnt)));
+        }
+    }
+
+    protected override void OnPreClose()
+    {
+        scroller.OnRefreshCell -= OnRefreshCell;
+        manager.OnUpdateHeroFatesInfoEvent -= OnUpdateHeroFatesInfo;
+        manager.OnRealChooseChangeEvent -= OnRealChooseChangeEvent;
+    }
+
+    private void OnRealChooseChangeEvent()
+    {
+        RefreshAll();
+    }
+
+
+    private void OnUpdateHeroFatesInfo()
+    {
+        if (manager.IsHeroFatesStarMax(manager.chooseHeroFatesId))
+        {
+            SysNotifyMgr.Instance.ShowTip("HeroFates02");
+            CloseWindow();
+            return;
+        }
+
+        RefreshAll();
+        var smallList = scroller.m_Scorller.GetActiveCellViews(); // 淇鎷煎啓閿欒
+        for (int i = 0; i < smallList.Count; i++)
+        {
+            var item = smallList[i];
+            var cellView = item as CellView;
+            if (cellView != null)
+            {
+                var _cell = cellView.GetComponent<HeroFatesUpgradeAttrCell>();
+                _cell?.Display(cellView.index, cellView, true);
+            }
+        }
+    }
+
+
+    void RefreshAll()
+    {
+        fatesID = manager.chooseHeroFatesId;
+        DisplayHeadCells(fatesID);
+        DisplayTotalStarCnt(fatesID);
+        DisplayLV(fatesID);
+        DisplayChoose(fatesID);
+        RefeshScoller();
+    }
+
+    private void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<HeroFatesUpgradeAttrCell>();
+        _cell?.Display(cell.index, cell);
+    }
+
+    void CreateScoller(int fatesID)
+    {
+        scroller.Refresh();
+        if (manager.TryGetAttrIDListAndLVAttrValueList(fatesID, out var attrIDList, out var lvAttrValueList))
+        {
+            for (int i = 0; i < attrIDList.Length; i++)
+            {
+                CellInfo cellInfo = new CellInfo();
+                cellInfo.infoInt1 = fatesID;
+                scroller.AddCell(ScrollerDataType.Header, i, cellInfo);
+            }
+        }
+        scroller.Restart();
+    }
+
+    void RefeshScoller()
+    {
+        scroller.m_Scorller.RefreshActiveCellViews();
+    }
+
+    private void DisplayLV(int fatesID)
+    {
+        int fromLv = 0;
+        int toLv = 0;
+
+        if (manager.TryGetHeroFates(fatesID, out HeroFates info))
+        {
+            fromLv = info.FatesLV;
+            toLv = info.FatesLV + 1;
+        }
+        txtFromLv.text = Language.Get("L1113", fromLv);
+        txtToLv.text = Language.Get("L1113", toLv);
+    }
+    public void DisplayHeadCells(int fatesID)
+    {
+        if (!HeroFatesConfig.HasKey(fatesID))
+            return;
+        if (!manager.TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config) || config.HeroIDList.IsNullOrEmpty())
+            return;
+        Dictionary<int, HeroInfo> dict = manager.GetFatesNowStarHeroInfoDict(fatesID);
+        if (dict.IsNullOrEmpty())
+            return;
+        for (int i = 0; i < headCells.Count; i++)
+        {
+            if (i < config.HeroIDList.Length)
+            {
+                int heroID = config.HeroIDList[i];
+                if (dict.TryGetValue(heroID, out HeroInfo info))
+                {
+                    headCells[i].Display(info);
+                    headCells[i].SetActive(true);
+                }
+                else
+                {
+                    headCells[i].SetActive(false);
+                }
+            }
+            else
+            {
+                headCells[i].SetActive(false);
+            }
+        }
+    }
+
+    public void DisplayChoose(int fatesID)
+    {
+        if (!manager.TryGetHeroFatesConfig(fatesID, out HeroFatesConfig config))
+        {
+            return;
+        }
+
+        if (!manager.TryGetNowLVAndNextLVConfig(fatesID, out int nowLv, out HeroFatesQualityLVConfig nowLVConfig, out HeroFatesQualityLVConfig nextLVConfig))
+        {
+            return;
+        }
+
+        int chooseCnt = manager.realCostHeroInfos.Count;
+        bool isChoose = chooseCnt > 0;
+
+        imgQualityHeroIcon.SetActive(isChoose);
+        btnQualityHeroClear.SetActive(isChoose);
+        imgQualityHeroNull.SetActive(!isChoose);
+
+        imgQualityHeroBg.SetItemBackGround(config.FatesQuality);
+        imgQualityHeroIcon.SetSprite(StringUtility.Concat("HeroFatesQualityHead", config.FatesQuality.ToString()));
+        txtQualityHeroName.text = StringUtility.Concat(RichTextMsgReplaceConfig.GetRichReplace("HeroQuality", config.FatesQuality), Language.Get("herocard3"));
+        txtQualityHeroName.color = UIHelper.GetUIColorByFunc(config.FatesQuality);
+        int needCnt = nextLVConfig.NeedHeroCnt;
+        txtNeedQualityHeroCnt.text = UIHelper.AppendColor(chooseCnt >= needCnt ? TextColType.DarkGreen : TextColType.Red, Language.Get("BoneField09", chooseCnt, needCnt));
+
+        bool isShowRed = manager.IsShowRedDot(fatesID);
+        imgPutRed.SetActive(isShowRed && !isChoose);
+    }
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesUpgradeWin.cs.meta b/Main/System/HeroFates/HeroFatesUpgradeWin.cs.meta
new file mode 100644
index 0000000..753cc06
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesUpgradeWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 36a41763eeea97f4284585b5e64c3fa8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroFates/HeroFatesWin.cs b/Main/System/HeroFates/HeroFatesWin.cs
new file mode 100644
index 0000000..6a2e7d3
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesWin.cs
@@ -0,0 +1,90 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+public class HeroFatesWin : UIBase
+{
+    [SerializeField] ScrollerController scroller;
+    [SerializeField] List<TextEx> attrs;
+    HeroFatesManager manager { get { return HeroFatesManager.Instance; } }
+    protected override void InitComponent()
+    {
+
+    }
+
+    protected override void OnPreOpen()
+    {
+        scroller.OnRefreshCell += OnRefreshCell;
+        manager.OnUpdateHeroFatesInfoEvent += OnUpdateHeroFatesInfo;
+        CreateScoller();
+
+        int firstIndex = 0;
+        manager.HasShowRedDotFates(out firstIndex);
+        scroller.JumpIndex(firstIndex);
+
+        DisplayAttrs();
+    }
+
+    protected override void OnPreClose()
+    {
+        scroller.OnRefreshCell -= OnRefreshCell;
+        manager.OnUpdateHeroFatesInfoEvent -= OnUpdateHeroFatesInfo;
+    }
+
+    private void OnUpdateHeroFatesInfo()
+    {
+        RefeshScoller();
+        DisplayAttrs();
+    }
+    List<int> fatesIDList = new List<int>();
+    private void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<HeroFatesCell>();
+        _cell?.Display(cell.index, fatesIDList);
+    }
+
+    void CreateScoller()
+    {
+        scroller.Refresh();
+        fatesIDList = manager.GetFatesIDList();
+        if (!fatesIDList.IsNullOrEmpty())
+        {
+            for (int i = 0; i < fatesIDList.Count; i++)
+            {
+                scroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        scroller.Restart();
+    }
+
+    void RefeshScoller()
+    {
+        scroller.m_Scorller.RefreshActiveCellViews();
+    }
+
+    void DisplayAttrs()
+    {
+        Dictionary<int, long> totalAttrDict = manager.GetTotalAttr();
+        if (attrs.IsNullOrEmpty() || totalAttrDict.IsNullOrEmpty())
+            return;
+        List<int> totalAttrIdList = totalAttrDict.Keys
+            .OrderByDescending(attrID => totalAttrDict[attrID] > 0)  // 灞炴�у��>0鐨勬帓鍓嶉潰
+            .ThenBy(attrID => attrID)  // 鐒跺悗鎸塧ttrID鍗囧簭
+            .ToList();
+        for (int i = 0; i < attrs.Count; i++)
+        {
+            if (i < totalAttrDict.Count)
+            {
+                int attrID = totalAttrIdList[i];
+                long attrValue = totalAttrDict[attrID];
+                attrs[i].text = PlayerPropertyConfig.GetFullDescription(attrID, attrValue);
+                attrs[i].SetActive(true);
+            }
+            else
+            {
+                attrs[i].SetActive(false);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/Main/System/HeroFates/HeroFatesWin.cs.meta b/Main/System/HeroFates/HeroFatesWin.cs.meta
new file mode 100644
index 0000000..efb5375
--- /dev/null
+++ b/Main/System/HeroFates/HeroFatesWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 65869ccc1efcb1741ae8394076307030
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroUI/HeroBaseWin.cs b/Main/System/HeroUI/HeroBaseWin.cs
index ad69532..5342bd9 100644
--- a/Main/System/HeroUI/HeroBaseWin.cs
+++ b/Main/System/HeroUI/HeroBaseWin.cs
@@ -16,8 +16,8 @@
         base.InitComponent();
 
         //鎷涘嫙涓哄彟澶栦竴涓晫闈紝閬垮厤鍏抽棴鏃舵樉绀虹┖鐧�
-        callBtn.AddListener(()=>
-        { 
+        callBtn.AddListener(() =>
+        {
             //鎵撳紑鎷涘嫙鐣岄潰
             UIManager.Instance.OpenWindow<HeroCallWin>();
         });
@@ -51,6 +51,7 @@
                 currentSubUI = UIManager.Instance.OpenWindow<HeroCollectionWin>();
                 break;
             case 2:
+                currentSubUI = UIManager.Instance.OpenWindow<HeroFatesWin>();
                 break;
             default:
                 Debug.LogWarning("鏈煡鐨勬爣绛剧储寮�: " + functionOrder);
diff --git a/Main/System/HeroUI/HeroHeadBaseCell.cs b/Main/System/HeroUI/HeroHeadBaseCell.cs
index cc3c51e..0a705bd 100644
--- a/Main/System/HeroUI/HeroHeadBaseCell.cs
+++ b/Main/System/HeroUI/HeroHeadBaseCell.cs
@@ -140,7 +140,10 @@
     public void Init(int heroID, int skinID, int star = 0, int awakelv = 0, int lv = 0, UnityAction onclick = null)
     {
         LoadPrefab();   //瀛樺湪琚嵏杞界殑鍙兘锛岄噸鏂板姞杞�
-        clickBtn.AddListener(onclick);
+        if (onclick != null)
+        {
+            clickBtn.AddListener(onclick);
+        }
         var heroConfig = HeroConfig.Get(heroID);
         qualityBG.SetSprite("heroheadBG" + heroConfig.Quality);
         // int skinID = 0;
@@ -186,7 +189,7 @@
         }
 
         countryImg.SetSprite(HeroUIManager.Instance.GetCountryIconName(heroConfig.Country));
-        lvText.text = lv == 0 ? "": Language.Get("L1094") + lv;
+        lvText.text = lv == 0 ? "" : Language.Get("L1094") + lv;
 
         awakeLvRect.SetActive(awakelv > 0);
         awakeLvText.text = awakelv.ToString();
@@ -216,7 +219,7 @@
                 cellContainer.transform.SetAsFirstSibling();
             }
         }
-        
+
         //缂╂斁鍒板拰鐖秗ect涓�鏍峰ぇ
         var scale = 1f;
         var rect = cellContainer.GetComponent<RectTransform>();
@@ -227,10 +230,10 @@
             //澶栭儴鎺у埗浜嗗昂瀵歌幏鍙栦负0
             GridLayoutGroup grid = GetComponentInParent<GridLayoutGroup>();
             if (grid != null)
-            { 
+            {
                 width = grid.cellSize.x;
             }
-            
+
         }
         scale = width / rect.sizeDelta.x;
         cellContainer.transform.localScale = cellContainer.transform.localScale * scale;
diff --git a/Main/System/Main/FightPowerManager.cs b/Main/System/Main/FightPowerManager.cs
index 9f6f3d8..79ef913 100644
--- a/Main/System/Main/FightPowerManager.cs
+++ b/Main/System/Main/FightPowerManager.cs
@@ -318,8 +318,8 @@
         propertyVariables[HORSE_PER] = HorseManager.Instance.GetAttrPer(attrType) / 10000.0f;
         propertyVariables[BEAUTY_VALUE] = 0;
         propertyVariables[BEAUTY_PER] = 0;
-        propertyVariables[FATES_VALUE] = 0;
-        propertyVariables[FATES_PER] = 0;
+        propertyVariables[FATES_VALUE] = HeroFatesManager.Instance.GetAttrValue(attrType);
+        propertyVariables[FATES_PER] = HeroFatesManager.Instance.GetAttrPer(attrType) / 10000.0f;
 
         //鍏ㄤ綋鍗$墝鍔犳垚
         propertyVariables[HERO_CARDPER] = allHeroAddPer;
diff --git a/Main/System/Redpoint/MainRedDot.cs b/Main/System/Redpoint/MainRedDot.cs
index cd3f9e1..1638c8c 100644
--- a/Main/System/Redpoint/MainRedDot.cs
+++ b/Main/System/Redpoint/MainRedDot.cs
@@ -135,7 +135,8 @@
     public const int TianziBillboradRepoint = 471; //澶╁瓙鐨勮�冮獙
     public const int PhantasmPavilionRepoint = 472; //骞诲闃�
     public const int LineupRecommendRepoint = 473; //闃靛鎺ㄨ崘
-     public const int FunctionPreviewRepoint = 474; //鍔熻兘棰勫憡
+    public const int FunctionPreviewRepoint = 474; //鍔熻兘棰勫憡
+    public const int HeroFatesRepoint = 475;//瀹跨紭
     public void Register()
     {
 
diff --git a/Main/Utility/EnumHelper.cs b/Main/Utility/EnumHelper.cs
index d325a98..ce8fa28 100644
--- a/Main/Utility/EnumHelper.cs
+++ b/Main/Utility/EnumHelper.cs
@@ -843,6 +843,7 @@
     OSHeroCall = 46, //寮�鏈嶆灏嗗彫鍞ゆ娲诲姩
     OSGala = 47, //寮�鏈嶇洓鍏告椿鍔�
     FunctionPreview = 48, //鍔熻兘棰勮
+    HeroFatesUpgrade = 49, //瀹跨紭鍗囩骇
 }
 
 

--
Gitblit v1.8.0