yyl
2026-02-25 9fb3c8e114a67c0cdf353b32b1b50e90414fd597
Merge branch 'master' into h5version

# Conflicts:
# Main/ResModule/ResManager.cs
# Main/System/Hero/UIHeroController.cs
30个文件已修改
36个文件已添加
3111 ■■■■■ 已修改文件
Main/Config/ConfigManager.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActLunhuidianTypeConfig.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActLunhuidianTypeConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/HeroSkinConfig.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/ActLunhuidianTypeConfig.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/ActLunhuidianTypeConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA88_tagMCActLunhuidianInfo.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA88_tagMCActLunhuidianInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA89_tagMCActLunhuidianPlayerInfo.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA89_tagMCActLunhuidianPlayerInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA88_tagMCActLunhuidianInfo.cs 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA88_tagMCActLunhuidianInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA89_tagMCActLunhuidianPlayerInfo.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA89_tagMCActLunhuidianPlayerInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/UIManager.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/ResModule/ResManager.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleEffectMgr.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/BattleField.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleObject/BattleObjectLayerMgr.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BeautyMM/BeautyMMManager.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BeautyMM/BeautyMMTravelWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBulletView.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatManager.cs 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatPlayerMineCell.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatPlayerOtherCell.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatWin.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildBossManager.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildBossWin.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildManager.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HappyXB/HeroCallWin.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/UIHeroController.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/ItemTip/ItemBatchUseWin.cs 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/KnapSack/Logic/ItemLogicUtility.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Main/HomeWin.cs 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OpenServerActivity/OperationTimeHepler.cs 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Redpoint/MainRedDot.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/OperationCycleHall.cs 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/OperationCycleHall.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushCell.cs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushGiftCell.cs 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushGiftCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushManager.cs 522 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushTabCell.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushTabCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushTaskCell.cs 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushTaskCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushTipWin.cs 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushTipWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushWin.cs 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimeRush/TimeRushWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftManager.cs 120 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftWin.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Video.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Video/UIVideoPlayer.cs 505 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Video/UIVideoPlayer.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Video/VideoManager.cs 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Video/VideoManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Video/VideoWin.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Video/VideoWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/EnumHelper.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/Extension.cs 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigManager.cs
@@ -38,6 +38,7 @@
        // 加载配置文件
        HashSet<Type> configTypes = new HashSet<Type>() {
            typeof(ActLunhuidianTypeConfig),
            typeof(ADAwardConfig),
            typeof(BattleMapConfig),
            typeof(BeautyConfig),
@@ -91,6 +92,7 @@
            typeof(PlayerAttrConfig),
            typeof(PlayerFaceConfig),
            typeof(PresetUnlockConfig),
            typeof(PriorBundleConfig),
            typeof(RandomNameConfig),
            typeof(SignInConfig),
            typeof(SkillSkinConfig),
@@ -296,6 +298,8 @@
    public override void Release()
    {
        // 清空 ActLunhuidianTypeConfig 字典
        ClearConfigDictionary<ActLunhuidianTypeConfig>();
        // 清空 ADAwardConfig 字典
        ClearConfigDictionary<ADAwardConfig>();
        // 清空 BattleMapConfig 字典
@@ -402,6 +406,8 @@
        ClearConfigDictionary<PlayerFaceConfig>();
        // 清空 PresetUnlockConfig 字典
        ClearConfigDictionary<PresetUnlockConfig>();
        // 清空 PriorBundleConfig 字典
        ClearConfigDictionary<PriorBundleConfig>();
        // 清空 RandomNameConfig 字典
        ClearConfigDictionary<RandomNameConfig>();
        // 清空 SignInConfig 字典
Main/Config/Configs/ActLunhuidianTypeConfig.cs
New file
@@ -0,0 +1,62 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           Wednesday, February 11, 2026
//--------------------------------------------------------
using System.Collections.Generic;
using System;
using UnityEngine;
using LitJson;
public partial class ActLunhuidianTypeConfig : ConfigBase<int, ActLunhuidianTypeConfig>
{
    static ActLunhuidianTypeConfig()
    {
        // 访问过静态构造函数
        visit = true;
    }
    public int ID;
    public int RoundType;
    public int TabType;
    public int TabSort;
    public string TabName;
    public string bgImage;
    public string TitleBgImage;
    public string InfoBgImage;
    public string InfoImage;
    public override int LoadKey(string _key)
    {
        int key = GetKey(_key);
        return key;
    }
    public override void LoadConfig(string input)
    {
        try {
        string[] tables = input.Split('\t');
        int.TryParse(tables[0],out ID);
            int.TryParse(tables[1],out RoundType);
            int.TryParse(tables[2],out TabType);
            int.TryParse(tables[3],out TabSort);
            TabName = tables[4];
            bgImage = tables[5];
            TitleBgImage = tables[6];
            InfoBgImage = tables[7];
            InfoImage = tables[8];
        }
        catch (Exception exception)
        {
            Debug.LogError(exception);
        }
    }
}
Main/Config/Configs/ActLunhuidianTypeConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68536606680071843968c757d340ca39
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/Configs/HeroSkinConfig.cs
@@ -1,6 +1,6 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           Friday, November 14, 2025
//    [  Date ]:           2026年2月13日
//--------------------------------------------------------
using System.Collections.Generic;
@@ -27,6 +27,9 @@
    public string RectangleIcon;
    public string SpineRes;
    public string InitialSkinName;
    public string ApearMotionName;
    public string TransfMotionName;
    public string LoopMotionName;
    public override int LoadKey(string _key)
    {
@@ -119,6 +122,12 @@
            SpineRes = tables[9];
            InitialSkinName = tables[10];
            ApearMotionName = tables[11];
            TransfMotionName = tables[12];
            LoopMotionName = tables[13];
        }
        catch (Exception exception)
        {
Main/Config/PartialConfigs/ActLunhuidianTypeConfig.cs
New file
@@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.Linq;
public partial class ActLunhuidianTypeConfig : ConfigBase<int, ActLunhuidianTypeConfig>
{
    static List<int> tabIndexList = new List<int>();
    //<轮回类型,<界面类型,配置ID>>
    static Dictionary<int, Dictionary<int, int>> infoDict = new Dictionary<int, Dictionary<int, int>>();
    protected override void OnConfigParseCompleted()
    {
        if (!infoDict.ContainsKey(RoundType))
            infoDict[RoundType] = new Dictionary<int, int>();
        infoDict[RoundType][TabType] = ID;
    }
    public static List<int> GetTabList()
    {
        if (tabIndexList.IsNullOrEmpty())
        {
            tabIndexList = GetKeys().OrderBy(id => Get(id)?.TabSort ?? int.MaxValue).ToList();
        }
        return tabIndexList;
    }
    public static int GetId(int roundType, int tabType)
    {
        return infoDict.TryGetValue(roundType, out var info) && info.TryGetValue(tabType, out var index) ? index : 0;
    }
    public static bool TryGetId(int roundType, int tabType, out int id)
    {
        id = 0;
        return infoDict != null && infoDict.TryGetValue(roundType, out var info) && info.TryGetValue(tabType, out id);
    }
    public static bool TryGetConfig(int id, out ActLunhuidianTypeConfig config)
    {
        config = null;
        if (!HasKey(id))
            return false;
        config = Get(id);
        return true;
    }
    public static bool TryGetConfig(int roundType, int tabType, out ActLunhuidianTypeConfig config)
    {
        config = null;
        if (!TryGetId(roundType, tabType, out int id))
            return false;
        return TryGetConfig(id, out config);
    }
}
Main/Config/PartialConfigs/ActLunhuidianTypeConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d2ecb0a241fb274ca3795554f869a1b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA88_tagMCActLunhuidianInfo.cs
New file
@@ -0,0 +1,14 @@
using UnityEngine;
using System.Collections;
// AA 88 轮回殿活动信息 #tagMCActLunhuidianInfo
public class DTCAA88_tagMCActLunhuidianInfo : DtcBasic
{
    public override void Done(GameNetPackBasic vNetPack)
    {
        base.Done(vNetPack);
        HAA88_tagMCActLunhuidianInfo vNetData = vNetPack as HAA88_tagMCActLunhuidianInfo;
        OperationTimeHepler.Instance.UpdateActLunhuidianInfo(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA88_tagMCActLunhuidianInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 449e1ce1e1ee26940a650a40f4b02770
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA89_tagMCActLunhuidianPlayerInfo.cs
New file
@@ -0,0 +1,12 @@
using UnityEngine;
using System.Collections;
// AA 89 轮回殿活动玩家信息 #tagMCActLunhuidianPlayerInfo
public class DTCAA89_tagMCActLunhuidianPlayerInfo : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HAA89_tagMCActLunhuidianPlayerInfo vNetData = vNetPack as HAA89_tagMCActLunhuidianPlayerInfo;
        TimeRushManager.Instance.UpdatePlayerInfo(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HAA_SaleActivity/DTCAA89_tagMCActLunhuidianPlayerInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af4c2ff45746d0242b3debab8472b7e9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -151,6 +151,8 @@
        Register(typeof(HA001_tagBroadCastInfo), typeof(DTCA001_tagBroadCastInfo));
        Register(typeof(HA519_tagSCTagFamilyInfo), typeof(DTCA519_tagSCTagFamilyInfo));
        Register(typeof(HA131_tagSCSettingDataInfo), typeof(DTCA131_tagSCSettingDataInfo));
        Register(typeof(HAA88_tagMCActLunhuidianInfo), typeof(DTCAA88_tagMCActLunhuidianInfo));
        Register(typeof(HAA89_tagMCActLunhuidianPlayerInfo), typeof(DTCAA89_tagMCActLunhuidianPlayerInfo));
    }
    //主工程注册封包
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA88_tagMCActLunhuidianInfo.cs
New file
@@ -0,0 +1,79 @@
using UnityEngine;
using System.Collections;
// AA 88 轮回殿活动信息 #tagMCActLunhuidianInfo
public class HAA88_tagMCActLunhuidianInfo : GameNetPackBasic {
    public byte ActNum;    // 活动编号
    public string StartDate;    // 开始日期 y-m-d
    public string EndtDate;    // 结束日期 y-m-d
    public byte ResetType;    // 重置类型,0-0点重置;1-5点重置
    public ushort LimitLV;    // 限制等级
    public byte RoundCount;
    public  tagMCActLunhuidianRound[] RoundList;    // 轮回列表,支持多个不同类型轮回同时开启
    public HAA88_tagMCActLunhuidianInfo () {
        _cmd = (ushort)0xAA88;
    }
    public override void ReadFromBytes (byte[] vBytes) {
        TransBytes (out ActNum, vBytes, NetDataType.BYTE);
        TransBytes (out StartDate, vBytes, NetDataType.Chars, 10);
        TransBytes (out EndtDate, vBytes, NetDataType.Chars, 10);
        TransBytes (out ResetType, vBytes, NetDataType.BYTE);
        TransBytes (out LimitLV, vBytes, NetDataType.WORD);
        TransBytes (out RoundCount, vBytes, NetDataType.BYTE);
        RoundList = new tagMCActLunhuidianRound[RoundCount];
        for (int i = 0; i < RoundCount; i ++) {
            RoundList[i] = new tagMCActLunhuidianRound();
            TransBytes (out RoundList[i].RoundType, vBytes, NetDataType.BYTE);
            TransBytes (out RoundList[i].AwardType, vBytes, NetDataType.BYTE);
            TransBytes (out RoundList[i].AwardTypeValue, vBytes, NetDataType.DWORD);
            TransBytes (out RoundList[i].RoundMax, vBytes, NetDataType.BYTE);
            TransBytes (out RoundList[i].AwardCount, vBytes, NetDataType.BYTE);
            RoundList[i].AwardList = new tagMCActLunhuidianAward[RoundList[i].AwardCount];
            for (int j = 0; j < RoundList[i].AwardCount; j ++) {
                RoundList[i].AwardList[j] = new tagMCActLunhuidianAward();
                TransBytes (out RoundList[i].AwardList[j].AwardIndex, vBytes, NetDataType.BYTE);
                TransBytes (out RoundList[i].AwardList[j].NeedValue, vBytes, NetDataType.DWORD);
                TransBytes (out RoundList[i].AwardList[j].Count, vBytes, NetDataType.BYTE);
                RoundList[i].AwardList[j].AwardItemList = new tagMCActLunhuidianItem[RoundList[i].AwardList[j].Count];
                for (int k = 0; k < RoundList[i].AwardList[j].Count; k ++) {
                    RoundList[i].AwardList[j].AwardItemList[k] = new tagMCActLunhuidianItem();
                    TransBytes (out RoundList[i].AwardList[j].AwardItemList[k].ItemID, vBytes, NetDataType.DWORD);
                    TransBytes (out RoundList[i].AwardList[j].AwardItemList[k].ItemCount, vBytes, NetDataType.WORD);
                    TransBytes (out RoundList[i].AwardList[j].AwardItemList[k].IsBind, vBytes, NetDataType.BYTE);
                }
            }
            TransBytes (out RoundList[i].CTGIDCount, vBytes, NetDataType.BYTE);
            TransBytes (out RoundList[i].CTGIDList, vBytes, NetDataType.WORD, RoundList[i].CTGIDCount);
            TransBytes (out RoundList[i].ShopType, vBytes, NetDataType.WORD);
        }
    }
    public class tagMCActLunhuidianItem {
        public uint ItemID;
        public ushort ItemCount;
        public byte IsBind;
    }
    public class tagMCActLunhuidianRound {
        public byte RoundType;        // 轮回类型
        public byte AwardType;        // 奖励类型 1-消耗货币;2-寻宝次数
        public uint AwardTypeValue;        // 奖励类型对应值,消耗货币时为对应的货币类型,寻宝时为对应的寻宝类型
        public byte RoundMax;        // 最大可循环轮次
        public byte AwardCount;
        public  tagMCActLunhuidianAward[] AwardList;        // 每轮奖励列表
        public byte CTGIDCount;
        public  ushort[] CTGIDList;        // CTGID列表
        public ushort ShopType;        // 开放商店类型,可能为0不开放
    }
    public class tagMCActLunhuidianAward {
        public byte AwardIndex;        // 奖励记录索引 0~30
        public uint NeedValue;        // 奖励所需值
        public byte Count;        // 奖励物品数
        public  tagMCActLunhuidianItem[] AwardItemList;        // 奖励物品列表
    }
}
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA88_tagMCActLunhuidianInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43c3e23f4ddcc8c4f9a2e6ffd631b763
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA89_tagMCActLunhuidianPlayerInfo.cs
New file
@@ -0,0 +1,25 @@
using UnityEngine;
using System.Collections;
// AA 89 轮回殿活动玩家信息 #tagMCActLunhuidianPlayerInfo
public class HAA89_tagMCActLunhuidianPlayerInfo : GameNetPackBasic {
    public byte ActNum;    // 活动编号
    public byte RoundType;    // 轮回类型
    public byte CurRound;    // 当前轮次
    public uint CurValue;    // 累计值
    public uint AwardRecord;    // 当前轮次奖励领奖记录,按奖励索引二进制位存储是否已领取,所有奖励已领取后自动进入下一轮,且重置该奖励状态
    public HAA89_tagMCActLunhuidianPlayerInfo () {
        _cmd = (ushort)0xAA89;
    }
    public override void ReadFromBytes (byte[] vBytes) {
        TransBytes (out ActNum, vBytes, NetDataType.BYTE);
        TransBytes (out RoundType, vBytes, NetDataType.BYTE);
        TransBytes (out CurRound, vBytes, NetDataType.BYTE);
        TransBytes (out CurValue, vBytes, NetDataType.DWORD);
        TransBytes (out AwardRecord, vBytes, NetDataType.DWORD);
    }
}
Main/Core/NetworkPackage/ServerPack/HAA_SaleActivity/HAA89_tagMCActLunhuidianPlayerInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b06e47c41ad471429c77f4c82a23f08
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Main.cs
@@ -116,6 +116,7 @@
        managers.Add(GMNotifyManager.Instance);
        managers.Add(TimingGiftManager.Instance);
        managers.Add(SettingDataManager.Instance);
        managers.Add(TimeRushManager.Instance);
        foreach (var manager in managers)
        {
Main/Manager/UIManager.cs
@@ -657,7 +657,7 @@
        }
        else
        {
            prefab = await ResManager.Instance.LoadAssetAsync<GameObject>("UI", uiName);
            prefab = await ResManager.Instance.LoadAssetAsync<GameObject>("UI", uiName, true);
        }
        if (prefab == null)
Main/ResModule/ResManager.cs
@@ -234,6 +234,27 @@
        LoadAssetAsyncInternal<T>(directory, name, callBack, needExt);
    }
    public async UniTask<T> LoadAssetAsync<T>(string directory, string name) where T : UnityEngine.Object
    {
        return await LoadAssetAsync<T>(directory, name, needExt: true);
    }
    public async UniTask<T> LoadAssetAsync<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object
    {
        var tcs = new UniTaskCompletionSource<T>();
        LoadAssetAsync<T>(directory, name, (isLoaded, asset) => {
            if (isLoaded)
            {
                tcs.TrySetResult(asset as T);
            }
            else
            {
                tcs.TrySetException(new Exception($"Failed to load asset: {directory}/{name}"));
            }
        }, needExt);
        return await tcs.Task;
    }
    private void LoadSpriteAsync<T>(string atlasName, string spriteName, Action<bool, UnityEngine.Object> callBack) where T : UnityEngine.Object
    {
        if (!AssetSource.isUseAssetBundle)
Main/System/Battle/BattleEffectMgr.cs
@@ -132,7 +132,15 @@
        if (effectDict.TryGetValue(effectId, out effectPlayers))
        {
            effectPlayers.Remove(effectPlayer);
            if (null == effectPlayer)
            {
                BattleDebug.LogError("effect player is null, effectid : " + effectId);
                return;
            }
            if (null != effectPlayer.gameObject)
            {
            GameObject.DestroyImmediate(effectPlayer.gameObject);
            }
            effectPlayer = null;
        }
        else
Main/System/Battle/BattleField/BattleField.cs
@@ -170,7 +170,7 @@
    public virtual void Init(int _MapID, int _FuncLineID, JsonData _extendData,
        List<TeamBase> _redTeamList, List<TeamBase> _blueTeamList, byte _turnMax)
    {
        SetData(_MapID, _FuncLineID, _extendData, _redTeamList, _blueTeamList, turnMax);
        SetData(_MapID, _FuncLineID, _extendData, _redTeamList, _blueTeamList, _turnMax);
        battleEffectMgr.Init(this);
        battleTweenMgr.Init(this);
        recordPlayer.Init(this);
Main/System/Battle/BattleObject/BattleObjectLayerMgr.cs
@@ -95,7 +95,7 @@
            effectPlayers[i].SetActive(isFront);
        }
        //  策划要在释放技能的时候 其他角色隐藏 这里在back的时候就直接设置成0  在最底下 看不到的地方
        int baseOrder = isFront ? BattleConst.SkillMaskOrder : BattleConst.BattleBackgroundOrder;
        int baseOrder = isFront ? BattleConst.SkillMaskOrder : BattleConst.BattleBackgroundOrder - BattleConst.BattleFrontHeroEffect;
        SetSortingOrder(baseOrder);
    }
Main/System/BeautyMM/BeautyMMManager.cs
@@ -237,22 +237,23 @@
    public int GetUsedSkinID(int mmID)
    {
        var _list = BeautySkinConfig.GetSkinListByMMID(mmID);
        if (beautyMMDataDict.ContainsKey(mmID))
        {
            var _beauty = beautyMMDataDict[mmID];
            if (_beauty.State == 1)
            {
                foreach (var skin in beautyMMSkinDataDict)
                foreach (var skinCfg in _list)
                {
                    if (skin.Value.Used == 1)
                    beautyMMSkinDataDict.TryGetValue(skinCfg.SkinID, out BeautyMMSkinData skin);
                    if (skin != null && skin.Used == 1)
                    {
                        return skin.Key;
                        return skinCfg.SkinID;
                    }
                }
            }
        }
        //默认第一个
        var _list = BeautySkinConfig.GetSkinListByMMID(mmID);
        if (_list.Count > 0)
        {
            return _list[0].SkinID;
Main/System/BeautyMM/BeautyMMTravelWin.cs
@@ -307,7 +307,7 @@
    void AddEnergy()
    {
        ItemLogicUtility.Instance.ShowItemBatchUseWin(BeautyMMManager.Instance.addEnergyItemID, ShowTip);
        ItemLogicUtility.Instance.ShowItemBatchUseWin(BeautyMMManager.Instance.addEnergyItemID, ShowTip, Language.Get("GuildBoss22"));
    }
    string ShowTip(long useCnt)
Main/System/Chat/ChatBulletView.cs
@@ -61,6 +61,8 @@
                    fmlv = fairyMember.FmLV;
                }
                return Language.Get("Chat16", RichTextMsgReplaceConfig.GetRichReplace("FAMILY", fmlv), data.Name, data.Content);
            case ChatChannel.CrossServer:
                return Language.Get("Chat17", data.Name, data.Content);
            default:
                return data.Content;
        }
Main/System/Chat/ChatManager.cs
@@ -115,7 +115,7 @@
    {
        talkDict.Clear();
        lastTalkDataDict.Clear();
        currentDay = -1;
        currentDayDict.Clear();
        nowChatChannel = ChatChannel.World;
        nowChatTab = ChatTab.World;
    }
@@ -413,13 +413,19 @@
        }
    }
    public int currentDay = -1;
    public Dictionary<ChatChannel, int> currentDayDict = new Dictionary<ChatChannel, int>();
    public bool TryAddDate(int allSeconds, ChatChannel type, bool isSendBullet)
    {
        DateTime talkTime = TimeUtility.GetTime((uint)allSeconds);
        if (talkTime.Day != currentDay)
        // 确保字典中包含该频道的条目
        if (!currentDayDict.ContainsKey(type))
        {
            currentDay = talkTime.Day;
            currentDayDict[type] = -1;
        }
        // 每个频道独立检查日期
        if (talkTime.Day != currentDayDict[type])
        {
            currentDayDict[type] = talkTime.Day;
            AddTalkData(type, new TalkData()
            {
                ChannelType = (byte)type,
@@ -434,7 +440,7 @@
    public void AddSysData(string msg, ArrayList infoList, ChatChannel type, bool isSendBullet)
    {
        int allSeconds = TimeUtility.AllSeconds;
        int allSeconds = type == ChatChannel.CrossServer ? TimeUtility.GetCommServerTick(GuildManager.Instance.zoneID) : TimeUtility.AllSeconds;
        // 如果隔天,增加日期行
        TryAddDate(allSeconds, type, isSendBullet);
@@ -464,7 +470,7 @@
            talkDict[type] = new List<TalkData>();
        }
        int allSeconds = TimeUtility.AllSeconds;
        int allSeconds = type == ChatChannel.CrossServer ? TimeUtility.GetCommServerTick(GuildManager.Instance.zoneID) : TimeUtility.AllSeconds;
        // 如果隔天,增加日期行
        TryAddDate(allSeconds, type, true);
@@ -572,6 +578,7 @@
    {
        ChatTab.World,
        ChatTab.Guild,
        ChatTab.CrossServer,
        // ChatTab.Person,
        // ChatTab.BlackList,
    };
@@ -583,8 +590,6 @@
        switch (chatTab)
        {
            case ChatTab.World:
                return true;
            case ChatTab.Guild:
                //没有公会
                if (!PlayerDatas.Instance.fairyData.HasFairy)
@@ -594,8 +599,17 @@
                    return false;
                }
                return true;
            default:
            case ChatTab.CrossServer:
                //没有合服
                if (GuildManager.Instance.zoneID <= 0)
                {
                    if (isTip)
                        SysNotifyMgr.Instance.ShowTip("CrossServerNoOpen");
                return false;
                }
                return true;
            default:
                return true;
        }
    }
@@ -697,8 +711,9 @@
{
    World = 0,      //世界
    Guild = 1,      //公会      
    Person = 2,     //私聊
    BlackList = 3,  //黑名单
    CrossServer = 2,    //跨服
    Person = 3,     //私聊
    BlackList = 4,  //黑名单
}
public enum ChatChannel
{
Main/System/Chat/ChatPlayerMineCell.cs
@@ -30,11 +30,16 @@
            string serverName = ServerListCenter.Instance.GetServerName(UIHelper.GetServerIDByAccount(PlayerDatas.Instance.baseData.AccID));
            m_PlayerName.text = Language.Get("Chat08", serverName, PlayerDatas.Instance.baseData.PlayerName);
        }
        else
        else if (manager.nowChatTab == ChatTab.Guild)
        {
            int fmlv = PlayerDatas.Instance.fairyData.mine.FmLV;
            m_PlayerName.text = Language.Get("Chat08", RichTextMsgReplaceConfig.GetRichReplace("FAMILY", fmlv), PlayerDatas.Instance.baseData.PlayerName);
        }
        else if (manager.nowChatTab == ChatTab.CrossServer)
        {
            string serverName = ServerListCenter.Instance.GetServerName(UIHelper.GetServerIDByAccount(PlayerDatas.Instance.baseData.AccID));
            m_PlayerName.text = Language.Get("Chat08", serverName, PlayerDatas.Instance.baseData.PlayerName);
        }
    }
    public float GetHeight(string content, ArrayList list)
Main/System/Chat/ChatPlayerOtherCell.cs
@@ -32,7 +32,7 @@
            string serverName = ServerListCenter.Instance.GetServerName((int)data.ServerID);
            m_PlayerName.text = Language.Get("Chat08", serverName, data.Name);
        }
        else
        else if (manager.nowChatTab == ChatTab.Guild)
        {
            FairyMember fairyMember = PlayerDatas.Instance.fairyData.GetMember((int)data.PlayerID);
            int fmlv = 0;
@@ -42,6 +42,11 @@
            }
            m_PlayerName.text = Language.Get("Chat08", RichTextMsgReplaceConfig.GetRichReplace("FAMILY", fmlv), data.Name);
        }
        else if (manager.nowChatTab == ChatTab.CrossServer)
        {
            string serverName = ServerListCenter.Instance.GetServerName((int)data.ServerID);
            m_PlayerName.text = Language.Get("Chat08", serverName, data.Name);
        }
        
        avatarCell.SetListener(() =>
        {
Main/System/Chat/ChatWin.cs
@@ -22,7 +22,9 @@
    [SerializeField] TextEx txtSendChat;
    [SerializeField] ScrollerController scrWorld;
    [SerializeField] ScrollerController scrGuild;
    [SerializeField] ScrollerController scrCrossServer;
    [SerializeField] ButtonEx serversBtn;
    private int unreadMsgCount = 0;
    [SerializeField] ButtonEx btnNewMsgTip;
    [SerializeField] TextEx txtNewMsgTip;
@@ -32,6 +34,7 @@
    bool isSettingOpen = false;
    [SerializeField] ChatSettingButton btnWorldSetting;
    [SerializeField] ChatSettingButton btnGuildSetting;
    [SerializeField] ChatSettingButton btnCrossServerSetting;
    ChatManager manager { get { return ChatManager.Instance; } }
    protected override void InitComponent()
@@ -69,6 +72,11 @@
                RefreshChat(manager.nowChatChannel, scrGuild);
                ScrollerJump(scrGuild, ChatChannel.Guild);
            }
            else if (manager.nowChatChannel == ChatChannel.CrossServer)
            {
                RefreshChat(manager.nowChatChannel, scrCrossServer);
                ScrollerJump(scrCrossServer, ChatChannel.CrossServer);
            }
            ClearUnreadMsg();
        });
        btnSetting.SetListener(() =>
@@ -76,7 +84,10 @@
            isSettingOpen = !isSettingOpen;
            transSettings.SetActive(isSettingOpen);
        });
        serversBtn.SetListener(() =>
        {
            UIHelper.ShowServersPanel(GuildManager.Instance.crossServerIDList);
        });
    }
    // 清理未读消息状态
