From 37aa92cbb341cc15a7f0e60ceebceda68a67780c Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期一, 06 七月 2026 10:07:25 +0800
Subject: [PATCH] 581 炼器大会

---
 Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs            |   13 
 Main/Config/Configs/ActTypeConfig.cs                                                                  |   80 
 Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs                        |   24 
 Main/System/LianQi/LianQiZhanLingPreviewCell.cs.meta                                                  |   11 
 Main/System/LianQi/LianQiGiftWin.cs.meta                                                              |   11 
 Main/System/LianQi/DragSelectComponentEx.cs.meta                                                      |   11 
 Main/System/LianQi/LianQiRoundSettlementWin.cs.meta                                                   |   11 
 Main/System/Redpoint/MainRedDot.cs                                                                    |    2 
 Main/System/LianQi/LianQiRankTop3Cell.cs.meta                                                         |   11 
 Main/System/LianQi/LianQiRankAwardItemCell.cs.meta                                                    |   11 
 Main/System/LianQi/LianQiCoolTime.cs.meta                                                             |   11 
 Main/System/LianQi/OperationLianQiCross.cs.meta                                                       |   11 
 Main/System/BattlePass/BattlePassManager.cs                                                           |    6 
 Main/Config/Configs/ActTypeConfig.cs.meta                                                             |   11 
 Main/System/LianQi/LianQiActLevel1.cs.meta                                                            |   11 
 Main/System/LianQi/LianQiItemInfoWin.cs                                                               |   83 +
 Main/System/LianQi/LianQiPopTipWin.cs.meta                                                            |   11 
 Main/System/LianQi/LianQiWin.cs.meta                                                                  |   11 
 Main/Config/Configs/ActTimeFlowConfig.cs.meta                                                         |   11 
 Main/System/LianQi/LianQiZhanLingPreviewCell.cs                                                       |   26 
 Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs.meta       |   11 
 Main/System/LianQi/LianQiPopTipWin.cs                                                                 |   30 
 Main/Config/Configs/ActTimeFlowConfig.cs                                                              |   62 
 Main/Utility/EnumHelper.cs                                                                            |    1 
 Main/Config/Configs/ActTimeConfig.cs.meta                                                             |   11 
 Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs.meta           |   11 
 Main/System/LianQi/LianQiGiftCell.cs.meta                                                             |   11 
 Main/System/LianQi/LianQiZhanLingWin.cs                                                               |  114 +
 Main/System/LianQi/LianQiCoolTime.cs                                                                  |   70 
 Main/System/LianQi/LianQiItemInfoWin.cs.meta                                                          |   11 
 Main/Config/PartialConfigs/ActTypeConfig.cs.meta                                                      |   11 
 Main/System/LianQi/LianQiRankAwardCell.cs.meta                                                        |   11 
 Main/System/LianQi/LianQiZhanLingWin.cs.meta                                                          |   11 
 Main/System/LianQi/LianQiRankCell.cs                                                                  |   89 +
 Main/System/OpenServerActivity/OperationTimeHepler.cs                                                 |   25 
 Main/System/LianQi/DragSelectComponentEx.cs                                                           |   99 +
 Main/System/LianQi/LianQiSmallTipWin.cs.meta                                                          |   11 
 Main/System/LianQi/LianQiManager.cs                                                                   | 1248 +++++++++++++++
 Main/System/LianQi/LianQiWin.cs                                                                       |  740 +++++++++
 Main/Config/ConfigManager.cs                                                                          |   17 
 Main/System/LianQi/LianQiActLevel1.cs                                                                 |    7 
 Main/System/LianQi/LianQiActLevel2.cs.meta                                                            |   11 
 Main/System/LianQi/LianQiRankWin.cs.meta                                                              |   11 
 Main/System/LianQi/LianQiSmallTipWin.cs                                                               |  124 +
 Main/System/LianQi/OperationLianQiCross.cs                                                            |  162 +
 Main/System/LianQi/LianQiZhanLingCell.cs                                                              |  126 +
 Main/System/LianQi/LianQiManager.cs.meta                                                              |   11 
 Main/System/Main/HomeWin.cs                                                                           |   21 
 Main/Main.cs                                                                                          |    1 
 Main/System/LianQi/LianQiItemInfoDoubleWin.cs.meta                                                    |   11 
 Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs      |   12 
 Main/System/LianQi/LianQiActLevel2.cs                                                                 |   14 
 Main/Config/PartialConfigs/ActTimeFlowConfig.cs                                                       |   29 
 Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs.meta                   |   11 
 Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs.meta                 |   11 
 Main/System/LianQi/LianQiItemInfoDoubleWin.cs                                                         |  113 +
 Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs.meta |   11 
 Main/System/LianQi/LianQiRankAwardItemCell.cs                                                         |   15 
 Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs                      |   29 
 Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs                |   57 
 Main/System/LianQi/LianQiAwardWin.cs.meta                                                             |   11 
 Main/System/LianQi.meta                                                                               |    8 
 Main/System/LianQi/LianQiRankCell.cs.meta                                                             |   11 
 Main/Config/Configs/ActTimeConfig.cs                                                                  |   50 
 Main/System/LianQi/LianQiRankTop3Cell.cs                                                              |   58 
 Main/System/LianQi/LianQiZhanLingPreviewWin.cs.meta                                                   |   11 
 Main/System/LianQi/LianQiGiftWin.cs                                                                   |   74 
 Main/System/LianQi/LianQiAwardCell.cs.meta                                                            |   11 
 Main/Config/PartialConfigs/ActTimeFlowConfig.cs.meta                                                  |   11 
 Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs                                                  |    2 
 Main/System/LianQi/LianQiAwardCell.cs                                                                 |   75 
 Main/System/LianQi/LianQiRankAwardCell.cs                                                             |   72 
 Main/System/LianQi/LianQiAwardWin.cs                                                                  |  174 ++
 Main/System/LianQi/LianQiRoundSettlementWin.cs                                                        |   35 
 Main/System/LianQi/LianQiGiftCell.cs                                                                  |  228 ++
 Main/Config/PartialConfigs/ActTypeConfig.cs                                                           |   71 
 Main/System/LianQi/LianQiZhanLingCell.cs.meta                                                         |   11 
 Main/System/LianQi/LianQiZhanLingPreviewWin.cs                                                        |  115 +
 Main/System/LianQi/LianQiRankWin.cs                                                                   |   83 +
 79 files changed, 4,867 insertions(+), 2 deletions(-)

diff --git a/Main/Config/ConfigManager.cs b/Main/Config/ConfigManager.cs
index 5c842f4..965e50e 100644
--- a/Main/Config/ConfigManager.cs
+++ b/Main/Config/ConfigManager.cs
@@ -38,6 +38,14 @@
 
         // 鍔犺浇閰嶇疆鏂囦欢
         // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
+        // 鍔犺浇閰嶇疆鏂囦欢
         HashSet<Type> configTypes = new HashSet<Type>() {
             typeof(ActBillboardAwardConfig),
             typeof(ActHeroAppearArtConfig),
@@ -50,10 +58,13 @@
             typeof(ActSpecialSaleConfig),
             typeof(ActTaskConfig),
             typeof(ActTaskTempConfig),
+            typeof(ActTimeConfig),
+            typeof(ActTimeFlowConfig),
             typeof(ActTotalRechargeConfig),
             typeof(ActTotalRechargeTempConfig),
             typeof(ActTotDayRechargeConfig),
             typeof(ActTotDayRechargeTempConfig),
+            typeof(ActTypeConfig),
             typeof(ADAwardConfig),
             typeof(BattleMapConfig),
             typeof(BeautyConfig),
@@ -306,6 +317,10 @@
         ClearConfigDictionary<ActTaskConfig>();
         // 娓呯┖ ActTaskTempConfig 瀛楀吀
         ClearConfigDictionary<ActTaskTempConfig>();
+        // 娓呯┖ ActTimeConfig 瀛楀吀
+        ClearConfigDictionary<ActTimeConfig>();
+        // 娓呯┖ ActTimeFlowConfig 瀛楀吀
+        ClearConfigDictionary<ActTimeFlowConfig>();
         // 娓呯┖ ActTotalRechargeConfig 瀛楀吀
         ClearConfigDictionary<ActTotalRechargeConfig>();
         // 娓呯┖ ActTotalRechargeTempConfig 瀛楀吀
@@ -314,6 +329,8 @@
         ClearConfigDictionary<ActTotDayRechargeConfig>();
         // 娓呯┖ ActTotDayRechargeTempConfig 瀛楀吀
         ClearConfigDictionary<ActTotDayRechargeTempConfig>();
+        // 娓呯┖ ActTypeConfig 瀛楀吀
+        ClearConfigDictionary<ActTypeConfig>();
         // 娓呯┖ ADAwardConfig 瀛楀吀
         ClearConfigDictionary<ADAwardConfig>();
         // 娓呯┖ BattleMapConfig 瀛楀吀
diff --git a/Main/Config/Configs/ActTimeConfig.cs b/Main/Config/Configs/ActTimeConfig.cs
new file mode 100644
index 0000000..5e420a0
--- /dev/null
+++ b/Main/Config/Configs/ActTimeConfig.cs
@@ -0,0 +1,50 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           Thursday, July 2, 2026
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class ActTimeConfig : ConfigBase<int, ActTimeConfig>
+{
+    static ActTimeConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int CfgID;
+	public int ActNum;
+	public string ActFlow;
+	public int ActFuncType;
+	public int ActTempID;
+
+    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 CfgID); 
+
+			int.TryParse(tables[1],out ActNum); 
+
+			ActFlow = tables[2];
+
+			int.TryParse(tables[3],out ActFuncType); 
+
+			int.TryParse(tables[4],out ActTempID); 
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/Config/Configs/ActTimeConfig.cs.meta b/Main/Config/Configs/ActTimeConfig.cs.meta
new file mode 100644
index 0000000..06c54c6
--- /dev/null
+++ b/Main/Config/Configs/ActTimeConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6fcca7d3328b986479acbe95affd4556
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/Configs/ActTimeFlowConfig.cs b/Main/Config/Configs/ActTimeFlowConfig.cs
new file mode 100644
index 0000000..ecc8f9b
--- /dev/null
+++ b/Main/Config/Configs/ActTimeFlowConfig.cs
@@ -0,0 +1,62 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           2026骞�7鏈�2鏃�
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class ActTimeFlowConfig : ConfigBase<int, ActTimeFlowConfig>
+{
+    static ActTimeFlowConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int ID;
+	public string ActFlow;
+	public int StartDay;
+	public int StartHour;
+	public int StartMinute;
+	public int EndDay;
+	public int EndHour;
+	public int EndMinute;
+	public int StateValue;
+
+    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); 
+
+			ActFlow = tables[1];
+
+			int.TryParse(tables[2],out StartDay); 
+
+			int.TryParse(tables[3],out StartHour); 
+
+			int.TryParse(tables[4],out StartMinute); 
+
+			int.TryParse(tables[5],out EndDay); 
+
+			int.TryParse(tables[6],out EndHour); 
+
+			int.TryParse(tables[7],out EndMinute); 
+
+			int.TryParse(tables[8],out StateValue); 
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/Config/Configs/ActTimeFlowConfig.cs.meta b/Main/Config/Configs/ActTimeFlowConfig.cs.meta
new file mode 100644
index 0000000..5392314
--- /dev/null
+++ b/Main/Config/Configs/ActTimeFlowConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 47860d5770f7897429c9ae853fc82e59
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/Configs/ActTypeConfig.cs b/Main/Config/Configs/ActTypeConfig.cs
new file mode 100644
index 0000000..885ba3c
--- /dev/null
+++ b/Main/Config/Configs/ActTypeConfig.cs
@@ -0,0 +1,80 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           2026骞�7鏈�2鏃�
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class ActTypeConfig : ConfigBase<int, ActTypeConfig>
+{
+    static ActTypeConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int CfgID;
+	public int ActFuncType;
+	public int ActTempID;
+	public int JoinFamilyCnt;
+	public int ActShopType;
+	public int ADID;
+	public int[] CTGIDList;
+	public int CTGAssistTempID;
+	public int GuessTempID;
+	public int PersonalTempID;
+	public int FamilyTemplID;
+
+    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 CfgID); 
+
+			int.TryParse(tables[1],out ActFuncType); 
+
+			int.TryParse(tables[2],out ActTempID); 
+
+			int.TryParse(tables[3],out JoinFamilyCnt); 
+
+			int.TryParse(tables[4],out ActShopType); 
+
+			int.TryParse(tables[5],out ADID); 
+
+			if (tables[6].Contains("["))
+			{
+				CTGIDList = JsonMapper.ToObject<int[]>(tables[6]);
+			}
+			else
+			{
+				string[] CTGIDListStringArray = tables[6].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+				CTGIDList = new int[CTGIDListStringArray.Length];
+				for (int i=0;i<CTGIDListStringArray.Length;i++)
+				{
+					 int.TryParse(CTGIDListStringArray[i],out CTGIDList[i]);
+				}
+			}
+
+			int.TryParse(tables[7],out CTGAssistTempID); 
+
+			int.TryParse(tables[8],out GuessTempID); 
+
+			int.TryParse(tables[9],out PersonalTempID); 
+
+			int.TryParse(tables[10],out FamilyTemplID); 
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/Config/Configs/ActTypeConfig.cs.meta b/Main/Config/Configs/ActTypeConfig.cs.meta
new file mode 100644
index 0000000..74bc214
--- /dev/null
+++ b/Main/Config/Configs/ActTypeConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4eb6bc33c43c6d248a6cc06bf0928533
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/PartialConfigs/ActTimeFlowConfig.cs b/Main/Config/PartialConfigs/ActTimeFlowConfig.cs
new file mode 100644
index 0000000..6d49863
--- /dev/null
+++ b/Main/Config/PartialConfigs/ActTimeFlowConfig.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+public partial class ActTimeFlowConfig : ConfigBase<int, ActTimeFlowConfig>
+{
+    static Dictionary<string, List<ActTimeFlowConfig>> flowDict = new Dictionary<string, List<ActTimeFlowConfig>>();
+
+    protected override void OnConfigParseCompleted()
+    {
+        if (!flowDict.ContainsKey(ActFlow))
+            flowDict[ActFlow] = new List<ActTimeFlowConfig>();
+        flowDict[ActFlow].Add(this);
+    }
+
+    public static List<ActTimeFlowConfig> GetByActFlow(string actFlow)
+    {
+        if (!flowDict.TryGetValue(actFlow, out var list) || list == null)
+            return new List<ActTimeFlowConfig>();
+        var sorted = new List<ActTimeFlowConfig>(list);
+        sorted.Sort((a, b) =>
+        {
+            int c = a.StartDay.CompareTo(b.StartDay);
+            if (c != 0) return c;
+            c = a.StartHour.CompareTo(b.StartHour);
+            if (c != 0) return c;
+            return a.StartMinute.CompareTo(b.StartMinute);
+        });
+        return sorted;
+    }
+}
diff --git a/Main/Config/PartialConfigs/ActTimeFlowConfig.cs.meta b/Main/Config/PartialConfigs/ActTimeFlowConfig.cs.meta
new file mode 100644
index 0000000..144fd03
--- /dev/null
+++ b/Main/Config/PartialConfigs/ActTimeFlowConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 447d8b9a08c6a22479478123eac98628
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/PartialConfigs/ActTypeConfig.cs b/Main/Config/PartialConfigs/ActTypeConfig.cs
new file mode 100644
index 0000000..6afc310
--- /dev/null
+++ b/Main/Config/PartialConfigs/ActTypeConfig.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public partial class ActTypeConfig : ConfigBase<int, ActTypeConfig>
+{
+    // 娉細CfgID 浠� 1 寮�濮嬶紝0 鐢ㄤ綔 GetCfgID/TryGetCfgID 鐨�"鏈懡涓�"鍗犱綅杩斿洖鍊�
+    //<鍔熻兘绫诲瀷,<妯℃澘ID,閰嶇疆ID>>
+    static Dictionary<int, Dictionary<int, int>> infoDict = new Dictionary<int, Dictionary<int, int>>();
+
+    // 閰嶇疆琛岃В鏋愬畬鎴愬洖璋冿細寤虹珛 ActFuncType 鈫� ActTempID 鈫� CfgID 浜岀骇绱㈠紩锛岄噸澶嶇粍鍚堜細鍛婅
+    protected override void OnConfigParseCompleted()
+    {
+        if (!infoDict.ContainsKey(ActFuncType))
+            infoDict[ActFuncType] = new Dictionary<int, int>();
+        if (infoDict[ActFuncType].ContainsKey(ActTempID))
+        {
+            Debug.LogError(typeof(ActTypeConfig).Name + " 閲嶅鐨� ActFuncType " + ActFuncType + " ActTempID " + ActTempID + " 鏃fgID " + infoDict[ActFuncType][ActTempID] + " 鏂癈fgID " + CfgID);
+        }
+        infoDict[ActFuncType][ActTempID] = CfgID;
+    }
+
+    // 閫氳繃娲诲姩鍔熻兘绫诲瀷鍜屾ā鏉縄D鏌ヨ閰嶇疆ID锛屾湭鍛戒腑杩斿洖 0
+    public static int GetCfgID(int actFuncType, int actTempID)
+    {
+        return infoDict.TryGetValue(actFuncType, out var info) && info.TryGetValue(actTempID, out var cfgID) ? cfgID : 0;
+    }
+
+    // 灏濊瘯閫氳繃娲诲姩鍔熻兘绫诲瀷鍜屾ā鏉縄D鏌ヨ閰嶇疆ID锛屽懡涓繑鍥� true
+    public static bool TryGetCfgID(int actFuncType, int actTempID, out int cfgID)
+    {
+        cfgID = 0;
+        return infoDict != null && infoDict.TryGetValue(actFuncType, out var info) && info.TryGetValue(actTempID, out cfgID);
+    }
+
+    // 閫氳繃娲诲姩鍔熻兘绫诲瀷鍜屾ā鏉縄D鑾峰彇閰嶇疆锛屾湭鍛戒腑杩斿洖 null
+    public static ActTypeConfig GetConfig(int actFuncType, int actTempID)
+    {
+        int cfgID = GetCfgID(actFuncType, actTempID);
+        return cfgID == 0 ? null : Get(cfgID);
+    }
+
+    // 閫氳繃閰嶇疆ID鑾峰彇閰嶇疆锛屾湭鍛戒腑杩斿洖 false
+    public static bool TryGetConfig(int cfgID, out ActTypeConfig config)
+    {
+        config = null;
+        if (!HasKey(cfgID))
+            return false;
+        config = Get(cfgID);
+        return true;
+    }
+
+    // 閫氳繃娲诲姩鍔熻兘绫诲瀷鍜屾ā鏉縄D鑾峰彇閰嶇疆锛屾湭鍛戒腑杩斿洖 false
+    public static bool TryGetConfig(int actFuncType, int actTempID, out ActTypeConfig config)
+    {
+        config = null;
+        if (!TryGetCfgID(actFuncType, actTempID, out int cfgID))
+            return false;
+        return TryGetConfig(cfgID, out config);
+    }
+
+    // 鑾峰彇鎸囧畾娲诲姩鍔熻兘绫诲瀷涓嬬殑鍏ㄩ儴閰嶇疆ID
+    public static List<int> GetCfgIDsByFuncType(int actFuncType)
+    {
+        List<int> result = new List<int>();
+        if (infoDict.TryGetValue(actFuncType, out var info))
+        {
+            result.AddRange(info.Values);
+        }
+        return result;
+    }
+}
diff --git a/Main/Config/PartialConfigs/ActTypeConfig.cs.meta b/Main/Config/PartialConfigs/ActTypeConfig.cs.meta
new file mode 100644
index 0000000..fa9b6b7
--- /dev/null
+++ b/Main/Config/PartialConfigs/ActTypeConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ad332adb5561d3d4e96d0d7278e81601
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs b/Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs
new file mode 100644
index 0000000..bfb5242
--- /dev/null
+++ b/Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs
@@ -0,0 +1,24 @@
+using UnityEngine;
+using System.Collections;
+
+// AA 25 鐐煎櫒鎿嶄綔 #tagCSActLianqiOP
+
+public class CAA25_tagCSActLianqiOP : GameNetPackBasic {
+    public byte OPType;    // 1-绉诲姩锛�2-浣跨敤閬撳叿锛�3-閲嶆柊寮�濮嬶紱4-棰嗗彇绛夌骇濂栧姳锛�5-棰嗗彇姒滃崟濂栧姳
+    public uint OPValue;    // 绉诲姩鏃�-鍙�1涓�2涓�3宸�4鍙筹紱浣跨敤閬撳叿鏃�-鍙戜娇鐢ㄤ釜鏁�*10+閬撳叿ID鎵�鍦ㄩ厤缃储寮曪紱绛夌骇濂栧姳鏃�-鍙戦鍙栫殑濂栧姳绛夌骇
+    public uint OPValue2;    // 浣跨敤閬撳叿鏃�-鍙戦�変腑鏍煎瓙A鐨勮鍒楀�� 琛�*10+鍒�
+    public uint OPValue3;    // 浣跨敤閬撳叿鏃�-鍙戦�変腑鏍煎瓙B鐨勮鍒楀�� 琛�*10+鍒�
+
+    public CAA25_tagCSActLianqiOP () {
+        combineCmd = (ushort)0x03FE;
+        _cmd = (ushort)0xAA25;
+    }
+
+    public override void WriteToBytes () {
+        WriteBytes (OPType, NetDataType.BYTE);
+        WriteBytes (OPValue, NetDataType.DWORD);
+        WriteBytes (OPValue2, NetDataType.DWORD);
+        WriteBytes (OPValue3, NetDataType.DWORD);
+    }
+
+}
diff --git a/Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs.meta b/Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs.meta
new file mode 100644
index 0000000..4836021
--- /dev/null
+++ b/Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bad8f07ed5cf8724e80d5f737617f8dd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs
new file mode 100644
index 0000000..6146d1a
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs
@@ -0,0 +1,13 @@
+using UnityEngine;
+using System.Collections;
+
+// AA 90 鐐煎櫒璺ㄦ湇娲诲姩淇℃伅 #tagSCActLianqiInfo
+
+public class DTCAA90_tagSCActLianqiInfo : DtcBasic {
+    public override void Done(GameNetPackBasic vNetPack) {
+        base.Done(vNetPack);
+        HAA90_tagSCActLianqiInfo vNetData = vNetPack as HAA90_tagSCActLianqiInfo;
+        OperationTimeHepler.Instance.UpdateCrossActLianqiInfo(vNetData);
+    }
+}
+
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs.meta b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs.meta
new file mode 100644
index 0000000..09bd532
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9a99e20beaa9ab143bb352ee87bdd090
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs
new file mode 100644
index 0000000..0422a6a
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs
@@ -0,0 +1,12 @@
+using UnityEngine;
+using System.Collections;
+
+// AA 91 鐐煎櫒娲诲姩鐜╁淇℃伅 #tagSCActLianqiPlayerInfo
+
+public class DTCAA91_tagSCActLianqiPlayerInfo : DtcBasic {
+    public override void Done(GameNetPackBasic vNetPack) {
+        base.Done(vNetPack);
+        HAA91_tagSCActLianqiPlayerInfo vNetData = vNetPack as HAA91_tagSCActLianqiPlayerInfo;
+        LianQiManager.Instance.UpdateActLianqiPlayerInfo(vNetData);
+    }
+}
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs.meta b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs.meta
new file mode 100644
index 0000000..ff17572
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 64b6cfa4866f22d4f8a122f07fe76ca9
+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 57c672d..4a734f2 100644
--- a/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
+++ b/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -174,6 +174,8 @@
         Register(typeof(HAA72_tagSCActTaskPlayerValueInfo), typeof(DTCAA72_tagSCActTaskPlayerValueInfo));
         Register(typeof(HAA73_tagSCActTaskPlayerInfo), typeof(DTCAA73_tagSCActTaskPlayerInfo));
         Register(typeof(HA525_tagSCFamilyAtkDefBatInfo), typeof(DTCA525_tagSCFamilyAtkDefBatInfo));
+        Register(typeof(HAA90_tagSCActLianqiInfo), typeof(DTCAA90_tagSCActLianqiInfo));
+        Register(typeof(HAA91_tagSCActLianqiPlayerInfo), typeof(DTCAA91_tagSCActLianqiPlayerInfo));
     }
 
     //涓诲伐绋嬫敞鍐屽皝鍖�
