lcy
2 天以前 3575c016271e455cfaab964b98e3d4ef84ef75cb
597 时装特卖
13个文件已修改
11个文件已添加
831 ■■■■■ 已修改文件
Main/Config/Configs/ActSpecialSaleConfig.cs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/StoreConfig.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroSkinWin.cs 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Main/HomeWin.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OpenServerActivity/OperationTimeHepler.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Redpoint/MainRedDot.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Store/SkinStoreCell.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Store/SkinStoreLineCell.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Store/SkinStoreWin.cs 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Store/StoreModel.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/UIBase/UIJumpManager.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/EnumHelper.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ActSpecialSaleConfig.cs
@@ -1,6 +1,6 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2026年4月29日
//    [  Date ]:           Monday, May 11, 2026
//--------------------------------------------------------
using System.Collections.Generic;
@@ -21,6 +21,8 @@
    public int ActShopType;
    public int ADID;
    public int[][] PopItems;
    public float[] OriginalPrices;
    public float[] PercentageTexts;
    public override int LoadKey(string _key)
    {
@@ -53,6 +55,34 @@
            int.TryParse(tables[3],out ADID); 
            PopItems = JsonMapper.ToObject<int[][]>(tables[4].Replace("(", "[").Replace(")", "]")); 
            if (tables[5].Contains("["))
            {
                OriginalPrices = JsonMapper.ToObject<float[]>(tables[5]);
            }
            else
            {
                string[] OriginalPricesStringArray = tables[5].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                OriginalPrices = new float[OriginalPricesStringArray.Length];
                for (int i=0;i<OriginalPricesStringArray.Length;i++)
                {
                     float.TryParse(OriginalPricesStringArray[i],out OriginalPrices[i]);
                }
            }
            if (tables[6].Contains("["))
            {
                PercentageTexts = JsonMapper.ToObject<float[]>(tables[6]);
            }
            else
            {
                string[] PercentageTextsStringArray = tables[6].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                PercentageTexts = new float[PercentageTextsStringArray.Length];
                for (int i=0;i<PercentageTextsStringArray.Length;i++)
                {
                     float.TryParse(PercentageTextsStringArray[i],out PercentageTexts[i]);
                }
            }
        }
        catch (Exception exception)
        {
Main/Config/Configs/StoreConfig.cs
@@ -1,6 +1,6 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2026年3月5日
//    [  Date ]:           2026年5月9日
//--------------------------------------------------------
using System.Collections.Generic;
@@ -32,6 +32,8 @@
    public int UnlockValue;
    public int IsExclusive;
    public string Name;
    public int StartTime;
    public int EndTime;
    public override int LoadKey(string _key)
    {
@@ -74,6 +76,10 @@
            int.TryParse(tables[14],out IsExclusive); 
            Name = tables[15];
            int.TryParse(tables[16],out StartTime);
            int.TryParse(tables[17],out EndTime);
        }
        catch (Exception exception)
        {
Main/Main.cs
@@ -123,7 +123,7 @@
        managers.Add(FestivalActivityCheckInManager.Instance);
        managers.Add(FestivalActivityMissionManager.Instance);
        managers.Add(SuperVipManager.Instance);
        managers.Add(HeroSkinFlashSaleManager.Instance);
        foreach (var manager in managers)
        {
Main/System/HeroSkinFlashSale.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c64d4c964e9ce144f93fe33877c7a7e2
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs
New file
@@ -0,0 +1,18 @@
using UnityEngine;
public class HeroSkinFlashSaleAwardCell : CellView
{
    [SerializeField] ItemCell itemCell;
    public void Display(int index, int[][] arr)
    {
        if (arr == null || arr.Length <= index) return;
        int itemID = arr[index][0];
        long itemCount = arr[index][1];
        itemCell.Init(new ItemCellModel(itemID, false, itemCount));
        itemCell.button.AddListener(() =>
        {
            ItemTipUtility.Show(itemID);
        });
    }
}
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2bb86c6193afa54a8e94eecd3e431ea
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs
New file
@@ -0,0 +1,45 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
public class HeroSkinFlashSaleCell : MonoBehaviour
{
    [SerializeField] ButtonEx clickButton;
    [SerializeField] ImageEx bgImage;
    [SerializeField] ImageEx heroImage;
    HeroSkinFlashSaleManager manager => HeroSkinFlashSaleManager.Instance;
    public void Display()
    {
        clickButton.SetListener(() =>
        {
            UIManager.Instance.OpenWindow<HeroSkinFlashSaleWin>();
        });
        var act = manager.GetActInfo();
        if (act == null) return;
        var config = ActSpecialSaleConfig.Get(act.CfgID);
        if (config == null) return;
        var list = manager.GetSkinIDList(act.CfgID);
        if (list == null) return;
        int skinID = list[0];
        var skinConfig = HeroSkinConfig.Get(skinID);
        if (skinConfig == null) return;
        bgImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleEntryBg_", skinID.ToString()));
        SetHeroSquareIcon(heroImage, skinConfig.SquareIcon).Forget();
    }
    async UniTaskVoid SetHeroSquareIcon(ImageEx image, string name)
    {
        var sprite = UILoader.LoadSprite("HeroHead", name);
        if (sprite != null)
        {
            image.overrideSprite = sprite;
            return;
        }
        image.SetSprite("herohead_default");
    }
}
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86088ecd589cb704fab2ef2f87e31949
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs
New file
@@ -0,0 +1,242 @@
using System;
using System.Collections.Generic;
public class HeroSkinFlashSaleManager : GameSystemManager<HeroSkinFlashSaleManager>, IOpenServerActivity
{
    public override void Init()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
        OperationTimeHepler.Instance.operationTimeUpdateEvent += OperationTimeUpdateEvent;
        OperationTimeHepler.Instance.operationStartEvent += OperationStartEvent;
        OperationTimeHepler.Instance.operationEndEvent += OperationEndEvent;
        OperationTimeHepler.Instance.operationAdvanceEvent += OperationAdvanceEvent;
    }
    public override void Release()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk;
        OperationTimeHepler.Instance.operationTimeUpdateEvent -= OperationTimeUpdateEvent;
        OperationTimeHepler.Instance.operationStartEvent -= OperationStartEvent;
        OperationTimeHepler.Instance.operationEndEvent -= OperationEndEvent;
        OperationTimeHepler.Instance.operationAdvanceEvent -= OperationAdvanceEvent;
    }
    private void OnBeforePlayerDataInitializeEventOnRelogin()
    {
    }
    private void OnPlayerLoginOk()
    {
    }
    public const int activityID = (int)OperationType.HeroSkinFlashSale;
    public static OperationType operaType = OperationType.HeroSkinFlashSale;
    public Redpoint redPoint = new Redpoint(MainRedDot.HeroSkinFlashSaleRepoint);
    public bool IsOpen => OperationTimeHepler.Instance.SatisfyOpenCondition(operaType);
    public bool IsAdvance => OperationTimeHepler.Instance.SatisfyAdvanceCondition(operaType);
    public bool priorityOpen => redPoint.state == RedPointState.Simple;
    public readonly int actNum = 10;
    public event Action<int> onStateUpdate;
    private void OperationTimeUpdateEvent(OperationType type)
    {
        if (type == operaType)
        {
            if (UIManager.Instance.IsOpened<HeroSkinFlashSaleWin>())
                UIManager.Instance.CloseWindow<HeroSkinFlashSaleWin>();
        }
    }
    private void OperationStartEvent(OperationType type, int state)
    {
        if (type == operaType && state == 0)
        {
            onStateUpdate?.Invoke(activityID);
        }
    }
    private void OperationEndEvent(OperationType type, int state)
    {
        if (type == operaType)
        {
            if (UIManager.Instance.IsOpened<HeroSkinFlashSaleWin>())
                UIManager.Instance.CloseWindow<HeroSkinFlashSaleWin>();
            onStateUpdate?.Invoke(activityID);
        }
    }
    private void OperationAdvanceEvent(OperationType type)
    {
        if (type == operaType)
        {
            onStateUpdate?.Invoke(activityID);
        }
    }
    public bool IsHeroSkinFlashSaleOpen()
    {
        if (!IsOpen) return false;
        if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.HeroSkinFlashSale)) return false;
        return true;
    }
    public OperationFlashSaleActivityInfo GetActInfo()
    {
        OperationTimeHepler.Instance.TryGetOperation(operaType, out OperationFlashSaleActivityInfo act);
        return act;
    }
    public void GetActTimeStr(TextEx timeText, string key = "TimeRush05")
    {
        var act = GetActInfo();
        if (act == null)
        {
            timeText.text = Language.Get("OSActivity6");
            return;
        }
        timeText.text = Language.Get(key, TimeUtility.SecondsToShortDHMS(act.GetResetSurplusTime()));
    }
    public int GetDefaultSkinID(int heroID)
    {
        HeroConfig heroConfig = HeroConfig.Get(heroID);
        if (heroConfig == null || heroConfig.SkinIDList.IsNullOrEmpty()) return 0;
        return heroConfig.SkinIDList[0];
    }
    public bool IsNoSellOutCTGID(int ctgID)
    {
        CTGConfig config = CTGConfig.Get(ctgID);
        if (config == null) return false;
        if (!RechargeManager.Instance.TryGetRechargeCount(ctgID, out var rechargeCount)) return false;
        return rechargeCount.totalCount < config.TotalBuyCount;
    }
    public event Action OnCurrentChooseSkinIDChangeEevent;
    private int m_currentChooseSkinID = 0;
    public int currentChooseSkinID
    {
        get
        {
            return m_currentChooseSkinID;
        }
        set
        {
            if (m_currentChooseSkinID == value) return;
            m_currentChooseSkinID = value;
            OnCurrentChooseSkinIDChangeEevent?.Invoke();
        }
    }
    /// <summary>
    /// 通过skinID获取对应的heroID
    /// </summary>
    public int GetHeroIDBySkinID(int skinID)
    {
        foreach (var heroConfig in HeroConfig.GetValues())
        {
            if (heroConfig.SkinIDList != null && heroConfig.SkinIDList.Contains(skinID))
            {
                return heroConfig.HeroID;
            }
        }
        return 0;
    }
    /// <summary>
    /// 获取skinID在HeroConfig.SkinIDList中的索引
    /// </summary>
    public int GetSkinIndexInHeroConfig(int heroID, int skinID)
    {
        var heroConfig = HeroConfig.Get(heroID);
        if (heroConfig?.SkinIDList == null) return int.MaxValue;
        for (int i = 0; i < heroConfig.SkinIDList.Length; i++)
        {
            if (heroConfig.SkinIDList[i] == skinID)
                return i;
        }
        return int.MaxValue;
    }
    //<skinID,ctgID>
    Dictionary<int, int> ctgDict = new();
    public int GetCtgIDBySkinID(int skinID)
    {
        if (ctgDict.IsNullOrEmpty())
        {
            GetSkinIDToCtgIDDict();
        }
        int ctgID;
        ctgDict.TryGetValue(skinID, out ctgID);
        return ctgID;
    }
    Dictionary<int, int> GetSkinIDToCtgIDDict()
    {
        if (!ctgDict.IsNullOrEmpty())
        {
            return ctgDict;
        }
        foreach (var config in ActSpecialSaleConfig.GetValues())
        {
            if (config == null || config.CTGIDList == null) return null;
            for (int i = 0; i < config.CTGIDList.Length; i++)
            {
                var ctgID = config.CTGIDList[i];
                var ctgConfig = CTGConfig.Get(ctgID);
                if (ctgConfig == null || ctgConfig.GainItemList == null) continue;
                for (int j = 0; j < ctgConfig.GainItemList.Length; j++)
                {
                    var itemID = ctgConfig.GainItemList[j][0];
                    var itemConfig = ItemConfig.Get(itemID);
                    if (itemConfig == null) continue;
                    if (!HeroSkinAttrConfig.TryGetSkinIDByItemID(itemID, out var skinID)) continue;
                    if (ctgDict.ContainsKey(skinID)) continue;
                    ctgDict[skinID] = ctgID;
                }
            }
        }
        return ctgDict;
    }
    public List<int> GetSkinIDList(int cfgID)
    {
        var config = ActSpecialSaleConfig.Get(cfgID);
        if (config == null || config.CTGIDList == null) return null;
        var res = new List<int>();
        for (int i = 0; i < config.CTGIDList.Length; i++)
        {
            var ctgID = config.CTGIDList[i];
            var ctgConfig = CTGConfig.Get(ctgID);
            if (ctgConfig == null || ctgConfig.GainItemList == null) continue;
            for (int j = 0; j < ctgConfig.GainItemList.Length; j++)
            {
                var itemID = ctgConfig.GainItemList[j][0];
                var itemConfig = ItemConfig.Get(itemID);
                if (itemConfig == null) continue;
                if (!HeroSkinAttrConfig.TryGetSkinIDByItemID(itemID, out var skinID)) continue;
                if (res.Contains(skinID)) continue;
                res.Add(skinID);
            }
        }
        return res;
    }
}
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 950599ca1ed881243991a8e394b1a0d3
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs
New file
@@ -0,0 +1,33 @@
using System.Collections.Generic;
using UnityEngine;
public class HeroSkinFlashSaleTabCell : CellView
{
    [SerializeField] ImageEx tabBgImage;
    [SerializeField] ButtonEx tabButton;
    [SerializeField] RectTransform chooseRect;
    [SerializeField] RectTransform maskRect;
    [SerializeField] TextEx rateText;
    HeroSkinFlashSaleManager manager => HeroSkinFlashSaleManager.Instance;
    public void Display(int index, List<int> list)
    {
        if (list?.Count <= index) return;
        var skinID = list[index];
        tabBgImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleTabInfo_", skinID.ToString()));
        tabBgImage.SetNativeSize();
        maskRect.SetActive(manager.currentChooseSkinID != skinID);
        chooseRect.SetActive(manager.currentChooseSkinID == skinID);
        tabButton.SetListener(() =>
        {
            manager.currentChooseSkinID = skinID;
        });
        var act = manager.GetActInfo();
        if (act == null) return;
        var config = ActSpecialSaleConfig.Get(act.CfgID);
        if (config?.PercentageTexts == null || config.PercentageTexts.Length <= index) return;
        rateText.text = Language.Get("storename5", config.PercentageTexts[index].ToString());
    }
}
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1cdbf2b01a9ccb7459cf78b0cd8796fd
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs
New file
@@ -0,0 +1,229 @@
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class HeroSkinFlashSaleWin : UIBase
{
    [SerializeField] float modelScale = 1f;
    [SerializeField] RawImage bgImage;
    [SerializeField] ImageEx heroNameImage;
    [SerializeField] ImageEx skinInfoImage;
    [SerializeField] ImageEx awardBgImage;
    [SerializeField] TextEx timeText;
    [SerializeField] ButtonEx buyButton;
    [SerializeField] ImageEx buyImage;
    [SerializeField] ImageEx countryImage;
    [SerializeField] TextEx buyText;
    [SerializeField] ImageEx originalPriceImage;
    [SerializeField] TextEx originalPriceText;
    [SerializeField] ButtonEx closeButton;
    [SerializeField] UIHeroController uiHeroController;
    [SerializeField] UIHeroController lhController;
    [SerializeField] ScrollerController awardScroller;
    [SerializeField] ScrollerController tabScroller;
    [SerializeField] Color numColor;
    [SerializeField] TextEx[] wearAttrText;
    [SerializeField] TextEx[] roleAttrText;
    [SerializeField] ButtonEx infoButton;
    HeroSkinFlashSaleManager manager => HeroSkinFlashSaleManager.Instance;
    protected override void InitComponent()
    {
        closeButton.SetListener(CloseWindow);
        infoButton.SetListener(() =>
        {
            int heroID = manager.GetHeroIDBySkinID(manager.currentChooseSkinID);
            HeroUIManager.Instance.selectForPreviewHeroID = heroID;
            HeroUIManager.Instance.selectSkinIndex = manager.GetSkinIndexInHeroConfig(heroID, manager.currentChooseSkinID);
            UIManager.Instance.OpenWindow<HeroBestBaseWin>(1);
        });
    }
    protected override void OnPreOpen()
    {
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        RechargeManager.Instance.rechargeCountEvent += OnRechargeCountEvent;
        awardScroller.OnRefreshCell += OnRefreshAwardCell;
        tabScroller.OnRefreshCell += OnRefreshTabCell;
        manager.OnCurrentChooseSkinIDChangeEevent += OnCurrentChooseSkinIDChange;
        CreateTabScroller();
        Display();
    }
    protected override void OnPreClose()
    {
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        RechargeManager.Instance.rechargeCountEvent -= OnRechargeCountEvent;
        awardScroller.OnRefreshCell -= OnRefreshAwardCell;
        tabScroller.OnRefreshCell -= OnRefreshTabCell;
        manager.OnCurrentChooseSkinIDChangeEevent -= OnCurrentChooseSkinIDChange;
    }
    private void OnCurrentChooseSkinIDChange()
    {
        tabScroller.m_Scorller.RefreshActiveCellViews();
        Display();
    }
    void OnRefreshTabCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as HeroSkinFlashSaleTabCell;
        _cell.Display(cell.index, skinIDList);
    }
    List<int> skinIDList;
    void CreateTabScroller()
    {
        var act = manager.GetActInfo();
        if (act == null) return;
        var config = ActSpecialSaleConfig.Get(act.CfgID);
        if (config == null) return;
        int heroID = manager.GetHeroIDBySkinID(manager.currentChooseSkinID);
        skinIDList = manager.GetSkinIDList(act.CfgID);
        if (skinIDList.IsNullOrEmpty()) return;
        manager.currentChooseSkinID = skinIDList[0];
        tabScroller.Refresh();
        for (int i = 0; i < skinIDList.Count; i++)
        {
            tabScroller.AddCell(ScrollerDataType.Header, i);
        }
        tabScroller.Restart();
    }
    int[][] arr = null;
    void OnRefreshAwardCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as HeroSkinFlashSaleAwardCell;
        _cell.Display(cell.index, arr);
    }
    void CreateAwardScroller(int[][] sourceArr)
    {
        if (sourceArr == null) return;
        // 1. 获取已拥有的 ID 集合 (使用 LINQ 简洁明了)
        var ownedItems = HeroSkinAttrConfig.GetItemList();
        var itemIds = ownedItems != null ? new HashSet<int>(ownedItems) : new HashSet<int>();
        // 2. 过滤掉 items 中已有的项,并直接赋值给成员变量
        // arr[i][0] 假设为判断是否存在于 items 中的 ID
        this.arr = System.Array.FindAll(sourceArr, row => !itemIds.Contains(row[0]));
        // 3. 刷新 UI
        awardScroller.Refresh();
        for (int i = 0; i < this.arr.Length; i++)
        {
            awardScroller.AddCell(ScrollerDataType.Header, i);
        }
        awardScroller.Restart();
    }
    private void OnSecondEvent()
    {
        manager.GetActTimeStr(timeText);
    }
    private void OnRechargeCountEvent(int obj)
    {
        Display();
    }
    public void Display()
    {
        var act = manager.GetActInfo();
        if (act == null) return;
        var config = ActSpecialSaleConfig.Get(act.CfgID);
        if (config == null) return;
        int heroID = manager.GetHeroIDBySkinID(manager.currentChooseSkinID);
        var heroConfig = HeroConfig.Get(heroID);
        if (heroConfig == null) return;
        int skinID = manager.currentChooseSkinID;
        var heroSkinAttrConfig = HeroSkinAttrConfig.Get(skinID);
        if (heroSkinAttrConfig == null) return;
        if (heroSkinAttrConfig.WearAttrIDList == null) return;
        if (heroSkinAttrConfig.WearAttrValueList == null) return;
        if (heroSkinAttrConfig.RoleAttrIDList == null) return;
        if (heroSkinAttrConfig.RoleAttrValueList == null) return;
        var skinIDList = manager.GetSkinIDList(act.CfgID);
        if (skinIDList.IsNullOrEmpty()) return;
        int ctgId = manager.GetCtgIDBySkinID(skinID);
        var ctgConfig = CTGConfig.Get(ctgId);
        if (ctgConfig == null) return;
        if (!RechargeManager.Instance.TryGetOrderInfo(ctgId, out var orderConfig)) return;
        if (!RechargeManager.Instance.TryGetRechargeCount(ctgId, out var rechargeCount)) return;
        if (!RechargeManager.Instance.TryGetRechargeItem(ctgId, out var rechargeItemList)) return;
        bgImage.SetTexture2D(StringUtility.Concat("HeroSkinFlashSaleBG_", skinID.ToString()));
        heroNameImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleHeroName_", skinID.ToString()));
        heroNameImage.SetNativeSize();
        awardBgImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleAwardBG_", skinID.ToString()));
        awardBgImage.SetNativeSize();
        uiHeroController.Create(skinID, modelScale);
        lhController.Create(skinID, 1, motionName: "", isLh: true);
        countryImage.SetSprite(HeroUIManager.Instance.GetCountryIconName(heroConfig.Country));
        OnSecondEvent();
        CreateAwardScroller(ctgConfig.GainItemList);
        bool isCanBuy = manager.IsNoSellOutCTGID(ctgId);
        //buyImage.gray = !isCanBuy;
        buyText.text = !isCanBuy ? Language.Get("storename11") : Language.Get("PayMoneyNum", UIHelper.GetMoneyFormat(orderConfig.PayRMBNumOnSale));
        originalPriceImage.SetActive(isCanBuy);
        // 根据 ctgId 在 CTGIDList 中的索引,从 OriginalPrices 取原价
        int originalPriceIndex = System.Array.IndexOf(config.CTGIDList, ctgId);
        float originalPrice = originalPriceIndex >= 0 && config.OriginalPrices.Length > originalPriceIndex ? config.OriginalPrices[originalPriceIndex] : 0;
        originalPriceText.text = Language.Get("storename9",Language.Get("PayMoneyNum", UIHelper.GetMoneyFormat(originalPrice)));
        buyButton.interactable = isCanBuy;
        buyButton.SetListener(() =>
        {
            RechargeManager.Instance.CTG(ctgId);
        });
        for (int i = 0; i < wearAttrText.Length; i++)
            SetAttrInfo(0, i, heroSkinAttrConfig, wearAttrText[i]);
        for (int i = 0; i < roleAttrText.Length; i++)
            SetAttrInfo(1, i, heroSkinAttrConfig, roleAttrText[i]);
    }
    // type 0 穿戴属性值 1 主公属性
    public void SetAttrInfo(int type, int index, HeroSkinAttrConfig heroSkinAttrConfig, TextEx info)
    {
        if (heroSkinAttrConfig == null) return;
        int[] arrID = type == 0 ? heroSkinAttrConfig.WearAttrIDList : heroSkinAttrConfig.RoleAttrIDList;
        int[] arrValue = type == 0 ? heroSkinAttrConfig.WearAttrValueList : heroSkinAttrConfig.RoleAttrValueList;
        if (arrID?.Length <= index || arrValue?.Length <= index)
        {
            info.text = string.Empty;
            return;
        }
        info.text = GetFullDescription(arrID[index], arrValue[index]);
    }
    string GetFullDescription(int id, long value)
    {
        var config = PlayerPropertyConfig.Get(id);
        if (config == null) return string.Empty;
        return Language.Get("HeroDebut33", config.ShowName, PlayerPropertyConfig.GetValueDescription(id, value));
    }
}
Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3368c493a4730a45bd445fbcf6ffa3d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/HeroUI/HeroSkinWin.cs
@@ -27,6 +27,7 @@
    [SerializeField] ScrollerController skinScroller;
    [SerializeField] Button unlockBtn;
    [SerializeField] ImageEx actBubbleImg;
    [SerializeField] Image itemIcon;
    [SerializeField] Text itemCountText;
    [SerializeField] Button putonBtn;