@@ -104,12 +115,18 @@
        manager.OnUpdatePlayerInfoEvent += OnUpdatePlayerInfoEvent;
        scrChatTab.OnRefreshCell += OnRefreshChatTabCell;
        scrWorld.OnGetDynamicSize += OnGetWorldChatDynamicSize;
        scrWorld.OnRefreshCell += OnRefreshWorldCell;
        scrWorld.mScrollRect.onValueChanged.AddListener(OnWorldScrollValChange);
        scrGuild.OnGetDynamicSize += OnGetGuildChatDynamicSize;
        scrGuild.OnRefreshCell += OnRefreshGuildCell;
        scrGuild.mScrollRect.onValueChanged.AddListener(OnGuildScrollValChange);
        scrCrossServer.OnGetDynamicSize += OnGetChatDynamicSize;
        scrCrossServer.OnRefreshCell += OnRefreshCell;
        scrCrossServer.mScrollRect.onValueChanged.AddListener(OnCrossServerScrollValChange);
        clickScreenOtherSpace.AddListener(OnClickScreenOtherSpace);
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
@@ -119,6 +136,7 @@
        transSettings.SetActive(isSettingOpen);
        btnWorldSetting.SetChannelType(ChatChannel.World);
        btnGuildSetting.SetChannelType(ChatChannel.Guild);
        btnCrossServerSetting.SetChannelType(ChatChannel.CrossServer);
        inputChat.characterLimit = ChatManager.Instance.characterLimit;
        CreaterAll(manager.nowChatTab);
    }