diff --git a/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs
new file mode 100644
index 0000000..826b10d
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs
@@ -0,0 +1,29 @@
+using UnityEngine;
+using System.Collections;
+
+// AA 90 鐐煎櫒璺ㄦ湇娲诲姩淇℃伅 #tagSCActLianqiInfo
+
+public class HAA90_tagSCActLianqiInfo : GameNetPackBasic {
+    public ushort ServerLen;
+    public string ServerIDInfo;    // 娲诲姩鏈嶅姟鍣↖D淇℃伅锛孾[浠庢湇鍔″櫒IDA, 鍒版湇鍔″櫒IDB], 鎴栨寚瀹氭煇涓湇鍔″櫒ID, ...]
+    public uint CrossServerID;    // 鎵�灞炶法鏈嶆湇鍔″櫒ID
+    public uint ZoneID;    // 鎵�灞炲垎鍖猴紝涔熸槸娲诲姩姒滃崟鍒嗙粍鍊糋roupValue1锛岀敤浜庢煡璇㈠搴旀鍗�
+    public string StartDate;    // 寮�濮嬫棩鏈� y-m-d
+    public string EndtDate;    // 缁撴潫鏃ユ湡 y-m-d
+    public ushort CfgID;    // 娲诲姩鏃堕棿琛ㄩ厤缃甀D
+
+    public HAA90_tagSCActLianqiInfo () {
+        _cmd = (ushort)0xAA90;
+    }
+
+    public override void ReadFromBytes (byte[] vBytes) {
+        TransBytes (out ServerLen, vBytes, NetDataType.WORD);
+        TransBytes (out ServerIDInfo, vBytes, NetDataType.Chars, ServerLen);
+        TransBytes (out CrossServerID, vBytes, NetDataType.DWORD);
+        TransBytes (out ZoneID, vBytes, NetDataType.DWORD);
+        TransBytes (out StartDate, vBytes, NetDataType.Chars, 10);
+        TransBytes (out EndtDate, vBytes, NetDataType.Chars, 10);
+        TransBytes (out CfgID, vBytes, NetDataType.WORD);
+    }
+
+}
diff --git a/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs.meta b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs.meta
new file mode 100644
index 0000000..8a62b70
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c740d821af03f144eba03eae95f4ac10
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs
new file mode 100644
index 0000000..9d3b43c
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs
@@ -0,0 +1,57 @@
+using UnityEngine;
+using System.Collections;
+
+// AA 91 鐐煎櫒娲诲姩鐜╁淇℃伅 #tagSCActLianqiPlayerInfo
+
+public class HAA91_tagSCActLianqiPlayerInfo : GameNetPackBasic {
+    public uint Score;    // 褰撳墠娲诲姩绉垎
+    public uint ScoreHighest;    // 褰撳墠娲诲姩鏈�楂樼Н鍒嗭紝鍗充笂姒滅Н鍒�
+    public ushort Energy;    // 褰撳墠浣撳姏
+    public uint EnergyTime;    // 涓婃鎭㈠浣撳姏鏃堕棿鎴筹紝涓�0鏃朵笉鐢ㄥ鐞嗗�掕鏃�
+    public uint LVAwardMax;    // 宸叉縺娲荤殑鏈�澶у悎鎴愬鍔辩瓑绾�
+    public uint LVAwardState;    // 鏈�澶у悎鎴愮瓑绾у鍔遍鍙栬褰曪紝鎸夌瓑绾т簩杩涘埗浣嶅瓨鍌ㄦ槸鍚﹀凡棰嗗彇
+    public byte BillAwardState;    // 姒滃崟濂栧姳鏄惁宸查鍙�
+    public byte UseItemLen;
+    public  ushort[] UseItemCntList;    // 鏈眬宸蹭娇鐢ㄨ緟鍔╅亾鍏锋鏁� [杈呭姪閬撳叿1浣跨敤娆℃暟, ...]
+    public byte GridDataLen;    // 鏍煎瓙鏁版嵁闀垮害
+    public string GridData;    // 鏍煎瓙浜岀淮琛屽垪鏁版嵁锛屼竴瀹氫細鍚屾锛岀洿鎺ユ浛鎹紝琛屼粠涓婂線涓嬫帓 [[绗�1琛屾牸瀛�1,鏍煎瓙2, ...], ...]
+    public byte OPType;    // 0-鏃�(濡傚垵濮嬪寲锛孏M绛夊悗绔洿鎺ヨ缃�)锛�1-绉诲姩锛�2-浣跨敤閬撳叿锛�3-閲嶆柊寮�濮嬶紱濡傛灉鏄洜涓烘搷浣滃紩璧风殑鏍煎瓙鏁版嵁鍙樺寲锛屽垯鍦ㄧ浉鍏虫搷浣滆〃鐜板畬姣曞悗鍐嶅睍绀烘渶鏂拌鍒楁暟鎹紝鍚﹀垯鐩存帴鍙樻洿
+    public byte MoveCount;
+    public  tagSCActLianqiTileMove[] MoveList;    // 鍥惧潡绉诲姩鍒楄〃锛屽彲鑳芥病鏈夋暟鎹紝鏈夌殑璇濆厛琛ㄧ幇绉诲姩
+
+    public HAA91_tagSCActLianqiPlayerInfo () {
+        _cmd = (ushort)0xAA91;
+    }
+
+    public override void ReadFromBytes (byte[] vBytes) {
+        TransBytes (out Score, vBytes, NetDataType.DWORD);
+        TransBytes (out ScoreHighest, vBytes, NetDataType.DWORD);
+        TransBytes (out Energy, vBytes, NetDataType.WORD);
+        TransBytes (out EnergyTime, vBytes, NetDataType.DWORD);
+        TransBytes (out LVAwardMax, vBytes, NetDataType.DWORD);
+        TransBytes (out LVAwardState, vBytes, NetDataType.DWORD);
+        TransBytes (out BillAwardState, vBytes, NetDataType.BYTE);
+        TransBytes (out UseItemLen, vBytes, NetDataType.BYTE);
+        TransBytes (out UseItemCntList, vBytes, NetDataType.WORD, UseItemLen);
+        TransBytes (out GridDataLen, vBytes, NetDataType.BYTE);
+        TransBytes (out GridData, vBytes, NetDataType.Chars, GridDataLen);
+        TransBytes (out OPType, vBytes, NetDataType.BYTE);
+        TransBytes (out MoveCount, vBytes, NetDataType.BYTE);
+        MoveList = new tagSCActLianqiTileMove[MoveCount];
+        for (int i = 0; i < MoveCount; i ++) {
+            MoveList[i] = new tagSCActLianqiTileMove();
+            TransBytes (out MoveList[i].Row, vBytes, NetDataType.BYTE);
+            TransBytes (out MoveList[i].Col, vBytes, NetDataType.BYTE);
+            TransBytes (out MoveList[i].ToRow, vBytes, NetDataType.BYTE);
+            TransBytes (out MoveList[i].ToCol, vBytes, NetDataType.BYTE);
+        }
+    }
+
+    public class tagSCActLianqiTileMove {
+        public byte Row;        // 琛岋紝0涓虹1琛�
+        public byte Col;        // 鍒楋紝0涓虹1鍒�
+        public byte ToRow;        // 绉诲姩鍒扮洰鏍囪
+        public byte ToCol;        // 绉诲姩鍒扮洰鏍囧垪
+    }
+
+}
diff --git a/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs.meta b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs.meta
new file mode 100644
index 0000000..80a1d67
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d47f7cbada3f84b48b8a6ab37cf094ef
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Main.cs b/Main/Main.cs
index 2d87679..01f1ef1 100644
--- a/Main/Main.cs
+++ b/Main/Main.cs
@@ -126,6 +126,7 @@
         managers.Add(HeroSkinFlashSaleManager.Instance);
         managers.Add(GuildAtkDefBatManager.Instance);
         managers.Add(HeroTrialManager.Instance);
+        managers.Add(LianQiManager.Instance);
 
         foreach (var manager in managers)
         {
diff --git a/Main/System/BattlePass/BattlePassManager.cs b/Main/System/BattlePass/BattlePassManager.cs
index d4f3745..b5a265c 100644
--- a/Main/System/BattlePass/BattlePassManager.cs
+++ b/Main/System/BattlePass/BattlePassManager.cs
@@ -375,7 +375,10 @@
                     //閫氬叧
                     return layerNum * 100 + levelNum;
                 }
-
+            case BattlePassType.LianQi:
+                {
+                    return (int)battlePassData.value1;
+                }
 
         }
 
@@ -422,4 +425,5 @@
     Week = 6,    //鍛ㄦ垬浠�
     BetterMainLine = 7,    //楂樼骇鍏冲崱鍩洪噾
     DJG = 8,    // 瀹氬啗闃�
+    LianQi = 9,    // 鐐煎櫒澶т細
 }
