lcy
2026-07-06 37aa92cbb341cc15a7f0e60ceebceda68a67780c
581 炼器大会
8个文件已修改
71个文件已添加
4869 ■■■■■ 已修改文件
Main/Config/ConfigManager.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActTimeConfig.cs 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActTimeConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActTimeFlowConfig.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActTimeFlowConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActTypeConfig.cs 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActTypeConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/ActTimeFlowConfig.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/ActTimeFlowConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/ActTypeConfig.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/ActTypeConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BattlePass/BattlePassManager.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/DragSelectComponentEx.cs 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/DragSelectComponentEx.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiActLevel1.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiActLevel1.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiActLevel2.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiActLevel2.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiAwardCell.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiAwardCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiAwardWin.cs 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiAwardWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiCoolTime.cs 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiCoolTime.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiGiftCell.cs 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiGiftCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiGiftWin.cs 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiGiftWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiItemInfoDoubleWin.cs 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiItemInfoDoubleWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiItemInfoWin.cs 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiItemInfoWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiManager.cs 1248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiPopTipWin.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiPopTipWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankAwardCell.cs 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankAwardCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankAwardItemCell.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankAwardItemCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankCell.cs 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankTop3Cell.cs 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankTop3Cell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankWin.cs 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRankWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRoundSettlementWin.cs 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiRoundSettlementWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiSmallTipWin.cs 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiSmallTipWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiWin.cs 740 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingCell.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingPreviewCell.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingPreviewCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingPreviewWin.cs 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingPreviewWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingWin.cs 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/LianQiZhanLingWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/OperationLianQiCross.cs 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/LianQi/OperationLianQiCross.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Main/HomeWin.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OpenServerActivity/OperationTimeHepler.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Redpoint/MainRedDot.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/EnumHelper.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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 字典
Main/Config/Configs/ActTimeConfig.cs
New file
@@ -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);
        }
    }
}
Main/Config/Configs/ActTimeConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fcca7d3328b986479acbe95affd4556
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/Configs/ActTimeFlowConfig.cs
New file
@@ -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);
        }
    }
}
Main/Config/Configs/ActTimeFlowConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 47860d5770f7897429c9ae853fc82e59
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/Configs/ActTypeConfig.cs
New file
@@ -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);
        }
    }
}
Main/Config/Configs/ActTypeConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4eb6bc33c43c6d248a6cc06bf0928533
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/PartialConfigs/ActTimeFlowConfig.cs
New file
@@ -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;
    }
}
Main/Config/PartialConfigs/ActTimeFlowConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 447d8b9a08c6a22479478123eac98628
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/PartialConfigs/ActTypeConfig.cs
New file
@@ -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 + " 旧CfgID " + infoDict[ActFuncType][ActTempID] + " 新CfgID " + CfgID);
        }
        infoDict[ActFuncType][ActTempID] = CfgID;
    }
    // 通过活动功能类型和模板ID查询配置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;
    }
    // 尝试通过活动功能类型和模板ID查询配置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);
    }
    // 通过活动功能类型和模板ID获取配置,未命中返回 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;
    }
    // 通过活动功能类型和模板ID获取配置,未命中返回 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;
    }
}
Main/Config/PartialConfigs/ActTypeConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad332adb5561d3d4e96d0d7278e81601
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs
New file
@@ -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);
    }
}
Main/Core/NetworkPackage/ClientPack/CAA SaleActivity/CAA25_tagCSActLianqiOP.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bad8f07ed5cf8724e80d5f737617f8dd
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs
New file
@@ -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);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA90_tagSCActLianqiInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a99e20beaa9ab143bb352ee87bdd090
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs
New file
@@ -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);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA91_tagSCActLianqiPlayerInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 64b6cfa4866f22d4f8a122f07fe76ca9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
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));
    }
    //主工程注册封包
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs
New file
@@ -0,0 +1,29 @@
using UnityEngine;
using System.Collections;
// AA 90 炼器跨服活动信息 #tagSCActLianqiInfo
public class HAA90_tagSCActLianqiInfo : GameNetPackBasic {
    public ushort ServerLen;
    public string ServerIDInfo;    // 活动服务器ID信息,[[从服务器IDA, 到服务器IDB], 或指定某个服务器ID, ...]
    public uint CrossServerID;    // 所属跨服服务器ID
    public uint ZoneID;    // 所属分区,也是活动榜单分组值GroupValue1,用于查询对应榜单
    public string StartDate;    // 开始日期 y-m-d
    public string EndtDate;    // 结束日期 y-m-d
    public ushort CfgID;    // 活动时间表配置ID
    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);
    }
}
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA90_tagSCActLianqiInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c740d821af03f144eba03eae95f4ac10
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs
New file
@@ -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-无(如初始化,GM等后端直接设置);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;        // 移动到目标列
    }
}
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA91_tagSCActLianqiPlayerInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d47f7cbada3f84b48b8a6ab37cf094ef
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
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)
        {
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,    // 炼器大会
}
Main/System/LianQi.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1319eea20bd178749b0fe96b53fe6909
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/DragSelectComponentEx.cs
New file
@@ -0,0 +1,99 @@
using 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;
    }
}
Main/System/LianQi/DragSelectComponentEx.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d85f87c3c94502408fe0bcd595e7278
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiActLevel1.cs
New file
@@ -0,0 +1,7 @@
using UnityEngine;
public class LianQiActLevel1 : MonoBehaviour
{
    public ImageEx imgItem;
    public TextEx txtLv;
}
Main/System/LianQi/LianQiActLevel1.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 084cabb1f52e0d04f8b140b81bbe0e35
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiActLevel2.cs
New file
@@ -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;
}
Main/System/LianQi/LianQiActLevel2.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46aa030177529cf46864e0292c216f37
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiAwardCell.cs
New file
@@ -0,0 +1,75 @@
using 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>();
            }
        });
    }
}
Main/System/LianQi/LianQiAwardCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2a508bea89d8fe4681cca10aeea85f9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiAwardWin.cs
New file
@@ -0,0 +1,174 @@
using 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();
        }
    }
}
Main/System/LianQi/LianQiAwardWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c56c8195deb41048bf513812bb2d4a6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiCoolTime.cs
New file
@@ -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;
    }
}
Main/System/LianQi/LianQiCoolTime.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23cbd852d3b19ad4ea7625afd99aa05d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiGiftCell.cs
New file
@@ -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);
            }
        }
    }
}
Main/System/LianQi/LianQiGiftCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e621e92b191df16418aa8d25c4336fbf
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiGiftWin.cs
New file
@@ -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();
    }
}
Main/System/LianQi/LianQiGiftWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 668f585a6fcbafd47961af6a84bc558f
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiItemInfoDoubleWin.cs
New file
@@ -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;
    }
}
Main/System/LianQi/LianQiItemInfoDoubleWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 197d45697dfbe8e49a50290f9ba1af78
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiItemInfoWin.cs
New file
@@ -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();
    }
}
Main/System/LianQi/LianQiItemInfoWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3e4c8989dda2f84d9513d1b607b1642
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiManager.cs
New file
@@ -0,0 +1,1248 @@
using 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阶以下进阶,并获得积分及解锁等级奖励
    /// 辅助道具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>
    /// 入口倒计时显示开始时间,活动第N天 时 分
    /// </summary>
    public int enterCountdownStartDay;
    public int enterCountdownStartHour;
    public int enterCountdownStartMinute;
    /// <summary>
    /// 入口倒计时显示结束时间,活动第N天 时 分
    /// </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;
}
Main/System/LianQi/LianQiManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d7b7777c8faf2b45b0e10f65ecbaafd
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiPopTipWin.cs
New file
@@ -0,0 +1,30 @@
using 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();
        // });
    }
}
Main/System/LianQi/LianQiPopTipWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4d572792b25a8444a433009e2d6e612
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiRankAwardCell.cs
New file
@@ -0,0 +1,72 @@
using 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;
    }
}
Main/System/LianQi/LianQiRankAwardCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: abbe3f17f7bb33a41a72cc99c6a72fbd
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiRankAwardItemCell.cs
New file
@@ -0,0 +1,15 @@
using 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));
    }
}
Main/System/LianQi/LianQiRankAwardItemCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56c2cae1dd95d3a4e87a0dd7ead5c260
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiRankCell.cs
New file
@@ -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 代表玩家自己
    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);
            });
        }
    }
}
Main/System/LianQi/LianQiRankCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca5fdd745e5d54947a6bd54c0a558f45
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiRankTop3Cell.cs
New file
@@ -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);
            }
        }
    }
}
Main/System/LianQi/LianQiRankTop3Cell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6440faf5ed25ad45bde8aa5a9be7e88
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiRankWin.cs
New file
@@ -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);
        }
    }
}
Main/System/LianQi/LianQiRankWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 27286beae2fe3a8499faf7a1ebc6d524
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiRoundSettlementWin.cs
New file
@@ -0,0 +1,35 @@
using 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")));
    }
}
Main/System/LianQi/LianQiRoundSettlementWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 726a07544ea852f4ba12e5aa78776817
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiSmallTipWin.cs
New file
@@ -0,0 +1,124 @@
using 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;
    }
}
Main/System/LianQi/LianQiSmallTipWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcb7be20f4a34e2429c33ee894152490
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiWin.cs
New file
@@ -0,0 +1,740 @@
using 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;  // 防御:Clock 回调在窗口销毁后触发
        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;
    }
}
Main/System/LianQi/LianQiWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af0caad58ecd61d4698d52b34c9b1bbc
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiZhanLingCell.cs
New file
@@ -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);
            });
        }
    }
}
Main/System/LianQi/LianQiZhanLingCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 01d25e56104ca3641bc593d2d438f7fc
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiZhanLingPreviewCell.cs
New file
@@ -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);
        });
    }
}
Main/System/LianQi/LianQiZhanLingPreviewCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2665fd70d1cbb434daeaac801ad084e5
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiZhanLingPreviewWin.cs
New file
@@ -0,0 +1,115 @@
using 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();
    }
}
Main/System/LianQi/LianQiZhanLingPreviewWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8bb1a13a5649904dbc930f266fec87e
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/LianQiZhanLingWin.cs
New file
@@ -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);
    }
}
Main/System/LianQi/LianQiZhanLingWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c4d75e9485645649935520c73ea3dc8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/LianQi/OperationLianQiCross.cs
New file
@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using UnityEngine;
//轮回殿活动
public class OperationLianQiCross : OperationBase
{
    string m_ServerIDRangeInfo;    //开放该活动的服务器ID范围列表,json格式 [[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);
    }
}
Main/System/LianQi/OperationLianQiCross.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3150b2cf4acd60448a57d04ba4c924d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
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);
    }
}
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,
}
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()
    {
Main/Utility/EnumHelper.cs
@@ -863,6 +863,7 @@
    HeroSkinFlashSale = 66,//时装特卖
    GuildAtkDefBat = 67,//公会攻防战
    HeroTrial = 68,//武将试炼
    LianQi = 69,//炼器大会
}