@@ -139,9 +157,15 @@
        scrWorld.OnGetDynamicSize -= OnGetWorldChatDynamicSize;
        scrWorld.OnRefreshCell -= OnRefreshWorldCell;
        scrWorld.mScrollRect.onValueChanged.RemoveListener(OnWorldScrollValChange);
        scrGuild.OnGetDynamicSize -= OnGetGuildChatDynamicSize;
        scrGuild.OnRefreshCell -= OnRefreshGuildCell;
        scrGuild.mScrollRect.onValueChanged.RemoveListener(OnGuildScrollValChange);
        scrCrossServer.OnGetDynamicSize -= OnGetChatDynamicSize;
        scrCrossServer.OnRefreshCell -= OnRefreshCell;
        scrCrossServer.mScrollRect.onValueChanged.RemoveListener(OnCrossServerScrollValChange);
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        clickScreenOtherSpace.RemoveAllListeners();
    }
@@ -165,6 +189,10 @@
        else if (channel == ChatChannel.Guild)
        {
            CreateScroller(scrGuild, channel);
        }
        else if (channel == ChatChannel.CrossServer)
        {
            CreateScroller(scrCrossServer, channel);
        }
    }
@@ -227,7 +255,10 @@
            {
                if (scrWorld.lockType != EnhanceLockType.LockVerticalBottom)
                    scrWorld.lockType = EnhanceLockType.LockVerticalBottom;
                if (scrGuild.lockType != EnhanceLockType.LockVerticalBottom)
                    scrGuild.lockType = EnhanceLockType.LockVerticalBottom;
                if (scrCrossServer.lockType != EnhanceLockType.LockVerticalBottom)
                    scrCrossServer.lockType = EnhanceLockType.LockVerticalBottom;
                // 如果回到底部,直接清零消息
                ClearUnreadMsg();
            }
@@ -236,6 +267,10 @@
            {
                if (scrWorld.lockType != EnhanceLockType.KeepVertical)
                    scrWorld.lockType = EnhanceLockType.KeepVertical;
                if (scrGuild.lockType != EnhanceLockType.KeepVertical)
                    scrGuild.lockType = EnhanceLockType.KeepVertical;
                if (scrCrossServer.lockType != EnhanceLockType.KeepVertical)
                    scrCrossServer.lockType = EnhanceLockType.KeepVertical;
            }
        }
    }
@@ -248,6 +283,10 @@
    private void OnGuildScrollValChange(Vector2 _pos)
    {
        OnScrollValChange(scrGuild, _pos);
    }
    private void OnCrossServerScrollValChange(Vector2 _pos)
    {
        OnScrollValChange(scrCrossServer, _pos);
    }
    private void OnScrollValChange(ScrollerController scorller, Vector2 _pos)
@@ -308,7 +347,11 @@
        scrGuild.lockType = EnhanceLockType.LockVerticalBottom; // 初始锁定底部
        scrGuild.SetActive(chatTab == ChatTab.Guild);
        transInput.SetActive(chatTab == ChatTab.World || chatTab == ChatTab.Guild);
        scrCrossServer.lockType = EnhanceLockType.LockVerticalBottom; // 初始锁定底部
        scrCrossServer.SetActive(chatTab == ChatTab.CrossServer);
        transInput.SetActive(true);
        serversBtn.SetActive(chatTab == ChatTab.CrossServer);
        CreateChatTabScroller();
@@ -324,6 +367,12 @@
                manager.nowChatChannel = ChatChannel.Guild;
                CreateScroller(scrGuild, ChatChannel.Guild);
                ScrollerJump(scrGuild, ChatChannel.Guild);
                ClearUnreadMsg();
                break;
            case ChatTab.CrossServer:
                manager.nowChatChannel = ChatChannel.CrossServer;
                CreateScroller(scrCrossServer, ChatChannel.CrossServer);
                ScrollerJump(scrCrossServer, ChatChannel.CrossServer);
                ClearUnreadMsg();
                break;
        }
@@ -354,6 +403,10 @@
        else if (type == ChatChannel.Guild)
        {
            RefreshChat(type, scrGuild, playerId, isUpdatePlayerInfo);
        }
        else if (type == ChatChannel.CrossServer)
        {
            RefreshChat(type, scrCrossServer, playerId, isUpdatePlayerInfo);
        }
    }
@@ -391,6 +444,8 @@
            return;
        if (type == ChatChannel.Guild && manager.nowChatTab != ChatTab.Guild)
            return;
        if (type == ChatChannel.CrossServer && manager.nowChatTab != ChatTab.CrossServer)
            return;
        // 1. 自己发送的消息 -> 强制跳转到底部 + 清零
        if (playerId == PlayerDatas.Instance.PlayerId)
        {
@@ -423,7 +478,7 @@
            }
            // 特殊情况:如果未读数量巨大(超过了总显示数量),说明整个列表都被刷新了,直接消零
            if (unreadMsgCount >= scrWorld.GetNumberOfCells(scrWorld.m_Scorller))
            if (unreadMsgCount >= scroller.GetNumberOfCells(scroller.m_Scorller))
            {
                ClearUnreadMsg();
            }
Main/System/Guild/GuildBossManager.cs
@@ -100,12 +100,14 @@
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
        GuildManager.Instance.EnterOrQuitGuildEvent += EnterOrQuitGuildEvent;
        PlayerDatas.Instance.fairyData.OnRefreshFairyInfo += OnRefreshFairyInfo;
        ParseConfig();
    }
    public override void Release()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
        GuildManager.Instance.EnterOrQuitGuildEvent -= EnterOrQuitGuildEvent;
        PlayerDatas.Instance.fairyData.OnRefreshFairyInfo -= OnRefreshFairyInfo;
    }
    void OnBeforePlayerDataInitializeEventOnRelogin()
@@ -123,6 +125,10 @@
        taofaPlayerData.Clear();
    }
    private void OnRefreshFairyInfo()
    {
        UpdateRedpoint();
    }
    void EnterOrQuitGuildEvent(bool isEnter)
    {
Main/System/Guild/GuildBossWin.cs
@@ -298,6 +298,7 @@
            //时间到了 客户端自己清0
            layerNum = 0;
        }
        layerNum = (uint)Math.Min(layerNum, GuildBossManager.Instance.bzMaxLevel);
        var superHitRate = GuildBossManager.Instance.initSuperHitRate + (int)layerNum * GuildBossManager.Instance.bzAddAttrs[0];
        var addHurtPer = (int)layerNum * GuildBossManager.Instance.bzAddAttrs[1];
Main/System/Guild/GuildManager.cs
@@ -64,6 +64,7 @@
    private void OnRefreshFairyInfo()
    {
        UpdateRequestRedpoint();
        UpdateDonateRedPoint();
    }
    void OnBeforePlayerDataInitialize()
@@ -77,6 +78,8 @@
    void OnBeforePlayerDataInitializeEx()
    {
        isQueryZBGYet = false;
        //增量的形式
        PlayerDatas.Instance.fairyData.ClearData();
    }
    void OnPlayerLoginOk()
@@ -170,7 +173,24 @@
    //珍宝阁(行商)
    public int zhenbaogeCutState = 0;
    int m_ZhenbaogeCutState = 0;
    public int zhenbaogeCutState
    {
        get
        {
            if (familyZBGActions.Count - 1 >= 50)
            {
                //砍价人数超过上限后可直接购买
                m_ZhenbaogeCutState = 1;
            }
            return m_ZhenbaogeCutState;
        }
        set
        {
            m_ZhenbaogeCutState = value;
        }
    }
    public int zhenbaogeBuyState = 0;
    public bool needCheckCutCD = false;
    public event Action UpdateZhenbaogeEvent;
Main/System/HappyXB/HeroCallWin.cs
@@ -57,10 +57,11 @@
                HappyXBModel.Instance.SendXBManyQuest((int)HappXBTitle.HeroCallAdvanced);
            });
        });
        scoreBtn.AddListener(() =>
        {
            UIManager.Instance.OpenWindow<HeroCallScoreWin>();
        });
        //策划改变需求 暂时关闭
        // scoreBtn.AddListener(() =>
        // {
        //     UIManager.Instance.OpenWindow<HeroCallScoreWin>();
        // });
        ruleBtn.AddListener(() =>
        {
Main/System/Hero/UIHeroController.cs
@@ -1,6 +1,7 @@
using System;
using Cysharp.Threading.Tasks;
using Spine;
using Spine.Unity;
using UnityEngine;
using UnityEngine.UI;
@@ -11,7 +12,7 @@
    private int skinID;
    protected SkeletonGraphic skeletonGraphic;
    protected Spine.AnimationState spineAnimationState;
    public Spine.AnimationState spineAnimationState;
    private GameObject instanceGO;
    public Action onComplete;
@@ -27,7 +28,7 @@
                if (isLh)
                {
                    var skinConfigTmp = HeroSkinConfig.Get(skinID);
                    if (skinConfigTmp != null && skinConfigTmp.Tachie.Contains("SkeletonData"))
                    if (skinConfigTmp != null && skinConfigTmp.Tachie.IsSpine())
                    {
                        skeletonGraphic.enabled = true;
                    }
@@ -61,7 +62,7 @@
            //立绘特殊处理,没有spine动画的改用图片
            var lhImg = this.AddMissingComponent<RawImage>();
            if (!skinConfig.Tachie.Contains("SkeletonData"))
            if (!skinConfig.Tachie.IsSpine())
            {
                //图片替换
                lhImg.SetTexture2DPNG(skinConfig.Tachie);
@@ -289,6 +290,10 @@
    }
    public bool HasAnimation(string motionName)
    {
        return skeletonGraphic != null && skeletonGraphic.Skeleton != null && skeletonGraphic.Skeleton.ContainsMotion(motionName);
    }
    protected void OnDestroy()
@@ -309,15 +314,15 @@
    /// <param name="motionName">动作名</param>
    /// <param name="loop">循环</param>
    /// <param name="replay">如果相同动作是否再次重播,比如跑步重播就会跳帧不顺滑</param>
    public virtual void PlayAnimation(string motionName, bool loop = false, bool replay = true)
    public virtual TrackEntry PlayAnimation(string motionName, bool loop = false, bool replay = true)
    {
        if (spineAnimationState == null) return;
        if (spineAnimationState == null) return null;
        if (GetCurrentAnimationName() == motionName && !replay)
            return;
            return null;
        // 直接使用 ToString() 而不是调用 GetAnimationName
        spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
        return spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
    }
    // 播放第一个动画(作为默认动画)
Main/System/ItemTip/ItemBatchUseWin.cs
@@ -8,29 +8,38 @@
public class ItemBatchUseWin : UIBase
{
    [SerializeField] ItemCell itemCell;
    [SerializeField] Text itemCntTxt;
    [SerializeField] LongPressButton subBtn;
    [SerializeField] LongPressButton addBtn;
    [SerializeField] Text nameText;
    [SerializeField] Text countText;
    [SerializeField] Text descText;
    [SerializeField] SliderPanel sliderPanel;
    [SerializeField] Text tipText;
    [SerializeField] Button okBtn;
    [SerializeField] Text btnNameText;
    [SerializeField] Text titleText;
    public static Func<long, string> ShowTextEvent;
    public static int itemID;
    long curUseCount;
    long maxCount;
    public static string btnName;
    int useCnt;
    long maxCnt;
    protected override void InitComponent()
    {
        addBtn.SetListener(OnClickPlus);
        addBtn.onPress.AddListener(OnClickPlus);
        subBtn.SetListener(OnClickReduce);
        subBtn.onPress.AddListener(OnClickReduce);
        okBtn.AddListener(OnOK);
    }
    protected override void OnPreOpen()
    {
        useCnt = 1;
        Display();
    }
    void Display()
    {
        var key = "UseItemTitle_" + itemID;
        if (LanguageConfig.HasKey(key))
        {
@@ -40,32 +49,36 @@
        {
            titleText.text = Language.Get("UseItemDefault");
        }
        maxCount = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
        curUseCount = maxCount > 0 ? 1 : 0;
        itemCell.Init(new ItemCellModel(itemID, false, curUseCount));
        maxCnt = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
        useCnt = maxCnt > 0 ? 1 : 0;
        itemCell.Init(new ItemCellModel(itemID, false, useCnt));
        itemCell.button.AddListener(() =>
        {
            ItemTipUtility.Show(itemID);
        });
        RefreshCount();
        RefreshBtn();
        var itemConfig = ItemConfig.Get(itemID);
        nameText.text = itemConfig.ItemName;
        countText.text = Language.Get("storename12", maxCnt);
        descText.text = itemConfig.Description;
        OnSliderChange(useCnt);
        sliderPanel.Init((value) => { OnSliderChange(value); }, (int)maxCnt);
        btnNameText.text = btnName;
    }
    protected override void OnPreClose()
    void OnSliderChange(int value)
    {
    }
        useCnt = value;
        itemCell.countText.text = UIHelper.ReplaceLargeNum(useCnt);
    void RefreshCount()
    {
        itemCntTxt.text = curUseCount + "/" + maxCount;
        itemCell.countText.text = curUseCount.ToString();
        if (ShowTextEvent != null)
        {
            tipText.text = ShowTextEvent(curUseCount);
            tipText.text = ShowTextEvent(useCnt);
        }
        else
        {
@@ -74,66 +87,21 @@
    }
    void RefreshBtn()
    {
        if (curUseCount >= maxCount)
        {
            addBtn.interactable = false;
            addBtn.SetColorful(null, false);
        }
        else
        {
            addBtn.interactable = true;
            addBtn.SetColorful(null, true);
        }
        if (curUseCount == 0)
        {
            subBtn.interactable = false;
            subBtn.SetColorful(null, false);
        }
        else
        {
            subBtn.interactable = true;
            subBtn.SetColorful(null, true);
        }
    }
    void OnClickPlus()
    {
        if (curUseCount >= maxCount)
        {
            curUseCount = maxCount;
            return;
        }
        curUseCount++;
        RefreshCount();
        RefreshBtn();
    }
    void OnClickReduce()
    {
        if (curUseCount == 0)
            return;
        curUseCount--;
        RefreshCount();
        RefreshBtn();
    }
    void OnOK()
    {
        CloseWindow();
        if (curUseCount == 0)
        if (useCnt == 0)
        {
            SysNotifyMgr.Instance.ShowTip("UseItem1");
            return;
        }
        if (ItemLogicUtility.CheckItemCount(PackType.Item, itemID, curUseCount, 2))
        if (ItemLogicUtility.CheckItemCount(PackType.Item, itemID, useCnt, 1))
        {
            ItemLogicUtility.Instance.UseItem(PackManager.Instance.GetItemGUIDByID(itemID), (int)curUseCount);
            ItemLogicUtility.Instance.UseItem(PackManager.Instance.GetItemGUIDByID(itemID), useCnt);
        }
        SysNotifyMgr.Instance.ShowTip("UseOK");
    }
}
Main/System/KnapSack/Logic/ItemLogicUtility.cs
@@ -292,18 +292,20 @@
    /// </summary>
    /// <param name="itemID">要使用的物品</param>
    /// <param name="showTipEvent">参数为要使用的物品数量,返回值为文本</param>
    public void ShowItemBatchUseWin(int itemID, Func<long, string> showTipEvent)
    public void ShowItemBatchUseWin(int itemID, Func<long, string> showTipEvent, string btnName)
    {
        if (UIManager.Instance.IsOpened<ItemBatchUseWin>())
        {
            UIManager.Instance.CloseWindow<ItemBatchUseWin>();
        }
        if (!CheckItemCount(PackType.Item, itemID, 1, 2))
        //提示类型后续有变化,要约定下如何提示
        if (!CheckItemCount(PackType.Item, itemID, 1, 1))
        {
            return;
        }
        ItemBatchUseWin.itemID = itemID;
        ItemBatchUseWin.ShowTextEvent = showTipEvent;
        ItemBatchUseWin.btnName = btnName;
        UIManager.Instance.OpenWindow<ItemBatchUseWin>();
    }
Main/System/Main/HomeWin.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using DG.Tweening;
using UnityEngine;
@@ -67,6 +68,7 @@
    [SerializeField] Button osHeroCallBtn;
    [SerializeField] Button osGalaBtn;
    [SerializeField] TimingGiftCell timingGiftCell;
    [SerializeField] TimeRushCell timeRushCell;
    //坐骑
    [SerializeField] Image horseBGImg;
@@ -269,13 +271,14 @@
        ChatManager.Instance.OnUpdateTalkEvent += OnUpdateTalkEvent;
        UIManager.Instance.OnOpenWindow += OnOpenWindow;
        TimingGiftManager.Instance.OnShowGiftIdListAddEvent += OnShowGiftIdListAddEvent;
        OpenServerActivityCenter.Instance.openServerActivityStateChange += OpenServerActivityStateChange;
        TryPlayAutoFightBoss();
        Display();
        DisplayFirstChargeBtn();
        DisplayOSActivity();
        timingGiftCell.InitUI();
        // var battleWin = UIManager.Instance.OpenWindow<BattleWin>();
        // battleWin.SetBattleField(BattleManager.Instance.storyBattleField);
        DisplayTimeRush();
        DelayPlayMusic().Forget();
@@ -305,8 +308,14 @@
        ChatManager.Instance.OnUpdateTalkEvent -= OnUpdateTalkEvent;
        UIManager.Instance.OnOpenWindow -= OnOpenWindow;
        TimingGiftManager.Instance.OnShowGiftIdListAddEvent -= OnShowGiftIdListAddEvent;
        OpenServerActivityCenter.Instance.openServerActivityStateChange -= OpenServerActivityStateChange;
        //  关闭的时候把战斗界面也给关了 虽然是在外面开的
        UIManager.Instance.CloseWindow<BattleWin>();
    }
    private void OpenServerActivityStateChange()
    {
        DisplayTimeRush();
    }
    private void OnShowGiftIdListAddEvent()
@@ -365,6 +374,10 @@
                case ChatChannel.Guild:
                    ChatManager.Instance.nowChatTab = ChatTab.Guild;
                    ChatManager.Instance.nowChatChannel = ChatChannel.Guild;
                    break;
                case ChatChannel.CrossServer:
                    ChatManager.Instance.nowChatTab = ChatTab.CrossServer;
                    ChatManager.Instance.nowChatChannel = ChatChannel.CrossServer;
                    break;
            }
        }
@@ -794,6 +807,10 @@
        {
            timingGiftCell.InitUI();
        }
        else if (funcId == (int)FuncOpenEnum.TimeRush)
        {
            DisplayTimeRush();
        }
    }
    private void OnUpdateFirstChargeInfo()
@@ -848,6 +865,16 @@
    {
        DisplayOSActivity();
    }
    void DisplayTimeRush()
    {
        bool isOpen = TimeRushManager.Instance.IsFuncOpen();
        timeRushCell.SetActive(isOpen);
        if (!isOpen)
            return;
        timeRushCell.InitUI();
    }
}
Main/System/OpenServerActivity/OperationTimeHepler.cs
@@ -432,34 +432,34 @@
    //     }
    // }
    // public void UpdateActLunhuidianInfo(HAA88_tagMCActLunhuidianInfo package)
    // {
    //     OperationBase operationBase = null;
    //     operationDict.TryGetValue(Operation.default47, out operationBase);
    //     if (string.IsNullOrEmpty(package.StartDate) || string.IsNullOrEmpty(package.EndtDate))
    //     {
    //         ForceStopOperation(Operation.default47);
    //     }
    //     else
    //     {
    //         if (operationBase == null)
    //         {
    //             operationBase = new OperationCycleHall();
    //             operationDict.Add(Operation.default47, operationBase);
    //         }
    //         OperationCycleHall operation = operationBase as OperationCycleHall;
    //         operation.Reset();
    //         operation.startDate = ParseOperationDate(package.StartDate);
    //         operation.endDate = ParseOperationDate(package.EndtDate);
    //         operation.resetType = package.ResetType;
    //         operation.limitLv = package.LimitLV;
    //         operation.ParseCycleHallInfo(package);
    //         if (operationTimeUpdateEvent != null)
    //         {
    //             operationTimeUpdateEvent(Operation.default47);
    //         }
    //     }
    // }
    public void UpdateActLunhuidianInfo(HAA88_tagMCActLunhuidianInfo package)
    {
        OperationBase operationBase = null;
        operationDict.TryGetValue(OperationType.default47, out operationBase);
        if (string.IsNullOrEmpty(package.StartDate) || string.IsNullOrEmpty(package.EndtDate))
        {
            ForceStopOperation(OperationType.default47);
        }
        else
        {
            if (operationBase == null)
            {
                operationBase = new OperationCycleHall();
                operationDict.Add(OperationType.default47, operationBase);
            }
            OperationCycleHall operation = operationBase as OperationCycleHall;
            operation.Reset();
            operation.startDate = ParseOperationDate(package.StartDate);
            operation.endDate = ParseOperationDate(package.EndtDate);
            operation.resetType = package.ResetType;
            operation.limitLv = package.LimitLV;
            operation.ParseCycleHallInfo(package);
            if (operationTimeUpdateEvent != null)
            {
                operationTimeUpdateEvent(OperationType.default47);
            }
        }
    }
    // public void UpdateActYunShiInfo(HAA87_tagMCActYunshiInfo package)
    // {