\ No newline at end of file
diff --git a/Main/System/LianQi.meta b/Main/System/LianQi.meta
new file mode 100644
index 0000000..e672dbb
--- /dev/null
+++ b/Main/System/LianQi.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1319eea20bd178749b0fe96b53fe6909
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/DragSelectComponentEx.cs b/Main/System/LianQi/DragSelectComponentEx.cs
new file mode 100644
index 0000000..1cbc754
--- /dev/null
+++ b/Main/System/LianQi/DragSelectComponentEx.cs
@@ -0,0 +1,99 @@
+锘縰sing System;
+using UnityEngine;
+using UnityEngine.EventSystems;
+using System.Collections.Generic;
+
+[DisallowMultipleComponent]
+public class DragSelectComponentEx : MonoBehaviour
+{
+    [SerializeField] float m_Sensitive = 10f;
+    [SerializeField] GameObject m_TargetObject;
+
+    public event Action<int> onDragComplete;
+
+    private bool m_StartDrag = false;
+
+    Vector3 start_position = Vector3.zero;
+    bool isArea = false;
+
+    private void LateUpdate()
+    {
+        if (Input.touchCount > 1)
+        {
+            m_StartDrag = false;
+            return;
+        }
+        if (Input.GetMouseButtonDown(0))
+        {
+            m_StartDrag = true;
+            start_position = Input.mousePosition;
+            isArea = RectTransformUtility.RectangleContainsScreenPoint(this.transform as RectTransform, start_position, CameraManager.uiCamera);
+            if (!IsOverTarget())
+            {
+                m_StartDrag = false;
+                return;
+            }
+        }
+        else if (Input.GetMouseButtonUp(0) && m_StartDrag)
+        {
+            var delta = Input.mousePosition - start_position;
+            m_StartDrag = false;
+
+            // 鍒ゆ柇婊戝姩鏂瑰悜
+            if (Mathf.Abs(delta.x) >= m_Sensitive || Mathf.Abs(delta.y) >= m_Sensitive)
+            {
+                int direction = 0;
+
+                // 姘村钩鏂瑰悜浼樺厛
+                if (Mathf.Abs(delta.x) > Mathf.Abs(delta.y))
+                {
+                    direction = delta.x > 0 ? 1 : -1; // 1 琛ㄧず鍚戝彸婊戝姩锛�-1 琛ㄧず鍚戝乏婊戝姩
+                }
+                // 鍨傜洿鏂瑰悜浼樺厛
+                else
+                {
+                    direction = delta.y > 0 ? 2 : -2; // 2 琛ㄧず鍚戜笂婊戝姩锛�-2 琛ㄧず鍚戜笅婊戝姩
+                }
+
+                // 鏂板鐩爣瀵硅薄妫�娴嬫潯浠�
+                if (onDragComplete != null && isArea)
+                {
+                    onDragComplete(direction);
+                }
+            }
+        }
+    }
+
+    // 鏂板鐩爣妫�娴嬫柟娉�
+    private bool IsOverTarget()
+    {
+        if (m_TargetObject == null) return true;
+
+        PointerEventData eventData = new PointerEventData(EventSystem.current)
+        {
+            position = Input.mousePosition
+        };
+
+        List<RaycastResult> results = new List<RaycastResult>();
+        EventSystem.current.RaycastAll(eventData, results);
+
+        if (results.Count > 0)
+        {
+            RaycastResult result = results[0];
+            if (result.gameObject == m_TargetObject)
+            {
+                return true;
+            }
+            if (result.gameObject.transform.parent.gameObject == m_TargetObject)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void OnDisable()
+    {
+        m_StartDrag = false;
+    }
+}
\ No newline at end of file
diff --git a/Main/System/LianQi/DragSelectComponentEx.cs.meta b/Main/System/LianQi/DragSelectComponentEx.cs.meta
new file mode 100644
index 0000000..854d562
--- /dev/null
+++ b/Main/System/LianQi/DragSelectComponentEx.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6d85f87c3c94502408fe0bcd595e7278
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiActLevel1.cs b/Main/System/LianQi/LianQiActLevel1.cs
new file mode 100644
index 0000000..96aaed2
--- /dev/null
+++ b/Main/System/LianQi/LianQiActLevel1.cs
@@ -0,0 +1,7 @@
+using UnityEngine;
+
+public class LianQiActLevel1 : MonoBehaviour
+{
+    public ImageEx imgItem;
+    public TextEx txtLv;
+}
\ No newline at end of file
diff --git a/Main/System/LianQi/LianQiActLevel1.cs.meta b/Main/System/LianQi/LianQiActLevel1.cs.meta
new file mode 100644
index 0000000..464323f
--- /dev/null
+++ b/Main/System/LianQi/LianQiActLevel1.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 084cabb1f52e0d04f8b140b81bbe0e35
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiActLevel2.cs b/Main/System/LianQi/LianQiActLevel2.cs
new file mode 100644
index 0000000..db2ad2a
--- /dev/null
+++ b/Main/System/LianQi/LianQiActLevel2.cs
@@ -0,0 +1,14 @@
+using UnityEngine;
+
+public class LianQiActLevel2 : MonoBehaviour
+{
+    public int row;
+    public int col;
+    public ImageEx imgItem;
+    public UIEffectPlayer synUIEffect;
+    public UIEffectPlayer hammerUIEffect;
+    public bool isChooseShow = false;
+    public ImageEx imgChoose;
+    public ButtonEx btnChoose;
+    public TextEx txtLv;
+}
diff --git a/Main/System/LianQi/LianQiActLevel2.cs.meta b/Main/System/LianQi/LianQiActLevel2.cs.meta
new file mode 100644
index 0000000..528be90
--- /dev/null
+++ b/Main/System/LianQi/LianQiActLevel2.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 46aa030177529cf46864e0292c216f37
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiAwardCell.cs b/Main/System/LianQi/LianQiAwardCell.cs
new file mode 100644
index 0000000..bc3a20c
--- /dev/null
+++ b/Main/System/LianQi/LianQiAwardCell.cs
@@ -0,0 +1,75 @@
+锘縰sing System.Collections.Generic;
+
+using UnityEngine;
+using Cysharp.Threading.Tasks;
+
+public class LianQiAwardCell : CellView
+{
+    [SerializeField] ImageEx imgCorrespondItem;
+    [SerializeField] ButtonEx btnCorrespondItem;
+    [SerializeField] ImageEx imgProgressBar;
+    [SerializeField] ImageEx imgHaveBG;
+    [SerializeField] TextEx txtItemCount;
+    [SerializeField] Transform transPoint;
+    [SerializeField] Transform transCanHave;
+    LianQiManager M => LianQiManager.Instance;
+
+    public void Display(int index)
+    {
+        Dictionary<int, List<Item>> correspondingRewards = M.correspondingRewards;
+        if (correspondingRewards == null)
+            return;
+
+        int[] keysArr = new int[correspondingRewards.Count];
+        int _idx = 0;
+        foreach (int key in correspondingRewards.Keys)
+        {
+            keysArr[_idx] = key;
+            _idx++;
+        }
+        if (index < 0 || keysArr.Length <= index)
+            return;
+
+        int awardLv = keysArr[index];
+
+        HAA91_tagSCActLianqiPlayerInfo info = M.nowPlayerInfo;
+        if (info == null)
+            return;
+
+        LianQiCompositeItemInfo itemInfo;
+        if (!M.TryGetCompositeItemInfoByAwardLv(awardLv, out itemInfo) || itemInfo == null)
+            return;
+
+        bool isItemCountShow = awardLv >= M.MaxItemLv;
+        txtItemCount.SetActive(isItemCountShow);
+        if (isItemCountShow)
+        {
+            int itemCount = awardLv - M.MaxItemLv + 1;
+            txtItemCount.text = StringUtility.Concat("X", itemCount.ToString());
+        }
+
+        //0 涓嶅彲棰嗗彇 1 鍙鍙�  2 宸查鍙�
+        int state = M.GetAwardState(awardLv, (int)info.LVAwardState, (int)info.LVAwardMax);
+
+        imgHaveBG.SetActive(state == 2);
+        imgProgressBar.SetActive(state != 0);
+        imgCorrespondItem.SetSprite(itemInfo.Icon);
+        imgCorrespondItem.SetNativeSize();
+        imgCorrespondItem.gray = state == 2;
+        transCanHave.SetActive(state == 1);
+        btnCorrespondItem.SetListener(() =>
+        {
+            if (state == 1)
+            {
+                M.ClaimReward();
+            }
+            else
+            {
+                LianQiSmallTipWin.worldPos = transPoint.position;
+                LianQiSmallTipWin.showAwardLv = awardLv;
+                LianQiSmallTipWin.isDownShow = true;
+                UIManager.Instance.OpenWindow<LianQiSmallTipWin>();
+            }
+        });
+    }
+}
diff --git a/Main/System/LianQi/LianQiAwardCell.cs.meta b/Main/System/LianQi/LianQiAwardCell.cs.meta
new file mode 100644
index 0000000..e23a344
--- /dev/null
+++ b/Main/System/LianQi/LianQiAwardCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b2a508bea89d8fe4681cca10aeea85f9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiAwardWin.cs b/Main/System/LianQi/LianQiAwardWin.cs
new file mode 100644
index 0000000..7b3a4a0
--- /dev/null
+++ b/Main/System/LianQi/LianQiAwardWin.cs
@@ -0,0 +1,174 @@
+锘縰sing System.Collections.Generic;
+using UnityEngine;
+
+public class LianQiAwardWin : UIBase
+{
+    [SerializeField] ScrollerController awardScroller;
+    [HideInInspector] public string valueFormat = "{0}";
+    [SerializeField] TextEx myScore;//璇█琛細
+    [SerializeField] TextEx myRank;
+    [SerializeField] ScrollerController myAwardScroller;
+    [SerializeField] ButtonEx getButton;
+    [SerializeField] ImageEx getImage;
+    [SerializeField] ImageEx redImage;
+    LianQiManager manager => LianQiManager.Instance;
+    bool hasRankRefresh = false;
+
+    protected override void InitComponent()
+    {
+        base.InitComponent();
+        getButton.SetListener(OnClickGetButton);
+    }
+
+    protected override void OnPreOpen()
+    {
+        base.OnPreOpen();
+        hasRankRefresh = false;
+        RankModel.Instance.onRankRefresh += OnRankRefresh;
+        manager.OnLianqiPlayerInfoAction += OnPlayerInfoRefresh;
+        manager.OnLianqiPhaseChanged += OnPhaseChanged;
+        awardScroller.OnRefreshCell += OnRefreshAwardCell;
+        myAwardScroller.OnRefreshCell += OnRefreshMyAwardCell;
+        RankModel.Instance.ResetQueryParam();
+        RankModel.Instance.QueryRankByPage(manager.sendRankType, watchID: (int)PlayerDatas.Instance.baseData.PlayerID, forceQuery: true, groupValue1: manager.groupValue1, crossServerID: manager.crossServerID);
+        Display();
+    }
+
+    protected override void OnPreClose()
+    {
+        base.OnPreClose();
+        RankModel.Instance.onRankRefresh -= OnRankRefresh;
+        manager.OnLianqiPlayerInfoAction -= OnPlayerInfoRefresh;
+        manager.OnLianqiPhaseChanged -= OnPhaseChanged;
+        awardScroller.OnRefreshCell -= OnRefreshAwardCell;
+        myAwardScroller.OnRefreshCell -= OnRefreshMyAwardCell;
+        TryClearEntryRedpointByNoAward();
+    }
+
+    void OnRankRefresh(int type)
+    {
+        if (type != manager.sendRankType) return;
+        hasRankRefresh = true;
+        RankRefresh();
+    }
+
+    void RankRefresh()
+    {
+        DisplayMyRank();
+    }
+
+    private void OnPlayerInfoRefresh()
+    {
+        DisplayMyRank();
+    }
+
+    private void OnPhaseChanged(int state)
+    {
+        DisplayMyRank();
+    }
+
+    public void Display()
+    {
+        DisplayMyRank();
+        CreateAwardScroller();
+    }
+
+    private void DisplayMyRank()
+    {
+        RankData rankData = null;
+        ActBillboardAwardConfig config = null;
+        bool hasAward = hasRankRefresh && manager.TryGetMyBillAwardConfig(out rankData, out config);
+        uint score = manager.nowPlayerInfo != null ? manager.nowPlayerInfo.ScoreHighest : 0;
+
+        if (rankData == null)
+        {
+            myRank.text = Language.Get("L1045");
+        }
+        else
+        {
+            myRank.text = rankData.rank.ToString();
+        }
+        myScore.text = Language.Get("LianQi14", string.Format(valueFormat, UIHelper.ReplaceLargeNum(score)));
+
+        SetGetButtonState(hasRankRefresh && manager.CanHaveBillAward());
+        if (hasAward && config != null)
+        {
+            myAwardScroller.SetActive(true);
+            CreateMyAwardScroller(config.AwardItemList);
+        }
+        else
+        {
+            myAwardScroller.Refresh();
+            myAwardScroller.Restart();
+            myAwardScroller.SetActive(false);
+        }
+    }
+
+    int billTempID;
+    private void CreateAwardScroller()
+    {
+        billTempID = manager.billTempID;
+        var list = ActBillboardAwardConfig.GetRankASortList(billTempID);
+        if (list == null) return;
+
+        awardScroller.Refresh();
+        for (int i = 0; i < list.Count; i++)
+        {
+            awardScroller.AddCell(ScrollerDataType.Header, list[i]);
+        }
+        awardScroller.Restart();
+    }
+
+    private void OnRefreshAwardCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<LianQiRankAwardCell>();
+        _cell?.Display(cell.index, billTempID);
+    }
+
+    private void CreateMyAwardScroller(int[][] awardItemList)
+    {
+        myAwardScroller.Refresh();
+        for (int i = 0; i < awardItemList.Length; i++)
+        {
+            CellInfo info = new CellInfo(awardItemList[i][0], awardItemList[i][1], null);
+            myAwardScroller.AddCell(ScrollerDataType.Header, i, info);
+        }
+        myAwardScroller.Restart();
+    }
+
+    private void OnRefreshMyAwardCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<LianQiRankAwardItemCell>();
+        _cell?.Display(cell);
+    }
+
+    private void OnClickGetButton()
+    {
+        if (!hasRankRefresh || !manager.CanHaveBillAward())
+        {
+            SetGetButtonState(false);
+            return;
+        }
+        SetGetButtonState(false);
+        manager.SendHaveBillAward();
+    }
+
+    private void SetGetButtonState(bool canGet)
+    {
+        getButton.interactable = canGet;
+        getImage.gray = !canGet;
+        redImage.SetActive(canGet);
+    }
+
+    private void TryClearEntryRedpointByNoAward()
+    {
+        if (!hasRankRefresh || manager.IsBillAwardReceived())
+            return;
+        RankData rankData;
+        ActBillboardAwardConfig config;
+        if (!manager.TryGetMyBillAwardConfig(out rankData, out config))
+        {
+            manager.ClearBillAwardEntryRedpointByNoAward();
+        }
+    }
+}
diff --git a/Main/System/LianQi/LianQiAwardWin.cs.meta b/Main/System/LianQi/LianQiAwardWin.cs.meta
new file mode 100644
index 0000000..29e340c
--- /dev/null
+++ b/Main/System/LianQi/LianQiAwardWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7c56c8195deb41048bf513812bb2d4a6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiCoolTime.cs b/Main/System/LianQi/LianQiCoolTime.cs
new file mode 100644
index 0000000..1bb2ea6
--- /dev/null
+++ b/Main/System/LianQi/LianQiCoolTime.cs
@@ -0,0 +1,70 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class LianQiCoolTime : MonoBehaviour
+{
+    [SerializeField] Text m_Time;
+    [SerializeField] Image m_bg;
+    [SerializeField] Text title;
+
+    private void OnEnable()
+    {
+        GlobalTimeEvent.Instance.secondEvent += RefreshSecond;
+        RefreshSecond();
+    }
+
+    private void OnDisable()
+    {
+        GlobalTimeEvent.Instance.secondEvent -= RefreshSecond;
+    }
+
+    private void RefreshSecond()
+    {
+        bool showCountdown = IsInEnterCountdownTime(out OperationBase operation);
+        bool showAwardText = operation != null && !showCountdown && LianQiManager.Instance.IsInAwardPhase;
+        bool showTime = showCountdown || showAwardText;
+
+        m_bg.SetActive(showTime);
+        m_Time.SetActive(showTime);
+        title.SetActive(!showTime);
+
+        if (showCountdown)
+        {
+            int seconds = Mathf.Max(0, GetOperationSurplusTime(LianQiManager.operaType));
+            m_Time.text = StringUtility.Concat("<color=#8DDC11FF>", TimeUtility.SecondsToHMS(seconds), "</color>");
+        }
+        else if (showAwardText)
+        {
+            m_Time.text = UIHelper.AppendColor(TextColType.Red, Language.Get("LianQi15"));
+        }
+    }
+
+    private bool IsInEnterCountdownTime(out OperationBase operation)
+    {
+        operation = null;
+        if (!OperationTimeHepler.Instance.TryGetOperationTime(LianQiManager.operaType, out operation))
+            return false;
+
+        var manager = LianQiManager.Instance;
+        var now = TimeUtility.CrossServerNow;
+        var startTime = operation.startDate
+            .AddDays(manager.enterCountdownStartDay - 1)
+            .AddSeconds(manager.enterCountdownStartHour * 60 * 60 + manager.enterCountdownStartMinute * 60);
+        var endTime = operation.startDate
+            .AddDays(manager.enterCountdownEndDay - 1)
+            .AddSeconds(manager.enterCountdownEndHour * 60 * 60 + manager.enterCountdownEndMinute * 60)
+            .AddMinutes(1);
+
+        return now >= startTime && now < endTime;
+    }
+
+    public int GetOperationSurplusTime(OperationType type)
+    {
+        if (OperationTimeHepler.Instance.TryGetOperationTime(type, out OperationBase operation))
+        {
+            return operation.GetSurplusTime(TimeUtility.CrossServerNow);
+        }
+        return 0;
+    }
+}
+
diff --git a/Main/System/LianQi/LianQiCoolTime.cs.meta b/Main/System/LianQi/LianQiCoolTime.cs.meta
new file mode 100644
index 0000000..1fad723
--- /dev/null
+++ b/Main/System/LianQi/LianQiCoolTime.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 23cbd852d3b19ad4ea7625afd99aa05d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiGiftCell.cs b/Main/System/LianQi/LianQiGiftCell.cs
new file mode 100644
index 0000000..3bb69c0
--- /dev/null
+++ b/Main/System/LianQi/LianQiGiftCell.cs
@@ -0,0 +1,228 @@
+using System.Collections.Generic;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+
+public class LianQiGiftCell : MonoBehaviour
+{
+    [SerializeField] ImageEx vipImage;
+    [SerializeField] TextEx vipText;
+    [SerializeField] OutlineEx vipTextOutline;
+    [SerializeField] ImageEx rateImage;
+    [SerializeField] TextEx rateText;
+
+    [SerializeField] ItemCell[] itemCells;
+    [SerializeField] TextEx titleText;
+    [SerializeField] ButtonEx buyButton;
+    [SerializeField] ImageEx buyImage;
+
+    [SerializeField] TextEx buyText;
+    [SerializeField] TextEx buyText1;
+    [SerializeField] ImageEx moneyIconImage;
+    [SerializeField] ImageEx adImage;
+    [SerializeField] TextEx limitCountText;
+    [SerializeField] ImageEx redImage;
+    LianQiManager manager => LianQiManager.Instance;
+    StoreModel storeModel => StoreModel.Instance;
+
+    public void Display(int index, List<LianQiGiftItem> giftItems)
+    {
+        if (giftItems.IsNullOrEmpty() || index < 0 || index >= giftItems.Count) return;
+
+        LianQiGiftItem item = giftItems[index];
+        if (item.type == 0)
+        {
+            DisplayStore(item.id);
+            return;
+        }
+
+        if (item.type == 1)
+        {
+            DisplayAD(item.id);
+            return;
+        }
+
+
+        DisplayCTG(item.id);
+    }
+
+    private void DisplayCTG(int ctgId)
+    {
+        redImage.SetActive(false);
+        buyText.SetActive(true);
+        buyText1.SetActive(false);
+        moneyIconImage.SetActive(false);
+        adImage.SetActive(false);
+        rateImage.SetActive(true);
+
+        if (!RechargeManager.Instance.TryGetOrderInfo(ctgId, out var orderConfig)) return;
+        if (!RechargeManager.Instance.TryGetRechargeCount(ctgId, out var rechargeCount)) return;
+        if (!CTGConfig.HasKey(ctgId)) return;
+        if (!RechargeManager.Instance.TryGetRechargeItem(ctgId, out var rechargeItemList)) return;
+
+        CTGConfig config = CTGConfig.Get(ctgId);
+        vipImage.SetActive(config.VipLevel > 0);
+        if (config.VipLevel > 0)
+        {
+            vipImage.SetSprite($"VipLevel{config.VipLevel}");
+            vipText.text = Language.Get($"VipLevelInfo{config.VipLevel}");
+            vipText.color = InvestModel.Instance.GetTextColor(config.VipLevel);
+            vipTextOutline.OutlineColor = InvestModel.Instance.GetOutlineColor(config.VipLevel);
+        }
+
+        rateImage.SetActive(true);
+        rateText.text = Language.Get("DailySpecials07", config.Percentage);
+
+        bool isCanBuy = manager.IsCanBuy(ctgId, out bool isNoLimitBuy, out int nowBuyCnt, out int maxBuyCnt);
+        titleText.text = config.Title;
+        buyImage.SetSprite(isCanBuy ? "DailySpecialsBuy1" : "DailySpecialsBuy2");
+        buyText.text = !isCanBuy ? Language.Get("storename11") : Language.Get("PayMoneyNum", UIHelper.GetMoneyFormat(orderConfig.PayRMBNumOnSale));
+        limitCountText.SetActive(!isNoLimitBuy && maxBuyCnt > 0);
+        if (!isNoLimitBuy && maxBuyCnt > 0)
+        {
+            limitCountText.text = Language.Get("HeroDebut39", UIHelper.AppendColor(nowBuyCnt >= maxBuyCnt ? TextColType.Red : TextColType.LightGreen, Mathf.Max(0, maxBuyCnt - nowBuyCnt).ToString()));
+        }
+        buyButton.interactable = isCanBuy;
+        buyButton.SetListener(() =>
+        {
+            if (config.VipLevel > 0 && !FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.PrivilegeCard))
+            {
+                SysNotifyMgr.Instance.ShowTip("MinggeAuto8");
+                return;
+            }
+            if (config.VipLevel == 1 && !InvestModel.Instance.IsInvested(InvestModel.monthCardType))
+            {
+                SysNotifyMgr.Instance.ShowTip("MinggeAuto5");
+                UIManager.Instance.OpenWindow<PrivilegeCardWin>();
+                return;
+            }
+            if (config.VipLevel == 2 && !InvestModel.Instance.IsInvested(InvestModel.foreverCardType))
+            {
+                SysNotifyMgr.Instance.ShowTip("MinggeAuto7");
+                UIManager.Instance.OpenWindow<PrivilegeCardWin>();
+                return;
+            }
+            RechargeManager.Instance.CTG(ctgId);
+        });
+
+        for (int i = 0; i < itemCells.Length; i++)
+        {
+            var itemBaisc = itemCells[i];
+            if (i < rechargeItemList.Count)
+            {
+                var itemInfo = rechargeItemList[i];
+                itemBaisc.SetActive(true);
+                itemBaisc.Init(new ItemCellModel((int)itemInfo.id, false, itemInfo.countEx));
+                itemBaisc.button.AddListener(() =>
+                {
+                    ItemTipUtility.Show((int)itemInfo.id);
+                });
+            }
+            else
+            {
+                itemBaisc.SetActive(false);
+            }
+        }
+    }
+
+    private void DisplayStore(int id)
+    {
+        rateImage.SetActive(false);
+        vipImage.SetActive(false);
+        if (!StoreConfig.HasKey(id)) return;
+
+        StoreConfig storeConfig = StoreConfig.Get(id);
+        int remainNum;
+        storeModel.TryGetIsSellOut(storeConfig, out remainNum);
+
+        bool isFree = manager.IsFreeShop(id);
+        titleText.text = storeConfig.Name;
+
+        limitCountText.SetActive(!isFree);
+        limitCountText.text = Language.Get("HeroDebut39", UIHelper.AppendColor(remainNum == 0 ? TextColType.Red : TextColType.LightGreen, Mathf.Max(0, remainNum).ToString(), true));
+
+        bool isCanBuy = manager.IsNoSellOutShopID(id);
+
+        redImage.SetActive(isFree && isCanBuy);
+        buyText.SetActive(isFree || !isCanBuy);
+        buyText.text = isFree ? Language.Get("L1127") : Language.Get("storename11");
+        buyText1.SetActive(!isFree && isCanBuy);
+        buyText1.text = UIHelper.GetMoneyFormat(storeConfig.MoneyNum);
+        adImage.SetActive(false);
+        moneyIconImage.SetActive(!isFree && isCanBuy);
+        moneyIconImage.SetIconWithMoneyType(1);
+        buyImage.SetSprite(isCanBuy ? "DailySpecialsBuy1" : "DailySpecialsBuy2");
+        buyButton.interactable = isCanBuy;
+        buyButton.SetListener(() =>
+        {
+            storeModel.SendBuyShopItemWithPopCheck(storeConfig, 1, (int)BuyStoreItemCheckType.ActGift);
+        });
+
+        var items = storeModel.GetShopItemlistByIndex(storeConfig);
+        for (int i = 0; i < itemCells.Length; i++)
+        {
+            var itemBaisc = itemCells[i];
+            if (i < items.Count)
+            {
+                var itemInfo = items[i];
+                itemBaisc.SetActive(true);
+                itemBaisc.Init(new ItemCellModel(itemInfo.itemId, false, itemInfo.count));
+                itemBaisc.button.AddListener(() =>
+                {
+                    ItemTipUtility.Show(itemInfo.itemId);
+                });
+            }
+            else
+            {
+                itemBaisc.SetActive(false);
+            }
+        }
+    }
+
+    private void DisplayAD(int ADID)
+    {
+        rateImage.SetActive(false);
+        vipImage.SetActive(false);
+
+        ADAwardConfig config = ADAwardConfig.Get(ADID);
+        if (config == null) return;
+        bool isCanBuy = manager.IsAdAwardCanReceive(ADID);
+        int receivedCnt = AdsManager.Instance.GetADCntByADID(ADID);
+
+        titleText.text = config.Name;
+        limitCountText.SetActive(true);
+        limitCountText.text = Language.Get("HeroDebut39", UIHelper.AppendColor(!isCanBuy ? TextColType.Red : TextColType.LightGreen, Mathf.Max(config.ADCntMax - receivedCnt, 0).ToString()));
+        redImage.SetActive(isCanBuy);
+        buyText.SetActive(false);
+        buyText1.SetActive(true);
+        buyText1.text = Language.Get("L1127");
+        moneyIconImage.SetActive(false);
+        adImage.SetActive(true);
+        adImage.gray = !isCanBuy;
+        buyImage.SetSprite(isCanBuy ? "DailySpecialsBuy1" : "DailySpecialsBuy2");
+        buyButton.interactable = isCanBuy;
+        buyButton.SetListener(() =>
+        {
+            AdsManager.Instance.GetAdsAward(ADID);
+        });
+
+        var items = config.ADAwardItemList;
+        for (int i = 0; i < itemCells.Length; i++)
+        {
+            var itemBaisc = itemCells[i];
+            if (i < items.Length)
+            {
+                var itemInfo = items[i];
+                itemBaisc.SetActive(true);
+                itemBaisc.Init(new ItemCellModel(itemInfo[0], false, itemInfo[1]));
+                itemBaisc.button.AddListener(() =>
+                {
+                    ItemTipUtility.Show(itemInfo[0]);
+                });
+            }
+            else
+            {
+                itemBaisc.SetActive(false);
+            }
+        }
+    }
+}
diff --git a/Main/System/LianQi/LianQiGiftCell.cs.meta b/Main/System/LianQi/LianQiGiftCell.cs.meta
new file mode 100644
index 0000000..bc2626b
--- /dev/null
+++ b/Main/System/LianQi/LianQiGiftCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e621e92b191df16418aa8d25c4336fbf
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiGiftWin.cs b/Main/System/LianQi/LianQiGiftWin.cs
new file mode 100644
index 0000000..5e6b3d4
--- /dev/null
+++ b/Main/System/LianQi/LianQiGiftWin.cs
@@ -0,0 +1,74 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class LianQiGiftWin : UIBase
+{
+    [SerializeField] ButtonEx closeButton;
+
+    [SerializeField] ScrollerController scroller;
+    LianQiManager manager => LianQiManager.Instance;
+    StoreModel storeModel => StoreModel.Instance;
+    protected override void InitComponent()
+    {
+        closeButton.SetListener(CloseWindow);
+    }
+
+    protected override void OnPreOpen()
+    {
+        scroller.OnRefreshCell += OnRefreshCell;
+        RechargeManager.Instance.rechargeCountEvent += OnRechargeCountEvent;
+        storeModel.RefreshBuyShopLimitEvent += RefreshBuyShopLimitEvent;
+        AdsManager.Instance.OnAdsInfoListUpdateEvent += OnAdsInfoListUpdateEvent;
+
+        CreateGiftScroller();
+    }
+
+    protected override void OnPreClose()
+    {
+        scroller.OnRefreshCell -= OnRefreshCell;
+        RechargeManager.Instance.rechargeCountEvent -= OnRechargeCountEvent;
+        storeModel.RefreshBuyShopLimitEvent -= RefreshBuyShopLimitEvent;
+        AdsManager.Instance.OnAdsInfoListUpdateEvent -= OnAdsInfoListUpdateEvent;
+    }
+
+    private void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<LianQiGiftCell>();
+        _cell?.Display(cell.index, giftItems);
+    }
+
+    private void OnRechargeCountEvent(int obj)
+    {
+        RefreshAll();
+    }
+
+    private void RefreshBuyShopLimitEvent()
+    {
+        RefreshAll();
+    }
+
+    private void OnAdsInfoListUpdateEvent(int arg1, int arg2, int arg3)
+    {
+        RefreshAll();
+    }
+
+    List<LianQiGiftItem> giftItems;
+    private void CreateGiftScroller()
+    {
+        giftItems = manager.GetGiftItemList(true);
+        scroller.Refresh();
+        if (!giftItems.IsNullOrEmpty())
+        {
+            for (int i = 0; i < giftItems.Count; i++)
+            {
+                scroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        scroller.Restart();
+    }
+
+    void RefreshAll()
+    {
+        scroller.m_Scorller.RefreshActiveCellViews();
+    }
+}
diff --git a/Main/System/LianQi/LianQiGiftWin.cs.meta b/Main/System/LianQi/LianQiGiftWin.cs.meta
new file mode 100644
index 0000000..d63905f
--- /dev/null
+++ b/Main/System/LianQi/LianQiGiftWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 668f585a6fcbafd47961af6a84bc558f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiItemInfoDoubleWin.cs b/Main/System/LianQi/LianQiItemInfoDoubleWin.cs
new file mode 100644
index 0000000..a5bffb4
--- /dev/null
+++ b/Main/System/LianQi/LianQiItemInfoDoubleWin.cs
@@ -0,0 +1,113 @@
+using System;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class LianQiItemInfoDoubleWin : UIBase
+{
+    [SerializeField] ItemCell itemCell;
+    [SerializeField] Text nameText;
+    [SerializeField] Text countText;
+    [SerializeField] Text descText;
+    [SerializeField] SliderPanel sliderPanel;
+    [SerializeField] Text limitText;
+    [SerializeField] Button buyButton;
+
+    LianQiManager M => LianQiManager.Instance;
+    int useCount = 1;
+
+    protected override void InitComponent()
+    {
+        buyButton.AddListener(OnUseClick);
+    }
+
+    protected override void OnPreOpen()
+    {
+        useCount = 1;
+        Display();
+    }
+
+    /// <summary>
+    /// 灞曠ず鐜夐湶(閬撳叿4)鐨勮缁嗕俊鎭笌鏁伴噺閫夋嫨婊戝潡
+    /// </summary>
+    private void Display()
+    {
+        int itemID = M.nowAssistiveItemId;
+        if (!ItemConfig.HasKey(itemID)) return;
+        var itemConfig = ItemConfig.Get(itemID);
+        long hasCount = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
+
+        itemCell.Init(new ItemCellModel(itemID, false, hasCount));
+        itemCell.countText.SetActive(true);
+        itemCell.countText.text = "1";
+        countText.text = Language.Get("storename12", hasCount);
+        nameText.text = itemConfig.ItemName;
+        descText.text = itemConfig.Description;
+
+        if (!M.limitUsesToolCntDict.TryGetValue(itemID, out int limitCnt)) return;
+        int useItemCnt = M.GetUseItemCnt(M.nowPlayerInfo.UseItemCntList, M.nowAssistiveItemNum);
+        int hasCnt = hasCount > limitCnt ? limitCnt : (int)hasCount;
+        int nowResCnt = Mathf.Max(0, limitCnt - useItemCnt); // 鏈疆鍓╀綑娆℃暟
+        int realCnt = nowResCnt > hasCnt ? hasCnt : nowResCnt;
+
+        limitText.text = Language.Get("LianQi10", Mathf.Max(0, limitCnt - useItemCnt), limitCnt);
+        useCount = 1;
+        // 鍒濆鍖栨粦鍧楋細鏁伴噺鍙樺寲鏃舵洿鏂� useCount
+        sliderPanel.Init((value) => { useCount = value; }, realCnt, 1);
+    }
+
+    /// <summary>
+    /// 浣跨敤鎸夐挳鍥炶皟锛氭鏌ユ暟閲忎笌娆℃暟锛岀‘璁ゅ悗鍙戦��
+    /// </summary>
+    private void OnUseClick()
+    {
+        int itemID = M.nowAssistiveItemId;
+        int num = M.nowAssistiveItemNum;
+        if (!ItemConfig.HasKey(itemID)) return;
+        var itemConfig = ItemConfig.Get(itemID);
+
+        // 閬撳叿鏁伴噺涓嶈冻
+        if (PackManager.Instance.GetItemCountByID(PackType.Item, itemID) <= 0)
+        {
+            SysNotifyMgr.Instance.ShowTip("LianQiAct02");
+            return;
+        }
+
+        // 璇ラ亾鍏疯灞�宸茶揪浣跨敤娆℃暟涓婇檺
+        if (!M.limitUsesToolCntDict.TryGetValue(itemID, out int cnt)) return;
+        int useItemCnt = M.GetUseItemCnt(M.nowPlayerInfo.UseItemCntList, num);
+        if (useItemCnt >= cnt)
+        {
+            SysNotifyMgr.Instance.ShowTip("LianQiAct03");
+            return;
+        }
+
+        if (M.isSkip4)
+        {
+            if (M.IsCanSendUseItem())
+                M.SendUseOP(useCount, 3, 0, 0);
+            if (UIManager.Instance.IsOpened<LianQiItemInfoDoubleWin>())
+                UIManager.Instance.CloseWindow<LianQiItemInfoDoubleWin>();
+        }
+        else
+        {
+            ConfirmCancel.ToggleConfirmCancelEx(Language.Get("Mail101"), Language.Get("LianQi27", useCount, itemConfig.ItemName), Language.Get("ConfirmCancel102"), OnConfirm4, OnConfirm4, M.isSkip4);
+        }
+    }
+
+    private void OnConfirm4(bool isOk, bool isToggle)
+    {
+        M.isSkip4 = isToggle;
+        if (isOk && M.IsCanSendUseItem())
+            M.SendUseOP(useCount, 3, 0, 0);
+        if (UIManager.Instance.IsOpened<LianQiItemInfoDoubleWin>())
+            UIManager.Instance.CloseWindow<LianQiItemInfoDoubleWin>();
+    }
+
+    /// <summary>
+    /// 鍏抽棴鏃堕噸缃亾鍏蜂娇鐢ㄧ姸鎬侊紙鐜夐湶涓嶈繘鍏ラ�夋牸锛屽叧闂嵆缁撴潫閬撳叿妯″紡锛�
+    /// </summary>
+    protected override void OnPreClose()
+    {
+        M.isUseTool = false;
+    }
+}
diff --git a/Main/System/LianQi/LianQiItemInfoDoubleWin.cs.meta b/Main/System/LianQi/LianQiItemInfoDoubleWin.cs.meta
new file mode 100644
index 0000000..c56a106
--- /dev/null
+++ b/Main/System/LianQi/LianQiItemInfoDoubleWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 197d45697dfbe8e49a50290f9ba1af78
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiItemInfoWin.cs b/Main/System/LianQi/LianQiItemInfoWin.cs
new file mode 100644
index 0000000..d930e28
--- /dev/null
+++ b/Main/System/LianQi/LianQiItemInfoWin.cs
@@ -0,0 +1,83 @@
+using System;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class LianQiItemInfoWin : UIBase
+{
+    [SerializeField] ItemCell itemCell;
+    [SerializeField] Text nameText;
+    [SerializeField] Text countText;
+    [SerializeField] Text descText;
+    [SerializeField] Text limitText;
+    [SerializeField] Button buyButton;
+
+    LianQiManager M => LianQiManager.Instance;
+
+    protected override void InitComponent()
+    {
+        buyButton.AddListener(OnUseClick);
+    }
+
+    protected override void OnPreOpen()
+    {
+        // 璇诲彇褰撳墠杈呭姪閬撳叿淇℃伅骞跺睍绀�
+        int itemID = M.nowAssistiveItemId;
+        if (!ItemConfig.HasKey(itemID)) return;
+        var itemConfig = ItemConfig.Get(itemID);
+        long hasCount = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
+        itemCell.Init(new ItemCellModel(itemID, false, hasCount));
+        itemCell.countText.SetActive(true);
+        itemCell.countText.text = "1";
+        countText.text = Language.Get("storename12", hasCount);
+        nameText.text = itemConfig.ItemName;
+        descText.text = itemConfig.Description;
+
+        if (M.limitUsesToolCntDict.TryGetValue(itemID, out int limitCnt))
+        {
+            int useItemCnt = M.GetUseItemCnt(M.nowPlayerInfo.UseItemCntList, M.nowAssistiveItemNum);
+            limitText.text = Language.Get("LianQi10", Mathf.Max(0, limitCnt - useItemCnt), limitCnt);
+        }
+    }
+
+    protected override void OnPreClose()
+    {
+        var M = LianQiManager.Instance;
+        // 浠呭湪鏈繘鍏ラ�夋牸锛堢敤鎴峰彇娑堬級鏃堕��鍑洪亾鍏锋ā寮忥紱宸茬偣"浣跨敤"鍒欎繚鐣欑姸鎬佷緵 LianQiWin 閫夋牸
+        if (!M.isStartChoose)
+        {
+            M.isUseTool = false;
+        }
+    }
+
+    /// <summary>
+    /// 浣跨敤鎸夐挳鍥炶皟锛氭鏌ユ暟閲忎笌娆℃暟锛岃繘鍏ラ�夋牸妯″紡
+    /// </summary>
+    private void OnUseClick()
+    {
+        int itemID = M.nowAssistiveItemId;
+        int num = M.nowAssistiveItemNum;
+        if (!ItemConfig.HasKey(itemID)) return;
+
+        // 閬撳叿鏁伴噺涓嶈冻
+        if (PackManager.Instance.GetItemCountByID(PackType.Item, itemID) <= 0)
+        {
+            SysNotifyMgr.Instance.ShowTip("LianQiAct02");
+            return;
+        }
+
+        // 璇ラ亾鍏疯灞�宸茶揪浣跨敤娆℃暟涓婇檺
+        if (!M.limitUsesToolCntDict.TryGetValue(itemID, out int cnt)) return;
+        int useItemCnt = M.GetUseItemCnt(M.nowPlayerInfo.UseItemCntList, num);
+        if (useItemCnt >= cnt)
+        {
+            SysNotifyMgr.Instance.ShowTip("LianQiAct03");
+            return;
+        }
+
+        // 杩涘叆閫夋牸妯″紡锛堢帀闇�(閬撳叿4)涓嶈蛋閫夋牸锛岀敱 DoubleWin 澶勭悊锛�
+        M.isStartChoose = true;
+        M.isChoose1 = false;
+        M.isChoose2 = false;
+        CloseWindow();
+    }
+}
diff --git a/Main/System/LianQi/LianQiItemInfoWin.cs.meta b/Main/System/LianQi/LianQiItemInfoWin.cs.meta
new file mode 100644
index 0000000..8632fc7
--- /dev/null
+++ b/Main/System/LianQi/LianQiItemInfoWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f3e4c8989dda2f84d9513d1b607b1642
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiManager.cs b/Main/System/LianQi/LianQiManager.cs
new file mode 100644
index 0000000..fa09310
--- /dev/null
+++ b/Main/System/LianQi/LianQiManager.cs
@@ -0,0 +1,1248 @@
+锘縰sing LitJson;
+using System;
+using System.Collections.Generic;
+
+using UnityEngine;
+using Cysharp.Threading.Tasks;
+
+public class LianQiManager : GameSystemManager<LianQiManager>, IOpenServerActivity
+{
+    public const int activityID = (int)OperationType.LianQiCross;
+    public static OperationType operaType = OperationType.LianQiCross;
+    private const int AwardPhaseState = 99;
+    public int ZhanLingType => 9;
+
+    public bool IsOpen => OperationTimeHepler.Instance.SatisfyOpenCondition(operaType);
+    public bool IsAdvance => OperationTimeHepler.Instance.SatisfyAdvanceCondition(operaType);
+    public bool priorityOpen => redPoint.state == RedPointState.Simple;
+    public readonly int actNum = 99;
+
+    public const int regesitCrossType = 221;
+    public readonly int sendRankType = 155;
+    public int loadRankType => sendRankType;
+    public int groupValue1 => operationCrossAct != null ? operationCrossAct.m_GroupValue1 : 0;
+    public int crossServerID => operationCrossAct != null ? operationCrossAct.m_CrossServerID : 0;
+    public int billTempID => operationCrossAct != null && operationCrossAct.actTempConfig != null ? operationCrossAct.actTempConfig.PersonalTempID : 0;
+    public OperationLianQiCross GetOperation() => operationCrossAct;
+
+    public OperationLianQiCross operationCrossAct = null;
+
+    public HAA91_tagSCActLianqiPlayerInfo lastPlayerInfo = null;
+    public HAA91_tagSCActLianqiPlayerInfo nowPlayerInfo = null;
+    public int[][] nowGridData;
+    public int[][] lastGridData;
+
+    public bool isWaitReStart = false;
+    public bool isPause = false;
+    public bool isUseTool = false;
+
+    //1 鐮村ぉ閿� 2 閫犲寲鐏电帀 3 涔濆ぉ闄ㄩ搧 4 鐢橀湶浠欐恫
+    public int nowAssistiveItemNum = 0;
+    public int nowAssistiveItemId;
+
+    public bool isStartChoose = false;
+    public bool isChoose1 = false;
+    public int useRow1;
+    public int useCol1;
+    public bool isChoose2 = false;
+    public int useRow2;
+    public int useCol2;
+    public bool isPlayAssistiveItemAnimation;
+
+    public bool isSkip1 = false;
+    public bool isSkip2 = false;
+    public bool isSkip3 = false;
+    public bool isSkip4 = false;
+
+    public readonly int MaxItemLv = 11; //鏈�澶у悎鎴愮墿绛夌骇
+    public readonly int MaxGridRows = 4;
+    public readonly int MaxGridCols = 4;
+    public readonly int MaxGridCount = 16;
+
+    public int actScore { get; private set; }
+
+    public bool IsJoin => operationCrossAct != null && operationCrossAct.IsInJoinPhase();
+    public bool IsPrepareTime => IsInPublicityPhase;
+    public bool IsResultShowTime => IsInAwardPhase;
+    public bool IsInPublicityPhase => operationCrossAct != null && operationCrossAct.IsInPublicityPhase();
+    public bool IsInAwardPhase => operationCrossAct != null && operationCrossAct.IsInAwardPhase();
+
+    public int SelectRank { get; set; }
+    public int SelectAwardRank { get; set; }
+
+    public int no1PlayerID = 0;
+    public int no1PlayerIDCross = 0;
+
+    PackManager packManager => PackManager.Instance;
+
+    public event Action OnLianqiPlayerInfoAction;
+    public event Action<int> onStateUpdate;
+    public event Action<int> OnLianqiPhaseChanged;
+
+    private int lastPhaseState = -1;
+    private bool hasCheckedNoBillAward = false;
+
+    private void OperationTimeUpdateEvent(OperationType type)
+    {
+        if (type == operaType)
+        {
+            if (UIManager.Instance.IsOpened<LianQiWin>())
+                UIManager.Instance.CloseWindow<LianQiWin>();
+            if (OperationTimeHepler.Instance.TryGetOperationTime(operaType, out var operationBase1))
+            {
+                operationCrossAct = operationBase1 as OperationLianQiCross;
+            }
+            UpdateRedpoint();
+        }
+    }
+
+    private void OperationStartEvent(OperationType type, int state)
+    {
+        if (type == operaType && state == 0)
+        {
+            UpdateRedpoint();
+            onStateUpdate?.Invoke(activityID);
+        }
+    }
+
+    private void OperationEndEvent(OperationType type, int state)
+    {
+        if (type == operaType && state == 0)
+        {
+            if (UIManager.Instance.IsOpened<LianQiWin>())
+                UIManager.Instance.CloseWindow<LianQiWin>();
+            UpdateRedpoint();
+            onStateUpdate?.Invoke(activityID);
+            if (!OperationTimeHepler.Instance.TryGetOperationTime(operaType, out _))
+                operationCrossAct = null;
+        }
+    }
+    private void OperationAdvanceEvent(OperationType type)
+    {
+        if (type == operaType)
+        {
+            UpdateRedpoint();
+            onStateUpdate?.Invoke(activityID);
+        }
+    }
+
+    private void OnSecondEvent()
+    {
+        if (operationCrossAct == null) return;
+        int cur = operationCrossAct.GetCurrentPhaseState();
+        if (cur != lastPhaseState)
+        {
+            bool isEnterAwardPhase = cur == AwardPhaseState && lastPhaseState != AwardPhaseState;
+            lastPhaseState = cur;
+            if (isEnterAwardPhase)
+                hasCheckedNoBillAward = false;
+            UpdateRedpoint();
+            OnLianqiPhaseChanged?.Invoke(cur);
+        }
+    }
+
+    public override void Init()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
+        FuncOpen.Instance.OnFuncStateChangeEvent += OnFuncStateChangeEvent;
+        OperationTimeHepler.Instance.operationTimeUpdateEvent += OperationTimeUpdateEvent;
+        OperationTimeHepler.Instance.operationStartEvent += OperationStartEvent;
+        OperationTimeHepler.Instance.operationEndEvent += OperationEndEvent;
+        OperationTimeHepler.Instance.operationAdvanceEvent += OperationAdvanceEvent;
+        StoreModel.Instance.RefreshBuyShopLimitEvent += RefreshBuyShopLimitEvent;
+        RechargeManager.Instance.rechargeCountEvent += OnRechargeCountEvent;
+        AdsManager.Instance.OnAdsInfoListUpdateEvent += OnAdsInfoListUpdateEvent;
+        BattlePassManager.Instance.BattlePassDataUpdateEvent += OnBattlePassDataUpdate;
+        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
+        OpenServerActivityCenter.Instance.Register(regesitCrossType, this);
+        ParseConfig();
+    }
+
+    public override void Release()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
+        FuncOpen.Instance.OnFuncStateChangeEvent -= OnFuncStateChangeEvent;
+        OperationTimeHepler.Instance.operationTimeUpdateEvent -= OperationTimeUpdateEvent;
+        OperationTimeHepler.Instance.operationStartEvent -= OperationStartEvent;
+        OperationTimeHepler.Instance.operationEndEvent -= OperationEndEvent;
+        OperationTimeHepler.Instance.operationAdvanceEvent -= OperationAdvanceEvent;
+        StoreModel.Instance.RefreshBuyShopLimitEvent -= RefreshBuyShopLimitEvent;
+        RechargeManager.Instance.rechargeCountEvent -= OnRechargeCountEvent;
+        AdsManager.Instance.OnAdsInfoListUpdateEvent -= OnAdsInfoListUpdateEvent;
+
+        BattlePassManager.Instance.BattlePassDataUpdateEvent -= OnBattlePassDataUpdate;
+        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
+    }
+
+    private void OnBeforePlayerDataInitializeEventOnRelogin()
+    {
+        operationCrossAct = null;
+        actScore = 0;
+        no1PlayerID = 0;
+        no1PlayerIDCross = 0;
+        isSkip1 = false;
+        isSkip2 = false;
+        isSkip3 = false;
+        isSkip4 = false;
+        lastPhaseState = -1;
+        hasCheckedNoBillAward = false;
+        ClearGameplayState();
+    }
+
+    private void OnFuncStateChangeEvent(int obj)
+    {
+        if (obj == (int)FuncOpenEnum.LianQi)
+        {
+            UpdateRedpoint();
+        }
+    }
+    private void OnBattlePassDataUpdate(int type)
+    {
+        if (type == ZhanLingType)
+        {
+            UpdateRedpoint();
+            OnZhanLingUpdateEvent?.Invoke();
+        }
+    }
+
+    private void RefreshBuyShopLimitEvent()
+    {
+        UpdateRedpoint();
+    }
+
+    private void OnRechargeCountEvent(int obj)
+    {
+        UpdateRedpoint();
+    }
+
+    private void OnAdsInfoListUpdateEvent(int arg1, int arg2, int arg3)
+    {
+        UpdateRedpoint();
+    }
+
+    public bool IsLianQiOpen()
+    {
+        return FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.LianQi) && IsOpen;
+    }
+    #region 閰嶇疆琛ㄨВ鏋�
+
+    /// <summary>
+    /// 鍚堟垚绛夌骇瀵瑰簲濂栧姳 {"濂栧姳绛夌骇":[[鐗╁搧ID,涓暟], ...], ...}
+    /// </summary>
+    public Dictionary<int, List<Item>> correspondingRewards = new Dictionary<int, List<Item>>();
+
+    /// <summary>
+    /// 闄愬埗鍗曞眬浣跨敤娆℃暟 {"鐗╁搧ID":鍗曞眬浣跨敤娆℃暟, ...}
+    /// </summary>
+    public Dictionary<int, int> limitUsesToolCntDict = new Dictionary<int, int>();
+
+    /// <summary>
+    /// 杈呭姪閬撳叿瀵瑰簲鐗╁搧ID鍒楄〃 [杈呭姪閬撳叿ID1, ...]
+    /// 杈呭姪閬撳叿1 - 纰庡ぉ閿わ紝娑堥櫎浠绘剰浣嶇疆锛屼笖涓嶅噺鍒�
+    /// 杈呭姪閬撳叿2 - 閫犲寲鐏电帀锛屼氦鎹换鎰忎袱涓綅缃�
+    /// 杈呭姪閬撳叿3 - 涔濆ぉ闄ㄩ搧锛屽皢浠绘剰1涓�7闃朵互涓嬭繘闃讹紝骞惰幏寰楃Н鍒嗗強瑙i攣绛夌骇濂栧姳
+    /// 杈呭姪閬撳叿4 - 鐢橀湶浠欐恫锛屼綋鍔涘姞15
+    /// </summary>
+    public int[] assistiveItemArr;
+
+    /// <summary>
+    /// 涔濆ぉ闄ㄩ搧閬撳叿闄愬埗x闃朵互涓�
+    /// </summary>
+    public int jiuTianYunTieLevelLimit;
+
+    /// <summary>
+    /// 鏈�澶т綋鍔涳紝鍒濆浣撳姏榛樿涓烘渶澶т綋鍔涳紝绉诲姩1娆′綋鍔�-1
+    /// </summary>
+    public int maxEnergy;
+
+    /// <summary>
+    /// 鎭㈠1鐐逛綋鍔涙墍闇�鏃堕棿锛岀
+    /// </summary>
+    public int rSecond;
+
+    /// <summary>
+    /// 鍏ュ彛鍊掕鏃舵樉绀哄紑濮嬫椂闂达紝娲诲姩绗琋澶� 鏃� 鍒�
+    /// </summary>
+    public int enterCountdownStartDay;
+    public int enterCountdownStartHour;
+    public int enterCountdownStartMinute;
+
+    /// <summary>
+    /// 鍏ュ彛鍊掕鏃舵樉绀虹粨鏉熸椂闂达紝娲诲姩绗琋澶� 鏃� 鍒�
+    /// </summary>
+    public int enterCountdownEndDay;
+    public int enterCountdownEndHour;
+    public int enterCountdownEndMinute;
+
+    private void ParseConfig()
+    {
+        var config = FuncConfigConfig.Get("LianqiSet");
+        maxEnergy = int.Parse(config.Numerical1);
+        rSecond = int.Parse(config.Numerical2);
+        var rewardArrDict = ConfigParse.ParseIntArray2Dict(config.Numerical3);
+        foreach (var kvp in rewardArrDict)
+        {
+            correspondingRewards[kvp.Key] = new List<Item>();
+            var value = kvp.Value;
+            for (int j = 0; j < value.Length; j++)
+                correspondingRewards[kvp.Key].Add(new Item(value[j][0], value[j][1]));
+        }
+        config = FuncConfigConfig.Get("LianqiUseItem");
+        assistiveItemArr = JsonMapper.ToObject<int[]>(config.Numerical1);
+        limitUsesToolCntDict = ConfigParse.ParseIntDict(config.Numerical2);
+        jiuTianYunTieLevelLimit = int.Parse(config.Numerical3);
+
+        config = FuncConfigConfig.Get("LianqiEnter");
+        var enterCountdownStartTime = config.Numerical1.Split('|');
+        enterCountdownStartDay = int.Parse(enterCountdownStartTime[0]);
+        enterCountdownStartHour = int.Parse(enterCountdownStartTime[1]);
+        enterCountdownStartMinute = int.Parse(enterCountdownStartTime[2]);
+        var enterCountdownEndTime = config.Numerical2.Split('|');
+        enterCountdownEndDay = int.Parse(enterCountdownEndTime[0]);
+        enterCountdownEndHour = int.Parse(enterCountdownEndTime[1]);
+        enterCountdownEndMinute = int.Parse(enterCountdownEndTime[2]);
+    }
+    #endregion
+    #region 绀煎寘
+    public List<LianQiGiftItem> GetGiftItemList(bool isSort = false)
+    {
+        if (operationCrossAct == null || operationCrossAct.actTempConfig == null) return null;
+        var config = operationCrossAct.actTempConfig;
+        List<LianQiGiftItem> res = new List<LianQiGiftItem>();
+
+        if (StoreModel.Instance.storeTypeDict.TryGetValue(config.ActShopType, out var list))
+        {
+            if (!list.IsNullOrEmpty())
+            {
+                for (int i = 0; i < list.Count; i++)
+                {
+                    var item = list[i];
+                    if (item.storeConfig == null) continue;
+                    res.Add(new LianQiGiftItem { type = 0, id = item.storeConfig.ID });
+                }
+            }
+        }
+
+        if (ADAwardConfig.Get(config.ADID) != null)
+        {
+            res.Add(new LianQiGiftItem { type = 1, id = config.ADID });
+        }
+
+        if (config.CTGIDList != null)
+        {
+            for (int i = 0; i < config.CTGIDList.Length; i++)
+            {
+                res.Add(new LianQiGiftItem { type = 2, id = config.CTGIDList[i] });
+            }
+        }
+
+        if (isSort)
+        {
+            Dictionary<LianQiGiftItem, int> originalIndexMap = new Dictionary<LianQiGiftItem, int>();
+            for (int i = 0; i < res.Count; i++)
+                originalIndexMap[res[i]] = i;
+            res.Sort((a, b) =>
+            {
+                bool notBuyA = GetIsNotCanBuy(a);
+                bool notBuyB = GetIsNotCanBuy(b);
+                if (notBuyA != notBuyB)
+                    return notBuyA.CompareTo(notBuyB);
+                int orderA = GetSortOrder(a);
+                int orderB = GetSortOrder(b);
+                if (orderA != orderB)
+                    return orderA.CompareTo(orderB);
+                int idCmp = a.id.CompareTo(b.id);
+                if (idCmp != 0)
+                    return idCmp;
+                return originalIndexMap[a].CompareTo(originalIndexMap[b]);
+            });
+        }
+        return res;
+    }
+
+    public bool IsCanBuy(int ctgID, out bool isNoLimitBuy, out int nowBuyCnt, out int maxBuyCnt)
+    {
+        isNoLimitBuy = false;
+        nowBuyCnt = 0;
+        maxBuyCnt = 0;
+        CTGConfig config = CTGConfig.Get(ctgID);
+        if (config == null)
+            return false;
+        if (!RechargeManager.Instance.TryGetRechargeCount(ctgID, out var rechargeCount))
+            return false;
+
+        if (config.TotalBuyCount == 0 && config.DailyBuyCount == 0 &&
+            config.WeekBuyCount == 0 && config.MonthBuyCount == 0)
+        {
+            isNoLimitBuy = true;
+            return true;
+        }
+
+        if (config.DailyBuyCount > 0)
+        {
+            nowBuyCnt = rechargeCount.todayCount;
+            maxBuyCnt = config.DailyBuyCount;
+            return rechargeCount.todayCount < config.DailyBuyCount;
+        }
+        if (config.WeekBuyCount > 0)
+        {
+            nowBuyCnt = rechargeCount.weekPayCount;
+            maxBuyCnt = config.WeekBuyCount;
+            return rechargeCount.weekPayCount < config.WeekBuyCount;
+        }
+        if (config.MonthBuyCount > 0)
+        {
+            nowBuyCnt = rechargeCount.monthPayCount;
+            maxBuyCnt = config.MonthBuyCount;
+            return rechargeCount.monthPayCount < config.MonthBuyCount;
+        }
+        if (config.TotalBuyCount > 0)
+        {
+            nowBuyCnt = rechargeCount.totalCount;
+            maxBuyCnt = config.TotalBuyCount;
+            return rechargeCount.totalCount < config.TotalBuyCount;
+        }
+        return false;
+    }
+
+    public bool IsFreeShop(int shopID)
+    {
+        StoreConfig config = StoreConfig.Get(shopID);
+        if (config == null) return false;
+        return config.MoneyNum == 0;
+    }
+
+    public bool IsNoSellOutShopID(int shopID)
+    {
+        StoreConfig config = StoreConfig.Get(shopID);
+        if (config == null) return false;
+
+        StoreModel.Instance.TryGetIsSellOut(config, out int remainNum);
+        return remainNum > 0;
+    }
+
+    public bool IsAdAwardCanReceive(int adID)
+    {
+        var config = ADAwardConfig.Get(adID);
+        if (config == null) return false;
+        int receivedCnt = AdsManager.Instance.GetADCntByADID(adID);
+        return receivedCnt < config.ADCntMax;
+    }
+
+    private bool GetIsNotCanBuy(LianQiGiftItem item)
+    {
+        if (item.type == 0) return !IsNoSellOutShopID(item.id);
+        else if (item.type == 1) return !IsAdAwardCanReceive(item.id);
+        else return !IsCanBuy(item.id, out bool _, out int _, out int _);
+    }
+
+    private int GetSortOrder(LianQiGiftItem item)
+    {
+        if (item.type == 0)
+        {
+            return IsFreeShop(item.id) ? 0 : 2;
+        }
+        if (item.type == 1)
+        {
+            return 1;
+        }
+        return 3;
+    }
+
+    public bool HasGiftCanHave()
+    {
+        if (operationCrossAct == null || operationCrossAct.actTempConfig == null) return false;
+        var config = operationCrossAct.actTempConfig;
+        if (StoreModel.Instance.storeTypeDict.TryGetValue(config.ActShopType, out var list) && !list.IsNullOrEmpty())
+        {
+            for (int i = 0; i < list.Count; i++)
+            {
+                if (list[i].storeConfig == null) continue;
+                int shopID = list[i].storeConfig.ID;
+                if (IsFreeShop(shopID) && IsNoSellOutShopID(shopID)) return true;
+            }
+        }
+        if (ADAwardConfig.Get(config.ADID) != null && IsAdAwardCanReceive(config.ADID)) return true;
+        return false;
+    }
+    #endregion
+    #region 绾㈢偣
+    public Redpoint redPoint = new Redpoint(MainRedDot.LianQiRepoint);
+    public Redpoint zhanlingRedPoint = new Redpoint(MainRedDot.LianQiRepoint, MainRedDot.LianQiRepoint * 10 + 1);
+    public Redpoint awardRedPoint = new Redpoint(MainRedDot.LianQiRepoint, MainRedDot.LianQiRepoint * 10 + 2);
+    public Redpoint giftRedpoint = new Redpoint(MainRedDot.LianQiRepoint, MainRedDot.LianQiRepoint * 10 + 3);
+    private void UpdateRedpoint()
+    {
+        redPoint.state = RedPointState.None;
+        zhanlingRedPoint.state = RedPointState.None;
+        awardRedPoint.state = RedPointState.None;
+        giftRedpoint.state = RedPointState.None;
+        if (!IsLianQiOpen())
+            return;
+        if (HasGiftCanHave())
+            giftRedpoint.state = RedPointState.Simple;
+        if (HasZhanLingCanHave())
+            zhanlingRedPoint.state = RedPointState.Simple;
+        if (HasBillAwardEntryRedpoint())
+            awardRedPoint.state = RedPointState.Simple;
+
+    }
+
+    private bool HasBillAwardEntryRedpoint()
+    {
+        if (!IsInAwardPhase)
+            return false;
+        if (nowPlayerInfo == null || nowPlayerInfo.BillAwardState != 0)
+            return false;
+        return !hasCheckedNoBillAward;
+    }
+
+    public void ClearBillAwardEntryRedpointByNoAward()
+    {
+        if (!IsInAwardPhase)
+            return;
+        if (nowPlayerInfo == null || nowPlayerInfo.BillAwardState != 0)
+            return;
+        hasCheckedNoBillAward = true;
+        UpdateRedpoint();
+    }
+    #endregion
+
+    #region 鎴樹护
+    public event Action OnZhanLingUpdateEvent;
+
+    public int GetZhanLingValue()
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(ZhanLingType);
+        return data != null ? (int)data.value1 : 0;
+    }
+
+    public static int GetZhanLingValueByType(int zhanLingType)
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(zhanLingType);
+        return data != null ? (int)data.value1 : 0;
+    }
+
+    // 0 娌¤喘涔拌繘闃跺拰鐜勭骇 1 璐拱浜嗚繘闃舵病涔扮巹绾� 2 璐拱浜嗙巹绾ф病涔拌繘闃� 3 璐拱浜嗚繘闃跺拰鐜勭骇
+    public int GetZhanLingBuyState()
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(ZhanLingType);
+        if (data == null)
+            return 0;
+        bool isBuyPaid = data.isActivite > 0;
+        bool isBuyPaidH = data.isActiviteH > 0;
+        if (isBuyPaid && isBuyPaidH)
+            return 3;
+        if (isBuyPaid)
+            return 1;
+        if (isBuyPaidH)
+            return 2;
+        return 0;
+    }
+
+    public bool HasZhanLingCanHave()
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(ZhanLingType);
+        if (data == null)
+            return false;
+
+        var dict = ZhanlingConfig.GetTypeToIDDict(ZhanLingType);
+        if (dict.IsNullOrEmpty())
+            return false;
+
+        int buyState = GetZhanLingBuyState();
+        bool isBuyPaid = buyState == 1 || buyState == 3;
+        bool isBuyPaidH = buyState == 2 || buyState == 3;
+
+        foreach (var kvp in dict)
+        {
+            int needValue = kvp.Key;
+            int freeState = GetZhanLingFreeGiftState(needValue);
+            if (freeState == 1)
+                return true;
+
+            if (isBuyPaid)
+            {
+                int paidState = GetZhanLingPaidGiftState(needValue);
+                if (paidState == 1)
+                    return true;
+            }
+
+            if (isBuyPaidH)
+            {
+                int paidHState = GetZhanLingPaidGiftHState(needValue);
+                if (paidHState == 1)
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    // 0 涓嶈兘棰� 1 鏈鍙� 2 宸查鍙�
+    public int GetZhanLingFreeGiftState(int needValue)
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(ZhanLingType);
+        if (data == null)
+            return 0;
+        return BattlePassManager.Instance.GetBPCellAwardState(data, GetZhanLingValue(), needValue, 0);
+    }
+
+    public int GetZhanLingPaidGiftState(int needValue)
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(ZhanLingType);
+        if (data == null)
+            return 0;
+        return BattlePassManager.Instance.GetBPCellAwardState(data, GetZhanLingValue(), needValue, 1);
+    }
+
+    public int GetZhanLingPaidGiftHState(int needValue)
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(ZhanLingType);
+        if (data == null)
+            return 0;
+        return BattlePassManager.Instance.GetBPCellAwardState(data, GetZhanLingValue(), needValue, 2);
+    }
+
+    public void HaveAllZhanLingGift()
+    {
+        var data = BattlePassManager.Instance.GetBattlePassData(ZhanLingType);
+        if (data == null)
+            return;
+        if (PackManager.Instance.GetEmptyGridCount(PackType.Item) < 1)
+        {
+            SysNotifyMgr.Instance.ShowTip("GeRen_lhs_202580");
+            return;
+        }
+        BattlePassManager.Instance.GetAllAward(data, ZhanLingType, GetZhanLingValue());
+    }
+    #endregion
+
+    #region 鎴樹护棰勮璐拱
+    public int previewZhanLingType { get; private set; }
+    public int previewGiftType { get; private set; }
+
+    public void ShowZhanLingHBuy(int zhanLingType, int showGiftType)
+    {
+        previewZhanLingType = zhanLingType;
+        previewGiftType = showGiftType;
+        if (showGiftType == 1 || showGiftType == 2)
+            UIManager.Instance.OpenWindow<LianQiZhanLingPreviewWin>();
+    }
+
+    public int GetPreviewCtgID()
+    {
+        int ctgid = 0;
+        if (previewGiftType == 1)
+        {
+            ctgid = BattlePassManager.Instance.GetCTGIDByType(previewZhanLingType);
+        }
+        else if (previewGiftType == 2)
+        {
+            var config = FuncConfigConfig.Get("Zhanling");
+            if (config != null)
+            {
+                var dict = ConfigParse.ParseIntArrayDict(config.Numerical3);
+                if (dict.TryGetValue(previewZhanLingType, out var arr) && arr.Length > 0)
+                    ctgid = arr[0];
+            }
+        }
+        return ctgid;
+    }
+
+    public List<Item> GetPreviewGiftAllItem(int maxCountPerSlot = 20)
+    {
+        List<int> collectList = new List<int>();
+        var dict = ZhanlingConfig.GetTypeToIDDict(previewZhanLingType);
+        if (dict.IsNullOrEmpty())
+            return new List<Item>();
+        int[] keysArr = new int[dict.Count];
+        int _idx = 0;
+        foreach (int key in dict.Keys)
+        {
+            keysArr[_idx] = key;
+            _idx++;
+        }
+        for (int i = 0; i < keysArr.Length; i++)
+        {
+            int needValue = keysArr[i];
+            collectList.Add(dict[needValue]);
+        }
+        return CollectPreviewItems(collectList, maxCountPerSlot);
+    }
+
+    public List<Item> GetPreviewGiftNowItem(int maxCountPerSlot = 20)
+    {
+        List<int> collectList = new List<int>();
+        var dict = ZhanlingConfig.GetTypeToIDDict(previewZhanLingType);
+        if (dict.IsNullOrEmpty())
+            return new List<Item>();
+
+        int nowValue = GetZhanLingValueByType(previewZhanLingType);
+        foreach (var kv in dict)
+        {
+            if (kv.Key <= nowValue)
+                collectList.Add(kv.Value);
+        }
+        return CollectPreviewItems(collectList, maxCountPerSlot);
+    }
+
+    private List<Item> CollectPreviewItems(List<int> collectList, int maxCountPerSlot)
+    {
+        Dictionary<int, int> resultDict = new Dictionary<int, int>();
+        for (int i = 0; i < collectList.Count; i++)
+        {
+            int zhanLingId = collectList[i];
+            int[][] itemArr = GetPreviewItemArr(zhanLingId, previewGiftType);
+            if (itemArr.IsNullOrEmpty())
+                continue;
+            for (int j = 0; j < itemArr.Length; j++)
+            {
+                int itemID = itemArr[j][0];
+                int count = itemArr[j][1];
+                if (resultDict.ContainsKey(itemID))
+                    resultDict[itemID] += count;
+                else
+                    resultDict[itemID] = count;
+            }
+        }
+        List<Item> result = GetItemListByDict(resultDict, maxCountPerSlot);
+        result.Sort(CmpPreviewItem);
+        return result;
+    }
+
+    private int[][] GetPreviewItemArr(int zhanLingId, int showGiftType)
+    {
+        var config = ZhanlingConfig.Get(zhanLingId);
+        if (config == null)
+            return new int[][] { };
+        if (showGiftType == 1)
+            return config.ZLRewardItemList;
+        if (showGiftType == 2)
+            return config.ZLRewardItemListH;
+        return new int[][] { };
+    }
+
+    private List<Item> GetItemListByDict(Dictionary<int, int> dict, int maxCountPerSlot)
+    {
+        List<Item> result = new List<Item>();
+        int[] keysArr = new int[dict.Count];
+        int _idx = 0;
+        foreach (int key in dict.Keys)
+        {
+            keysArr[_idx] = key;
+            _idx++;
+        }
+        for (int i = 0; i < keysArr.Length; i++)
+        {
+            int itemID = keysArr[i];
+            int count = dict[itemID];
+            result.Add(new Item(itemID, count));
+        }
+        return result;
+    }
+
+    private int CmpPreviewItem(Item a, Item b)
+    {
+        int quality1 = ItemConfig.Get(a.id).ItemColor;
+        int quality2 = ItemConfig.Get(b.id).ItemColor;
+        if (quality1 != quality2)
+            return quality2.CompareTo(quality1);
+        return a.countEx.CompareTo(b.countEx);
+    }
+    #endregion
+
+    #region 鐐煎櫒妫嬬洏鐜╂硶
+    private void ClearGameplayState()
+    {
+        nowPlayerInfo = null;
+        lastPlayerInfo = null;
+        nowGridData = null;
+        lastGridData = null;
+        isWaitReStart = false;
+        isPause = false;
+        isUseTool = false;
+        nowAssistiveItemNum = 0;
+        nowAssistiveItemId = 0;
+        isStartChoose = false;
+        isChoose1 = false;
+        isChoose2 = false;
+        useRow1 = 0;
+        useCol1 = 0;
+        useRow2 = 0;
+        useCol2 = 0;
+        isPlayAssistiveItemAnimation = false;
+        SelectRank = 0;
+        SelectAwardRank = 0;
+    }
+
+    public int GetPlayerId(RankData rankData)
+    {
+        if (rankData == null)
+            return 0;
+
+        switch (SelectRank)
+        {
+            case 0:
+            case 1:
+                return (int)rankData.id;
+
+            case 2:
+                return (int)rankData.id2;
+
+            default:
+                return 0;
+        }
+    }
+
+    public int GetUseItemCnt(ushort[] UseItemCntList, int nowAssistiveItemNum)
+    {
+        if (nowAssistiveItemNum < 1 || nowAssistiveItemNum > 4)
+            return 0;
+        if (UseItemCntList.IsNullOrEmpty() || UseItemCntList.Length != 4)
+            return 0;
+        return UseItemCntList[Mathf.Max(0, nowAssistiveItemNum - 1)];
+    }
+
+    public int GetAwardState(int awardLv, int lvAwardState, int lvAwardMax)
+    {
+        if (lvAwardMax >= awardLv)
+            return ((int)Math.Pow(2, awardLv) & lvAwardState) == 0 ? 1 : 2;
+        return 0;
+    }
+
+    public bool TryGetCompositeItemInfoByAwardLv(int awardLv, out LianQiCompositeItemInfo lianQiCompositeItemInfo)
+    {
+        lianQiCompositeItemInfo = new LianQiCompositeItemInfo();
+
+        string iconStr = StringUtility.Concat("LianQiCompositeItemIcon", "_", awardLv.ToString());
+        if (!IconConfig.HasKey(iconStr))
+        {
+            Debug.LogWarning($"娌℃湁閰嶇疆I.Icon琛ㄤ腑LianQiCompositeItemIcon_{awardLv}椤�");
+            return false;
+        }
+        lianQiCompositeItemInfo.Icon = iconStr;
+
+        string nameStr = StringUtility.Concat("LianQiCompositeItemName", "_", awardLv.ToString());
+        if (!LanguageConfig.HasKey(StringUtility.Concat("LianQiCompositeItemName", "_", awardLv.ToString())))
+        {
+            Debug.LogWarning($"娌℃湁閰嶇疆Y.璇█琛ㄤ腑LianQiCompositeItemName_{awardLv}椤�");
+            return false;
+        }
+        lianQiCompositeItemInfo.Name = Language.Get(nameStr);
+
+        string descriptionStr = StringUtility.Concat("LianQiCompositeItemDescription", "_", awardLv.ToString());
+        if (!LanguageConfig.HasKey(StringUtility.Concat("LianQiCompositeItemDescription", "_", awardLv.ToString())))
+        {
+            Debug.LogWarning($"娌℃湁閰嶇疆Y.璇█琛ㄤ腑LianQiCompositeItemDescription_{awardLv}椤�");
+            return false;
+        }
+        lianQiCompositeItemInfo.Description = Language.Get(descriptionStr);
+
+        return true;
+    }
+
+    public void SetActive(Component compoent, bool active)
+    {
+        if (compoent != null)
+        {
+            if (active && !compoent.gameObject.activeSelf)
+            {
+                compoent.gameObject.SetActive(true);
+            }
+            else if (!active && compoent.gameObject.activeSelf)
+            {
+                compoent.gameObject.SetActive(false);
+            }
+        }
+    }
+
+    public void SetSprite(ImageEx compoent, int awardLv)
+    {
+        if (compoent != null)
+        {
+            if (awardLv > 0)
+            {
+                SetActive(compoent, true);
+                compoent.SetSprite(StringUtility.Concat("LianQiCompositeItemIcon", "_", awardLv.ToString()));
+            }
+            else
+            {
+                SetActive(compoent, false);
+            }
+        }
+    }
+
+    public void SetTextLv(TextEx compoent, int awardLv)
+    {
+        if (compoent != null)
+        {
+            if (awardLv > 0)
+            {
+                SetActive(compoent, true);
+                compoent.text = awardLv.ToString();
+            }
+            else
+            {
+                SetActive(compoent, false);
+            }
+        }
+    }
+
+    public bool IsEnergyEnough(int energy)
+    {
+        return energy > 0;
+    }
+
+    public void UpdateActLianqiPlayerInfo(HAA91_tagSCActLianqiPlayerInfo pack)
+    {
+        lastPlayerInfo = nowPlayerInfo;
+        nowPlayerInfo = pack;
+        lastGridData = nowGridData;
+        actScore = (int)pack.ScoreHighest;
+        nowGridData = JsonMapper.ToObject<int[][]>(pack.GridData);
+
+        if (pack.OPType == 1 || pack.OPType == 3)
+        {
+            isPause = false;
+        }
+        else if (pack.OPType == 2 && nowAssistiveItemNum != 4)
+        {
+            isPause = false;
+        }
+
+        if (pack.OPType == 3)
+        {
+            isWaitReStart = false;
+        }
+        OnLianqiPlayerInfoAction?.Invoke();
+        UpdateRedpoint();
+    }
+
+    public bool IsBillAwardReceived()
+    {
+        return nowPlayerInfo != null && nowPlayerInfo.BillAwardState != 0;
+    }
+
+    public bool TryGetMyBillAwardConfig(out RankData rankData, out ActBillboardAwardConfig config)
+    {
+        rankData = RankModel.Instance.GetMyRank(loadRankType);
+        config = null;
+        if (rankData == null || rankData.rank <= 0)
+            return false;
+
+        var list = ActBillboardAwardConfig.GetRankASortList(billTempID);
+        if (list == null)
+            return false;
+
+        for (int i = 0; i < list.Count; i++)
+        {
+            var tempConfig = ActBillboardAwardConfig.GetConfig(billTempID, list[i]);
+            if (tempConfig == null)
+                continue;
+            if (rankData.rank >= tempConfig.RankA && rankData.rank <= tempConfig.RankB)
+            {
+                config = tempConfig;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public bool CanHaveBillAward()
+    {
+        if (!IsInAwardPhase)
+            return false;
+        if (nowPlayerInfo == null || nowPlayerInfo.BillAwardState != 0)
+            return false;
+        RankData rankData;
+        ActBillboardAwardConfig config;
+        return TryGetMyBillAwardConfig(out rankData, out config);
+    }
+
+    public int PieceCount(int[][] board)
+    {
+        if (board.IsNullOrEmpty())
+            return 0;
+        int count = 0;
+        for (int i = 0; i < MaxGridRows; i++)
+        {
+            for (int j = 0; j < MaxGridCols; j++)
+            {
+                if (board[i][j] > 0)
+                {
+                    count += 1;
+                }
+            }
+        }
+        return count;
+    }
+
+    public bool IsCanMove(int[][] board, int direction)
+    {
+        if (board.IsNullOrEmpty())
+            return false;
+
+        switch (direction)
+        {
+            case 1: // 鍚戜笂
+                for (int i = 1; i < MaxGridRows; i++)
+                {
+                    for (int j = 0; j < MaxGridCols; j++)
+                    {
+                        if (board[i][j] != 0 && (board[i - 1][j] == 0 || board[i][j] == board[i - 1][j]) || board[i][j] < MaxItemLv)
+                        {
+                            return true;
+                        }
+                    }
+                }
+                break;
+
+            case 2: // 鍚戜笅
+                for (int i = 0; i < MaxGridRows - 1; i++)
+                {
+                    for (int j = 0; j < MaxGridCols; j++)
+                    {
+                        if (board[i][j] != 0 && (board[i + 1][j] == 0 || board[i][j] == board[i + 1][j]) || board[i][j] < MaxItemLv)
+                        {
+                            return true;
+                        }
+                    }
+                }
+                break;
+
+            case 3: // 鍚戝乏
+                for (int i = 0; i < MaxGridRows; i++)
+                {
+                    for (int j = 1; j < MaxGridCols; j++)
+                    {
+                        if (board[i][j] != 0 && (board[i][j - 1] == 0 || board[i][j] == board[i][j - 1]) || board[i][j] < MaxItemLv)
+                        {
+                            return true;
+                        }
+                    }
+                }
+                break;
+
+            case 4: // 鍚戝彸
+                for (int i = 0; i < MaxGridRows; i++)
+                {
+                    for (int j = 0; j < MaxGridCols - 1; j++)
+                    {
+                        if (board[i][j] != 0 && (board[i][j + 1] == 0 || board[i][j] == board[i][j + 1]) || board[i][j] < MaxItemLv)
+                        {
+                            return true;
+                        }
+                    }
+                }
+                break;
+        }
+
+        return false;
+    }
+
+    public bool IsDeadLock(int[][] board)
+    {
+        for (int i = 0; i < MaxGridRows; i++)
+        {
+            for (int j = 0; j < MaxGridCols; j++)
+            {
+                if (board[i][j] == 0)
+                {
+                    return false;
+                }
+            }
+        }
+
+        for (int i = 0; i < 4; i++)
+        {
+            for (int j = 0; j < 4; j++)
+            {
+                if (j < 3 && board[i][j] == board[i][j + 1])
+                {
+                    if (board[i][j] < MaxItemLv)
+                    {
+                        return false;
+                    }
+                }
+                if (i < 3 && board[i][j] == board[i + 1][j])
+                {
+                    if (board[i][j] < MaxItemLv)
+                    {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public void SendMoveOP(int direction)
+    {
+        SendAA25LianqiOP(1, direction, 0, 0);
+    }
+
+    public void SendUseOP(int useCount, int useItemIndex, int valueARow, int valueALine, int valueBRow = 0, int valueBLine = 0)
+    {
+        int value1 = useCount * 10 + useItemIndex;
+        int value2 = valueARow * 10 + valueALine;
+        int value3 = valueBRow * 10 + valueBLine;
+        SendAA25LianqiOP(2, value1, value2, value3);
+    }
+
+    public void SendRestartOP()
+    {
+        SendAA25LianqiOP(3, 0, 0, 0);
+    }
+
+    public void SendHaveAward(int awardLv)
+    {
+        SendAA25LianqiOP(4, awardLv, 0, 0);
+    }
+
+    public void SendHaveBillAward()
+    {
+        if (!CanHaveBillAward())
+            return;
+        SendAA25LianqiOP(5, 0, 0, 0);
+    }
+
+    public void ClaimReward()
+    {
+        if (nowPlayerInfo == null)
+            return;
+        if (correspondingRewards == null)
+            return;
+        int[] keysArr = new int[correspondingRewards.Count];
+        int _idx = 0;
+        foreach (int key in correspondingRewards.Keys)
+        {
+            keysArr[_idx] = key;
+            _idx++;
+        }
+        for (int i = 0; i < keysArr.Length; i++)
+        {
+            int rewardLv = keysArr[i];
+            int state = GetAwardState(rewardLv, (int)nowPlayerInfo.LVAwardState, (int)nowPlayerInfo.LVAwardMax);
+            if (state == 1)
+            {
+                SendHaveAward(rewardLv);
+            }
+        }
+    }
+
+    public int RewardJumpIndex()
+    {
+        if (nowPlayerInfo == null)
+            return 0;
+        if (correspondingRewards == null)
+            return 0;
+
+        int[] keysArr = new int[correspondingRewards.Count];
+        int _idx = 0;
+        foreach (int key in correspondingRewards.Keys)
+        {
+            keysArr[_idx] = key;
+            _idx++;
+        }
+        int lastNonClaimedIndex = -1;
+
+        for (int i = 0; i < keysArr.Length; i++)
+        {
+            int rewardLv = keysArr[i];
+            int state = GetAwardState(rewardLv, (int)nowPlayerInfo.LVAwardState, (int)nowPlayerInfo.LVAwardMax);
+            if (state != 2)
+            {
+                return i;
+            }
+            else
+            {
+                lastNonClaimedIndex = i;
+            }
+        }
+
+        return lastNonClaimedIndex >= 0 ? lastNonClaimedIndex : 0;
+    }
+
+    public bool IsCanSendUseItem()
+    {
+        if (!IsJoin)
+        {
+            SysNotifyMgr.Instance.ShowTip("ActionUnOpen1");
+            return false;
+        }
+
+        int itemID = nowAssistiveItemId;
+        int num = nowAssistiveItemNum;
+
+        if (packManager.GetItemCountByID(PackType.Item, itemID) <= 0)
+        {
+            SysNotifyMgr.Instance.ShowTip("LianQiAct02");
+            return false;
+        }
+
+        if (!limitUsesToolCntDict.TryGetValue(itemID, out int cnt))
+            return false;
+        int useItemCnt = GetUseItemCnt(nowPlayerInfo.UseItemCntList, num);
+        if (useItemCnt >= cnt)
+        {
+            SysNotifyMgr.Instance.ShowTip("LianQiAct03");
+            return false;
+        }
+
+        if (nowAssistiveItemNum == 1)
+        {
+            if (PieceCount(nowGridData) <= 1)
+            {
+                SysNotifyMgr.Instance.ShowTip("LianQiAct06");
+                return false;
+            }
+        }
+        else if (nowAssistiveItemNum == 2)
+        {
+            if (PieceCount(nowGridData) <= 1)
+            {
+                SysNotifyMgr.Instance.ShowTip("LianQiAct08");
+                return false;
+            }
+        }
+        else if (nowAssistiveItemNum == 4)
+        {
+            if (nowPlayerInfo.Energy >= maxEnergy)
+            {
+                SysNotifyMgr.Instance.ShowTip("LianQiAct05");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private void SendAA25LianqiOP(int type, int value1, int value2, int value3)
+    {
+        CAA25_tagCSActLianqiOP pack = new CAA25_tagCSActLianqiOP();
+        pack.OPType = (byte)type;
+        pack.OPValue = (uint)value1;
+        pack.OPValue2 = (uint)value2;
+        pack.OPValue3 = (uint)value3;
+        GameNetSystem.Instance.SendInfo(pack);
+    }
+    #endregion
+
+
+}
+
+public class LianQiGiftItem
+{
+    public int type; // 0 鍟嗗簵 / 1 骞垮憡 / 2 CTG鍏呭��
+    public int id;
+}
+
+public class LianQiCompositeItemInfo
+{
+    public int AwardLv;
+    public string Name;
+    public string Icon;
+    public string Description;
+}
diff --git a/Main/System/LianQi/LianQiManager.cs.meta b/Main/System/LianQi/LianQiManager.cs.meta
new file mode 100644
index 0000000..102665b
--- /dev/null
+++ b/Main/System/LianQi/LianQiManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6d7b7777c8faf2b45b0e10f65ecbaafd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiPopTipWin.cs b/Main/System/LianQi/LianQiPopTipWin.cs
new file mode 100644
index 0000000..46223cb
--- /dev/null
+++ b/Main/System/LianQi/LianQiPopTipWin.cs
@@ -0,0 +1,30 @@
+锘縰sing Cysharp.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class LianQiPopTipWin : UIBase
+{
+    [SerializeField] Button popConfirmBtn;
+    [SerializeField] Button popCancelBtn;
+   // [SerializeField] Button closeBtn;
+    LianQiManager model => LianQiManager.Instance;
+
+    protected override void InitComponent()
+    {
+        popConfirmBtn.AddListener(() =>
+        {
+            model.isWaitReStart = true;
+            UIManager.Instance.OpenWindow<LianQiRoundSettlementWin>();
+        });
+        popCancelBtn.AddListener(() =>
+        {
+            model.isPause = true;
+            CloseWindow();
+        });
+        // closeBtn.onClick.AddListener(() =>
+        // {
+        //     model.isPause = true;
+        //     CloseWindow();
+        // });
+    }
+}
\ No newline at end of file
diff --git a/Main/System/LianQi/LianQiPopTipWin.cs.meta b/Main/System/LianQi/LianQiPopTipWin.cs.meta
new file mode 100644
index 0000000..81145ad
--- /dev/null
+++ b/Main/System/LianQi/LianQiPopTipWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a4d572792b25a8444a433009e2d6e612
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiRankAwardCell.cs b/Main/System/LianQi/LianQiRankAwardCell.cs
new file mode 100644
index 0000000..6e5471c
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankAwardCell.cs
@@ -0,0 +1,72 @@
+锘縰sing UnityEngine;
+
+public class LianQiRankAwardCell : CellView
+{
+    [SerializeField] ImageEx imgRank;
+    [SerializeField] TextEx txtRank;
+    [SerializeField] ScrollerController itemScroller;
+
+    private void OnDisable()
+    {
+        UnbindItemScroller();
+    }
+
+    public void Display(int rankA, int templateID)
+    {
+        UnbindItemScroller();
+        var config = ActBillboardAwardConfig.GetConfig(templateID, rankA);
+        if (config == null)
+        {
+            itemScroller.Refresh();
+            itemScroller.Restart();
+            return;
+        }
+
+
+        if (rankA <= 3)
+        {
+            imgRank.SetActive(true);
+            txtRank.SetActive(false);
+            imgRank.SetSprite(StringUtility.Concat("Rank", rankA.ToString()));
+            txtRank.text = rankA.ToString();
+        }
+        else
+        {
+            imgRank.SetActive(false);
+            txtRank.SetActive(true);
+            txtRank.text = Language.Get("Arena15", rankA, config.RankB);
+        }
+
+        CreateItemScroller(config.AwardItemList);
+    }
+
+    private void CreateItemScroller(int[][] rewardArr)
+    {
+        itemScroller.OnRefreshCell += OnRefreshItemCell;
+        itemScroller.OnRefreshCompleteEvent += OnRefreshItemComplete;
+        itemScroller.Refresh();
+        for (int i = 0; i < rewardArr.Length; i++)
+        {
+            CellInfo info = new CellInfo(rewardArr[i][0], rewardArr[i][1], null);
+            itemScroller.AddCell(ScrollerDataType.Header, i, info);
+        }
+        itemScroller.Restart();
+    }
+
+    private void OnRefreshItemComplete()
+    {
+        UnbindItemScroller();
+    }
+
+    private void OnRefreshItemCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<LianQiRankAwardItemCell>();
+        _cell?.Display(cell);
+    }
+
+    private void UnbindItemScroller()
+    {
+        itemScroller.OnRefreshCell -= OnRefreshItemCell;
+        itemScroller.OnRefreshCompleteEvent -= OnRefreshItemComplete;
+    }
+}
diff --git a/Main/System/LianQi/LianQiRankAwardCell.cs.meta b/Main/System/LianQi/LianQiRankAwardCell.cs.meta
new file mode 100644
index 0000000..b08dfd8
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankAwardCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: abbe3f17f7bb33a41a72cc99c6a72fbd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiRankAwardItemCell.cs b/Main/System/LianQi/LianQiRankAwardItemCell.cs
new file mode 100644
index 0000000..f463898
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankAwardItemCell.cs
@@ -0,0 +1,15 @@
+锘縰sing UnityEngine;
+
+public class LianQiRankAwardItemCell : CellView
+{
+    [SerializeField] ItemCell itemCell;
+    public void Display(CellView cell)
+    {
+        if (cell == null || !cell.info.HasValue)
+            return;
+        int itemID = cell.info.Value.infoInt1;
+        int count = cell.info.Value.infoInt2;
+        itemCell.Init(new ItemCellModel(itemID, true, count));
+        itemCell.button.SetListener(() => ItemTipUtility.Show(itemID, true));
+    }
+}
diff --git a/Main/System/LianQi/LianQiRankAwardItemCell.cs.meta b/Main/System/LianQi/LianQiRankAwardItemCell.cs.meta
new file mode 100644
index 0000000..101871e
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankAwardItemCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 56c2cae1dd95d3a4e87a0dd7ead5c260
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiRankCell.cs b/Main/System/LianQi/LianQiRankCell.cs
new file mode 100644
index 0000000..87dd4e1
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankCell.cs
@@ -0,0 +1,89 @@
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class LianQiRankCell : MonoBehaviour
+{
+    [SerializeField] AvatarCell avatarCell;
+    [SerializeField] Text rankText;
+    [SerializeField] Text rankValueText;    //鎺掑悕姣旇緝鍐呭
+    [SerializeField] Text nameText;
+    [SerializeField] OfficialTitleCell officialTitleCell;
+    [SerializeField] Button queryPlayerBtn; //鍚庣画娣诲姞鐐瑰嚮鏌ョ湅鐜╁璇︽儏
+    [SerializeField] TextEx countText;//鍚堟垚鐗╂暟閲�
+
+    LianQiManager manager => LianQiManager.Instance;
+    // rank 涓�0 浠h〃鐜╁鑷繁
+    public void Display(int rankType, int rank, string valueFormat)
+    {
+        int billTempID = manager.billTempID;
+        var awardConfig = ActBillboardAwardConfig.GetConfig(billTempID, rank);
+
+        RankData rankData = null;
+        int viewPlayerId = (int)PlayerDatas.Instance.baseData.PlayerID;
+        int sid = 0;
+        if (rank != 0)
+        {
+            rankData = RankModel.Instance.GetRankDataByRank(rankType, rank);
+        }
+        else
+        {
+            rankData = RankModel.Instance.GetMyRank(rankType);
+            if (rankData == null)
+            {
+                //鍙栫帺瀹惰嚜宸辩殑鏁版嵁
+                avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)PlayerDatas.Instance.baseData.PlayerID,
+                                                PlayerDatas.Instance.baseData.face,
+                                                PlayerDatas.Instance.baseData.facePic));
+                rankText.text = Language.Get("L1045");
+                rankValueText.text = awardConfig == null || awardConfig.NeedValue == 0 ? "0" : Language.Get("HeroDebut27", awardConfig.NeedValue);
+                nameText.text = PlayerDatas.Instance.baseData.PlayerName;
+                officialTitleCell.InitUI(PlayerDatas.Instance.baseData.realmLevel, PlayerDatas.Instance.baseData.TitleID);
+                if (countText != null) countText.SetActive(false);
+                return;
+            }
+            rank = rankData.rank;
+        }
+        if (rankData == null)
+        {
+            officialTitleCell.SetActive(false);
+            avatarCell.SetActive(false);
+            nameText.text = Language.Get("L1124");
+            rankValueText.text = awardConfig == null || awardConfig.NeedValue == 0 ? "0" : Language.Get("HeroDebut27", awardConfig.NeedValue);
+            if (countText != null) countText.SetActive(false);
+        }
+        else
+        {
+            officialTitleCell.SetActive(true);
+            officialTitleCell.InitUI((int)rankData.value1, (int)rankData.value2);
+            avatarCell.SetActive(true);
+            avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)rankData.id, (int)rankData.value3, (int)rankData.value4));
+            viewPlayerId = (int)rankData.id;
+            sid = UIHelper.GetServerIDByAccount(rankData.name2);
+            nameText.text = rankData.name1;
+            rankValueText.text = string.Format(valueFormat, UIHelper.ReplaceLargeNum(rankData.cmpValue));
+            if (countText != null)
+            {
+                int cmpVal = (int)rankData.cmpValue2;
+                if (cmpVal >= manager.MaxItemLv)
+                {
+                    countText.SetActive(true);
+                    countText.text = StringUtility.Concat("X", (cmpVal - manager.MaxItemLv + 1).ToString());
+                }
+                else
+                {
+                    countText.SetActive(false);
+                }
+            }
+        }
+
+        rankText.text = rank.ToString();
+        if (queryPlayerBtn != null)
+        {
+            queryPlayerBtn.AddListener(() =>
+            {
+                AvatarHelper.TryViewOtherPlayerInfo(viewPlayerId, sid);
+            });
+        }
+    }
+}
diff --git a/Main/System/LianQi/LianQiRankCell.cs.meta b/Main/System/LianQi/LianQiRankCell.cs.meta
new file mode 100644
index 0000000..bac723d
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ca5fdd745e5d54947a6bd54c0a558f45
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiRankTop3Cell.cs b/Main/System/LianQi/LianQiRankTop3Cell.cs
new file mode 100644
index 0000000..565b83f
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankTop3Cell.cs
@@ -0,0 +1,58 @@
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class LianQiRankTop3Cell : MonoBehaviour
+{
+    //[SerializeField] Model 鏄剧ずNPC 姝﹀皢妯″瀷
+    [SerializeField] Text rankValueText;    //鎺掑悕姣旇緝鍐呭
+    [SerializeField] Text nameText;
+    [SerializeField] OfficialTitleCell officialTitleCell;
+    [SerializeField] Button queryPlayerBtn; //鍚庣画娣诲姞鐐瑰嚮鏌ョ湅鐜╁璇︽儏
+    [SerializeField] HorseController model;
+    [SerializeField] TextEx countText;//鍚堟垚鐗╂暟閲�
+    
+    LianQiManager manager => LianQiManager.Instance;
+    public void Display(int rankType, int rank, string valueFormat = "{0}")
+    {
+        int billTempID = manager.billTempID;
+        var awardConfig = ActBillboardAwardConfig.GetConfig(billTempID, rank);
+
+        var rankData = RankModel.Instance.GetRankDataByRank(rankType, rank);
+        if (rankData == null)
+        {
+            rankValueText.text = awardConfig == null || awardConfig.NeedValue == 0 ? "0" : Language.Get("HeroDebut40", awardConfig.NeedValue);
+            nameText.text = Language.Get("L1124");
+            officialTitleCell.SetActive(false);
+            model.SetActive(false);
+            if (countText != null) countText.SetActive(false);
+            return;
+        }
+        officialTitleCell.SetActive(true);
+        int sid = UIHelper.GetServerIDByAccount(rankData.name2);
+        rankValueText.text = string.Format(valueFormat, UIHelper.ReplaceLargeNum(rankData.cmpValue));
+        nameText.text = rankData.name1;
+        officialTitleCell.InitUI((int)rankData.value1, (int)rankData.value2);
+        model.SetActive(true);
+        model.Create(HorseManager.Instance.GetOtherPlayerHorseSkinID((int)rankData.value6), (int)rankData.value5, rank == 1 ? 1f : 0.8f);
+        queryPlayerBtn.SetListener(() =>
+        {
+            AvatarHelper.TryViewOtherPlayerInfo((int)rankData.id, sid);
+        });
+        if (countText != null)
+        {
+            int cmpVal = (int)rankData.cmpValue2;
+            if (cmpVal >= manager.MaxItemLv)
+            {
+                countText.SetActive(true);
+                countText.text = StringUtility.Concat("X", (cmpVal - manager.MaxItemLv + 1).ToString());
+            }
+            else
+            {
+                countText.SetActive(false);
+            }
+        }
+    }
+
+
+}
diff --git a/Main/System/LianQi/LianQiRankTop3Cell.cs.meta b/Main/System/LianQi/LianQiRankTop3Cell.cs.meta
new file mode 100644
index 0000000..db5a3e1
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankTop3Cell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c6440faf5ed25ad45bde8aa5a9be7e88
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiRankWin.cs b/Main/System/LianQi/LianQiRankWin.cs
new file mode 100644
index 0000000..692f79d
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankWin.cs
@@ -0,0 +1,83 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class LianQiRankWin : UIBase
+{
+    [SerializeField] ScrollerController rankScroller;
+    [SerializeField] List<LianQiRankTop3Cell> playerTop3Cells;
+    [SerializeField] LianQiRankCell myRankCell;
+    [HideInInspector] public string valueFormat = "{0}";
+
+    LianQiManager manager => LianQiManager.Instance;
+
+    protected override void OnPreOpen()
+    {
+        base.OnPreOpen();
+        RankModel.Instance.ResetQueryParam();
+        RankModel.Instance.QueryRankByPage(manager.sendRankType, watchID: (int)PlayerDatas.Instance.baseData.PlayerID, groupValue1: manager.groupValue1, crossServerID: manager.crossServerID);
+
+        RankModel.Instance.onRankRefresh += OnRankRefresh;
+        rankScroller.OnRefreshCell += OnRefreshCell;
+        Display();
+    }
+
+    protected override void OnPreClose()
+    {
+        base.OnPreClose();
+        RankModel.Instance.onRankRefresh -= OnRankRefresh;
+        rankScroller.OnRefreshCell -= OnRefreshCell;
+    }
+
+    void OnRankRefresh(int type)
+    {
+        if (type != manager.sendRankType) return;
+        RankRefresh();
+    }
+
+    void RankRefresh()
+    {
+        ShowTop3();
+        rankScroller.m_Scorller.RefreshActiveCellViews();
+        DisplayMyRank();
+    }
+
+    void CreateRankScroller()
+    {
+        rankScroller.Refresh();
+        var cnt = RankModel.Instance.GetRankShowMaxCnt(manager.sendRankType);
+        for (int i = 3; i < cnt; i++)
+        {
+            rankScroller.AddCell(ScrollerDataType.Header, i);
+        }
+        rankScroller.Restart();
+    }
+
+
+    void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<LianQiRankCell>();
+        _cell.Display(manager.loadRankType, cell.index + 1, valueFormat);
+        RankModel.Instance.ListenRankPage(manager.sendRankType, cell.index, groupValue1: manager.groupValue1, crossServerID: manager.crossServerID);
+    }
+
+    public void Display()
+    {
+        ShowTop3();
+        CreateRankScroller();
+        DisplayMyRank();
+    }
+
+    private void DisplayMyRank()
+    {
+        myRankCell.Display(manager.loadRankType, 0, valueFormat);
+    }
+
+    void ShowTop3()
+    {
+        for (int i = 0; i < playerTop3Cells.Count; i++)
+        {
+            playerTop3Cells[i].Display(manager.loadRankType, i + 1);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/Main/System/LianQi/LianQiRankWin.cs.meta b/Main/System/LianQi/LianQiRankWin.cs.meta
new file mode 100644
index 0000000..5307b83
--- /dev/null
+++ b/Main/System/LianQi/LianQiRankWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 27286beae2fe3a8499faf7a1ebc6d524
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiRoundSettlementWin.cs b/Main/System/LianQi/LianQiRoundSettlementWin.cs
new file mode 100644
index 0000000..a56c76e
--- /dev/null
+++ b/Main/System/LianQi/LianQiRoundSettlementWin.cs
@@ -0,0 +1,35 @@
+锘縰sing UnityEngine;
+
+public class LianQiRoundSettlementWin : UIBase
+{
+    [SerializeField] TextEx txtInfo;
+    int score;
+    LianQiManager model => LianQiManager.Instance;
+
+    protected override void OnPreOpen()
+    {
+        if (model.nowPlayerInfo == null) return;
+        score = (int)model.nowPlayerInfo.Score;
+        RankModel.Instance.onRankRefresh += OnRankRefresh;
+        UIManager.Instance.CloseWindow<LianQiPopTipWin>();
+        model.SendRestartOP();
+        RankModel.Instance.QueryRankByPage(model.sendRankType, watchID: (int)PlayerDatas.Instance.baseData.PlayerID, groupValue1: model.groupValue1, forceQuery: true, crossServerID: model.crossServerID);
+        txtInfo.text = StringUtility.Concat(Language.Get("LianQi12", score));
+    }
+
+    protected override void OnPreClose()
+    {
+        RankModel.Instance.onRankRefresh -= OnRankRefresh;
+    }
+
+    private void OnRankRefresh(int type)
+    {
+        if (type != model.sendRankType) return;
+        if (model.nowPlayerInfo == null) return;
+        var myRank = RankModel.Instance.GetMyRank(model.loadRankType);
+        if (myRank != null)
+            txtInfo.text = StringUtility.Concat(Language.Get("LianQi12", score), " ", Language.Get("LianQi13", myRank.rank.ToString()));
+        else
+            txtInfo.text = StringUtility.Concat(Language.Get("LianQi12", score), " ", Language.Get("LianQi13", Language.Get("L1045")));
+    }
+}
\ No newline at end of file
diff --git a/Main/System/LianQi/LianQiRoundSettlementWin.cs.meta b/Main/System/LianQi/LianQiRoundSettlementWin.cs.meta
new file mode 100644
index 0000000..b3164ed
--- /dev/null
+++ b/Main/System/LianQi/LianQiRoundSettlementWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 726a07544ea852f4ba12e5aa78776817
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiSmallTipWin.cs b/Main/System/LianQi/LianQiSmallTipWin.cs
new file mode 100644
index 0000000..957749f
--- /dev/null
+++ b/Main/System/LianQi/LianQiSmallTipWin.cs
@@ -0,0 +1,124 @@
+锘縰sing System;
+using System.Collections.Generic;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+public class LianQiSmallTipWin : UIBase
+{
+    [SerializeField] RectTransform rectTransform;
+    [SerializeField] RectTransform arrowUpImage;
+    [SerializeField] Text descText;
+    [SerializeField] Text itemName;
+    [SerializeField] Image itemImg;
+    [SerializeField] ItemCell[] awards;
+
+    public static Vector3 worldPos; //涓栫晫鍧愭爣绯讳綅缃�
+    public static string showText;
+    public static bool isDownShow = false;  // 鏄惁鍚戜笅鏄剧ず
+    public static int showAwardLv;
+
+    protected override void OnPreOpen()
+    {
+        var M = LianQiManager.Instance;
+        LianQiCompositeItemInfo info;
+        if (M.TryGetCompositeItemInfoByAwardLv(showAwardLv, out info) && info != null)
+        {
+            itemName.text = info.Name;
+            descText.text = info.Description;
+            itemImg.SetSprite(info.Icon);
+        }
+        else
+        {
+            itemName.text = string.Empty;
+            descText.text = string.Empty;
+        }
+
+        List<Item> rewards = null;
+        bool hasRewards = M.correspondingRewards != null && M.correspondingRewards.TryGetValue(showAwardLv, out rewards) && rewards != null;
+        if (awards != null)
+        {
+            for (int i = 0; i < awards.Length; i++)
+            {
+                if (hasRewards && i < rewards.Count && awards[i] != null)
+                {
+                    awards[i].Init(new ItemCellModel(rewards[i].id, false, rewards[i].countEx));
+                    awards[i].SetActive(true);
+                }
+                else if (awards[i] != null)
+                {
+                    awards[i].SetActive(false);
+                }
+            }
+        }
+
+        rectTransform.position = new Vector3(100, 100, 100);   //鍒濆鍖栨椂锛屽睆骞曡寖鍥村
+        arrowUpImage.SetActive(isDownShow);
+    }
+
+    protected override void OnPreClose()
+    {
+        isDownShow = false;
+    }
+
+
+    protected override void OnOpen()
+    {
+        UpdatePos();
+    }
+
+    async UniTask UpdatePos()
+    {
+        await UniTask.DelayFrame(3);
+        if (this == null) return; // destroyed during await
+        // 闄愬埗鍦ㄥ睆骞曡寖鍥村唴
+        Vector3[] corners = new Vector3[4];
+        rectTransform.GetWorldCorners(corners);
+
+        float minY = corners[0].y;
+        float maxY = corners[0].y;
+
+        for (int i = 1; i < corners.Length; i++)
+        {
+            if (corners[i].y < minY) minY = corners[i].y;
+            if (corners[i].y > maxY) maxY = corners[i].y;
+        }
+
+
+        float screenHeight = maxY - minY;
+        Vector2 adjustedPos = new Vector2(worldPos.x, worldPos.y + (!isDownShow ? screenHeight * 0.5f : -screenHeight * 0.5f));
+
+        Vector2 screenAdjustedPos = CameraManager.uiCamera.WorldToScreenPoint(adjustedPos);
+        var rectWidth = rectTransform.rect.width * Screen.width / canvasScaler.referenceResolution.x;
+        screenAdjustedPos.x = Mathf.Clamp(screenAdjustedPos.x, rectWidth * 0.5f, Screen.width - rectWidth * 0.5f);
+        screenAdjustedPos.y = Mathf.Clamp(screenAdjustedPos.y, rectTransform.rect.height * 0.5f, Screen.height - rectTransform.rect.height * 0.5f - 15);
+
+        adjustedPos = CameraManager.uiCamera.ScreenToWorldPoint(screenAdjustedPos);
+        rectTransform.position = adjustedPos;
+
+        if (!isDownShow)
+        {
+            rectTransform.localPosition = new Vector3(rectTransform.localPosition.x, rectTransform.localPosition.y + 15, rectTransform.localPosition.z);
+        }
+        else
+        {
+            rectTransform.localPosition = new Vector3(rectTransform.localPosition.x, rectTransform.localPosition.y - 15, rectTransform.localPosition.z);
+        }
+
+        rectTransform.GetWorldCorners(corners);
+        float minX = corners[0].x;
+        float maxX = corners[0].x;
+
+        for (int i = 1; i < corners.Length; i++)
+        {
+            if (corners[i].x < minX) minX = corners[i].x;
+            if (corners[i].x > maxX) maxX = corners[i].x;
+        }
+
+
+        Vector3 arrowUpImagePosition = arrowUpImage.position;
+        arrowUpImagePosition.x = Mathf.Clamp(worldPos.x, minX, maxX);
+        arrowUpImage.position = arrowUpImagePosition;
+
+    }
+}
+
diff --git a/Main/System/LianQi/LianQiSmallTipWin.cs.meta b/Main/System/LianQi/LianQiSmallTipWin.cs.meta
new file mode 100644
index 0000000..5b0bded
--- /dev/null
+++ b/Main/System/LianQi/LianQiSmallTipWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dcb7be20f4a34e2429c33ee894152490
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiWin.cs b/Main/System/LianQi/LianQiWin.cs
new file mode 100644
index 0000000..028cc9c
--- /dev/null
+++ b/Main/System/LianQi/LianQiWin.cs
@@ -0,0 +1,740 @@
+锘縰sing DG.Tweening;
+using System.Collections.Generic;
+
+using UnityEngine;
+using Cysharp.Threading.Tasks;
+
+public class LianQiWin : UIBase
+{
+    [SerializeField] ButtonEx btnClose;
+    [SerializeField] ButtonEx btnRank;
+    [SerializeField] ButtonEx btnReward;
+    [SerializeField] ButtonEx btnGift;
+    [SerializeField] ButtonEx btnZhanLing;
+    [SerializeField] ButtonEx serverButton;
+
+    [SerializeField] ScrollerController awardScroller;
+    [SerializeField] Transform transGameEnd;
+    [SerializeField] TextEx txtEnd;
+    [SerializeField] TextEx txtTip;
+    [SerializeField] TextEx txtTip2;
+    [Header("杈呭姪閬撳叿")]
+    [SerializeField] List<ItemCell> assistiveItemCells;
+    [Header("浣撳姏鍜屽洖澶嶆椂闂�")]
+    [SerializeField] TextEx txtEnergy;
+
+    [SerializeField] Transform transEnergyTime;
+    [SerializeField] TextEx txtEnergyTime;
+    [Header("妫嬬洏鐩稿叧")]
+    [SerializeField] DragSelectComponentEx dragSelect;
+
+    [SerializeField] ClickScreenOtherSpace clickScreenOtherSpace;
+    [SerializeField] TextEx txtScore;
+    [SerializeField] List<RectTransform> rectStartPos;
+    RectTransform[][] rectStartPosArr;
+    [SerializeField] List<LianQiActLevel1> level1;
+    LianQiActLevel1[][] level1Arr;
+    [SerializeField] List<LianQiActLevel2> level2;
+    LianQiActLevel2[][] level2Arr;
+
+    Sequence sequence;
+
+    private const float SynEffectDelay = 0.8f;
+    private const float HammerEffectDelay = 1.4f;
+
+    protected override void InitComponent()
+    {
+        btnClose.SetListener(CloseWindow);
+        btnRank.SetListener(() => UIManager.Instance.OpenWindow<LianQiRankWin>());
+        btnReward.SetListener(() => UIManager.Instance.OpenWindow<LianQiAwardWin>());
+        btnZhanLing.SetListener(() => UIManager.Instance.OpenWindow<LianQiZhanLingWin>());
+        btnGift.SetListener(() => UIManager.Instance.OpenWindow<LianQiGiftWin>());
+
+        serverButton.SetListener(() =>
+        {
+            var act = LianQiManager.Instance.operationCrossAct;
+            if (act == null) return;
+
+            var serverIDList = act.GetServerIDList();
+            if (serverIDList.Count == 0) return;
+            UIHelper.ShowServersPanel(serverIDList);
+        });
+    }
+
+    protected override void OnPreOpen()
+    {
+        var M = LianQiManager.Instance;
+        M.isWaitReStart = false;
+        M.isPause = false;
+        M.isUseTool = false;
+        InitPanel();
+        dragSelect.onDragComplete += OnDragComplete;
+        M.OnLianqiPlayerInfoAction += OnLianqiPlayerInfoAction;
+        PackManager.Instance.RefreshItemEvent += OnRefreshItemCountEvent;
+        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
+        awardScroller.OnRefreshCell += OnRefreshAwardCell;
+        clickScreenOtherSpace.AddListener(() =>
+        {
+            var M = LianQiManager.Instance;
+            if (!M.isUseTool) return;
+            InitPanel();    // 鍙栨秷閬撳叿妯″紡锛岄噸缃墍鏈夌姸鎬�
+        });
+        // 缁戝畾杈呭姪閬撳叿閫夋牸鎸夐挳
+        for (int i = 0; i < level2.Count; i++)
+        {
+            int index = i;
+            level2[i].btnChoose.SetListener(() =>
+            {
+                var M = LianQiManager.Instance;
+                if (!M.isUseTool || !M.isStartChoose) return;
+                if (M.nowAssistiveItemNum == 1 && M.isChoose1) return;
+                if (M.nowAssistiveItemNum == 2 && M.isChoose1 && M.isChoose2) return;
+                if (M.nowAssistiveItemNum == 3 && M.isChoose1) return;
+                if (M.nowAssistiveItemNum == 4) return;
+
+                // 閬撳叿2(鍖栬厫鏈�)锛氭鐩樺彧鏈変竴涓墿鍝佹棤娉曚氦鎹�
+                if (M.nowAssistiveItemNum == 2 && M.PieceCount(M.nowGridData) <= 1)
+                {
+                    SysNotifyMgr.Instance.ShowTip("LianQiAct08");
+                    return;
+                }
+                // 閬撳叿3(涔濆ぉ鐜勯搧)锛氱瓑绾ц秴闄�
+                if (M.nowAssistiveItemNum == 3 && M.nowGridData[level2[index].row][level2[index].col] >= M.jiuTianYunTieLevelLimit)
+                {
+                    SysNotifyMgr.Instance.ShowTip("LianQiAct04");
+                    InitPanel();
+                    return;
+                }
+
+                level2[index].isChooseShow = !level2[index].isChooseShow;
+                level2[index].imgChoose.SetActive(level2[index].isChooseShow);
+
+                if (!M.isChoose1)
+                {
+                    M.useRow1 = level2[index].row;
+                    M.useCol1 = level2[index].col;
+                    M.isChoose1 = true;
+                    // 鍗曢�夐亾鍏�(1/3)閫夋弧绔嬪嵆纭
+                    if (M.nowAssistiveItemNum == 1 || M.nowAssistiveItemNum == 3)
+                        TryConfirmUseTool();
+                }
+                else if (!M.isChoose2)
+                {
+                    // 閬撳叿2绗�2鏍间笉鑳戒笌绗�1鏍肩浉鍚�
+                    if (level2[index].row == M.useRow1 && level2[index].col == M.useCol1)
+                    {
+                        SysNotifyMgr.Instance.ShowTip("LianQiAct07");
+                        InitPanel();
+                        return;
+                    }
+                    M.useRow2 = level2[index].row;
+                    M.useCol2 = level2[index].col;
+                    M.isChoose2 = true;
+                    // 鍙岄�夐亾鍏�(2)閫夋弧绔嬪嵆纭
+                    if (M.nowAssistiveItemNum == 2)
+                        TryConfirmUseTool();
+                }
+            });
+        }
+        DisplayScore();
+        DisplayEnergy();
+        SecondChange();
+        DisplayActivityTime();
+        RefreshGameEndState();
+    }
+
+    protected override void OnPreClose()
+    {
+        var M = LianQiManager.Instance;
+        dragSelect.onDragComplete -= OnDragComplete;
+        M.OnLianqiPlayerInfoAction -= OnLianqiPlayerInfoAction;
+        PackManager.Instance.RefreshItemEvent -= OnRefreshItemCountEvent;
+        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
+        awardScroller.OnRefreshCell -= OnRefreshAwardCell;
+        clickScreenOtherSpace.RemoveAllListeners();
+        sequence.Kill();
+    }
+
+    protected override void OnOpen()
+    {
+        CreateAwardScroller();
+    }
+
+    private void CreateAwardScroller()
+    {
+        var M = LianQiManager.Instance;
+        var dict = M.correspondingRewards;
+        awardScroller.Refresh();
+        if (dict == null)
+            return;
+        int[] keysArr = new int[dict.Count];
+        int _idx = 0;
+        foreach (int key in dict.Keys)
+        {
+            keysArr[_idx] = key;
+            _idx++;
+        }
+        for (int i = 0; i < keysArr.Length; i++)
+        {
+            awardScroller.AddCell(ScrollerDataType.Header, i);
+        }
+        awardScroller.Restart();
+        awardScroller.JumpIndex(M.RewardJumpIndex());
+    }
+
+    private void OnRefreshAwardCell(ScrollerDataType type, CellView cell)
+    {
+        cell.GetComponent<LianQiAwardCell>()?.Display(cell.index);
+    }
+
+    private void PlaySynEffect(LianQiActLevel2 grid)
+    {
+        grid.synUIEffect.Stop();
+        grid.synUIEffect.Play();
+    }
+
+    private void OnDragComplete(int direction)
+    {
+        var M = LianQiManager.Instance;
+        M.isPause = false;
+
+        if (M.nowPlayerInfo == null)
+            return;
+
+        if (!UIManager.Instance.IsOpened<LianQiWin>())
+            return;
+
+        if (!M.IsJoin)
+        {
+            SysNotifyMgr.Instance.ShowTip("ActionUnOpen1");
+            return;
+        }
+
+        if (!M.IsEnergyEnough(M.nowPlayerInfo.Energy))
+        {
+            SysNotifyMgr.Instance.ShowTip("LianQiAct01");
+            M.isUseTool = true;
+            int index = 3;
+            M.nowAssistiveItemNum = index + 1;  // = 4
+            int itemID = M.assistiveItemArr[index];
+            M.nowAssistiveItemId = itemID;
+            UIManager.Instance.OpenWindow<LianQiItemInfoDoubleWin>();
+            return;
+        }
+
+        InitPanel();
+
+        int dir = 0;
+        switch (direction)
+        {
+            case 1: dir = 4; break;   // 鍚戝彸
+            case -1: dir = 3; break;  // 鍚戝乏
+            case 2: dir = 1; break;   // 鍚戜笂
+            case -2: dir = 2; break;  // 鍚戜笅
+        }
+        if (!M.IsCanMove(M.nowGridData, dir))
+        {
+            SysNotifyMgr.Instance.ShowTip("LianqiMoveUnable");
+            return;
+        }
+        M.SendMoveOP(dir);
+    }
+
+    private void OnLianqiPlayerInfoAction()
+    {
+        var M = LianQiManager.Instance;
+        DisplayScore();
+        DisplayEnergy();
+        DisplayAssistiveItem();  // 鍒锋柊杈呭姪閬撳叿鏁伴噺锛堥亾鍏�4浣跨敤鍚庡挨鍏堕渶瑕侊級
+        SecondChange();
+        RefreshGameEndState();
+        awardScroller.m_Scorller.RefreshActiveCellViews();
+
+        int[][] nowGridData = M.nowGridData;
+        int[][] lastGridData = M.lastGridData;
+
+        if (lastGridData.IsNullOrEmpty())
+            return;
+
+        if (M.nowPlayerInfo.OPType == 1)
+        {
+            if (M.nowPlayerInfo.MoveList.Length > 0)
+            {
+                sequence = DOTween.Sequence();
+                bool hasSynEffect = false;
+                for (int i = 0; i < M.nowPlayerInfo.MoveList.Length; i++)
+                {
+                    var move = M.nowPlayerInfo.MoveList[i];
+                    var girdLevel1From = level1Arr[move.Row][move.Col];
+                    var girdLevel2From = level2Arr[move.Row][move.Col];
+                    var girdLevel2To = level2Arr[move.ToRow][move.ToCol];
+                    var StartPosFrom = rectStartPosArr[move.Row][move.Col];
+                    bool isComposite = nowGridData[move.ToRow][move.ToCol] > lastGridData[move.Row][move.Col];
+                    if (isComposite)
+                        hasSynEffect = true;
+                    M.SetTextLv(girdLevel1From.txtLv, lastGridData[move.Row][move.Col]);
+                    M.SetSprite(girdLevel1From.imgItem, lastGridData[move.Row][move.Col]);
+                    M.SetActive(girdLevel2From.imgItem, false);
+                    sequence.Join(girdLevel1From.transform.DOMove(rectStartPosArr[move.ToRow][move.ToCol].position, 0.1f).SetEase(Ease.Linear).OnComplete(() =>
+                    {
+                        M.SetTextLv(girdLevel2To.txtLv, nowGridData[move.ToRow][move.ToCol]);
+                        M.SetSprite(girdLevel2To.imgItem, nowGridData[move.ToRow][move.ToCol]);
+                        M.SetActive(girdLevel1From.imgItem, false);
+                        girdLevel1From.transform.position = StartPosFrom.position;
+
+                        if (isComposite)
+                            PlaySynEffect(girdLevel2To);
+                    }));
+                }
+                if (hasSynEffect)
+                    sequence.AppendInterval(SynEffectDelay);
+                sequence.OnComplete(() =>
+                {
+                    InitPanel();
+                });
+            }
+        }
+        else if (M.nowPlayerInfo.OPType == 2)
+        {
+            if (M.nowAssistiveItemNum == 1)  // 閿婚�犻敜锛氱壒鏁堝姩鐢�
+            {
+                M.isPlayAssistiveItemAnimation = true;
+                var grid = level2Arr[M.useRow1][M.useCol1];
+                grid.hammerUIEffect.Stop();
+                grid.hammerUIEffect.Play();
+                sequence = DOTween.Sequence();
+                sequence.AppendInterval(HammerEffectDelay).OnComplete(() => { InitPanel(); });
+            }
+            else if (M.nowAssistiveItemNum == 2)  // 鍖栬厫鏈斤細鍙屾牸level1浜掓崲
+            {
+                M.isPlayAssistiveItemAnimation = true;
+                var grid1Level1 = level1Arr[M.useRow1][M.useCol1];
+                var grid1Level2 = level2Arr[M.useRow1][M.useCol1];
+                var grid2Level1 = level1Arr[M.useRow2][M.useCol2];
+                var grid2Level2 = level2Arr[M.useRow2][M.useCol2];
+                var startPos1 = rectStartPosArr[M.useRow1][M.useCol1];
+                var startPos2 = rectStartPosArr[M.useRow2][M.useCol2];
+
+                sequence = DOTween.Sequence();
+                sequence.Join(grid1Level1.transform.DOMove(startPos2.position, 0.5f).SetEase(Ease.Linear)
+                    .OnStart(() =>
+                    {
+                        M.SetTextLv(grid1Level1.txtLv, M.lastGridData[M.useRow1][M.useCol1]);
+                        M.SetSprite(grid1Level1.imgItem, M.lastGridData[M.useRow1][M.useCol1]);
+                        M.SetActive(grid1Level2.imgItem, false);
+                    })
+                    .OnComplete(() =>
+                    {
+                        M.SetActive(grid1Level1.imgItem, false);
+                        M.SetSprite(grid1Level2.imgItem, M.nowGridData[M.useRow1][M.useCol1]);
+                        M.SetTextLv(grid1Level2.txtLv, M.nowGridData[M.useRow1][M.useCol1]);
+                        grid1Level1.transform.position = startPos1.position;
+                    }));
+                sequence.Join(grid2Level1.transform.DOMove(startPos1.position, 0.5f).SetEase(Ease.Linear)
+                    .OnStart(() =>
+                    {
+                        M.SetTextLv(grid2Level1.txtLv, M.lastGridData[M.useRow2][M.useCol2]);
+                        M.SetSprite(grid2Level1.imgItem, M.lastGridData[M.useRow2][M.useCol2]);
+                        M.SetActive(grid2Level2.imgItem, false);
+                    })
+                    .OnComplete(() =>
+                    {
+                        M.SetActive(grid2Level1.imgItem, false);
+                        M.SetSprite(grid2Level2.imgItem, M.nowGridData[M.useRow2][M.useCol2]);
+                        M.SetTextLv(grid2Level2.txtLv, M.nowGridData[M.useRow2][M.useCol2]);
+                        grid1Level2.transform.position = startPos2.position;
+                    }));
+                sequence.OnComplete(() => { InitPanel(); });
+            }
+            else if (M.nowAssistiveItemNum == 3)  // 涔濆ぉ鐜勯搧锛氬悎鎴愮壒鏁�
+            {
+                M.isPlayAssistiveItemAnimation = true;
+                var grid = level2Arr[M.useRow1][M.useCol1];
+                sequence = DOTween.Sequence();
+                PlaySynEffect(grid);
+                sequence.AppendInterval(SynEffectDelay).OnComplete(() => { InitPanel(); });
+            }
+            // nowAssistiveItemNum==4(鐜夐湶)锛氭棤妫嬬洏鍔ㄧ敾锛涢亾鍏锋暟閲忓凡鍦ㄦ湰鏂规硶寮�澶村埛鏂�
+        }
+        else if (M.nowPlayerInfo.OPType == 3)  // 閲嶆柊寮�濮嬶細鐩存帴鍒锋柊妫嬬洏
+        {
+            InitPanel();
+        }
+    }
+
+    private void LateUpdate()
+    {
+        var M = LianQiManager.Instance;
+        if (M.nowGridData == null)
+            return;
+
+        if (M.IsDeadLock(M.nowGridData))
+        {
+            if (UIManager.Instance.IsOpened<LianQiRoundSettlementWin>()) return;
+            if (UIManager.Instance.IsOpened<LianQiItemInfoDoubleWin>()) return;
+            if (UIManager.Instance.IsOpened<LianQiItemInfoWin>()) return;
+            if (!M.IsJoin) return;
+            if (M.isWaitReStart) return;
+            if (M.isPause) return;
+            if (M.isUseTool) return;
+            if (!UIManager.Instance.IsOpened<LianQiPopTipWin>())
+                UIManager.Instance.OpenWindow<LianQiPopTipWin>();
+        }
+    }
+
+    /// <summary>
+    /// 閫夋牸纭锛氭寜褰撳墠閬撳叿绫诲瀷鍙戣捣纭寮圭獥鎴栫洿鎺ュ彂閫�
+    /// </summary>
+    private void TryConfirmUseTool()
+    {
+        var M = LianQiManager.Instance;
+        if (M.nowAssistiveItemNum == 1 && M.isStartChoose && M.isChoose1)
+        {
+            if (!M.TryGetCompositeItemInfoByAwardLv(M.nowGridData[M.useRow1][M.useCol1], out LianQiCompositeItemInfo info) || info == null) return;
+            if (!ItemConfig.HasKey(M.nowAssistiveItemId)) return;
+            var itemConfig = ItemConfig.Get(M.nowAssistiveItemId);
+            if (M.isSkip1)
+            {
+                M.isUseTool = false;
+                if (M.IsCanSendUseItem()) M.SendUseOP(1, 0, M.useRow1, M.useCol1);
+                else InitPanel();
+            }
+            else
+            {
+                ConfirmCancel.ToggleConfirmCancelEx(Language.Get("Mail101"), Language.Get("LianQi29", info.Name, itemConfig.ItemName), Language.Get("ConfirmCancel102"), OnConfirm1, OnConfirm1, M.isSkip1);
+            }
+        }
+        else if (M.nowAssistiveItemNum == 2 && M.isStartChoose && M.isChoose1 && M.isChoose2)
+        {
+            if (!M.TryGetCompositeItemInfoByAwardLv(M.nowGridData[M.useRow1][M.useCol1], out LianQiCompositeItemInfo info1) || info1 == null) return;
+            if (!M.TryGetCompositeItemInfoByAwardLv(M.nowGridData[M.useRow2][M.useCol2], out LianQiCompositeItemInfo info2) || info2 == null) return;
+            if (!ItemConfig.HasKey(M.nowAssistiveItemId)) return;
+            var itemConfig = ItemConfig.Get(M.nowAssistiveItemId);
+            if (M.isSkip2)
+            {
+                M.isUseTool = false;
+                if (M.IsCanSendUseItem()) M.SendUseOP(1, 1, M.useRow1, M.useCol1, M.useRow2, M.useCol2);
+                else InitPanel();
+            }
+            else
+            {
+                ConfirmCancel.ToggleConfirmCancelEx(Language.Get("Mail101"), Language.Get("LianQi28", itemConfig.ItemName, info1.Name, info2.Name), Language.Get("ConfirmCancel102"), OnConfirm2, OnConfirm2, M.isSkip2);
+            }
+        }
+        else if (M.nowAssistiveItemNum == 3 && M.isStartChoose && M.isChoose1)
+        {
+            if (!M.TryGetCompositeItemInfoByAwardLv(M.nowGridData[M.useRow1][M.useCol1], out LianQiCompositeItemInfo info) || info == null) return;
+            if (!ItemConfig.HasKey(M.nowAssistiveItemId)) return;
+            var itemConfig = ItemConfig.Get(M.nowAssistiveItemId);
+            if (M.isSkip3)
+            {
+                M.isUseTool = false;
+                if (M.IsCanSendUseItem()) M.SendUseOP(1, 2, M.useRow1, M.useCol1);
+                else InitPanel();
+            }
+            else
+            {
+                ConfirmCancel.ToggleConfirmCancelEx(Language.Get("Mail101"), Language.Get("LianQi29", info.Name, itemConfig.ItemName), Language.Get("ConfirmCancel102"), OnConfirm3, OnConfirm3, M.isSkip3);
+            }
+        }
+    }
+
+    private void OnConfirm1(bool isOk, bool isToggle)
+    {
+        var M = LianQiManager.Instance;
+        M.isSkip1 = isToggle;
+        M.isUseTool = false;
+        if (isOk && M.IsCanSendUseItem()) M.SendUseOP(1, 0, M.useRow1, M.useCol1);
+        else InitPanel();
+    }
+
+    private void OnConfirm2(bool isOk, bool isToggle)
+    {
+        var M = LianQiManager.Instance;
+        M.isSkip2 = isToggle;
+        M.isUseTool = false;
+        if (isOk && M.IsCanSendUseItem()) M.SendUseOP(1, 1, M.useRow1, M.useCol1, M.useRow2, M.useCol2);
+        else InitPanel();
+    }
+
+    private void OnConfirm3(bool isOk, bool isToggle)
+    {
+        var M = LianQiManager.Instance;
+        M.isSkip3 = isToggle;
+        M.isUseTool = false;
+        if (isOk && M.IsCanSendUseItem()) M.SendUseOP(1, 2, M.useRow1, M.useCol1);
+        else InitPanel();
+    }
+
+    private void OnSecondEvent()
+    {
+        SecondChange();
+        RefreshGameEndState();
+    }
+
+    private void SecondChange()
+    {
+        var M = LianQiManager.Instance;
+        if (M.nowPlayerInfo == null)
+        {
+            transEnergyTime.SetActive(false);
+            return;
+        }
+
+        int currentTime = TimeUtility.AllSeconds;
+        int rSecond = M.rSecond;
+        int energyTime = (int)M.nowPlayerInfo.EnergyTime;
+
+        if (energyTime == 0)
+        {
+            transEnergyTime.SetActive(false);
+            return;
+        }
+
+        int resTime = rSecond - (currentTime - energyTime);
+        if (rSecond > 0)
+        {
+            txtEnergyTime.text = TimeUtility.SecondsToDHMS(resTime);
+            transEnergyTime.SetActive(true);
+        }
+        else
+        {
+            transEnergyTime.SetActive(false);
+        }
+    }
+
+    private void DisplayEnergy()
+    {
+        var M = LianQiManager.Instance;
+        if (M.nowPlayerInfo == null)
+            txtEnergy.text = Language.Get("BlessedLand036", 0, M.maxEnergy);
+        else
+            txtEnergy.text = Language.Get("BlessedLand036", M.nowPlayerInfo.Energy, M.maxEnergy);
+    }
+
+    private void DisplayScore()
+    {
+        var M = LianQiManager.Instance;
+        if (M.nowPlayerInfo == null)
+            txtScore.text = Language.Get("LianQi07", 0);
+        else
+            txtScore.text = Language.Get("LianQi07", M.nowPlayerInfo.Score);
+    }
+
+    private void DisplayActivityTime()
+    {
+        var M = LianQiManager.Instance;
+        var act = M.operationCrossAct;
+        if (act == null || act.flowPhases == null || act.flowPhases.Count == 0)
+            return;
+
+        var first = act.flowPhases[0];
+        var last = act.flowPhases[act.flowPhases.Count - 1];
+        string tip = string.Format("{0:D2}/{1:D2} {2:D2}:{3:D2}:00~{4:D2}/{5:D2} {6:D2}:{7:D2}:59",
+            act.startDate.month, act.startDate.day, first.StartHour, first.StartMinute,
+            act.endDate.month, act.endDate.day, last.EndHour, last.EndMinute);
+        txtTip.text = Language.Get("FestivalActivity01", tip);
+
+        string joinRange = string.Empty;
+        for (int i = 0; i < act.flowPhases.Count; i++)
+        {
+            if (act.flowPhases[i].StateValue == 1)
+            {
+                var p = act.flowPhases[i];
+                int totalMin = p.EndHour * 60 + p.EndMinute + 1;
+                joinRange = string.Format("{0:D2}:{1:D2}-{2:D2}:{3:D2}",
+                    p.StartHour, p.StartMinute, (totalMin / 60) % 24, totalMin % 60);
+                break;
+            }
+        }
+        txtTip2.text = Language.Get("LianQi08", joinRange);
+    }
+
+    private void RefreshGameEndState()
+    {
+        var M = LianQiManager.Instance;
+        if (M.operationCrossAct == null)
+        {
+            transGameEnd.SetActive(false);
+            return;
+        }
+
+        bool isJoin = M.IsJoin;
+        transGameEnd.SetActive(!isJoin);
+        if (!isJoin)
+        {
+            txtEnd.text = M.IsInPublicityPhase ? Language.Get("LianQi31") : Language.Get("LianQi32");
+        }
+    }
+
+    private void InitPanel()
+    {
+        if (this == null) return;  // 闃插尽锛欳lock 鍥炶皟鍦ㄧ獥鍙i攢姣佸悗瑙﹀彂
+        var M = LianQiManager.Instance;
+        sequence.Kill();
+
+        rectStartPosArr = RectTransListToArr(rectStartPos);
+        level1Arr = LianQiActLevelListToArr(level1);
+        level2Arr = LianQiActLevelListToArr(level2);
+        M.isPlayAssistiveItemAnimation = false;
+        M.isUseTool = false;
+        M.isStartChoose = false;
+        M.isChoose1 = false;
+        M.isChoose2 = false;
+
+        for (int i = 0; i < M.MaxGridCount; i++)
+        {
+            M.SetActive(level1[i].imgItem, false);
+            M.SetActive(level2[i].imgItem, false);
+            level1[i].transform.localPosition = rectStartPos[i].transform.localPosition;
+            level2[i].transform.localPosition = rectStartPos[i].transform.localPosition;
+            level2[i].synUIEffect.Stop();
+            level2[i].hammerUIEffect.Stop();
+            level2[i].imgChoose.SetActive(false);
+            level2[i].isChooseShow = false;
+            level2[i].imgChoose.SetActive(level2[i].isChooseShow);
+        }
+
+        if (M.nowPlayerInfo == null)
+            return;
+        int[][] gridData = M.nowGridData;
+
+        if (!gridData.IsNullOrEmpty())
+        {
+            for (int i = 0; i < gridData.Length; i++)
+            {
+                for (int j = 0; j < gridData[i].Length; j++)
+                {
+                    if (gridData[i][j] > 0)
+                    {
+                        level2Arr[i][j].imgItem.SetSprite(StringUtility.Concat("LianQiCompositeItemIcon", "_", gridData[i][j].ToString()));
+                        M.SetTextLv(level2Arr[i][j].txtLv, gridData[i][j]);
+                        M.SetActive(level2Arr[i][j].imgItem, true);
+                    }
+                }
+            }
+        }
+        // 姣忔鍒锋柊妫嬬洏鍚庡埛鏂拌緟鍔╅亾鍏锋樉绀�
+        DisplayAssistiveItem();
+    }
+
+    /// <summary>
+    /// 澶栭儴閬撳叿鏁伴噺鍙樺寲鏃跺埛鏂拌緟鍔╅亾鍏锋爮锛堥偖浠�/浠诲姟/璐拱绛夎幏寰楄緟鍔╅亾鍏凤級
+    /// </summary>
+    private void OnRefreshItemCountEvent(PackType type, int index, int itemID)
+    {
+        var M = LianQiManager.Instance;
+        if (M.assistiveItemArr.IsNullOrEmpty()) return;
+        // 浠呭埛鏂帮細閬撳叿鑳屽寘鏁翠綋鍙樺寲(type==Item) 鎴� 鍙樺寲鐗╁搧鏄緟鍔╅亾鍏蜂箣涓�
+        if (type != PackType.Item && !M.assistiveItemArr.Contains(itemID)) return;
+        DisplayAssistiveItem();
+    }
+
+    /// <summary>
+    /// 鍒锋柊杈呭姪閬撳叿鏍忔樉绀�
+    /// </summary>
+    private void DisplayAssistiveItem()
+    {
+        var M = LianQiManager.Instance;
+        if (M.assistiveItemArr.IsNullOrEmpty()) return;
+        for (int i = 0; i < assistiveItemCells.Count; i++)
+        {
+            int index = i;
+            if (index < M.assistiveItemArr.Length)
+            {
+                int itemID = M.assistiveItemArr[index];
+                long count = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
+                assistiveItemCells[index].Init(new ItemCellModel(itemID, false, count));
+                assistiveItemCells[index].countText.SetActive(true);
+                assistiveItemCells[index].countText.text = count.ToString();
+                assistiveItemCells[index].button.SetListener(() =>
+                {
+                    if (M.isPlayAssistiveItemAnimation) return;
+                    M.isUseTool = true;
+                    M.nowAssistiveItemNum = index + 1;
+                    M.nowAssistiveItemId = itemID;
+                    if (M.nowAssistiveItemNum == 4)
+                        UIManager.Instance.OpenWindow<LianQiItemInfoDoubleWin>();
+                    else
+                        UIManager.Instance.OpenWindow<LianQiItemInfoWin>();
+                });
+            }
+            else
+            {
+                assistiveItemCells[i].SetActive(false);
+            }
+        }
+    }
+
+    private RectTransform[][] RectTransListToArr(List<RectTransform> list)
+    {
+        var M = LianQiManager.Instance;
+        int maxRowGrid = M.MaxGridRows;
+        int maxLineGrid = M.MaxGridCols;
+
+        RectTransform[][] arr = new RectTransform[maxRowGrid][];
+        for (int i = 0; i < maxRowGrid; i++)
+        {
+            arr[i] = new RectTransform[maxLineGrid];
+        }
+
+        int index = 0;
+        for (int i = 0; i < maxRowGrid; i++)
+        {
+            for (int j = 0; j < maxLineGrid; j++)
+            {
+                arr[i][j] = list[index];
+                index++;
+            }
+        }
+        return arr;
+    }
+
+    private LianQiActLevel1[][] LianQiActLevelListToArr(List<LianQiActLevel1> list)
+    {
+        var M = LianQiManager.Instance;
+        int maxRowGrid = M.MaxGridRows;
+        int maxLineGrid = M.MaxGridCols;
+
+        LianQiActLevel1[][] arr = new LianQiActLevel1[maxRowGrid][];
+        for (int i = 0; i < maxRowGrid; i++)
+        {
+            arr[i] = new LianQiActLevel1[maxLineGrid];
+        }
+
+        int index = 0;
+        for (int i = 0; i < maxRowGrid; i++)
+        {
+            for (int j = 0; j < maxLineGrid; j++)
+            {
+                arr[i][j] = list[index];
+                index++;
+            }
+        }
+        return arr;
+    }
+
+    private LianQiActLevel2[][] LianQiActLevelListToArr(List<LianQiActLevel2> list)
+    {
+        var M = LianQiManager.Instance;
+        int maxRowGrid = M.MaxGridRows;
+        int maxLineGrid = M.MaxGridCols;
+
+        LianQiActLevel2[][] arr = new LianQiActLevel2[maxRowGrid][];
+        for (int i = 0; i < maxRowGrid; i++)
+        {
+            arr[i] = new LianQiActLevel2[maxLineGrid];
+        }
+
+        int index = 0;
+        for (int i = 0; i < maxRowGrid; i++)
+        {
+            for (int j = 0; j < maxLineGrid; j++)
+            {
+                arr[i][j] = list[index];
+                index++;
+            }
+        }
+        return arr;
+    }
+}
diff --git a/Main/System/LianQi/LianQiWin.cs.meta b/Main/System/LianQi/LianQiWin.cs.meta
new file mode 100644
index 0000000..45fd809
--- /dev/null
+++ b/Main/System/LianQi/LianQiWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: af0caad58ecd61d4698d52b34c9b1bbc
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiZhanLingCell.cs b/Main/System/LianQi/LianQiZhanLingCell.cs
new file mode 100644
index 0000000..b72de9e
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingCell.cs
@@ -0,0 +1,126 @@
+锘�
+using System.Collections.Generic;
+using UnityEngine;
+
+public class LianQiZhanLingCell : CellView
+{
+    [SerializeField] ItemCell freeItemCell;
+    [SerializeField] Transform freeGotRect;
+    [SerializeField] Transform baseCanGetAwardRect;
+
+    [SerializeField] ItemCell[] paidAwards;
+    [SerializeField] Transform[] paidGotRects;
+    [SerializeField] Transform[] paidCanGetRects;
+    [SerializeField] Transform[] paidLockRects;
+    [SerializeField] ItemCell[] paidHAwards;
+    [SerializeField] Transform[] paidHGotRects;
+    [SerializeField] Transform[] paidHCanGetRects;
+    [SerializeField] Transform[] paidHLockRects;
+    [SerializeField] Transform upProcssBGRect;
+    [SerializeField] Transform upProcessRect;
+    [SerializeField] Transform downProcssBGRect;
+    [SerializeField] Transform downProcessRect;
+    [SerializeField] TextEx txtUnlockLV;
+    [SerializeField] Transform mask;
+
+    LianQiManager manager => LianQiManager.Instance;
+
+    public void Display(int needValue)
+    {
+        var totalValue = BattlePassManager.Instance.GetTotalValue(manager.ZhanLingType);
+        int zhanlingId = ZhanlingConfig.GetTypeToIDDict(manager.ZhanLingType)[needValue];
+        int freeState = manager.GetZhanLingFreeGiftState(needValue);
+        int paidState = manager.GetZhanLingPaidGiftState(needValue);
+        int paidHState = manager.GetZhanLingPaidGiftHState(needValue);
+        int buyState = manager.GetZhanLingBuyState();
+
+        freeGotRect.SetActive(freeState == 2);
+        baseCanGetAwardRect.SetActive(freeState == 1);
+        mask.SetActive(freeState == 0);
+
+        for (int i = 0; i < paidAwards.Length; i++)
+        {
+            bool isBuy = buyState == 1 || buyState == 3;
+            paidGotRects[i].SetActive(paidState == 2);
+            paidCanGetRects[i].SetActive(paidState == 1 && isBuy);
+            paidLockRects[i].SetActive(!isBuy || paidState == 0);
+        }
+        for (int i = 0; i < paidHAwards.Length; i++)
+        {
+            bool isBuy = buyState == 2 || buyState == 3;
+            paidHGotRects[i].SetActive(paidHState == 2);
+            paidHCanGetRects[i].SetActive(paidHState == 1 && isBuy);
+            paidHLockRects[i].SetActive(!isBuy || paidHState == 0);
+        }
+        txtUnlockLV.text = needValue.ToString();
+
+        // 杩涘害鏉℃樉绀洪�昏緫
+        var ids = new List<int>();
+        foreach (int val in ZhanlingConfig.GetTypeToIDDict(manager.ZhanLingType).Values)
+        {
+            ids.Add(val);
+        }
+        ids.Sort();
+        upProcssBGRect.SetActive(ids[0] != zhanlingId);
+        upProcessRect.SetActive(freeState != 0);
+
+        downProcssBGRect.SetActive(ids[ids.Count - 1] != zhanlingId);
+        var nextConfig = ZhanlingConfig.Get(zhanlingId + 1);
+        downProcessRect.SetActive(nextConfig != null && totalValue >= nextConfig.NeedValue);
+
+        // 灞曠ずItemCell
+        var config = ZhanlingConfig.Get(zhanlingId);
+        if (config == null)
+            return;
+
+        // 鍏嶈垂濂栧姳
+        int freeItemId = config.FreeRewardItemList[0][0];
+        freeItemCell.Init(new ItemCellModel(freeItemId, false, config.FreeRewardItemList[0][1]));
+        freeItemCell.button.onClick.RemoveAllListeners();
+        freeItemCell.button.onClick.AddListener(() =>
+        {
+            if (freeState == 1)
+                manager.HaveAllZhanLingGift();
+            else
+                ItemTipUtility.Show(freeItemId);
+        });
+
+        // 鏅�氬鍔�
+        for (int i = 0; i < paidAwards.Length; i++)
+        {
+            if (i >= config.ZLRewardItemList.Length)
+                continue;
+            int itemId2 = config.ZLRewardItemList[i][0];
+            paidAwards[i].Init(new ItemCellModel(itemId2, false, config.ZLRewardItemList[i][1]));
+            paidAwards[i].button.onClick.RemoveAllListeners();
+            paidAwards[i].button.onClick.AddListener(() =>
+            {
+                if (buyState == 0 || buyState == 2)
+                    manager.ShowZhanLingHBuy(manager.ZhanLingType, 1);
+                else if (paidState == 1)
+                    manager.HaveAllZhanLingGift();
+                else
+                    ItemTipUtility.Show(itemId2);
+            });
+        }
+
+        // 楂樼骇濂栧姳
+        for (int i = 0; i < paidHAwards.Length; i++)
+        {
+            if (i >= config.ZLRewardItemListH.Length)
+                continue;
+            int itemId3 = config.ZLRewardItemListH[i][0];
+            paidHAwards[i].Init(new ItemCellModel(itemId3, false, config.ZLRewardItemListH[i][1]));
+            paidHAwards[i].button.onClick.RemoveAllListeners();
+            paidHAwards[i].button.onClick.AddListener(() =>
+            {
+                if (buyState == 0 || buyState == 1)
+                    manager.ShowZhanLingHBuy(manager.ZhanLingType, 2);
+                else if (paidHState == 1)
+                    manager.HaveAllZhanLingGift();
+                else
+                    ItemTipUtility.Show(itemId3);
+            });
+        }
+    }
+}
diff --git a/Main/System/LianQi/LianQiZhanLingCell.cs.meta b/Main/System/LianQi/LianQiZhanLingCell.cs.meta
new file mode 100644
index 0000000..8e58322
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 01d25e56104ca3641bc593d2d438f7fc
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiZhanLingPreviewCell.cs b/Main/System/LianQi/LianQiZhanLingPreviewCell.cs
new file mode 100644
index 0000000..069be3d
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingPreviewCell.cs
@@ -0,0 +1,26 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+public class LianQiZhanLingPreviewCell : CellView
+{
+    [SerializeField] ItemCell items;
+
+    public void Display(int rowIndex, List<Item> showItems)
+    {
+        if (showItems == null || rowIndex >= showItems.Count)
+        {
+            items.SetActive(false);
+            return;
+        }
+
+        items.SetActive(true);
+        int itemID = showItems[rowIndex].id;
+        long count = showItems[rowIndex].countEx;
+        items.Init(new ItemCellModel(itemID, false, count));
+        items.button.onClick.RemoveAllListeners();
+        items.button.onClick.AddListener(() =>
+        {
+            ItemTipUtility.Show(itemID);
+        });
+    }
+}
diff --git a/Main/System/LianQi/LianQiZhanLingPreviewCell.cs.meta b/Main/System/LianQi/LianQiZhanLingPreviewCell.cs.meta
new file mode 100644
index 0000000..82d2a56
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingPreviewCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2665fd70d1cbb434daeaac801ad084e5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiZhanLingPreviewWin.cs b/Main/System/LianQi/LianQiZhanLingPreviewWin.cs
new file mode 100644
index 0000000..e75304d
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingPreviewWin.cs
@@ -0,0 +1,115 @@
+锘縰sing System;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class LianQiZhanLingPreviewWin : UIBase
+{
+    [SerializeField] ScrollerController allScroller; //鎵�鏈夌瓑绾у鍔�
+    [SerializeField] Button btnClose;
+    [SerializeField] Button btnBuy;
+    [SerializeField] Text txtBuy;
+    [SerializeField] Text txtPercentage;
+    [SerializeField] Image imgMoney;
+    [SerializeField] Text txtMoney;
+
+    [SerializeField] Image imgTitleLow;
+    [SerializeField] Image imgTitleHigh;
+    [SerializeField] ScrollerController nowScroller; //褰撳墠绛夌骇濂栧姳
+    LianQiManager manager => LianQiManager.Instance;
+    int ctgID;
+    int showCnt = 10;
+
+    protected override void OnPreOpen()
+    {
+        allScroller.OnRefreshCell += OnScrollerRefreshAllCell;
+        nowScroller.OnRefreshCell += OnScrollerRefreshNowCell;
+        btnClose.AddListener(CloseWindow);
+        btnBuy.AddListener(OnBuyClick);
+        Display();
+    }
+
+    protected override void OnPreClose()
+    {
+        allScroller.OnRefreshCell -= OnScrollerRefreshAllCell;
+        nowScroller.OnRefreshCell -= OnScrollerRefreshNowCell;
+
+    }
+
+    void OnBuyClick()
+    {
+        if (ctgID == 0)
+        {
+            CloseWindow();
+            return;
+        }
+        CloseWindow();
+        RechargeManager.Instance.CTG(ctgID);
+    }
+
+    protected override void NextFrameAfterOpen()
+    {
+        CreateAllScroller();
+        CreateNowScroller();
+    }
+
+    private void OnScrollerRefreshAllCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell as LianQiZhanLingPreviewCell;
+        _cell.Display(_cell.index, manager.GetPreviewGiftAllItem(showCnt));
+    }
+
+    private void OnScrollerRefreshNowCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell as LianQiZhanLingPreviewCell;
+        _cell.Display(_cell.index, manager.GetPreviewGiftNowItem(showCnt));
+    }
+
+    void Display()
+    {
+        ctgID = manager.GetPreviewCtgID();
+        var config = CTGConfig.Get(ctgID);
+        if (config == null)
+        {
+            CloseWindow();
+            return;
+        }
+        OrderInfoConfig orderCfg;
+        RechargeManager.Instance.TryGetOrderInfo(ctgID, out orderCfg);
+
+        txtPercentage.text = Language.Get("TimingGift02", config.Percentage);
+        txtBuy.text = Language.Get("PayMoneyNum", UIHelper.GetMoneyFormat(orderCfg.PayRMBNumOnSale));
+        imgMoney.SetIconWithMoneyType(config.MoneyType);
+        txtMoney.text = config.GainGold.ToString();
+
+        imgTitleLow.SetActive(manager.previewGiftType == 1);
+        imgTitleHigh.SetActive(manager.previewGiftType == 2);
+    }
+
+    private void CreateAllScroller()
+    {
+        var items = manager.GetPreviewGiftAllItem(showCnt);
+        allScroller.Refresh();
+        if (items == null || items.Count == 0)
+            return;
+        for (int i = 0; i < items.Count; i++)
+        {
+            allScroller.AddCell(ScrollerDataType.Header, i);
+        }
+        allScroller.Restart();
+    }
+
+    private void CreateNowScroller()
+    {
+        var items = manager.GetPreviewGiftNowItem(showCnt);
+        nowScroller.Refresh();
+        if (items == null || items.Count == 0)
+            return;
+        for (int i = 0; i < items.Count; i++)
+        {
+            nowScroller.AddCell(ScrollerDataType.Header, i);
+        }
+        nowScroller.Restart();
+    }
+
+
+}
diff --git a/Main/System/LianQi/LianQiZhanLingPreviewWin.cs.meta b/Main/System/LianQi/LianQiZhanLingPreviewWin.cs.meta
new file mode 100644
index 0000000..2d8328d
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingPreviewWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a8bb1a13a5649904dbc930f266fec87e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/LianQiZhanLingWin.cs b/Main/System/LianQi/LianQiZhanLingWin.cs
new file mode 100644
index 0000000..fd281db
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingWin.cs
@@ -0,0 +1,114 @@
+锘�
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+// 姝﹀皢鐧诲満鎴樹护
+public class LianQiZhanLingWin : UIBase
+{
+    [SerializeField] ScrollerController scroller;
+    [SerializeField] ButtonEx closeBtn;
+    [SerializeField] ButtonEx btnPaid;
+    [SerializeField] ButtonEx btnPaidH;
+
+    [SerializeField] ImageEx imgPaidLock;
+    [SerializeField] ImageEx imgPaidLockH;
+
+    [SerializeField] Text txtNowNeedValue;
+
+    [SerializeField] Text txtPaidLock;
+    [SerializeField] Text txtPaidLockH;
+    [SerializeField] Text txtPaidLockBuyed;
+    [SerializeField] Text txtPaidLockBuyedH;
+
+    LianQiManager manager => LianQiManager.Instance;
+    protected override void InitComponent()
+    {
+        btnPaid.AddListener(OnPaidClick);
+        btnPaidH.AddListener(OnPaidHClick);
+        closeBtn.AddListener(CloseWindow);
+    }
+
+
+    protected override void OnPreOpen()
+    {
+        manager.OnZhanLingUpdateEvent += OnZhanLingUpdateEvent;
+        scroller.OnRefreshCell += OnScrollerRefreshCell;
+        Initialize();
+        Display();
+    }
+
+    protected override void OnPreClose()
+    {
+        manager.OnZhanLingUpdateEvent -= OnZhanLingUpdateEvent;
+        scroller.OnRefreshCell -= OnScrollerRefreshCell;
+    }
+
+    private void Display()
+    {
+        //0 娌¤喘涔拌繘闃跺拰鐜勭骇 1 璐拱浜嗚繘闃舵病涔扮巹绾� 2 璐拱浜嗙巹绾ф病涔拌繘闃� 3 璐拱浜嗚繘闃跺拰鐜勭骇
+        int buyState = manager.GetZhanLingBuyState();
+        scroller.m_Scorller.RefreshActiveCellViews();
+        txtNowNeedValue.text = Language.Get("LianQi24", manager.GetZhanLingValue());
+
+        // imgPaidLock: 鏈喘涔拌繘闃�(type 1)涓旀湭璐拱鐜勭骇(type 2)鏃舵樉绀洪攣锛屾垨宸茶喘涔扮巹绾т絾鏈喘涔拌繘闃舵椂鏄剧ず閿�
+        imgPaidLock.SetActive(buyState == 0 || buyState == 2);
+        // imgPaidLockH: 鏈喘涔扮巹绾т笖鏈喘涔拌繘闃舵椂鏄剧ず閿侊紝鎴栧凡璐拱杩涢樁浣嗘湭璐拱鐜勭骇鏃舵樉绀洪攣
+        imgPaidLockH.SetActive(buyState == 0 || buyState == 1);
+
+        txtPaidLock.SetActive(buyState == 0 || buyState == 2);
+        txtPaidLockBuyed.SetActive(buyState == 1 || buyState == 3);
+
+        txtPaidLockH.SetActive(buyState == 0 || buyState == 1);
+        txtPaidLockBuyedH.SetActive(buyState == 2 || buyState == 3);
+    }
+
+    private void OnZhanLingUpdateEvent()
+    {
+        Display();
+        scroller.m_Scorller.RefreshActiveCellViews();
+    }
+
+    private void OnScrollerRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell as LianQiZhanLingCell;
+        _cell.Display(_cell.index);
+    }
+
+    private void Initialize()
+    {
+        var cellList = new List<int>();
+        foreach (int key in ZhanlingConfig.GetTypeToIDDict(manager.ZhanLingType).Keys)
+        {
+            cellList.Add(key);
+        }
+        scroller.Refresh();
+        for (int i = 0; i < cellList.Count; i++)
+        {
+            scroller.AddCell(ScrollerDataType.Header, cellList[i]);
+        }
+        scroller.Restart();
+
+        var data = BattlePassManager.Instance.GetBattlePassData(manager.ZhanLingType);
+        var totalValue = BattlePassManager.Instance.GetTotalValue(manager.ZhanLingType);
+        scroller.JumpIndex(BattlePassManager.Instance.JumpIndex(data, manager.ZhanLingType, totalValue));
+    }
+
+    void OnPaidClick()
+    {
+        int buyState = manager.GetZhanLingBuyState();
+        // 宸茶喘涔拌繘闃�(type 1)鎴栧凡璐拱鐜勭骇鍜岃繘闃�(buyState == 3)鏃朵笉鍐嶈烦杞�
+        if (buyState == 1 || buyState == 3)
+            return;
+        manager.ShowZhanLingHBuy(manager.ZhanLingType, 1);
+    }
+
+    void OnPaidHClick()
+    {
+        int buyState = manager.GetZhanLingBuyState();
+        // 宸茶喘涔扮巹绾�(type 2)鎴栧凡璐拱鐜勭骇鍜岃繘闃�(buyState == 3)鏃朵笉鍐嶈烦杞�
+        if (buyState == 2 || buyState == 3)
+            return;
+        manager.ShowZhanLingHBuy(manager.ZhanLingType, 2);
+    }
+}
diff --git a/Main/System/LianQi/LianQiZhanLingWin.cs.meta b/Main/System/LianQi/LianQiZhanLingWin.cs.meta
new file mode 100644
index 0000000..1ea4b6d
--- /dev/null
+++ b/Main/System/LianQi/LianQiZhanLingWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2c4d75e9485645649935520c73ea3dc8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/LianQi/OperationLianQiCross.cs b/Main/System/LianQi/OperationLianQiCross.cs
new file mode 100644
index 0000000..f987ab4
--- /dev/null
+++ b/Main/System/LianQi/OperationLianQiCross.cs
@@ -0,0 +1,162 @@
+锘縰sing System;
+using System.Collections.Generic;
+using UnityEngine;
+
+//杞洖娈挎椿鍔�
+public class OperationLianQiCross : OperationBase
+{
+    string m_ServerIDRangeInfo;    //寮�鏀捐娲诲姩鐨勬湇鍔″櫒ID鑼冨洿鍒楄〃锛宩son鏍煎紡 [[IDA, IDB], ...]锛� [] 涓哄叏鏈�
+    public string m_ServerListStr;
+    public int m_GroupValue1;    // 娲诲姩姒滃崟鍒嗙粍鍊�1锛岀敤浜庢煡璇㈠搴旀鍗�
+    public int m_CrossServerID;    // 鎵�灞炶法鏈嶆湇鍔″櫒ID
+
+    public int CfgID;
+    public int ActFuncType;
+    public int ActTempID;
+    public ActTypeConfig actTempConfig;
+
+    public string ActFlow;
+    public List<ActTimeFlowConfig> flowPhases;
+
+    public override bool SatisfyOpenCondition()
+    {
+        return PlayerDatas.Instance.baseData.LV >= limitLv;
+    }
+
+    //鏄剧ず鍙備笌鏃堕棿
+    public string ToDisplayJoinTime()
+    {
+        if (flowPhases == null) return string.Empty;
+        var textBuilder = OperationTimeHepler.textBuilder;
+        textBuilder.Length = 0;
+        bool found = false;
+        for (int i = 0; i < flowPhases.Count; i++)
+        {
+            if (flowPhases[i].StateValue == 1)
+            {
+                textBuilder.Append(string.Format(" {0}:{1}", flowPhases[i].StartHour.ToString("D2"), flowPhases[i].StartMinute.ToString("D2")));
+                textBuilder.Append(" - ");
+                textBuilder.Append(string.Format(" {0}:{1}", flowPhases[i].EndHour.ToString("D2"), flowPhases[i].EndMinute.ToString("D2")));
+                found = true;
+                break;
+            }
+        }
+        return found ? textBuilder.ToString() : string.Empty;
+    }
+
+    public override string ToDisplayTime()
+    {
+        var textBuilder = OperationTimeHepler.textBuilder;
+        textBuilder.Length = 0;
+        textBuilder.Append(startDate.ToDisplay(false));
+        textBuilder.Append(" - ");
+        textBuilder.Append(endDate.ToDisplay(false));
+        return textBuilder.ToString();
+    }
+
+    //缁撴灉灞曠ず鏃堕棿 杞负鍊掕鏃讹紝浠ヨ法鏈嶆椂闂翠负鍑�
+    public string ShowResultTime()
+    {
+        var endDataTime = TimeUtility.GetDayStartTime(TimeUtility.CrossServerNow.Year, TimeUtility.CrossServerNow.Month, TimeUtility.CrossServerNow.Day).AddDays(1);
+        return TimeUtility.SecondsToHMS((int)(endDataTime - TimeUtility.CrossServerNow).TotalSeconds);
+    }
+
+    //鏄惁灞曠ず缁撴灉鏃堕棿, 浠ヨ法鏈嶆椂闂翠负鍑�(鍓嶆彁涓轰互鍒ゆ柇鏄椿鍔ㄤ腑)
+    public bool IsShowResultTime()
+    {
+        var endJoinDateTime = new DateTime(endDate.year, endDate.month, endDate.day, joinEndHour, joinEndMinute, 0);
+        return TimeUtility.CrossServerNow >= endJoinDateTime;
+    }
+
+    public int GetCurrentPhaseState()
+    {
+        if (flowPhases == null || flowPhases.Count == 0) return 0;
+        var startDateDt = new DateTime(startDate.year, startDate.month, startDate.day);
+        int day = (TimeUtility.CrossServerNow.Date - startDateDt).Days + 1;
+        if (day < 1) return 0;
+        if (TimeUtility.CrossServerNow.Date > new DateTime(endDate.year, endDate.month, endDate.day)) return 0;
+        int nowTotalMin = (day - 1) * 1440 + TimeUtility.CrossServerNow.Hour * 60 + TimeUtility.CrossServerNow.Minute;
+        for (int i = 0; i < flowPhases.Count; i++)
+        {
+            var p = flowPhases[i];
+            int phaseStartMin = (p.StartDay - 1) * 1440 + p.StartHour * 60 + p.StartMinute;
+            int phaseEndMin = (p.EndDay - 1) * 1440 + p.EndHour * 60 + p.EndMinute;
+            if (phaseStartMin <= nowTotalMin && nowTotalMin <= phaseEndMin)
+                return p.StateValue;
+        }
+        return 0;
+    }
+    public bool IsInJoinPhase() { return GetCurrentPhaseState() == 1; }
+    public bool IsInPublicityPhase() { return GetCurrentPhaseState() == 90; }
+    public bool IsInAwardPhase() { return GetCurrentPhaseState() == 99; }
+
+    public List<int> GetServerIDList()
+    {
+        List<int> serverIDList = new List<int>();
+        if (string.IsNullOrEmpty(m_ServerIDRangeInfo) || m_ServerIDRangeInfo == "[]")
+        {
+            return serverIDList;
+        }
+
+        LitJson.JsonData serverArray = LitJson.JsonMapper.ToObject(m_ServerIDRangeInfo);
+        for (int i = 0; i < serverArray.Count; i++)
+        {
+            LitJson.JsonData serverInfo = serverArray[i];
+            if (serverInfo.IsArray)
+            {
+                int startID = (int)serverInfo[0];
+                int endID = serverInfo.Count > 1 ? (int)serverInfo[1] : startID;
+                for (int j = startID; j <= endID; j++)
+                {
+                    serverIDList.Add(j);
+                }
+            }
+            else
+            {
+                serverIDList.Add((int)serverInfo);
+            }
+        }
+        return serverIDList;
+    }
+
+    public override void Reset()
+    {
+        base.Reset();
+        CfgID = 0;
+        ActFuncType = 0;
+        ActTempID = 0;
+        actTempConfig = null;
+        ActFlow = null;
+        flowPhases = null;
+        m_CrossServerID = 0;
+    }
+
+    public void ParseCrossActLianqiInfo(HAA90_tagSCActLianqiInfo package)
+    {
+        m_ServerIDRangeInfo = package.ServerIDInfo;
+        m_ServerListStr = UIHelper.GetServers(m_ServerIDRangeInfo);
+        m_GroupValue1 = (int)package.ZoneID;
+        m_CrossServerID = (int)package.CrossServerID;
+
+        CfgID = package.CfgID;
+        var actTime = ActTimeConfig.Get(CfgID);
+        if (actTime == null)
+        {
+            Debug.LogError(string.Format("ActTimeConfig 缂哄け CfgID={0}", CfgID));
+            return;
+        }
+
+        ActNum = actTime.ActNum;
+        ActFuncType = actTime.ActFuncType;
+        ActTempID = actTime.ActTempID;
+        actTempConfig = ActTypeConfig.GetConfig(ActFuncType, ActTempID);
+        if (actTempConfig == null)
+        {
+            Debug.LogError(string.Format("ActTypeConfig 缂哄け ActFuncType={0} ActTempID={1}", ActFuncType, ActTempID));
+            return;
+        }
+
+        ActFlow = actTime.ActFlow;
+        flowPhases = ActTimeFlowConfig.GetByActFlow(ActFlow);
+    }
+}
diff --git a/Main/System/LianQi/OperationLianQiCross.cs.meta b/Main/System/LianQi/OperationLianQiCross.cs.meta
new file mode 100644
index 0000000..6ae5561
--- /dev/null
+++ b/Main/System/LianQi/OperationLianQiCross.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d3150b2cf4acd60448a57d04ba4c924d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Main/HomeWin.cs b/Main/System/Main/HomeWin.cs
index 7f03812..7980038 100644
--- a/Main/System/Main/HomeWin.cs
+++ b/Main/System/Main/HomeWin.cs
@@ -79,6 +79,7 @@
     [SerializeField] GradientText festivalTitleText;
     [SerializeField] OutlineEx festivalTitleOutline;
     [SerializeField] Button superVIPBtn;
+    [SerializeField] Button lianQiBtn;
 
     //鍧愰獞
     [SerializeField] Image horseBGImg;
@@ -245,6 +246,10 @@
        {
            UIManager.Instance.OpenWindow<SuperVIPWin>();
        });
+        lianQiBtn.AddListener(() =>
+        {
+            UIManager.Instance.OpenWindow<LianQiWin>();
+        });
     }
 
 