@@ -326,18 +327,72 @@
                itemIcon.SetItemSprite(itemID);
                itemCountText.text = UIHelper.ShowUseItem(PackType.Item, itemID, 1, bright: false);
            }
            putonBtn.SetActive(false);
            putonYetObj.SetActive(false);
        putonBtn.SetActive(false);
        putonYetObj.SetActive(false);
        RefreshActBubbleImg();
        return;
    }
    unlockBtn.SetActive(false);
    if (hero == null) return;
    putonBtn.SetActive(hero.SkinAttrID != skinID);
    putonYetObj.SetActive(hero.SkinAttrID == skinID);
    }
    void RefreshActBubbleImg()
    {
        if (HeroUIManager.Instance.IsHeroSkinActive(heroID, skinID))
        {
            actBubbleImg.SetActive(false);
            return;
        }
        unlockBtn.SetActive(false);
        if (hero == null) return;
        if (HeroSkinFlashSaleManager.Instance.IsHeroSkinFlashSaleOpen())
        {
            var act = HeroSkinFlashSaleManager.Instance.GetActInfo();
            if (act != null)
            {
                var skinList = HeroSkinFlashSaleManager.Instance.GetSkinIDList(act.CfgID);
                if (skinList != null && skinList.Contains(skinID))
                {
                    actBubbleImg.SetActive(true);
                    return;
                }
            }
        }
        putonBtn.SetActive(hero.SkinAttrID != skinID);
        putonYetObj.SetActive(hero.SkinAttrID == skinID);
        if (HeroDebutManager.Instance.IsHeroDebutOpen())
        {
            var act = HeroDebutManager.Instance.GetOperationHeroAppearInfo();
            if (act != null)
            {
                var skinList = HeroDebutManager.Instance.GetSkinIDList(act.CfgID, heroID, 0);
                if (skinList != null && skinList.Contains(skinID))
                {
                    actBubbleImg.SetActive(true);
                    return;
                }
            }
        }
        if (HeroReturnManager.Instance.IsHeroReturnOpen())
        {
            var act = HeroReturnManager.Instance.GetOperationHeroAppearInfo();
            if (act != null)
            {
                var skinList = HeroReturnManager.Instance.GetSkinIDList(act.CfgID, heroID, 0);
                if (skinList != null && skinList.Contains(skinID))
                {
                    actBubbleImg.SetActive(true);
                    return;
                }
            }
        }
        actBubbleImg.SetActive(false);
    }
    void ChangeParentWinAlpha(float alpha)