@@ -1048,6 +1048,8 @@
public enum OperationType
{
    MultipleExp,
    ConsumeRebate,
    FlashSale,//限时特惠
Main/System/Redpoint/MainRedDot.cs
@@ -131,7 +131,7 @@
    public const int LoginZhanLingRedpoint = 449; //登录战令
    public const int FairyEmbleManageRepoint = 462;//仙盟徽章管理入口红点
    public const int CycleHallRepoint = 463; //轮回殿
    public const int YunShiRepoint = 464; //运势
    public const int LianQiRepoint = 465; //仙匠大会
    public const int FairySiegeRepoint = 466; //仙盟攻城战
@@ -146,6 +146,7 @@
    public const int HeroFatesRepoint = 475;//宿缘
    public const int DailyTehui = 476;//每日特惠
    public const int WarlordPavilionRepoint = 477;//定军阁
    public const int TimeRushRepoint = 478; //轮回殿
    public void Register()
    {
Main/System/TimeRush.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7332dc3700298964f9961b3ca4da2c98
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/OperationCycleHall.cs
New file
@@ -0,0 +1,84 @@
using System.Collections.Generic;
//轮回殿活动
public class OperationCycleHall : OperationBase
{
    // <轮回类型, HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianRound>
    public Dictionary<int, HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianRound> roundInfoDict = new Dictionary<int, HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianRound>();
    TimeRushManager model { get { return TimeRushManager.Instance; } }
    public bool TryGetRound(int roundType, out HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianRound round)
    {
        round = null;
        return roundInfoDict != null && roundInfoDict.TryGetValue(roundType, out round) && round != null;
    }
    public bool TryGetRoundInfoByIndex(int roundType, int awardIndex, out HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward awardInfo, out int listIndex)
    {
        listIndex = 0;
        awardInfo = new HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward { };
        if (!TryGetRound(roundType, out var round) || round.AwardList == null)
            return false;
        for (int i = 0; i < round.AwardList.Length; i++)
        {
            if (round.AwardList[i].AwardIndex == awardIndex)
            {
                awardInfo = round.AwardList[i];
                listIndex = i;
                return true;
            }
        }
        return false;
    }
    public bool TryGetRoundInfoByNeedValue(int roundType, int needValue, out HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward awardInfo)
    {
        awardInfo = new HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward { };
        if (!TryGetRound(roundType, out var round) || round.AwardList == null)
            return false;
        for (int i = 0; i < round.AwardList.Length; i++)
        {
            if (round.AwardList[i].NeedValue == needValue)
            {
                awardInfo = round.AwardList[i];
                return true;
            }
        }
        return true;
    }
    public override bool SatisfyOpenCondition()
    {
        return PlayerDatas.Instance.baseData.LV >= limitLv;
    }
    public override string ToDisplayTime()
    {
        var textBuilder = OperationTimeHepler.textBuilder;
        textBuilder.Length = 0;
        textBuilder.Append(startDate.ToDisplay());
        if (startDate != endDate)
        {
            textBuilder.Append("—");
            textBuilder.Append(endDate.ToDisplay());
        }
        return textBuilder.ToString();
    }
    public override void Reset()
    {
        base.Reset();
        roundInfoDict.Clear();
        model.lastCurRoundDict.Clear();
        model.lastCurValueDict.Clear();
    }
    public void ParseCycleHallInfo(HAA88_tagMCActLunhuidianInfo package)
    {
        roundInfoDict.Clear();
        for (int i = 0; i < package.RoundList.Length; i++)
        {
            roundInfoDict[package.RoundList[i].RoundType] = package.RoundList[i];
        }
    }
}
Main/System/TimeRush/OperationCycleHall.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0808e48a7e494424ab338530cfbc9c41
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/TimeRushCell.cs
New file
@@ -0,0 +1,32 @@
using System.Linq;
using UnityEngine;
public class TimeRushCell : MonoBehaviour
{
    [SerializeField] ImageEx bgImage;
    [SerializeField] TextEx titleText;
    [SerializeField] ButtonEx iconButton;
    TimeRushManager manager { get { return TimeRushManager.Instance; } }
    public void InitUI()
    {
        var list = TimeRushManager.Instance.GetTabIDList();
        if (list.IsNullOrEmpty())
            return;
        int id = list.First();
        if (!ActLunhuidianTypeConfig.TryGetConfig(id, out var config))
            return;
        int roundType = config.RoundType;
        bgImage.SetSprite($"TimeRushEntry{roundType}");
        bgImage.SetNativeSize();
        titleText.text = Language.Get($"TimeRushEntryTitle{roundType}");
        iconButton.SetListener(() =>
        {
            if (!UIManager.Instance.IsOpened<TimeRushWin>())
            {
                UIManager.Instance.OpenWindow<TimeRushWin>(0);
            }
        });
    }
}
Main/System/TimeRush/TimeRushCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cc10e231ecf97b9489602d412f470819
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/TimeRushGiftCell.cs
New file
@@ -0,0 +1,136 @@
using System.Collections.Generic;
using UnityEngine;
public class TimeRushGiftCell : MonoBehaviour
{
    [SerializeField] TextEx titleText;
    [SerializeField] ItemCell[] itemCells;
    [SerializeField] ButtonEx buyButton;
    [SerializeField] ImageEx buyImage;
    [SerializeField] TextEx buyText;
    [SerializeField] TextEx buyText1;
    [SerializeField] ImageEx moneyIconImage;
    [SerializeField] TextEx limitCountText;
    [SerializeField] ImageEx redImage;
    TimeRushManager manager { get { return TimeRushManager.Instance; } }
    StoreModel storeModel { get { return StoreModel.Instance; } }
    public void Display(int index, List<TimeRushGiftItem> giftItems)
    {
        if (giftItems.IsNullOrEmpty() || index < 0 || index >= giftItems.Count)
            return;
        TimeRushGiftItem item = giftItems[index];
        if (item.type == 0)
        {
            DisplayStore(item.id);
            return;
        }
        DisplayCTG(item.id);
    }
    private void DisplayCTG(int ctgId)
    {
        redImage.SetActive(false);
        buyText.SetActive(true);
        buyText1.SetActive(false);
        moneyIconImage.SetActive(false);
        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);
        bool isCanBuy = manager.IsCanBuyCTG(ctgId);
        titleText.text = config.Title;
        buyImage.SetSprite(isCanBuy ? "DailySpecialsBuy1" : "DailySpecialsBuy2");
        buyText.text = !isCanBuy ? Language.Get("storename11") : Language.Get("PayMoneyNum", UIHelper.GetMoneyFormat(orderConfig.PayRMBNum));
        limitCountText.SetActive(true);
        limitCountText.text = Language.Get("TimeRush07", UIHelper.AppendColor(rechargeCount.totalCount >= config.TotalBuyCount ? TextColType.Red : TextColType.LightGreen, Mathf.Max(0, config.TotalBuyCount - rechargeCount.totalCount).ToString()));
        buyButton.interactable = isCanBuy;
        buyButton.SetListener(() =>
        {
            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)
    {
        if (!StoreConfig.HasKey(id))
            return;
        StoreConfig storeConfig = StoreConfig.Get(id);
        int remainNum;
        storeModel.TryGetIsSellOut(storeConfig, out remainNum);
        bool isFree = manager.IsFree(id);
        titleText.text = storeConfig.Name;
        limitCountText.SetActive(!isFree);
        limitCountText.text = Language.Get("TimeRush08", UIHelper.AppendColor(remainNum == 0 ? TextColType.Red : TextColType.LightGreen, Mathf.Max(0, remainNum).ToString(), true));
        bool isCanBuy = manager.IsCanBuyShop(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);
        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);
            }
        }
    }
}
public class TimeRushGiftItem
{
    public int type;//0 商店id 1 充值id
    public int id;
}
Main/System/TimeRush/TimeRushGiftCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e9f60e895d200241adac4eec9b944d7
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/TimeRushManager.cs
New file
@@ -0,0 +1,522 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class TimeRushManager : GameSystemManager<TimeRushManager>, IOpenServerActivity
{
    public Action PlayAnimationSync;
    private bool isPlayAnimation = false;
    public bool IsPlayAnimation
    {
        get
        {
            return isPlayAnimation;
        }
        set
        {
            isPlayAnimation = value;
            if (isPlayAnimation)
            {
                PlayAnimationSync?.Invoke();
            }
            isPlayAnimation = false;
        }
    }
    public const int activityType = (int)OpenServerActivityCenter.ActivityType.AT_Activity2;
    public const int activityID = (int)NewDayActivityID.TimeRushAct;
    public int actNum = 10;
    public static OperationType operaType = OperationType.default47;
    public Redpoint redPoint = new Redpoint(MainRedDot.TimeRushRepoint);
    public bool IsOpen => OperationTimeHepler.Instance.SatisfyOpenCondition(operaType);
    public bool IsAdvance => OperationTimeHepler.Instance.SatisfyAdvanceCondition(operaType);
    public bool priorityOpen => redPoint.state == RedPointState.Simple;
    public event Action<int> onStateUpdate;
    //类型对应功能ID
    public Dictionary<int, int> funcIdDict = new Dictionary<int, int>();
    public bool TryGetFuncIdByRoundType(int roundType, out int funcId)
    {
        return funcIdDict.TryGetValue(roundType, out funcId);
    }
    //类型对应窗口ID
    public Dictionary<int, int> windowIDDict = new Dictionary<int, int>();
    public bool TryGetWindowIDByRoundType(int roundType, out int windowID)
    {
        return windowIDDict.TryGetValue(roundType, out windowID);
    }
    public Dictionary<int, int> moneyTypeDict = new Dictionary<int, int>();
    public bool TryGetMoneyTypeByRoundType(int roundType, out int windowID)
    {
        return moneyTypeDict.TryGetValue(roundType, out windowID);
    }
    public override void Init()
    {
        OperationTimeHepler.Instance.operationTimeUpdateEvent += OperationTimeUpdateEvent;
        OperationTimeHepler.Instance.operationStartEvent += OperationStartEvent;
        OperationTimeHepler.Instance.operationEndEvent += OperationEndEvent;
        OperationTimeHepler.Instance.operationAdvanceEvent += OperationAdvanceEvent;
        StoreModel.Instance.RefreshBuyShopLimitEvent += RefreshBuyShopLimitEvent;
        //storeModel.RefreshBuyShopLimitEvent += RefreshBuyShopLimitEvent;
        OpenServerActivityCenter.Instance.Register(activityID, this, activityType);
        var config = FuncConfigConfig.Get("TimeRush");
        windowIDDict = ConfigParse.ParseIntDict(config.Numerical1);
        funcIdDict = ConfigParse.ParseIntDict(config.Numerical2);
        moneyTypeDict = ConfigParse.ParseIntDict(config.Numerical3);
    }
    public override void Release()
    {
        OperationTimeHepler.Instance.operationTimeUpdateEvent -= OperationTimeUpdateEvent;
        OperationTimeHepler.Instance.operationStartEvent -= OperationStartEvent;
        OperationTimeHepler.Instance.operationEndEvent -= OperationEndEvent;
        OperationTimeHepler.Instance.operationAdvanceEvent -= OperationAdvanceEvent;
        StoreModel.Instance.RefreshBuyShopLimitEvent -= RefreshBuyShopLimitEvent;
    }
    private void OperationTimeUpdateEvent(OperationType type)
    {
        if (type == operaType)
        {
            UpdateRedpoint();
        }
    }
    private void RefreshBuyShopLimitEvent()
    {
        UpdateRedpoint();
    }
    private void OperationStartEvent(OperationType type, int state)
    {
        if (type == operaType && state == 0)
        {
            if (onStateUpdate != null)
            {
                onStateUpdate(activityID);
            }
            UpdateRedpoint();
        }
    }
    private void OperationEndEvent(OperationType type, int state)
    {
        if (type == operaType)
        {
            if (onStateUpdate != null)
            {
                onStateUpdate(activityID);
            }
            if (UIManager.Instance.IsOpened<TimeRushWin>())
            {
                UIManager.Instance.CloseWindow<TimeRushWin>();
            }
            UpdateRedpoint();
        }
    }
    private void OperationAdvanceEvent(OperationType type)
    {
        if (type == operaType)
        {
            if (onStateUpdate != null)
            {
                onStateUpdate(activityID);
            }
            UpdateRedpoint();
        }
    }
    public bool TryGetOperationInfo(out OperationCycleHall act)
    {
        act = null;
        return OperationTimeHepler.Instance.TryGetOperation(operaType, out act) && act != null;
    }
    public bool TryGetInListIndexById(int id, out int index)
    {
        index = -1;
        var list = GetTabIDList();
        if (list.IsNullOrEmpty() || !list.Contains(id))
            return false;
        index = list.IndexOf(id);
        return index >= 0;
    }
    public bool IsFuncOpen()
    {
        if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.TimeRush))
            return false;
        if (!IsOpen)
            return false;
        if (!TryGetOperationInfo(out OperationCycleHall act))
            return false;
        List<int> list = GetTabIDList();
        if (list.IsNullOrEmpty())
            return false;
        return true;
    }
    private int m_NowTabId;
    public event Action OnNowTabIdChangeEvent;
    public int nowTabId
    {
        get { return m_NowTabId; }
        set
        {
            if (m_NowTabId != value)
                m_NowTabId = value;
            OnNowTabIdChangeEvent?.Invoke();
        }
    }
    public List<int> GetTabIDList()
    {
        if (!TryGetOperationInfo(out OperationCycleHall act))
            return null;
        var list = ActLunhuidianTypeConfig.GetTabList();
        if (list.IsNullOrEmpty())
            return null;
        List<int> resList = new List<int>();
        for (int i = 0; i < list.Count; i++)
        {
            int id = list[i];
            if (!ActLunhuidianTypeConfig.TryGetConfig(id, out var config))
                continue;
            if (!act.TryGetRound(config.RoundType, out var info))
                continue;
            //没配功能ID直接加入
            if (!funcIdDict.TryGetValue(config.RoundType, out var funcId))
            {
                if (!resList.Contains(id))
                    resList.Add(id);
                continue;
            }
            //配了功能ID,判断功能是否开启
            if (FuncOpen.Instance.IsFuncOpen(funcId))
            {
                if (!resList.Contains(id))
                    resList.Add(id);
            }
        }
        return resList;
    }
    public Dictionary<int, HAA89_tagMCActLunhuidianPlayerInfo> playerInfoDict = new Dictionary<int, HAA89_tagMCActLunhuidianPlayerInfo>();
    public bool TryGetPlayerInfo(int roundType, out HAA89_tagMCActLunhuidianPlayerInfo playerInfo)
    {
        return playerInfoDict.TryGetValue(roundType, out playerInfo) && playerInfo != null;
    }
    public event Action OnUpdatePlayerInfoEvent;
    public void UpdatePlayerInfo(HAA89_tagMCActLunhuidianPlayerInfo netPack)
    {
        if (actNum != netPack.ActNum)
            return;
        if (!playerInfoDict.ContainsKey(netPack.RoundType))
            playerInfoDict[netPack.RoundType] = new HAA89_tagMCActLunhuidianPlayerInfo();
        playerInfoDict[netPack.RoundType].RoundType = netPack.RoundType;
        playerInfoDict[netPack.RoundType].CurRound = netPack.CurRound;
        playerInfoDict[netPack.RoundType].CurValue = netPack.CurValue;
        playerInfoDict[netPack.RoundType].AwardRecord = netPack.AwardRecord;
        CheckNewAwardHave(netPack.RoundType, (int)netPack.CurRound, (int)netPack.CurValue);
        OnUpdatePlayerInfoEvent?.Invoke();
        UpdateRedpoint();
    }
    public int newRoundType;
    public int newAwardType;
    public int newAwardTypeValue;
    public int newAwardIndex;
    // <轮回类型, 上一次的CurRound>
    public Dictionary<int, int> lastCurRoundDict = new Dictionary<int, int>();
    // <轮回类型, 上一次的CurValue>
    public Dictionary<int, int> lastCurValueDict = new Dictionary<int, int>();
    public event Action OnNewAwardHaveEvent;
    public void CheckNewAwardHave(int roundType, int curRound, int curValue)
    {
        if (lastCurRoundDict.ContainsKey(roundType) && lastCurValueDict.ContainsKey(roundType))
        {
            int lastCurRound = lastCurRoundDict[roundType];
            int lastCurValue = lastCurValueDict[roundType];
            if (lastCurRound < curRound)
            {
                lastCurValueDict[roundType] = 0;
            }
            lastCurValue = lastCurValueDict[roundType];
            if (lastCurValue < curValue)
            {
                if (!TryGetOperationInfo(out var act))
                    return;
                if (act.TryGetRound(roundType, out var round) && round.AwardList != null)
                {
                    for (int i = 0; i < round.AwardList.Length; i++)
                    {
                        var Award = round.AwardList[i];
                        int state = GetAwardState(roundType, Award.AwardIndex);
                        if (Award.NeedValue > lastCurValue && Award.NeedValue <= curValue && state == 1)
                        {
                            newRoundType = roundType;
                            newAwardType = round.AwardType;
                            newAwardTypeValue = (int)round.AwardTypeValue;
                            newAwardIndex = Award.AwardIndex;
                            if (!UIManager.Instance.IsOpened<TimeRushTipWin>())
                            {
                                UIManager.Instance.OpenWindow<TimeRushTipWin>();
                            }
                            OnNewAwardHaveEvent?.Invoke();
                        }
                    }
                }
            }
        }
        lastCurRoundDict[roundType] = curRound;
        lastCurValueDict[roundType] = curValue;
    }
    public int GetAwardState(int roundType, int awardIndex)
    {
        if (!TryGetPlayerInfo(roundType, out var playerInfo))
            return 0;
        if (!TryGetOperationInfo(out var act))
            return 0;
        if (!act.TryGetRoundInfoByIndex(roundType, awardIndex, out var awardInfo, out int listIndex))
            return 0;
        if (playerInfo.CurValue >= awardInfo.NeedValue)
        {
            return (playerInfo.AwardRecord & (1 << awardIndex)) != 0 ? 2 : 1;
        }
        else
        {
            return 0;
        }
    }
    public void HaveAllMissionAward(int roundType)
    {
        if (!TryGetOperationInfo(out var act))
            return;
        if (!act.TryGetRound(roundType, out var round) || round.AwardList == null)
            return;
        for (int i = 0; i < round.AwardList.Length; i++)
        {
            var award = round.AwardList[i];
            int state = GetAwardState(roundType, award.AwardIndex);
            if (state == 1)
            {
                SendGetAward(roundType, (int)award.NeedValue);
            }
        }
    }
    public bool IsCanBuyCTG(int ctgID)
    {
        if (!RechargeManager.Instance.TryGetRechargeCount(ctgID, out var rechargeCount))
            return false;
        if (!CTGConfig.HasKey(ctgID))
            return false;
        CTGConfig config = CTGConfig.Get(ctgID);
        return rechargeCount.totalCount < config.TotalBuyCount;
    }
    public bool IsFree(int shopID)
    {
        if (!StoreConfig.HasKey(shopID))
            return false;
        StoreConfig config = StoreConfig.Get(shopID);
        return config.MoneyNum == 0;
    }
    public bool IsCanBuyShop(int shopID)
    {
        if (!StoreConfig.HasKey(shopID))
            return false;
        StoreConfig config = StoreConfig.Get(shopID);
        int remainNum;
        StoreModel.Instance.TryGetIsSellOut(config, out remainNum);
        return remainNum > 0;
    }
    public List<TimeRushGiftItem> GetGiftItemList(int roundType, bool isSort = false)
    {
        List<TimeRushGiftItem> res = new List<TimeRushGiftItem>();
        if (TryGetOperationInfo(out var act) && act.TryGetRound(roundType, out var round) && round.CTGIDList != null)
        {
            var list = StoreModel.Instance.storeTypeDict[round.ShopType];
            if (!list.IsNullOrEmpty())
            {
                for (int i = 0; i < list.Count; i++)
                {
                    var item = list[i];
                    if (item.storeConfig == null)
                        continue;
                    res.Add(new TimeRushGiftItem
                    {
                        type = 0,
                        id = item.storeConfig.ID,
                    });
                }
            }
            for (int i = 0; i < round.CTGIDList.Length; i++)
            {
                var item = round.CTGIDList[i];
                res.Add(new TimeRushGiftItem
                {
                    type = 1,
                    id = item,
                });
            }
            if (isSort)
            {
                res = res.OrderBy(item =>
                {
                    bool isCanBuy = item.type == 0 ? IsCanBuyShop(item.id) : IsCanBuyCTG(item.id);
                    return !isCanBuy;
                })
                .ThenBy(item => item.type)
                .ThenBy(item => item.id)
                .ToList();
            }
        }
        return res;
    }
    public List<HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward> GetTaskList(int roundType, bool isSort = false)
    {
        if (!TryGetOperationInfo(out var act) || !act.TryGetRound(roundType, out var round) || round.AwardList == null)
            return null;
        List<HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward> res = round.AwardList.ToList();
        if (isSort)
        {
            // 先获取状态并缓存,避免排序时重复调用 GetAwardState
            res = res.Select(award => new
            {
                Data = award,
                State = GetAwardState(roundType, award.AwardIndex)
            }).OrderBy(x =>
            {
                // 定义优先级映射:数字越小,排得越靠前
                if (x.State == 1) return 0; // 最前:可领取
                if (x.State == 0) return 1; // 中间:未达成
                return 2;                   // 最后:已领取 (state == 2)
            })
            .ThenBy(x => x.Data.AwardIndex) // 状态相同时,AwardIndex 小的在前
            .Select(x => x.Data)            // 提取回原始数据结构
           .ToList();
        }
        return res;
    }
    public bool IsTabShowRed(int id)
    {
        if (!ActLunhuidianTypeConfig.TryGetConfig(id, out var config))
            return false;
        int roundType = config.RoundType;
        int tabType = config.TabType;
        if (tabType == 1)
        {
            var taskList = GetTaskList(roundType);
            if (taskList.IsNullOrEmpty())
                return false;
            for (int i = 0; i < taskList.Count; i++)
            {
                var task = taskList[i];
                int state = GetAwardState(roundType, task.AwardIndex);
                if (state == 1)
                    return true;
            }
            return false;
        }
        var list = GetGiftItemList(roundType);
        if (list.IsNullOrEmpty())
            return false;
        for (int i = 0; i < list.Count; i++)
        {
            var item = list[i];
            if (item.type == 0)
            {
                bool isFree = IsFree(item.id);
                bool isCanBuy = IsCanBuyShop(item.id);
                if (isFree && isCanBuy)
                    return true;
            }
        }
        return false;
    }
    public void SendGetAward(int roundType, int needValue)
    {
        CA504_tagCMPlayerGetReward getReward = new CA504_tagCMPlayerGetReward();
        getReward.RewardType = 78;
        getReward.DataEx = (uint)actNum;
        getReward.DataExStr = StringUtility.Concat(roundType.ToString(), "|", needValue.ToString());
        getReward.DataExStrLen = (byte)getReward.DataExStr.Length;
        GameNetSystem.Instance.SendInfo(getReward);
    }
    public void UpdateRedpoint()
    {
        redPoint.state = RedPointState.None;
        if (!IsFuncOpen())
            return;
        if (!TryGetOperationInfo(out var act) || act.roundInfoDict == null)
            return;
        foreach (var roundType in act.roundInfoDict.Keys)
        {
            ActLunhuidianTypeConfig config;
            if (ActLunhuidianTypeConfig.TryGetConfig(roundType, 1, out config))
            {
                bool isShow = IsTabShowRed(config.ID);
                if (isShow)
                {
                    redPoint.state = RedPointState.Simple;
                    return;
                }
            }
            if (ActLunhuidianTypeConfig.TryGetConfig(roundType, 2, out config))
            {
                bool isShow = IsTabShowRed(config.ID);
                if (isShow)
                {
                    redPoint.state = RedPointState.Simple;
                    return;
                }
            }
        }
    }
}
Main/System/TimeRush/TimeRushManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84c9a9bfadc4e62499711a6785fd2ad0
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/TimeRushTabCell.cs
New file
@@ -0,0 +1,29 @@
using UnityEngine;
public class TimeRushTabCell : MonoBehaviour
{
    [SerializeField] Color unSelectColor;
    [SerializeField] Color selectColor;
    [SerializeField] ImageEx bgImage;
    [SerializeField] TextEx titleText;
    [SerializeField] ImageEx redImage;
    [SerializeField] ButtonEx tabButton;
    TimeRushManager manager { get { return TimeRushManager.Instance; } }
    public void Display(int id)
    {
        if (!ActLunhuidianTypeConfig.TryGetConfig(id, out var config))
            return;
        bgImage.SetSprite(manager.nowTabId == id ? "TimeRushTabSelect" : "TimeRushTabUnSelect");
        bgImage.SetNativeSize();
        titleText.text = config.TabName;
        titleText.color = manager.nowTabId == id ? selectColor : unSelectColor;
        redImage.SetActive(manager.IsTabShowRed(id));
        tabButton.SetListener(() =>
        {
            manager.nowTabId = id;
        });
    }
}
Main/System/TimeRush/TimeRushTabCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 65202a9b70b44c747913e0d851db8707
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/TimeRushTaskCell.cs
New file
@@ -0,0 +1,136 @@
using System.Collections.Generic;
using UnityEngine;
public class TimeRushTaskCell : MonoBehaviour
{
    [SerializeField] ButtonEx clickButton;
    [SerializeField] TextEx titleText;
    [SerializeField] ImageEx sliderImage;
    [SerializeField] ImageEx maskImage;
    [SerializeField] TextEx sliderText;
    [SerializeField] ItemCell[] itemCells;
    [SerializeField] ImageEx[] grays;
    [SerializeField] RotationTween[] tweens;
    [SerializeField] UIEffectPlayer uiEffectPlayer;
    int awardIndex;
    int roundType;
    int tabType;
    TimeRushManager manager { get { return TimeRushManager.Instance; } }
    private void OnEnable()
    {
        manager.PlayAnimationSync += OnPlaySyncAnimation;
        for (int i = 0; i < tweens.Length; i++)
        {
            tweens[i].Stop();
            tweens[i].SetStartState();
        }
        if (!manager.TryGetOperationInfo(out var act))
            return;
        if (!act.TryGetRoundInfoByIndex(roundType, awardIndex, out var awardInfo, out int listIndex) || awardInfo.AwardItemList == null)
            return;
        int state = manager.GetAwardState(roundType, awardIndex);
        for (int i = 0; i < tweens.Length; i++)
        {
            if (i < awardInfo.AwardItemList.Length)
            {
                if (state == 1)
                {
                    tweens[i].Play();
                }
            }
        }
    }
    private void OnDisable()
    {
        manager.PlayAnimationSync -= OnPlaySyncAnimation;
    }
    private void OnPlaySyncAnimation()
    {
        if (tabType != 1)
            return;
        for (int i = 0; i < tweens.Length; i++)
        {
            tweens[i].Stop();
            tweens[i].SetStartState();
        }
        if (!manager.TryGetOperationInfo(out var act))
            return;
        if (!act.TryGetRoundInfoByIndex(roundType, awardIndex, out var awardInfo, out int listIndex) || awardInfo.AwardItemList == null)
            return;
        int state = manager.GetAwardState(roundType, awardIndex);
        for (int i = 0; i < tweens.Length; i++)
        {
            if (tweens[i].isActiveAndEnabled && state == 1)
            {
                tweens[i].Play();
            }
        }
    }
    public void Display(int index, CellView cell, List<HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward> taskList)
    {
        roundType = cell.info.Value.infoInt1;
        tabType = cell.info.Value.infoInt2;
        if (taskList.IsNullOrEmpty() || index < 0 || index >= taskList.Count)
            return;
        var task = taskList[index];
        awardIndex = task.AwardIndex;
        if (!manager.TryGetOperationInfo(out var act))
            return;
        if (!act.TryGetRound(roundType, out var round))
            return;
        if (!manager.TryGetPlayerInfo(roundType, out var playerInfo))
            return;
        int state = manager.GetAwardState(roundType, awardIndex);
        maskImage.SetActive(state == 2);
        uiEffectPlayer.SetActive(state == 1);
        titleText.text = Language.Get($"TimeRushTaskTitle_{round.AwardType}_{round.AwardTypeValue}", task.NeedValue);
        sliderImage.fillAmount = playerInfo.CurValue / (float)task.NeedValue;
        sliderText.text = Language.Get("BoneField09", playerInfo.CurValue, task.NeedValue);
        for (int i = 0; i < itemCells.Length; i++)
        {
            var itemBaisc = itemCells[i];
            if (i < task.AwardItemList.Length)
            {
                var itemInfo = task.AwardItemList[i];
                itemBaisc.SetActive(true);
                grays[i].SetActive(state == 2);
                ItemCellModel cellModel = new ItemCellModel((int)itemInfo.ItemID, false, itemInfo.ItemCount);
                itemBaisc.Init(cellModel);
                itemBaisc.button.AddListener(() =>
                {
                    if (state == 1)
                    {
                        manager.HaveAllMissionAward(roundType);
                    }
                    else
                    {
                        ItemTipUtility.Show((int)itemInfo.ItemID);
                    }
                });
            }
            else
            {
                itemBaisc.SetActive(false);
                grays[i].SetActive(false);
            }
        }
        clickButton.SetListener(() =>
        {
            if (state == 1)
            {
                manager.HaveAllMissionAward(roundType);
            }
        });
    }
}
Main/System/TimeRush/TimeRushTaskCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f2398658c04b264eba2671ce96dc050
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/TimeRushTipWin.cs
New file
@@ -0,0 +1,140 @@
using System.Collections.Generic;
using UnityEngine;
public class TimeRushTipWin : UIBase
{
    [SerializeField] RectTransform tip;
    [SerializeField] ButtonEx clickBtn;
    [SerializeField] TextEx awardTipText;
    [SerializeField] List<ItemCell> itemCells = new List<ItemCell>();
    [SerializeField] PositionTween positionTween;
    [SerializeField] float waitTime = 3.0f;
    [SerializeField] float moveDurationTime = 1.0f; // 移动持续时间
    [SerializeField] RectTransform startPos; // 初始位置
    [SerializeField] RectTransform downPos; // 往下移动位置
    [SerializeField] RectTransform upPos; // 往上移动位置
    int roundType;
    int awardType;
    int awardTypeValue;
    int awardIndex;
    float openTime;
    private enum MoveState
    {
        MovingDown,
        Staying,
        MovingUp,
        Finished
    }
    MoveState currentState;
    TimeRushManager manager { get { return TimeRushManager.Instance; } }
    protected override void InitComponent()
    {
        clickBtn.SetListener(() =>
        {
            if (UIManager.Instance.IsOpened<TimeRushWin>())
            {
                UIManager.Instance.CloseWindow<TimeRushWin>();
            }
            var list = manager.GetTabIDList();
            if (list.IsNullOrEmpty())
                return;
            if (!ActLunhuidianTypeConfig.TryGetConfig(roundType, 1, out var config) || !list.Contains(config.ID))
                return;
            UIManager.Instance.OpenWindow<TimeRushWin>(config.ID);
        });
    }
    protected override void OnPreOpen()
    {
        manager.OnNewAwardHaveEvent += OnNewAwardHaveEvent;
        ResetAnimation();
    }
    protected override void OnPreClose()
    {
        manager.OnNewAwardHaveEvent -= OnNewAwardHaveEvent;
    }
    void LateUpdate()
    {
        if (currentState == MoveState.Staying)
        {
            if (Time.time - openTime > waitTime)
            {
                currentState = MoveState.MovingUp;
                positionTween.reversal = true;
                positionTween.from = downPos.anchoredPosition;
                positionTween.to = upPos.anchoredPosition;
                positionTween.duration = moveDurationTime;
                positionTween.Play(OnMoveUpComplete);
            }
        }
    }
    private void OnMoveDownComplete()
    {
        currentState = MoveState.Staying;
        openTime = Time.time;
    }
    private void OnMoveUpComplete()
    {
        currentState = MoveState.Finished;
        if (UIManager.Instance.IsOpened<TimeRushTipWin>())
        {
            UIManager.Instance.CloseWindow<TimeRushTipWin>();
        }
    }
    private void Display()
    {
        roundType = manager.newRoundType;
        awardType = manager.newAwardType;
        awardTypeValue = manager.newAwardTypeValue;
        awardIndex = manager.newAwardIndex;
        if (!manager.TryGetOperationInfo(out var act) || !act.TryGetRoundInfoByIndex(roundType, awardIndex, out var award, out int listIndex) || award.AwardItemList == null)
            return;
        awardTipText.text = Language.Get($"TimeRushTaskTitle_{awardType}_{awardTypeValue}", award.NeedValue);
        for (int i = 0; i < itemCells.Count; i++)
        {
            var itemBaisc = itemCells[i];
            if (i < award.AwardItemList.Length)
            {
                var itemInfo = award.AwardItemList[i];
                itemBaisc.SetActive(true);
                itemBaisc.Init(new ItemCellModel((int)itemInfo.ItemID, false, itemInfo.ItemCount));
            }
            else
            {
                itemBaisc.SetActive(false);
            }
        }
    }
    private void OnNewAwardHaveEvent()
    {
        ResetAnimation();
    }
    private void ResetAnimation()
    {
        currentState = MoveState.MovingDown;
        openTime = Time.time;
        tip.anchoredPosition = startPos.anchoredPosition;
        positionTween.reversal = false;
        positionTween.from = startPos.anchoredPosition;
        positionTween.to = downPos.anchoredPosition;
        positionTween.duration = moveDurationTime;
        positionTween.Play(OnMoveDownComplete);
        Display();
    }
}
Main/System/TimeRush/TimeRushTipWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5d65c473483e9a468f8c7a9866199ba
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimeRush/TimeRushWin.cs
New file
@@ -0,0 +1,301 @@
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class TimeRushWin : UIBase
{
    [SerializeField] ScrollerController tabScroller;
    [SerializeField] ScrollerController taskScroller;
    [SerializeField] ScrollerController giftScroller;
    [SerializeField] ImageEx bgImage;
    [SerializeField] ImageEx infoBgImage;
    [SerializeField] ImageEx infoImage;
    [SerializeField] ImageEx titleBgImage;
    [SerializeField] GradientText titleText;
    [SerializeField] ImageEx giftBgImage;
    [SerializeField] ImageEx giftIconImage;
    [SerializeField] TextEx timeText;
    [SerializeField] TextEx roundText;
    [SerializeField] ButtonEx goButton;
    [SerializeField] TextEx goText;
    [SerializeField] OwnMoneyCell ownMoneyCell;
    [SerializeField] ButtonEx closeButton;
    TimeRushManager manager { get { return TimeRushManager.Instance; } }
    StoreModel storeModel { get { return StoreModel.Instance; } }
    int tabId;
    protected override void InitComponent()
    {
        closeButton.SetListener(CloseWindow);
    }
    protected override void OnPreOpen()
    {
        tabId = functionOrder;
        manager.OnNowTabIdChangeEvent += OnNowTabIdChangeEvent;
        tabScroller.OnRefreshCell += OnRefreshTabCell;
        taskScroller.OnRefreshCell += OnRefreshTaskCell;
        giftScroller.OnRefreshCell += OnRefreshGiftCell;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        RechargeManager.Instance.rechargeCountEvent += OnRechargeCountEvent;
        OperationTimeHepler.Instance.operationTimeUpdateEvent += OperationTimeUpdateEvent;
        storeModel.RefreshBuyShopLimitEvent += RefreshBuyShopLimitEvent;
        manager.OnUpdatePlayerInfoEvent += OnUpdatePlayerInfoEvent;
        var list = manager.GetTabIDList();
        if (list.IsNullOrEmpty())
        {
            DelayCloseWindow().Forget();
            return;
        }
        manager.nowTabId = list.Contains(tabId) ? tabId : list[0];
    }
    protected override void OnPreClose()
    {
        manager.OnNowTabIdChangeEvent -= OnNowTabIdChangeEvent;
        tabScroller.OnRefreshCell -= OnRefreshTabCell;
        taskScroller.OnRefreshCell -= OnRefreshTaskCell;
        giftScroller.OnRefreshCell -= OnRefreshGiftCell;
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        RechargeManager.Instance.rechargeCountEvent -= OnRechargeCountEvent;
        OperationTimeHepler.Instance.operationTimeUpdateEvent -= OperationTimeUpdateEvent;
        storeModel.RefreshBuyShopLimitEvent -= RefreshBuyShopLimitEvent;
        manager.OnUpdatePlayerInfoEvent -= OnUpdatePlayerInfoEvent;
    }
    private void OnUpdatePlayerInfoEvent()
    {
        RefreshAll();
    }
    private void RefreshBuyShopLimitEvent()
    {
        RefreshAll();
    }
    private void OperationTimeUpdateEvent(OperationType operation)
    {
        if (operation == OperationType.default47)
        {
            var list = manager.GetTabIDList();
            if (list.IsNullOrEmpty())
            {
                DelayCloseWindow().Forget();
                return;
            }
            manager.nowTabId = list[0];
            CreateAll();
        }
    }
    private void OnRechargeCountEvent(int obj)
    {
        RefreshAll();
    }
    private void OnSecondEvent()
    {
        GetActTime();
    }
    private void OnRefreshTabCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<TimeRushTabCell>();
        _cell?.Display(cell.index);
    }
    private void OnRefreshTaskCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<TimeRushTaskCell>();
        _cell?.Display(cell.index, cell, taskList);
    }
    private void OnRefreshGiftCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<TimeRushGiftCell>();
        _cell?.Display(cell.index, giftItems);
        manager.PlayAnimationSync?.Invoke();
    }
    private void OnNowTabIdChangeEvent()
    {
        CreateAll();
    }
    void RefreshAll()
    {
        int id = manager.nowTabId;
        if (!ActLunhuidianTypeConfig.TryGetConfig(id, out var config))
            return;
        int roundType = config.RoundType;
        int tabType = config.TabType;
        Display(id);
        tabScroller.m_Scorller.RefreshActiveCellViews();
        taskScroller.SetActive(tabType == 1);
        giftScroller.SetActive(tabType == 2);
        if (tabType == 1)
        {
            CreateTaskScroller(roundType, tabType);
            return;
        }
        if (tabType == 2)
        {
            giftScroller.m_Scorller.RefreshActiveCellViews();
            return;
        }
    }
    void CreateAll()
    {
        int id = manager.nowTabId;
        if (!ActLunhuidianTypeConfig.TryGetConfig(id, out var config))
            return;
        if (!manager.TryGetOperationInfo(out var act))
            return;
        int roundType = config.RoundType;
        int tabType = config.TabType;
        if (!act.TryGetRound(roundType, out var round))
            return;
        Display(id);
        CreateTabScroller();
        taskScroller.SetActive(tabType == 1);
        giftScroller.SetActive(tabType == 2);
        if (tabType == 1)
        {
            CreateTaskScroller(roundType, tabType);
            return;
        }
        if (tabType == 2)
        {
            CreateGiftScroller(roundType);
            return;
        }
    }
    private void Display(int id)
    {
        if (!ActLunhuidianTypeConfig.TryGetConfig(id, out var config))
            return;
        if (!manager.TryGetOperationInfo(out var act))
            return;
        int roundType = config.RoundType;
        int tabType = config.TabType;
        if (!act.TryGetRound(roundType, out var round))
            return;
        if (!manager.TryGetPlayerInfo(roundType, out var playerInfo))
            return;
        GetActTime();
        bgImage.SetSprite(config.bgImage);
        infoBgImage.SetSprite(config.InfoBgImage);
        infoImage.SetSprite(config.InfoImage);
        infoImage.SetNativeSize();
        titleBgImage.SetSprite(config.TitleBgImage);
        titleBgImage.SetNativeSize();
        titleText.text = Language.Get($"TimeRushTitle{roundType}");
        roundText.SetActive(tabType == 1);
        roundText.text = tabType == 1 ? Language.Get("TimeRush06", playerInfo.CurRound, round.RoundMax) : string.Empty;
        ownMoneyCell.SetActive(tabType == 2);
        ownMoneyCell.moneyType = manager.TryGetMoneyTypeByRoundType(roundType, out int monetyType) ? monetyType : 99;
        ownMoneyCell.Display(true);
        giftBgImage.SetActive(tabType == 2);
        giftIconImage.SetActive(tabType == 2);
        giftIconImage.SetSprite($"TimeRushGiftIcon{roundType}");
        DisplayGoButton(config);
    }
    void DisplayGoButton(ActLunhuidianTypeConfig config)
    {
        int roundType = config.RoundType;
        int tabType = config.TabType;
        bool hasWindowID = manager.TryGetWindowIDByRoundType(roundType, out int windowID);
        goButton.SetActive(tabType == 1 && hasWindowID);
        if (tabType == 1 && hasWindowID)
        {
            goText.text = Language.Get($"TimeRushGoTitle{roundType}");
            goButton.SetListener(() =>
            {
                if (manager.TryGetFuncIdByRoundType(roundType, out int funcId) && !FuncOpen.Instance.IsFuncOpen(funcId, true))
                    return;
                UIJumpManager.Instance.OpenWindow(windowID);
            });
        }
    }
    private void GetActTime()
    {
        if (!manager.TryGetOperationInfo(out var act))
        {
            timeText.text = Language.Get("OSActivity6");
            return;
        }
        timeText.text = Language.Get("TimeRush05", TimeUtility.SecondsToShortDHMS(act.GetResetSurplusTime()));
    }
    private void CreateTabScroller()
    {
        tabScroller.Refresh();
        var list = manager.GetTabIDList();
        if (!list.IsNullOrEmpty())
        {
            for (int i = 0; i < list.Count; i++)
            {
                tabScroller.AddCell(ScrollerDataType.Header, list[i]);
            }
        }
        tabScroller.Restart();
    }
    List<HAA88_tagMCActLunhuidianInfo.tagMCActLunhuidianAward> taskList;
    private void CreateTaskScroller(int roundType, int tabType)
    {
        taskList = manager.GetTaskList(roundType, true);
        taskScroller.Refresh();
        if (!taskList.IsNullOrEmpty())
        {
            for (int i = 0; i < taskList.Count; i++)
            {
                CellInfo cellInfo = new CellInfo();
                cellInfo.infoInt1 = roundType;
                cellInfo.infoInt2 = tabType;
                taskScroller.AddCell(ScrollerDataType.Header, i, cellInfo);
            }
        }
        taskScroller.Restart();
    }
    List<TimeRushGiftItem> giftItems;
    private void CreateGiftScroller(int roundType)
    {
        giftItems = manager.GetGiftItemList(roundType, true);
        giftScroller.Refresh();
        if (!giftItems.IsNullOrEmpty())
        {
            for (int i = 0; i < giftItems.Count; i++)
            {
                giftScroller.AddCell(ScrollerDataType.Header, i);
            }
        }
        giftScroller.Restart();
    }
}
Main/System/TimeRush/TimeRushWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37423ca15fdd33e45b288c668a3698a5
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimingGift/TimingGiftManager.cs
@@ -37,6 +37,10 @@
    public int[] selectCtgIds;
    public int[][] selectGainItemList;
    public int limitPopCnt;
    public int limitShowCnt;
    public int limitPopCd;
    public event Action OnSelectCtgIdIndexChangeEvent;
    public bool isLogShow = true;