@@ -314,6 +319,7 @@
         DisplayHeroSkinFlash();
         DisplayFestivalActivity();
         DisplaySuperVipBtn();
+        DisplayLianQiButton();
         DelayPlayMusic().Forget();
 
     }
@@ -371,6 +377,10 @@
         {
             DisplayHeroSkinFlash();
         }
+        else if (type == OperationType.LianQiCross)
+        {
+            DisplayLianQiButton();
+        }
     }
 
     private void OpenServerActivityStateChange()
@@ -381,6 +391,7 @@
         DisplayGalaBtn();
         DisplayFestivalActivity();
         DisplayHeroSkinFlash();
+        DisplayLianQiButton();
     }
 
     private void OnShowGiftIdListAddEvent()
@@ -896,6 +907,10 @@
         {
             DisplayHeroSkinFlash();
         }
+        else if (funcId == (int)FuncOpenEnum.LianQi)
+        {
+            DisplayLianQiButton();
+        }
     }
 
     private void OnUpdateFirstChargeInfo()
@@ -1022,6 +1037,12 @@
         superVIPBtn.SetActive(isOpen);
         SuperVipManager.Instance.UpdateRedpoint();
     }
+
+    void DisplayLianQiButton()
+    {
+        bool isOpen = LianQiManager.Instance.IsLianQiOpen();
+        lianQiBtn.SetActive(isOpen);
+    }
 }
 
 