Main/System/Main/HomeWin.cs
@@ -74,6 +74,7 @@
    [SerializeField] TimeRushCell timeRushCell;
    [SerializeField] HeroDebutCell heroDebutCell;
    [SerializeField] HeroReturnCell heroReturnCell;
    [SerializeField] HeroSkinFlashSaleCell heroSkinFlashSaleCell;
    [SerializeField] Button festivalActivityBtn;
    //坐骑
@@ -302,6 +303,7 @@
        DisplayTimeRush();
        DisplayHeroDebut();
        DisplayHeroReturn();
        DisplayHeroSkinFlash();
        DisplayFestivalActivity();
        DelayPlayMusic().Forget();
@@ -355,6 +357,10 @@
        {
            DisplayFestivalActivity();
        }
        else if (type == OperationType.HeroSkinFlashSale)
        {
            DisplayHeroSkinFlash();
        }
    }
    private void OpenServerActivityStateChange()
@@ -364,6 +370,7 @@
        DisplayHeroReturn();
        DisplayGalaBtn();
        DisplayFestivalActivity();
        DisplayHeroSkinFlash();
    }
    private void OnShowGiftIdListAddEvent()
@@ -870,6 +877,10 @@
        {
            DisplayFestivalActivity();
        }
        else if (funcId == (int)FuncOpenEnum.HeroSkinFlashSale)
        {
            DisplayHeroSkinFlash();
        }
    }
    private void OnUpdateFirstChargeInfo()