@@ -45,6 +49,11 @@
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        var config = FuncConfigConfig.Get("TimingGift");
        limitPopCnt = int.Parse(config.Numerical1);
        limitShowCnt = int.Parse(config.Numerical2);
        limitPopCd = int.Parse(config.Numerical3);
    }
    public override void Release()
@@ -165,9 +174,55 @@
        return showGiftIdList;
    }
    HashSet<int> typeHashSet;
    int GetShowCnt()
    {
        if (typeHashSet == null)
            typeHashSet = new HashSet<int>();
        typeHashSet.Clear();
        if (showGiftIdList.IsNullOrEmpty())
            return 0;
        for (int i = 0; i < showGiftIdList.Count; i++)
        {
            int id = showGiftIdList[i];
            if (!TimingGiftConfig.TryGetTimingGiftConfig(id, out var config))
                continue;
            typeHashSet.Add(config.GiftType);
        }
        return typeHashSet.Count;
    }
    int GetPopCnt()
    {
        if (lastTriggerTimeDict.IsNullOrEmpty())
            return 0;
        int cnt = 0;
        foreach (var item in lastTriggerTimeDict)
        {
            int type = item.Key;
            int time = item.Value;
            var timeData = TimeUtility.GetTime((uint)time);
            DateTime nowTime = TimeUtility.ServerNow;
            if (timeData.Day == nowTime.Day && timeData.Month == nowTime.Month && timeData.Year == nowTime.Year)
            {
                cnt += 1;
            }
        }
        return cnt;
    }
    bool IsInLimitCd()
    {
        if (lastTriggerTimeDict.IsNullOrEmpty())
            return false;
        int lastTime = lastTriggerTimeDict.Values.Max();
        return TimeUtility.AllSeconds - lastTime <= limitPopCd;
    }
    public void TryAdd(int type)
    {
        if (NewBieCenter.Instance.inGuiding)
        {
#if UNITY_EDITOR
@@ -191,25 +246,60 @@
            return;
        }
        if (!IsTodayUnlimited(type))
        {
#if UNITY_EDITOR
            if (isLogShow)
            {
                int lastTriggerTime = lastTriggerTimeDict[type];
                DateTime lastTime = TimeUtility.GetTime((uint)lastTriggerTime);
                Debug.Log($"[TimingGift] TryPop:触发Return,今天已弹出过{type}类型的礼包,该类型上次弹出时间为{lastTime:yyyy-MM-dd HH:mm:ss}");
            }
#endif
            return;
        }
        if (!TimingGiftConfig.TryGetTypeToGiftIdList(type, out List<int> giftIdList))
        {
#if UNITY_EDITOR
            if (isLogShow)
            {
                Debug.Log($"[TimingGift] TryPop:触发Return,表格中没有找到{type}类型的礼包ID");
            }
#endif
            return;
        }
        int showCnt = GetShowCnt();
        if (limitShowCnt <= showCnt)
        {
#if UNITY_EDITOR
            if (isLogShow)
            {
                Debug.Log($"[TimingGift] TryPop:触发Return,当前已达到同时展示的礼包类型数量总上限");
            }
#endif
            return;
        }
        int popCnt = GetPopCnt();
        if (limitPopCnt <= popCnt)
        {
#if UNITY_EDITOR
            if (isLogShow)
            {
                Debug.Log($"[TimingGift] TryPop:触发Return,今日已达到礼包类型触发数量总上限");
            }
#endif
            return;
        }
        bool isInLimitCd = IsInLimitCd();
        if (isInLimitCd)
        {
#if UNITY_EDITOR
            if (isLogShow)
            {
                Debug.Log($"[TimingGift] TryPop:触发Return,礼包触发冷却时间中");
            }
#endif
            return;
        }
        if (!IsTodayUnlimited(type))
        {
#if UNITY_EDITOR
            if (isLogShow)
            {
                int lastTriggerTime = lastTriggerTimeDict[type];
                Debug.Log($"[TimingGift] TryPop:触发Return,今天已弹出过{type}类型的礼包,该类型上次弹出时间为{TimeUtility.GetTime((uint)lastTriggerTime):yyyy-MM-dd HH:mm:ss}");
            }
#endif
            return;
@@ -225,7 +315,7 @@
                continue;
            isChange = true;
            showGiftIdList.Add(id);
            lastTriggerTimeDict[id] = TimeUtility.AllSeconds;
            lastTriggerTimeDict[config.GiftType] = TimeUtility.AllSeconds;
        }
        if (isChange)
Main/System/TimingGift/TimingGiftWin.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class TimingGiftWin : UIBase
{
@@ -10,7 +11,7 @@
    [SerializeField] TimingGiftCtgIdCell[] ctgIdCells;
    [SerializeField] TextEx timeText;
    [SerializeField] ImageEx bgImage;
    [SerializeField] RawImage bgImage;
    [SerializeField] ImageEx IconImage;
    [SerializeField] GradientText giftText; //"礼包"
    [SerializeField] OutlineEx giftTextOutline;
@@ -238,7 +239,7 @@
        leftButton.SetActive(isShowSwitch);
        rightButton.SetActive(isShowSwitch);
        bgImage.SetSprite($"TimingGiftBg_{manager.selectTabGiftId}");
        bgImage.SetTexture2DPNG(IconConfig.Get($"TimingGiftBg_{manager.selectTabGiftId}").sprite);
        bgImage.SetNativeSize();
        IconImage.SetSprite($"TimingGiftIcon_{manager.selectTabGiftId}");
        IconImage.SetNativeSize();
Main/System/Video.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9f8b60fd5cb8de143a61b1a3ea0fc728
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Video/UIVideoPlayer.cs
New file
@@ -0,0 +1,505 @@
using System;
using UnityEngine;
using UnityEngine.Video;
using UnityEngine.UI;
/// <summary>
/// UI视频播放器组件 - 支持WebM格式透明视频播放
/// </summary>
public class UIVideoPlayer : MonoBehaviour
{
    [Header("组件引用")]
    public VideoPlayer videoPlayer;
    public RawImage videoImage;
    [Header("调试信息")]
    public const string directory = "Video";
    public string videoName;
    public bool isLoop = false;
    [Header("资源管理")]
    [Tooltip("播放完成后是否自动卸载资源(仅当非循环播放时有效)")]
    public bool autoUnloadOnFinish = true;
    [Header("回调事件")]
    public Action onComplete;
    public Action onPrepared;
    private RenderTexture _renderTexture;
    private VideoClip _loadedClip;
    private bool _isPrepared;
    private bool _isLoading;
    private Action<bool> _onPrepareCallback;
    #region Public API
    /// <summary>
    /// 加载并播放视频(通过资源系统加载 VideoClip)
    /// </summary>
    /// <param name="_videoName">视频文件名(不含扩展名)</param>
    /// <param name="_loop">是否循环播放</param>
    /// <param name="_onComplete">播放完成回调(仅在非循环播放时调用)</param>
    public async void LoadAndPlayVideo(string _videoName, bool _loop = false, Action _onComplete = null)
    {
        if (!ValidateComponents()) return;
        if (_isLoading)
        {
            Debug.LogWarning($"UIVideoPlayer: Already loading video. Please wait.");
            return;
        }
        videoName = _videoName;
        isLoop = _loop;
        onComplete = _onComplete;
        _isPrepared = false;
        _isLoading = true;
        // 清理旧资源
        ReleaseRenderTexture();
        // 隐藏或设置空状态颜色
        SetEmptyState();
        // 通过 ResManager 加载 VideoClip 资源(支持 AB 打包)
        _loadedClip = await ResManager.Instance.LoadAssetAsync<VideoClip>(directory, videoName, false);
        // 检查是否在加载过程中被取消
        if (!_isLoading)
        {
            _loadedClip = null;
            return;
        }
        _isLoading = false;
        if (_loadedClip == null)
        {
            Debug.LogError($"UIVideoPlayer: Failed to load VideoClip: {directory}/{videoName}");
            _isPrepared = false;
            return;
        }
        // 创建并绑定 RenderTexture(按视频实际尺寸)
        _renderTexture = new RenderTexture((int)_loadedClip.width, (int)_loadedClip.height, 0);
        videoPlayer.targetTexture = _renderTexture;
        videoImage.texture = _renderTexture;
        videoImage.SetNativeSize();
        // 显示视频
        SetActiveState();
        videoPlayer.source = VideoSource.VideoClip;
        videoPlayer.clip = _loadedClip;
        videoPlayer.isLooping = isLoop;
        videoPlayer.prepareCompleted += OnVideoPrepared;
        // 非循环播放时注册完成事件
        if (!isLoop)
        {
            videoPlayer.loopPointReached += OnVideoFinished;
        }
        videoPlayer.Prepare();
    }
    /// <summary>
    /// 加载并播放指定视频
    /// </summary>
    public void LoadAndPlay(string videoName, bool loop = false, Action onCompleteCallback = null)
    {
        LoadAndPlayVideo(videoName, loop, onCompleteCallback);
    }
    [ContextMenu("播放")]
    public void Play()
    {
        if (!ValidateComponents()) return;
        if (_isPrepared && !videoPlayer.isPlaying)
        {
            videoPlayer.Play();
        }
    }
    [ContextMenu("暂停")]
    public void Pause()
    {
        if (!ValidateComponents()) return;
        if (videoPlayer.isPlaying)
        {
            videoPlayer.Pause();
        }
    }
    [ContextMenu("恢复")]
    public void Resume()
    {
        if (!ValidateComponents()) return;
        if (_isPrepared && !videoPlayer.isPlaying)
        {
            videoPlayer.Play();
        }
    }
    /// <summary>
    /// 预加载视频(不自动播放)
    /// </summary>
    /// <param name="_videoName">视频文件名(不含扩展名)</param>
    /// <param name="onPreparedOK">预加载完成回调,参数为是否成功</param>
    public async void Prepare(string _videoName, Action<bool> onPreparedOK)
    {
        if (!ValidateComponents())
        {
            onPreparedOK?.Invoke(false);
            return;
        }
        if (_isLoading)
        {
            Debug.LogWarning($"UIVideoPlayer: Already loading video. Please wait.");
            onPreparedOK?.Invoke(false);
            return;
        }
        videoName = _videoName;
        _onPrepareCallback = onPreparedOK;
        _isPrepared = false;
        _isLoading = true;
        // 清理旧资源
        ReleaseRenderTexture();
        SetEmptyState();
        // 通过 ResManager 加载 VideoClip 资源(支持 AB 打包)
        _loadedClip = await ResManager.Instance.LoadAssetAsync<VideoClip>(directory, videoName, false);
        // 检查是否在加载过程中被取消
        if (!_isLoading)
        {
            _loadedClip = null;
            return;
        }
        _isLoading = false;
        if (_loadedClip == null)
        {
            Debug.LogError($"UIVideoPlayer: Failed to load VideoClip: {directory}/{videoName}");
            _isPrepared = false;
            _onPrepareCallback?.Invoke(false);
            _onPrepareCallback = null;
            return;
        }
        // 创建并绑定 RenderTexture(按视频实际尺寸)
        _renderTexture = new RenderTexture((int)_loadedClip.width, (int)_loadedClip.height, 0);
        videoPlayer.targetTexture = _renderTexture;
        videoImage.texture = _renderTexture;
        videoImage.SetNativeSize();
        SetActiveState();
        videoPlayer.source = VideoSource.VideoClip;
        videoPlayer.clip = _loadedClip;
        videoPlayer.prepareCompleted += OnVideoPreparedForPreload;
        videoPlayer.Prepare();
    }
    /// <summary>
    /// 通过 URL 加载并播放视频
    /// </summary>
    /// <param name="url">视频 URL 地址</param>
    /// <param name="_loop">是否循环播放</param>
    /// <param name="_onComplete">播放完成回调(仅在非循环播放时调用)</param>
    public void LoadAndPlayFromURL(string url, bool _loop = false, Action _onComplete = null)
    {
        if (!ValidateComponents()) return;
        if (_isLoading)
        {
            Debug.LogWarning($"UIVideoPlayer: Already loading video. Please wait.");
            return;
        }
        videoName = url;
        isLoop = _loop;
        onComplete = _onComplete;
        _isPrepared = false;
        _isLoading = true;
        // 清理旧资源
        ReleaseRenderTexture();
        SetEmptyState();
        // 根据 URL 视频创建默认尺寸的 RenderTexture(准备完成后会调整)
        _renderTexture = new RenderTexture(1920, 1080, 0);
        videoPlayer.targetTexture = _renderTexture;
        videoImage.texture = _renderTexture;
        SetActiveState();
        videoPlayer.source = VideoSource.Url;
        videoPlayer.url = url;
        videoPlayer.isLooping = isLoop;
        videoPlayer.prepareCompleted += OnVideoPreparedForURL;
        // 非循环播放时注册完成事件
        if (!isLoop)
        {
            videoPlayer.loopPointReached += OnVideoFinished;
        }
        videoPlayer.Prepare();
    }
    [ContextMenu("停止")]
    public void Stop()
    {
        if (!ValidateComponents()) return;
        videoPlayer.Stop();
    }
    [ContextMenu("重新播放")]
    public void Restart()
    {
        if (!ValidateComponents()) return;
        if (_isPrepared)
        {
            videoPlayer.Stop();
            videoPlayer.Play();
        }
    }
    /// <summary>
    /// 卸载视频资源
    /// </summary>
    public void Unload()
    {
        if (videoPlayer == null) return;
        // 取消所有事件订阅
        videoPlayer.prepareCompleted -= OnVideoPrepared;
        videoPlayer.prepareCompleted -= OnVideoPreparedForPreload;
        videoPlayer.prepareCompleted -= OnVideoPreparedForURL;
        videoPlayer.loopPointReached -= OnVideoFinished;
        videoPlayer.Stop();
        videoPlayer.clip = null;
        videoPlayer.url = null;
        videoPlayer.targetTexture = null;
        if (videoImage != null)
        {
            videoImage.texture = null;
        }
        _loadedClip = null;
        ReleaseRenderTexture();
        // 恢复空状态
        SetEmptyState();
        _isPrepared = false;
        _isLoading = false;
        onComplete = null;
        onPrepared = null;
    }
    #endregion
    #region Properties
    /// <summary>
    /// 视频是否已准备好
    /// </summary>
    public bool IsPrepared => _isPrepared;
    /// <summary>
    /// 视频是否正在播放
    /// </summary>
    public bool IsPlaying => videoPlayer != null && videoPlayer.isPlaying;
    /// <summary>
    /// 视频是否正在加载
    /// </summary>
    public bool IsLoading => _isLoading;
    /// <summary>
    /// 当前播放时间(秒)
    /// </summary>
    public double CurrentTime => videoPlayer != null ? videoPlayer.time : 0;
    /// <summary>
    /// 视频总时长(秒)
    /// </summary>
    public double Duration => _loadedClip != null ? _loadedClip.length : 0;
    #endregion
    #region Private Methods
    [ContextMenu("重新加载")]
    private void Reload()
    {
        LoadAndPlayVideo(videoName, isLoop);
    }
    private void OnVideoPrepared(VideoPlayer vp)
    {
        vp.prepareCompleted -= OnVideoPrepared;
        _isPrepared = true;
        _isLoading = false;
        onPrepared?.Invoke();
        vp.Play();
    }
    private void OnVideoPreparedForPreload(VideoPlayer vp)
    {
        vp.prepareCompleted -= OnVideoPreparedForPreload;
        _isPrepared = true;
        _isLoading = false;
        _onPrepareCallback?.Invoke(true);
        _onPrepareCallback = null;
        // 预加载完成后不自动播放
    }
    private void OnVideoPreparedForURL(VideoPlayer vp)
    {
        vp.prepareCompleted -= OnVideoPreparedForURL;
        _isPrepared = true;
        _isLoading = false;
        // 调整 RenderTexture 尺寸为实际视频尺寸
        if (_renderTexture != null && vp.texture != null)
        {
            var newRT = new RenderTexture((int)vp.width, (int)vp.height, 0);
            ReleaseRenderTexture();
            _renderTexture = newRT;
            vp.targetTexture = _renderTexture;
            videoImage.texture = _renderTexture;
            videoImage.SetNativeSize();
        }
        onPrepared?.Invoke();
        vp.Play();
    }
    private void OnVideoFinished(VideoPlayer vp)
    {
        onComplete?.Invoke();
        // 根据设置自动清理资源
        if (autoUnloadOnFinish)
        {
            Unload();
        }
    }
    private void ReleaseRenderTexture()
    {
        if (_renderTexture != null)
        {
            _renderTexture.Release();
            Destroy(_renderTexture);
            _renderTexture = null;
        }
    }
    /// <summary>
    /// 设置空状态(未加载视频时)
    /// </summary>
    private void SetEmptyState()
    {
        if (videoImage != null)
        {
            videoImage.enabled = false;
        }
    }
    /// <summary>
    /// 设置激活状态(视频已加载)
    /// </summary>
    private void SetActiveState()
    {
        if (videoImage != null)
        {
            videoImage.enabled = true;
        }
    }
    /// <summary>
    /// 验证必需组件
    /// </summary>
    private bool ValidateComponents()
    {
        if (videoPlayer == null)
        {
            Debug.LogError("UIVideoPlayer: VideoPlayer component is not assigned.");
            return false;
        }
        if (videoImage == null)
        {
            Debug.LogError("UIVideoPlayer: RawImage component is not assigned.");
            return false;
        }
        return true;
    }
    #endregion
    #region Unity Lifecycle
    private void Awake()
    {
        // 初始化时设置为空状态
        SetEmptyState();
    }
    private void OnDestroy()
    {
        Unload();
    }
    /// <summary>
    /// 取消预加载
    /// </summary>
    public void CancelPrepare()
    {
        if (videoPlayer == null) return;
        // 取消所有准备相关的事件订阅
        videoPlayer.prepareCompleted -= OnVideoPrepared;
        videoPlayer.prepareCompleted -= OnVideoPreparedForPreload;
        videoPlayer.prepareCompleted -= OnVideoPreparedForURL;
        // 停止 VideoPlayer(如果正在加载)
        if (_isLoading)
        {
            videoPlayer.Stop();
        }
        // 清理资源
        videoPlayer.clip = null;
        videoPlayer.url = null;
        videoPlayer.targetTexture = null;
        if (videoImage != null)
        {
            videoImage.texture = null;
        }
        _loadedClip = null;
        ReleaseRenderTexture();
        SetEmptyState();
        // 重置状态
        _isPrepared = false;
        _isLoading = false;
        // 触发失败回调
        _onPrepareCallback?.Invoke(false);
        _onPrepareCallback = null;
    }
    #endregion
}
Main/System/Video/UIVideoPlayer.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 780579551ee7fe442bc047278c4b96d6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Video/VideoManager.cs
New file
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using UnityEngine;
public class VideoManager : Singleton<VideoManager>
{
    private GameObjectPoolManager.GameObjectPool uiVideoPool;
    private Dictionary<string, UIVideoPlayer> prepareDict = new Dictionary<string, UIVideoPlayer>();
    private Dictionary<string, Action<bool, UIVideoPlayer>> prepareCallbacks = new Dictionary<string, Action<bool, UIVideoPlayer>>();
    public void Init()
    {
        GameObject uiVideoPrefab = UILoader.LoadPrefab("UIVideoPlayer");
        uiVideoPool = GameObjectPoolManager.Instance.GetPool(uiVideoPrefab);
    }
    public void Release()
    {
        if (uiVideoPool != null)
        {
            uiVideoPool.Clear();
            uiVideoPool = null;
        }
    }
    private UIVideoPlayer GetUIVideoPlayer()
    {
        GameObject videoObj = uiVideoPool.Request();
        if (videoObj != null)
        {
            return videoObj.GetComponent<UIVideoPlayer>();
        }
        return null;
    }
    public UIVideoPlayer PrepareVideo(string videoName, Action<bool, UIVideoPlayer> onPrepared)
    {
        //  如果已经在准备中,直接返回 并给出提示 已经在加载准备中
        if (prepareDict.TryGetValue(videoName, out UIVideoPlayer existingPlayer))
        {
            Debug.LogError("Video " + videoName + " is already being prepared.");
            return existingPlayer;
        }
        UIVideoPlayer uiVideoPlayer = GetUIVideoPlayer();
        if (uiVideoPlayer != null)
        {
            string _videoName = videoName; // 避免闭包问题
            prepareDict.Add(_videoName, uiVideoPlayer);
            prepareCallbacks.Add(_videoName, onPrepared);
            uiVideoPlayer.Prepare(videoName, (success) =>
            {
                OnPrepareOK(videoName, success, uiVideoPlayer);
            });
        }
        else
        {
            Debug.LogError("Failed to get UIVideoPlayer from pool.");
            onPrepared?.Invoke(false, null);
        }
        return uiVideoPlayer;
    }
    public void CancelPrepare(UIVideoPlayer uIVideoPlayer)
    {
        if (uIVideoPlayer != null)
            uIVideoPlayer.CancelPrepare();
        {
            uiVideoPool.Release(uIVideoPlayer.gameObject);
            // 从 prepareDict 移除
            string keyToRemove = null;
            foreach (var kv in prepareDict)
            {
                if (kv.Value == uIVideoPlayer)
                {
                    keyToRemove = kv.Key;
                    break;
                }
            }
            if (keyToRemove != null)
            {
                prepareDict.Remove(keyToRemove);
                prepareCallbacks.Remove(keyToRemove);
            }
        }
    }
    private void OnPrepareOK(string videoName, bool success, UIVideoPlayer uiVideoPlayer)
    {
        // 安全移除和回调
        if (prepareDict.ContainsKey(videoName))
        {
            prepareDict.Remove(videoName);
        }
        Action<bool, UIVideoPlayer> callback = null;
        if (prepareCallbacks.TryGetValue(videoName, out callback))
        {
            callback?.Invoke(success, uiVideoPlayer);
            prepareCallbacks.Remove(videoName);
        }
    }
    public void ReleaseVideoPlayer(UIVideoPlayer uIVideoPlayer)
    {
        if (uIVideoPlayer != null)
        {
            CancelPrepare(uIVideoPlayer);
            uiVideoPool.Release(uIVideoPlayer.gameObject);
        }
    }
}
Main/System/Video/VideoManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 14d20be0871f2e24d96b79440816d7cb
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Video/VideoWin.cs
New file
@@ -0,0 +1,20 @@
using System;
using UnityEngine;
using UnityEngine.Video;
using UnityEngine.UI;
public class VideoWin : UIBase
{
    public UIVideoPlayer videoPlayer;
    protected override void OnOpen()
    {
        // 视频准备完成后会自动播放
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        videoPlayer.Unload();
    }
}
Main/System/Video/VideoWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf2a5c5ccb4f22f4c9408bd3b3efed69
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Utility/EnumHelper.cs
@@ -849,6 +849,7 @@
    WarlordPavilion = 55, //定军阁
    FuncPreset = 56, //流派预设
    TimingGift = 57, //时机礼包
    TimeRush = 58, //限时冲刺
}
@@ -1819,7 +1820,7 @@
    FairyAffinityLoginAct = 214,  //仙缘登陆活动
    FairyAffinityMissionAct = 215,  //仙缘任务活动
    FairyAffinityRechargeGiftAct = 216,  //仙缘礼包活动
    CycleHallAct = 217,  //轮回殿活动
    TimeRushAct = 217,  //轮回殿活动
    YunShiXBAct = 218,  //运势寻宝活动
    YunShiMissionAct = 219,  //运势任务活动
    YunShiRechargeGiftAct = 220,  //运势礼包活动
Main/Utility/Extension.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Spine;
using Spine.Unity;
public static class Extension
{
@@ -74,4 +76,61 @@
            }
        }
    }
    public static bool IsSpine(this string _resName)
    {
        if (string.IsNullOrEmpty(_resName))
        {
            return false;
        }
        return _resName.Contains("SkeletonData");
    }
    public static bool IsVideo(this string _resName)
    {
        if (string.IsNullOrEmpty(_resName))
        {
            return false;
        }
        return _resName.EndsWith(".mp4");
    }
    public static bool ContainsMotion(this Spine.Skeleton skeleton, string motionName)
    {
        if (skeleton == null || string.IsNullOrEmpty(motionName))
        {
            return false;
        }
        for (int i = 0; i < skeleton.Data.Animations.Count; i++)
        {
            if (skeleton.Data.Animations.Items[i].Name.ToLower() == motionName.ToLower())
            {
                return true;
            }
        }
        return false;
    }
    public static Spine.Animation GetSpineAnimation(this Spine.Skeleton skeleton, string motionName)
    {
        if (skeleton == null || string.IsNullOrEmpty(motionName))
        {
            return null;
        }
        for (int i = 0; i < skeleton.Data.Animations.Count; i++)
        {
            if (skeleton.Data.Animations.Items[i].Name.ToLower() == motionName.ToLower())
            {
                return skeleton.Data.Animations.Items[i];
            }
        }
        return null;
    }
}