diff --git a/Main/System/OpenServerActivity/OperationTimeHepler.cs b/Main/System/OpenServerActivity/OperationTimeHepler.cs
index fa71e93..4e86ad7 100644
--- a/Main/System/OpenServerActivity/OperationTimeHepler.cs
+++ b/Main/System/OpenServerActivity/OperationTimeHepler.cs
@@ -120,6 +120,30 @@
         }
     }
 
+    public void UpdateCrossActLianqiInfo(HAA90_tagSCActLianqiInfo package)
+    {
+        OperationBase operationBase = null;
+        operationDict.TryGetValue(OperationType.LianQiCross, out operationBase);
+        if (string.IsNullOrEmpty(package.StartDate) || string.IsNullOrEmpty(package.EndtDate))
+        {
+            ForceStopOperation(OperationType.LianQiCross);
+            return;
+        }
+
+        if (operationBase == null)
+        {
+            operationBase = new OperationLianQiCross();
+            operationDict.Add(OperationType.LianQiCross, operationBase);
+        }
+        OperationLianQiCross operation = operationBase as OperationLianQiCross;
+        operation.Reset();
+        operation.startDate = ParseOperationDate(package.StartDate);
+        operation.endDate = ParseOperationDate(package.EndtDate);
+        operation.ParseCrossActLianqiInfo(package);
+
+        operationTimeUpdateEvent?.Invoke(OperationType.LianQiCross);
+    }
+
     public void UpdateActLunhuidianInfo(HAA88_tagMCActLunhuidianInfo package)
     {
         OperationBase operationBase = null;
@@ -758,5 +782,6 @@
     FestivalActivity_CheckIn = 9,   //鑺傛棩娲诲姩-绛惧埌
     FestivalActivity_Mission = 10,   //鑺傛棩娲诲姩-浠诲姟
     HeroSkinFlashSale = 11, //鏃惰鐗瑰崠
+    LianQiCross = 12,       //鐐煎櫒澶т細璺ㄦ湇
     max,
 }