@@ -973,6 +984,15 @@
        heroReturnCell.Display();
    }
    void DisplayHeroSkinFlash()
    {
        bool isOpen = HeroSkinFlashSaleManager.Instance.IsHeroSkinFlashSaleOpen();
        heroSkinFlashSaleCell.SetActive(isOpen);
        if (!isOpen)
            return;
        heroSkinFlashSaleCell.Display();
    }
    void DisplayFestivalActivity()
    {
        bool isOpen = FestivalActivityManager.Instance.IsFestivalActivityOpen();
Main/System/OpenServerActivity/OperationTimeHepler.cs
@@ -272,6 +272,9 @@
        var opreationType = OperationType.FestivalActivity;
        switch (package.ActNum)
        {
            case 10:
                opreationType = OperationType.HeroSkinFlashSale;
                break;
            case 30:
                opreationType = OperationType.FestivalActivity;
                break;
@@ -754,5 +757,6 @@
    FestivalActivity_RechargeTotal = 8,   //节日活动-累充值
    FestivalActivity_CheckIn = 9,   //节日活动-签到
    FestivalActivity_Mission = 10,   //节日活动-任务
    HeroSkinFlashSale = 11, //时装特卖
    max,
}
Main/System/Redpoint/MainRedDot.cs
@@ -157,6 +157,7 @@
    public const int RedPoint_OSMingge = 483;
    public const int HeroReturnRepoint = 484;   //武将返场
    public const int FestivalActivityRepoint = 485;    //节日活动
    public const int HeroSkinFlashSaleRepoint = 486;
    public void Register()
    {
Main/System/Store/SkinStoreCell.cs
@@ -19,7 +19,7 @@
    public void Display(int index)
    {
        var list = StoreModel.Instance.storeTypeDict[(int)StoreFunc.HeroSkin];
        var list = StoreModel.Instance.GetTimeValidStoreDatas(StoreFunc.HeroSkin);
        var storeData = list[index];
        int shopID = storeData.shopId;
        var itemID = storeData.storeConfig.ItemID;
Main/System/Store/SkinStoreLineCell.cs
@@ -10,7 +10,7 @@
    public void Display(int index)
    {
        var list = StoreModel.Instance.storeTypeDict[(int)StoreFunc.HeroSkin];
        var list = StoreModel.Instance.GetTimeValidStoreDatas(StoreFunc.HeroSkin);
        for (int i = 0; i < storeCells.Length; i++)
        {
Main/System/Store/SkinStoreWin.cs
@@ -25,6 +25,7 @@
        scroller.OnRefreshCell += OnRefreshCell;
        StoreModel.Instance.RefreshShopEvent += Show;
        StoreModel.Instance.RefreshBuyShopLimitEvent += Show;
        TimeMgr.Instance.OnDayEvent += OnDayEvent;
        
        Display();
    }
@@ -34,6 +35,12 @@
        scroller.OnRefreshCell -= OnRefreshCell;
        StoreModel.Instance.RefreshShopEvent -= Show;
        StoreModel.Instance.RefreshBuyShopLimitEvent -= Show;
        TimeMgr.Instance.OnDayEvent -= OnDayEvent;
    }
    void OnDayEvent()
    {
        Display();
    }
    void Display()
@@ -55,7 +62,7 @@
        int jumpIndex = -1;
        scroller.Refresh();
        var list = StoreModel.Instance.storeTypeDict[(int)StoreFunc.HeroSkin];
        var list = StoreModel.Instance.GetTimeValidStoreDatas(StoreFunc.HeroSkin);
        for (int i = 0; i < list.Count; i++)
        {
            if (i % 4 == 0)
Main/System/Store/StoreModel.cs
@@ -181,7 +181,36 @@
        return datas;
    }
    /// <summary>
    /// 获取在上下架时间范围内的商品列表
    /// </summary>
    public List<StoreData> GetTimeValidStoreDatas(StoreFunc type)
    {
        List<StoreData> allDatas = TryGetStoreDatas(type);
        if (allDatas == null)
            return null;
        int curOpenDay = TimeUtility.OpenDay + 1;
        List<StoreData> validDatas = new List<StoreData>();
        for (int i = 0; i < allDatas.Count; i++)
        {
            StoreConfig cfg = allDatas[i].storeConfig;
            // <=0 或 StartTime >= EndTime 视为无限制
            if (cfg.StartTime <= 0 || cfg.EndTime <= 0 || cfg.StartTime >= cfg.EndTime)
            {
                validDatas.Add(allDatas[i]);
                continue;
            }
            // 未到上架时间
            if (curOpenDay < cfg.StartTime)
                continue;
            // 已过下架时间
            if (curOpenDay >= cfg.EndTime)
                continue;
            validDatas.Add(allDatas[i]);
        }
        return validDatas;
    }
 
    public StoreData GetStoreData(int shopId)
Main/System/UIBase/UIJumpManager.cs
@@ -98,7 +98,7 @@
        //武将返场相关
        else if (config.WinName == "HeroReturnCallWin" || config.WinName == "HeroReturnSkinWin" ||
                config.WinName == "HeroReturnCheckInWin" || config.WinName == "HeroReturnGiftWin" ||
                config.WinName == "HeroReturnShopWin"|| config.WinName == "HeroReturnZhanLingWin")
                config.WinName == "HeroReturnShopWin" || config.WinName == "HeroReturnZhanLingWin")
        {
            var heroDebutAct = HeroReturnManager.Instance.GetOperationHeroAppearInfo();
            if (heroDebutAct == null)
@@ -129,6 +129,28 @@
                UIManager.Instance.OpenWindow(config.WinName);
            }
        }
        //皮肤限时特惠相关
        else if (config.WinName == "HeroSkinFlashSaleWin")
        {
            var act = HeroSkinFlashSaleManager.Instance.GetActInfo();
            if (act == null)
            {
                SysNotifyMgr.Instance.ShowTip("ActivityNoOpen");
                return;
            }
            var actConfig = ActSpecialSaleConfig.Get(act.CfgID);
            if (actConfig == null)
            {
                SysNotifyMgr.Instance.ShowTip("ActivityNoOpen");
                return;
            }
            if (UIManager.Instance.IsOpened(config.WinName))
            {
                UIManager.Instance.CloseWindow(config.WinName);
                UIManager.Instance.OpenWindow(config.WinName);
            }
        }
        if (!UIManager.Instance.IsOpened(config.WinName))
        {
Main/Utility/EnumHelper.cs
@@ -860,6 +860,7 @@
    HeroDebut = 63,//武将登场
    HeroReturn = 64,  //武将返场
    FestivalActivity = 65,//节日活动
    HeroSkinFlashSale = 66,//时装特卖
}