\ No newline at end of file
diff --git a/Main/System/Redpoint/MainRedDot.cs b/Main/System/Redpoint/MainRedDot.cs
index ed6ecab..0ccf6b2 100644
--- a/Main/System/Redpoint/MainRedDot.cs
+++ b/Main/System/Redpoint/MainRedDot.cs
@@ -136,7 +136,6 @@
     public const int FairyEmbleManageRepoint = 462;//浠欑洘寰界珷绠$悊鍏ュ彛绾㈢偣
 
     public const int YunShiRepoint = 464; //杩愬娍
-    public const int LianQiRepoint = 465; //浠欏尃澶т細
     public const int FairySiegeRepoint = 466; //浠欑洘鏀诲煄鎴�
     public const int MailRepoint = 467; //閭
     public const int FirstChargeRepoint = 468; //棣栧厖
@@ -161,6 +160,7 @@
     public const int SuperVIPRepoint = 487;
     public const int GuildAtkDefBatRepoint = 488;//鍏細鏀婚槻鎴�
     public const int HeroTrialRepoint = 489;//姝﹀皢璇曠偧
+    public const int LianQiRepoint = 490;//鐐煎櫒
     public void Register()
     {
 
diff --git a/Main/Utility/EnumHelper.cs b/Main/Utility/EnumHelper.cs
index 83f3962..5517e46 100644
--- a/Main/Utility/EnumHelper.cs
+++ b/Main/Utility/EnumHelper.cs
@@ -863,6 +863,7 @@
     HeroSkinFlashSale = 66,//鏃惰鐗瑰崠
     GuildAtkDefBat = 67,//鍏細鏀婚槻鎴�
     HeroTrial = 68,//姝﹀皢璇曠偧
+    LianQi = 69,//鐐煎櫒澶т細
 }
 
 

--
Gitblit v1.8.0