From 4b169e9286cc62c2dff0b2cb2bc21e49c2b52596 Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期五, 06 二月 2026 17:09:48 +0800
Subject: [PATCH] 475 时机礼包-客户端 接入触发时机,山寨界面,记录在本地

---
 Main/System/TimingGift/TimingGiftAwardCell.cs.meta  |   11 
 Main/Config/ConfigManager.cs                        |    6 
 Main/System/Gubao/GubaoDetailWin.cs                 |   23 
 Main/Config/Configs/TimingGiftConfig.cs.meta        |   11 
 Main/System/BoneField/AdsManager.cs                 |    9 
 Main/System/TimingGift/TimingGiftCtgIdCell.cs.meta  |   11 
 Main/System/TimingGift/TimingGiftManager.cs         |  502 ++++++++++++++++++++
 Main/System/BeautyMM/BeautyMMShowWin.cs             |   13 
 Main/System/Gubao/GubaoCallWin.cs                   |   11 
 Main/System/TimingGift/TimingGiftAwardCell.cs       |   20 
 Main/System/Main/HomeWin.cs                         |   14 
 Main/System/TimingGift/TimingGiftManager.cs.meta    |   11 
 Main/Config/PartialConfigs/TimingGiftConfig.cs.meta |   11 
 Main/System/Mingge/MinggeManager.cs                 |   21 
 Main/System/Equip/BlessLVTimeUpWin.cs               |    8 
 Main/System/TimingGift.meta                         |    8 
 Main/Main.cs                                        |    1 
 Main/System/TimingGift/TimingGiftCtgIdCell.cs       |   31 +
 Main/System/HeroUI/HeroTrainWin.cs                  |   11 
 Main/Config/Configs/TimingGiftTypeConfig.cs         |   44 +
 Main/System/TimingGift/TimingGiftWin.cs             |  258 ++++++++++
 Main/System/TimingGift/TimingGiftTabCell.cs         |   29 +
 Main/Utility/EnumHelper.cs                          |    1 
 Main/System/Mingge/MinggeWin.cs                     |   13 
 Main/System/Mingge/MinggePrayWin.cs                 |   10 
 Main/System/HappyXB/HeroCallWin.cs                  |   10 
 Main/System/Equip/BlessLVADWin.cs                   |    5 
 Main/System/TimingGift/TimingGiftCell.cs            |  137 +++++
 Main/System/TimingGift/TimingGiftCell.cs.meta       |   11 
 Main/System/BeautyMM/BeautyMMTravelCell.cs          |    8 
 Main/System/TimingGift/TimingGiftWin.cs.meta        |   11 
 Main/Config/Configs/TimingGiftTypeConfig.cs.meta    |   11 
 Main/System/HeroUI/HeroGiftWashWin.cs               |   10 
 Main/System/TimingGift/TimingGiftTabCell.cs.meta    |   11 
 Main/Config/Configs/TimingGiftConfig.cs             |   59 ++
 Main/System/Horse/HorseWin.cs                       |   39 +
 Main/System/Horse/HorseRankUPWin.cs                 |   15 
 Main/Config/PartialConfigs/TimingGiftConfig.cs      |   52 ++
 38 files changed, 1,420 insertions(+), 47 deletions(-)

diff --git a/Main/Config/ConfigManager.cs b/Main/Config/ConfigManager.cs
index dfac8d5..81c5e34 100644
--- a/Main/Config/ConfigManager.cs
+++ b/Main/Config/ConfigManager.cs
@@ -98,6 +98,8 @@
             typeof(SuccessConfig),
             typeof(SysInfoConfig),
             typeof(TianziConfig),
+            typeof(TimingGiftConfig),
+            typeof(TimingGiftTypeConfig),
             typeof(TitleConfig),
             typeof(TitleStarUpConfig),
             typeof(TravelEventConfig),
@@ -376,6 +378,10 @@
         ClearConfigDictionary<SysInfoConfig>();
         // 娓呯┖ TianziConfig 瀛楀吀
         ClearConfigDictionary<TianziConfig>();
+        // 娓呯┖ TimingGiftConfig 瀛楀吀
+        ClearConfigDictionary<TimingGiftConfig>();
+        // 娓呯┖ TimingGiftTypeConfig 瀛楀吀
+        ClearConfigDictionary<TimingGiftTypeConfig>();
         // 娓呯┖ TitleConfig 瀛楀吀
         ClearConfigDictionary<TitleConfig>();
         // 娓呯┖ TitleStarUpConfig 瀛楀吀
diff --git a/Main/Config/Configs/TimingGiftConfig.cs b/Main/Config/Configs/TimingGiftConfig.cs
new file mode 100644
index 0000000..d474de9
--- /dev/null
+++ b/Main/Config/Configs/TimingGiftConfig.cs
@@ -0,0 +1,59 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           2026骞�2鏈�5鏃�
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class TimingGiftConfig : ConfigBase<int, TimingGiftConfig>
+{
+    static TimingGiftConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int GiftID;
+	public string GiftName;
+	public int GiftType;
+	public int[] CTGIDs;
+
+    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 GiftID); 
+
+			GiftName = tables[1];
+
+			int.TryParse(tables[2],out GiftType); 
+
+			if (tables[3].Contains("["))
+			{
+				CTGIDs = JsonMapper.ToObject<int[]>(tables[3]);
+			}
+			else
+			{
+				string[] CTGIDsStringArray = tables[3].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+				CTGIDs = new int[CTGIDsStringArray.Length];
+				for (int i=0;i<CTGIDsStringArray.Length;i++)
+				{
+					 int.TryParse(CTGIDsStringArray[i],out CTGIDs[i]);
+				}
+			}
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/Config/Configs/TimingGiftConfig.cs.meta b/Main/Config/Configs/TimingGiftConfig.cs.meta
new file mode 100644
index 0000000..d2a9ef1
--- /dev/null
+++ b/Main/Config/Configs/TimingGiftConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0d0f2d099d904734e8c8cdb9ffa748ad
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/Configs/TimingGiftTypeConfig.cs b/Main/Config/Configs/TimingGiftTypeConfig.cs
new file mode 100644
index 0000000..61c9ad1
--- /dev/null
+++ b/Main/Config/Configs/TimingGiftTypeConfig.cs
@@ -0,0 +1,44 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           2026骞�2鏈�5鏃�
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class TimingGiftTypeConfig : ConfigBase<int, TimingGiftTypeConfig>
+{
+    static TimingGiftTypeConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int GiftType;
+	public int Duration;
+	public int TriggleChangeRate;
+
+    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 GiftType); 
+
+			int.TryParse(tables[1],out Duration); 
+
+			int.TryParse(tables[2],out TriggleChangeRate); 
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/Config/Configs/TimingGiftTypeConfig.cs.meta b/Main/Config/Configs/TimingGiftTypeConfig.cs.meta
new file mode 100644
index 0000000..3686a8e
--- /dev/null
+++ b/Main/Config/Configs/TimingGiftTypeConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 79fc1592c88c38746980c51334e7ec72
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Config/PartialConfigs/TimingGiftConfig.cs b/Main/Config/PartialConfigs/TimingGiftConfig.cs
new file mode 100644
index 0000000..8e6765e
--- /dev/null
+++ b/Main/Config/PartialConfigs/TimingGiftConfig.cs
@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+public partial class TimingGiftConfig : ConfigBase<int, TimingGiftConfig>
+{
+
+    public static Dictionary<int, List<int>> typeToGiftIdList = null;
+
+    private static void LoadTypeToGiftIdList()
+    {
+        if (typeToGiftIdList != null)
+            return;
+        typeToGiftIdList = new Dictionary<int, List<int>>();
+
+        foreach (var config in GetValues())
+        {
+            if (!typeToGiftIdList.ContainsKey(config.GiftType))
+            {
+                typeToGiftIdList[config.GiftType] = new List<int>();
+            }
+            if (!typeToGiftIdList[config.GiftType].Contains(config.GiftID))
+            {
+                typeToGiftIdList[config.GiftType].Add(config.GiftID);
+            }
+        }
+
+        foreach (var type in typeToGiftIdList.Keys)
+        {
+            typeToGiftIdList[type].Sort();
+        }
+    }
+
+    public static Dictionary<int, List<int>> GetTypeToGiftIdDict()
+    {
+        LoadTypeToGiftIdList();
+        return typeToGiftIdList;
+    }
+
+    public static bool TryGetTypeToGiftIdList(int type, out List<int> giftIdList)
+    {
+        giftIdList = null;
+        LoadTypeToGiftIdList();
+        return !typeToGiftIdList.IsNullOrEmpty() && typeToGiftIdList.TryGetValue(type, out giftIdList) && giftIdList != null;
+    }
+
+    public static bool TryGetTimingGiftConfig(int id, out TimingGiftConfig config)
+    {
+        config = null;
+        if (!HasKey(id))
+            return false;
+        config = Get(id);
+        return true;
+    }
+}
diff --git a/Main/Config/PartialConfigs/TimingGiftConfig.cs.meta b/Main/Config/PartialConfigs/TimingGiftConfig.cs.meta
new file mode 100644
index 0000000..1f84a24
--- /dev/null
+++ b/Main/Config/PartialConfigs/TimingGiftConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1dc8c2bc48ca2254aaa2efe08e81f5f8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Main.cs b/Main/Main.cs
index 35335b9..521caec 100644
--- a/Main/Main.cs
+++ b/Main/Main.cs
@@ -104,6 +104,7 @@
         managers.Add(MinggeManager.Instance);
         managers.Add(FuncPresetManager.Instance);
         managers.Add(GMNotifyManager.Instance);
+        managers.Add(TimingGiftManager.Instance);
 
         foreach (var manager in managers)
         {
diff --git a/Main/System/BeautyMM/BeautyMMShowWin.cs b/Main/System/BeautyMM/BeautyMMShowWin.cs
index ead9102..08c5fa3 100644
--- a/Main/System/BeautyMM/BeautyMMShowWin.cs
+++ b/Main/System/BeautyMM/BeautyMMShowWin.cs
@@ -118,7 +118,7 @@
         loveScroller.OnRefreshCell += OnRefreshCell;
         BeautyMMManager.Instance.OnBeautyMMDataUpdate += OnBeautyMMDataUpdate;
         PackManager.Instance.RefreshItemEvent += OnRefreshItemEvent;
-        
+
         Display(true);
     }
 
@@ -197,7 +197,7 @@
 
             needExp = nextLVConfig.LVNeedExp;
             expText.text = (mmData != null ? mmData.Exp.ToString() : "0") + "/" + needExp;
-            process.fillAmount = mmData != null ? mmData.Exp*1.0f / needExp : 0;
+            process.fillAmount = mmData != null ? mmData.Exp * 1.0f / needExp : 0;
             showAwardCell.SetActive(true);
             int itemID = nextLVConfig.AwardItemList[0][0];
             showAwardCell.Init(new ItemCellModel(itemID, false, nextLVConfig.AwardItemList[0][1]));
@@ -224,7 +224,7 @@
 
             ShowTalent();
             nextLVTalentTipRect.SetActive(isActive);
-            nextLVTalentTip.text = Language.Get("BeautyMM26", (beforeRank + 1)* BeautyMMManager.Instance.needLVForTalent);
+            nextLVTalentTip.text = Language.Get("BeautyMM26", (beforeRank + 1) * BeautyMMManager.Instance.needLVForTalent);
             giftOPRect.SetActive(true);
             fullRect.SetActive(false);
         }
@@ -425,6 +425,7 @@
         if (cnt == 0)
         {
             ItemTipUtility.Show(BeautyMMManager.Instance.selectLoveItemID, true);
+            TimingGiftManager.Instance.TryAddWhenInsufficient(5, PackType.Item, BeautyMMManager.Instance.selectLoveItemID, 1);
             return;
         }
         //浣跨敤鍒板彲鍗囩骇鐨勬暟閲忥紝鎴栬�呮�绘暟
@@ -453,6 +454,12 @@
             GameNetSystem.Instance.SendInfo(pack);
             roleUPEffect.Play();
             SysNotifyMgr.Instance.ShowTip("BeautyMM2", mmConfig.Name, useCnt * value);
+
+            // 濡傛灉姝eソ娑堣�楀畬锛屼笉瑙﹀彂鍥哄畾姒傜巼鐨勫垽瀹�
+            if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(5, PackType.Item, BeautyMMManager.Instance.selectLoveItemID, onekeyToggle.isOn ? useCnt : 1))
+            {
+                TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(5, PackType.Item, BeautyMMManager.Instance.selectLoveItemID, onekeyToggle.isOn ? useCnt : 1);
+            }
         }
 
 
diff --git a/Main/System/BeautyMM/BeautyMMTravelCell.cs b/Main/System/BeautyMM/BeautyMMTravelCell.cs
index 1e12345..20464d9 100644
--- a/Main/System/BeautyMM/BeautyMMTravelCell.cs
+++ b/Main/System/BeautyMM/BeautyMMTravelCell.cs
@@ -126,10 +126,18 @@
             if (BeautyMMManager.Instance.m_Energy == 0)
             {
                 SysNotifyMgr.Instance.ShowTip("BeautyMM4");
+
+                TimingGiftManager.Instance.TryAddWhenInsufficient(5, BeautyMMManager.Instance.m_Energy, 1);
                 return;
             }
         }
 
+        // 濡傛灉姝eソ娑堣�楀畬锛屼笉瑙﹀彂鍥哄畾姒傜巼鐨勫垽瀹�
+        if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(5, BeautyMMManager.Instance.m_Energy, 1))
+        {
+            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(5, BeautyMMManager.Instance.m_Energy, 1);
+        }
+        
         if (grid.EventID == (int)GirdEventType.Empty)
         {
             // 绌烘牸瀛�
diff --git a/Main/System/BoneField/AdsManager.cs b/Main/System/BoneField/AdsManager.cs
index 1219506..25c3c99 100644
--- a/Main/System/BoneField/AdsManager.cs
+++ b/Main/System/BoneField/AdsManager.cs
@@ -78,7 +78,6 @@
                 BoneFieldManager.Instance.SendBBeginFBWipeOut(BoneFieldManager.Instance.DataMapID, (int)fbInfo1.PassLineID);
                 break;
             case 2:
-
                 if (!DungeonManager.Instance.TryGetFBInfoByMapID(TianziBillboradManager.Instance.DataMapID, out var fbInfo2))
                     return;
                 SendGetReward(ADID);
@@ -90,16 +89,22 @@
                     HeroUIManager.Instance.selectCallType = HappXBTitle.HeroCallAdvanced;
                     HeroUIManager.Instance.selectCallIndex = 0;
                     SendGetReward(ADID);
+                    TimingGiftManager.Instance.TryAddWhenAllAdsUsed(2, ADID);
                 });
                 break;
             case 4:
-            case 6:
+                TimingGiftManager.Instance.TryAddWhenAllAdsUsed(1, ADID);
                 SendGetReward(ADID);
                 break;
             case 5:
+                TimingGiftManager.Instance.TryAddWhenAllAdsUsed(7, ADID);
                 GubaoManager.Instance.selectCallIndex = 0;
                 SendGetReward(ADID);
                 break;
+            case 6:
+                SendGetReward(ADID);
+                break;
+
         }
     }
 
diff --git a/Main/System/Equip/BlessLVADWin.cs b/Main/System/Equip/BlessLVADWin.cs
index c97e1cf..0a89059 100644
--- a/Main/System/Equip/BlessLVADWin.cs
+++ b/Main/System/Equip/BlessLVADWin.cs
@@ -11,7 +11,7 @@
     [SerializeField] Image moneyTypeImg;
     [SerializeField] Button adBtn;
     [SerializeField] Button useMoneyBtn;
-    
+
 
 
 
@@ -33,8 +33,9 @@
     {
         AdsManager.Instance.PlayAds(4);
         CloseWindow();
+        TimingGiftManager.Instance.TryAddWhenExactConsumption(1, BlessLVManager.Instance.m_Energy, 1);
     }
-    
+
     void OnUseMoney()
     {
         CloseWindow();
diff --git a/Main/System/Equip/BlessLVTimeUpWin.cs b/Main/System/Equip/BlessLVTimeUpWin.cs
index 9f7d249..1e5b15b 100644
--- a/Main/System/Equip/BlessLVTimeUpWin.cs
+++ b/Main/System/Equip/BlessLVTimeUpWin.cs
@@ -45,7 +45,7 @@
     }
 
     void OnTimeEvent()
-    { 
+    {
         var remainTime = BlessLVManager.Instance.GetLVUPRemainTime();
         int needCount = (int)Math.Ceiling((float)remainTime / BlessLVManager.Instance.timeUpTreeItemSubTime);
         RefreshCount(needCount, remainTime);
@@ -118,11 +118,15 @@
         CloseWindow();
         int count = (int)PackManager.Instance.GetItemCountByID(PackType.Item, BlessLVManager.Instance.timeUpTreeItemID);
         if (count <= 0)
-        { 
+        {
             SysNotifyMgr.Instance.ShowTip("ItemNotEnoughCommon");
             ItemTipUtility.Show(BlessLVManager.Instance.timeUpTreeItemID, true);
+            TimingGiftManager.Instance.TryAddWhenInsufficient(1,PackType.Item, BlessLVManager.Instance.timeUpTreeItemID, showCount);
             return;
         }
+
+        TimingGiftManager.Instance.TryAddWhenExactConsumption(1,PackType.Item, BlessLVManager.Instance.timeUpTreeItemID, showCount);
+
         var pack = new CB224_tagCMUseTreeLVUPTimeItem();
         pack.UseCount = (uint)showCount;
         GameNetSystem.Instance.SendInfo(pack);
diff --git a/Main/System/Gubao/GubaoCallWin.cs b/Main/System/Gubao/GubaoCallWin.cs
index d7f9bdb..db71872 100644
--- a/Main/System/Gubao/GubaoCallWin.cs
+++ b/Main/System/Gubao/GubaoCallWin.cs
@@ -8,6 +8,7 @@
 /// </summary>
 public class GubaoCallWin : UIBase
 {
+    [SerializeField] TimingGiftCell timingGiftCell;
     [SerializeField] OwnItemCell ownItemCell;
     [SerializeField] Toggle skipToggle;
     [SerializeField] Button call1Btn;
@@ -72,10 +73,12 @@
     {
         HappyXBModel.Instance.RefreshXBTypeInfoAct += Display;
         HappyXBModel.Instance.RefreshXBResultAct += ShowResult;
+        TimingGiftManager.Instance.OnShowGiftIdListAddEvent += OnShowGiftIdListAddEvent;
         skipToggle.isOn = LocalSave.GetBool(GubaoManager.skipKey + PlayerDatas.Instance.baseData.PlayerID, false);
         opObj.SetActive(true);
         resultObj.SetActive(false);
-
+        timingGiftCell.InitUI();
+        
         ShowVenderTalk(0).Forget();
         Display();
     }
@@ -84,6 +87,12 @@
     {
         HappyXBModel.Instance.RefreshXBTypeInfoAct -= Display;
         HappyXBModel.Instance.RefreshXBResultAct -= ShowResult;
+        TimingGiftManager.Instance.OnShowGiftIdListAddEvent -= OnShowGiftIdListAddEvent;
+    }
+
+    private void OnShowGiftIdListAddEvent()
+    {
+        timingGiftCell.InitUI();
     }
 
 
diff --git a/Main/System/Gubao/GubaoDetailWin.cs b/Main/System/Gubao/GubaoDetailWin.cs
index 30414c3..84b000f 100644
--- a/Main/System/Gubao/GubaoDetailWin.cs
+++ b/Main/System/Gubao/GubaoDetailWin.cs
@@ -586,10 +586,11 @@
         if (PackManager.Instance.GetItemCountByID(PackType.Item, lvConfig.LVUPNeedItemInfo[0][0]) < lvConfig.LVUPNeedItemInfo[0][1])
         {
             ItemTipUtility.Show(lvConfig.LVUPNeedItemInfo[0][0], true);
+            GubaoTryAdd(lvConfig.LVUPNeedItemInfo[0][0], lvConfig.LVUPNeedItemInfo[0][1]);
             return;
         }
-
         lvupEffect.Play();
+        GubaoTryAdd(lvConfig.LVUPNeedItemInfo[0][0], lvConfig.LVUPNeedItemInfo[0][1]);
         GubaoManager.Instance.UpgradeGubaoLV(gubaoID);
     }
 
@@ -605,4 +606,24 @@
         GubaoManager.Instance.ActiveGubao(gubaoID);
     }
 
+    void GubaoTryAdd(int itemId, long needCount)
+    {
+        int timingType = itemId switch
+        {
+            21 => 8,  // 鍦拌剦绮夊皹
+            27 => 9,  // 鏄熻緣绮夊皹
+            28 => 10, // 鏈堝崕绮夊皹
+            29 => 11, // 鏃ユ洔绮夊皹
+            _ => -1   // 榛樿鍊�
+        };
+
+        if (timingType >= 0)
+        {
+            if (TimingGiftManager.Instance.TryAddWhenInsufficient(timingType,PackType.Item, itemId, needCount))
+                return;
+            if (TimingGiftManager.Instance.TryAddWhenExactConsumption(timingType, PackType.Item,itemId, needCount))
+                return;
+            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(timingType, PackType.Item,itemId, needCount);
+        }
+    }
 }
diff --git a/Main/System/HappyXB/HeroCallWin.cs b/Main/System/HappyXB/HeroCallWin.cs
index a48b162..5a1ff18 100644
--- a/Main/System/HappyXB/HeroCallWin.cs
+++ b/Main/System/HappyXB/HeroCallWin.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
@@ -9,6 +10,7 @@
 /// </summary>
 public class HeroCallWin : UIBase
 {
+    [SerializeField] TimingGiftCell timingGiftCell;
     [SerializeField] Button ruleBtn;
     [SerializeField] OwnItemCell ownItemCell;
     [SerializeField] Button scoreBtn;
@@ -76,6 +78,8 @@
 
         HappyXBModel.Instance.RefreshXBTypeInfoAct += Refresh;
         InvestModel.Instance.onInvestUpdate += OnInvestUpdate;
+        TimingGiftManager.Instance.OnShowGiftIdListAddEvent += OnShowGiftIdListAddEvent;
+        timingGiftCell.InitUI();
         skipToggle.isOn = LocalSave.GetBool(HeroUIManager.skipKey + PlayerDatas.Instance.baseData.PlayerID, false);
         Refresh();
         openPrivilegeTip.SetActive(!InvestModel.Instance.IsInvested(InvestModel.foreverCardType));
@@ -85,6 +89,12 @@
     {
         HappyXBModel.Instance.RefreshXBTypeInfoAct -= Refresh;
         InvestModel.Instance.onInvestUpdate -= OnInvestUpdate;
+        TimingGiftManager.Instance.OnShowGiftIdListAddEvent -= OnShowGiftIdListAddEvent;
+    }
+
+    private void OnShowGiftIdListAddEvent()
+    {
+        timingGiftCell.InitUI();
     }
 
     void OnInvestUpdate(int type)
diff --git a/Main/System/HeroUI/HeroGiftWashWin.cs b/Main/System/HeroUI/HeroGiftWashWin.cs
index d736dd8..ca1aad7 100644
--- a/Main/System/HeroUI/HeroGiftWashWin.cs
+++ b/Main/System/HeroUI/HeroGiftWashWin.cs
@@ -128,8 +128,10 @@
         if (!ItemLogicUtility.CheckItemCount(PackType.Item, HeroUIManager.Instance.washItemID,
         HeroUIManager.Instance.GetTalentLockUseWashCount(hero), 2))
         {
+            TimingGiftManager.Instance.TryAddWhenInsufficient(4, HeroUIManager.Instance.washItemID, HeroUIManager.Instance.GetTalentLockUseWashCount(hero));
             return;
         }
+
 
         for (int i = 0; i < currentGiftCells.Length; i++)
         {
@@ -155,8 +157,10 @@
         {
             return;
         }
-
-
+        if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(4,PackType.Item, HeroUIManager.Instance.washItemID, HeroUIManager.Instance.GetTalentLockUseWashCount(hero)))
+        {
+            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(4, PackType.Item,HeroUIManager.Instance.washItemID, HeroUIManager.Instance.GetTalentLockUseWashCount(hero));
+        }
         HeroUIManager.Instance.SendWash(hero, 0);
     }
 
@@ -240,7 +244,7 @@
 
 
     void ChangeLockEvent()
-    { 
+    {
         int useCount = HeroUIManager.Instance.GetTalentLockUseWashCount(hero);
         var itemCount = PackManager.Instance.GetItemCountByID(PackType.Item, HeroUIManager.Instance.washItemID);
         itemCountText.text = UIHelper.AppendColor(itemCount >= useCount ? TextColType.Green : TextColType.Red,
diff --git a/Main/System/HeroUI/HeroTrainWin.cs b/Main/System/HeroUI/HeroTrainWin.cs
index 07fd7f1..b9f6212 100644
--- a/Main/System/HeroUI/HeroTrainWin.cs
+++ b/Main/System/HeroUI/HeroTrainWin.cs
@@ -308,7 +308,7 @@
         deleteTip.SetActive(false);
         if (hero != null && hero.Quality == 1 && HeroUIManager.Instance.IsTheSameHeroFullStar(hero.heroId))
         {
-            
+
             deleteTip.SetActive(true);
         }
     }
@@ -578,7 +578,14 @@
                 });
 
                 HeroUIManager.Instance.lastFightPower = new KeyValuePair<string, long>(hero.itemHero.guid, hero.CalculateFightPower(false));
+                // 濡傛灉姝eソ娑堣�楀畬锛屼笉瑙﹀彂鍥哄畾姒傜巼鐨勫垽瀹�
+                if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(3, PackType.Item,itemID, needCount))
+                {
+                    TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(3, PackType.Item,itemID, needCount);
+                }
+                return;
             }
+            TimingGiftManager.Instance.TryAddWhenInsufficient(3, itemID, needCount);
 
         }
 
@@ -925,7 +932,7 @@
         }
 
     }
-    
+
     #region 绐佺牬鍔熻兘
 
     void DisplayTrainOrBreak(HeroInfo hero)
diff --git a/Main/System/Horse/HorseRankUPWin.cs b/Main/System/Horse/HorseRankUPWin.cs
index 0ad3fe1..e21a8db 100644
--- a/Main/System/Horse/HorseRankUPWin.cs
+++ b/Main/System/Horse/HorseRankUPWin.cs
@@ -24,7 +24,7 @@
     [SerializeField] Image costItemImg;
     [SerializeField] Button rankUpBtn;
 
-    
+
     protected override void InitComponent()
     {
         rankUpBtn.AddListener(HorseRankUpgrade);
@@ -35,7 +35,7 @@
         PackManager.Instance.RefreshItemEvent += OnRefreshItemEvent;
         Display();
     }
-    
+
     protected override void OnPreClose()
     {
         PackManager.Instance.RefreshItemEvent -= OnRefreshItemEvent;
@@ -57,8 +57,8 @@
                 var curValue = HorseManager.Instance.attrDic[keys[i]];
                 attrNameTexts[i].text = PlayerPropertyConfig.Get(keys[i]).Name;
                 attrValueTexts[i].text = PlayerPropertyConfig.GetValueDescription(keys[i], curValue, 2);
-                
-                nextAttrValueTexts[i].text = PlayerPropertyConfig.GetValueDescription(keys[i], 
+
+                nextAttrValueTexts[i].text = PlayerPropertyConfig.GetValueDescription(keys[i],
                     curValue + nextConfig.ClassAttrValueList[i] + nextConfig.PerLVAttrValueList[i], 2);
             }
         }
@@ -100,11 +100,18 @@
         var config = HorseClassConfig.Get(HorseManager.Instance.classLV);
         if (!ItemLogicUtility.CheckItemCount(PackType.Item, HorseManager.Instance.rankUPItemID, config.ClassUPItemCnt, 2))
         {
+            TimingGiftManager.Instance.TryAddWhenInsufficient(12, PackType.Item, HorseManager.Instance.rankUPItemID, config.ClassUPItemCnt);
             return;
         }
+
         var pack = new CB202_tagCSHorseClassUP();
         GameNetSystem.Instance.SendInfo(pack);
         CloseWindow();
+        
+        if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(12, PackType.Item, HorseManager.Instance.rankUPItemID, config.ClassUPItemCnt))
+        {
+            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(12, PackType.Item, HorseManager.Instance.rankUPItemID, config.ClassUPItemCnt);
+        }
     }
 
     void OnRefreshItemEvent(PackType type, int index, int itemID)
diff --git a/Main/System/Horse/HorseWin.cs b/Main/System/Horse/HorseWin.cs
index 58e9f7a..4400858 100644
--- a/Main/System/Horse/HorseWin.cs
+++ b/Main/System/Horse/HorseWin.cs
@@ -41,7 +41,7 @@
         });
         lvupBtn.AddListener(HorseUpgrade);
         lvupBtn.onPress.AddListener(HorseUpgrade);
-        quickUpToggle.onValueChanged.AddListener((bool value)=>{ OnToggle(value);});
+        quickUpToggle.onValueChanged.AddListener((bool value) => { OnToggle(value); });
         rankUpBtn.AddListener(HorseRankUpgrade);
     }
 
@@ -83,8 +83,14 @@
         }
 
         if (!ItemLogicUtility.CheckItemCount(PackType.Item, HorseManager.Instance.lvUPItemID, 1, 2))
-        {   
+        {
+            TimingGiftManager.Instance.TryAddWhenInsufficient(12, PackType.Item, HorseManager.Instance.lvUPItemID, isQuick ? GetQuickUseCnt() : 1);
             return;
+        }
+
+        if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(12, PackType.Item, HorseManager.Instance.lvUPItemID, isQuick ? GetQuickUseCnt() : 1))
+        {
+            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(12, PackType.Item, HorseManager.Instance.lvUPItemID, isQuick ? GetQuickUseCnt() : 1);
         }
 
         var pack = new CB201_tagCSHorseLVUP();
@@ -114,7 +120,7 @@
         specialAttrText.text = GetSpecialAttr();
 
         var config = HorseClassConfig.Get(HorseManager.Instance.classLV);
-        lvText.text = Language.Get("Horse8",HorseManager.Instance.classLV, HorseManager.Instance.horseLV);
+        lvText.text = Language.Get("Horse8", HorseManager.Instance.classLV, HorseManager.Instance.horseLV);
         processImg.fillAmount = HorseManager.Instance.exp / (float)config.LVUPItemCnt;
         processText.text = HorseManager.Instance.exp + "/" + config.LVUPItemCnt;
 
@@ -192,9 +198,9 @@
     {
         if (HorseManager.Instance.specialAttrDic.Count == 0)
             return "";
-            
+
         List<string> attrList = new List<string>();
-        foreach(var attrID in HorseManager.Instance.specialAttrDic.Keys)
+        foreach (var attrID in HorseManager.Instance.specialAttrDic.Keys)
         {
             attrList.Add(UIHelper.AppendColor(TextColType.itemchuanqi, PlayerPropertyConfig.GetFullDescription(attrID, HorseManager.Instance.specialAttrDic[attrID])));
         }
@@ -204,20 +210,25 @@
     void OnToggle(bool value)
     {
         LocalSave.SetBool("HorseQuickUp" + PlayerDatas.Instance.baseData.PlayerID, !quickUpToggle.isOn);
-        var config = HorseClassConfig.Get(HorseManager.Instance.classLV);
         var state = HorseManager.Instance.GetHorseState();
         if (state == 0)
         {
-            int useCnt = 1;
-            if (HorseManager.Instance.classLV >= HorseManager.Instance.quickRankLV && quickUpToggle.isOn)
-            {
-                useCnt = config.LVUPItemCnt - HorseManager.Instance.exp;
-            }
+            int useCnt = GetQuickUseCnt();
             costText.text = UIHelper.ShowUseItem(PackType.Item, HorseManager.Instance.lvUPItemID, useCnt);
         }
-
-
     }
+
+    int GetQuickUseCnt()
+    {
+        int useCnt = 1;
+        if (HorseManager.Instance.classLV >= HorseManager.Instance.quickRankLV && quickUpToggle.isOn)
+        {
+            var config = HorseClassConfig.Get(HorseManager.Instance.classLV);
+            useCnt = config.LVUPItemCnt - HorseManager.Instance.exp;
+        }
+        return useCnt;
+    }
+
 
     void PlayerDataRefresh(PlayerDataType type)
     {
@@ -232,7 +243,7 @@
 
 
     }
-    
+
 
     void OnRefreshItemEvent(PackType type, int index, int itemID)
     {
diff --git a/Main/System/Main/HomeWin.cs b/Main/System/Main/HomeWin.cs
index 734d192..dde90ca 100644
--- a/Main/System/Main/HomeWin.cs
+++ b/Main/System/Main/HomeWin.cs
@@ -66,6 +66,7 @@
     [SerializeField] Button osMainLevelBtn;
     [SerializeField] Button osHeroCallBtn;
     [SerializeField] Button osGalaBtn;
+    [SerializeField] TimingGiftCell timingGiftCell;
 
     //鍧愰獞
     [SerializeField] Image horseBGImg;
@@ -267,11 +268,12 @@
         TimeMgr.Instance.OnDayEvent += OnDayEvent;
         ChatManager.Instance.OnUpdateTalkEvent += OnUpdateTalkEvent;
         UIManager.Instance.OnOpenWindow += OnOpenWindow;
+        TimingGiftManager.Instance.OnShowGiftIdListAddEvent += OnShowGiftIdListAddEvent;
         TryPlayAutoFightBoss();
         Display();
         DisplayFirstChargeBtn();
         DisplayOSActivity();
-
+        timingGiftCell.InitUI();
         // var battleWin = UIManager.Instance.OpenWindow<BattleWin>();
         // battleWin.SetBattleField(BattleManager.Instance.storyBattleField);
 
@@ -302,8 +304,14 @@
         TimeMgr.Instance.OnDayEvent -= OnDayEvent;
         ChatManager.Instance.OnUpdateTalkEvent -= OnUpdateTalkEvent;
         UIManager.Instance.OnOpenWindow -= OnOpenWindow;
+        TimingGiftManager.Instance.OnShowGiftIdListAddEvent -= OnShowGiftIdListAddEvent;
         //  鍏抽棴鐨勬椂鍊欐妸鎴樻枟鐣岄潰涔熺粰鍏充簡 铏界劧鏄湪澶栭潰寮�鐨�
         UIManager.Instance.CloseWindow<BattleWin>();
+    }
+
+    private void OnShowGiftIdListAddEvent()
+    {
+        timingGiftCell.InitUI();
     }
 
     private void OnAutoAttackEvent()
@@ -781,6 +789,10 @@
         {
             DisplayDailySpecialsBtn();
         }
+        else if (funcId == (int)FuncOpenEnum.TimingGift)
+        {
+            timingGiftCell.InitUI();
+        }
     }
 
     private void OnUpdateFirstChargeInfo()
diff --git a/Main/System/Mingge/MinggeManager.cs b/Main/System/Mingge/MinggeManager.cs
index 2376158..9c9ffed 100644
--- a/Main/System/Mingge/MinggeManager.cs
+++ b/Main/System/Mingge/MinggeManager.cs
@@ -441,7 +441,7 @@
             var diffRate = littleRateValue + (bigRateValue - littleRateValue) / (float)(bigValue - littleValue) * (value - littleValue);
             rateList.Add((int)diffRate);
         }
-        
+
         return rateList;
     }
 
@@ -613,7 +613,7 @@
     {
         if (isStart)
         {
-            
+
             bool canStart = false;
             for (int i = 0; i < autoSetList.Count; i++)
             {
@@ -634,11 +634,14 @@
             if (!ItemLogicUtility.CheckItemCount(PackType.Item, tyItemID, useAutoCostCnt, 2))
             {
                 isStartAuto = false;
+                TimingGiftManager.Instance.TryAddWhenInsufficient(6, PackType.Item, tyItemID, useAutoCostCnt);
                 return;
             }
             UIManager.Instance.CloseWindow<MinggeAutoSetWin>();
+            TimingGiftManager.Instance.TryAddWhenExactConsumption(6, PackType.Item, tyItemID, useAutoCostCnt);
+
         }
-        
+
         isStartAuto = isStart;
         if (isStartAuto)
         {
@@ -837,7 +840,7 @@
 
         return deFightAttrOK && fightAttrOK;
     }
-    
+
     bool IsFightPowerOK(ItemModel mgEquip, MinggeAutoSet autoSet)
     {
         long showFightPower = FightPowerManager.Instance.GetFightPowerMinggeChange(mgEquip, autoSet.presetID);
@@ -871,12 +874,16 @@
         {
             isStartAuto = false;
             SysNotifyMgr.Instance.ShowTip("MinggeAuto1");
+            TimingGiftManager.Instance.TryAddWhenInsufficient(6, PackType.Item, tyItemID, useAutoCostCnt);
             return;
         }
+
+        TimingGiftManager.Instance.TryAddWhenExactConsumption(6, PackType.Item, tyItemID, useAutoCostCnt);
+
         autoTYTime = Time.time;
         SendTY(useAutoCostCnt);
-        
-        
+
+
     }
 
     void OnMSEvent()
@@ -1040,5 +1047,5 @@
     }
 
 
-    
+
 }
\ No newline at end of file
diff --git a/Main/System/Mingge/MinggePrayWin.cs b/Main/System/Mingge/MinggePrayWin.cs
index e91f3f9..9b45e73 100644
--- a/Main/System/Mingge/MinggePrayWin.cs
+++ b/Main/System/Mingge/MinggePrayWin.cs
@@ -42,7 +42,7 @@
     {
         MinggeManager.Instance.OnMinggeInfoUpdate -= Display;
     }
-    
+
     public void Display()
     {
         var config = ItemConfig.Get(MinggeManager.Instance.qlItemID);
@@ -98,6 +98,7 @@
         }
         if (!ItemLogicUtility.CheckItemCount(PackType.Item, MinggeManager.Instance.qlItemID, useCnt, 2))
         {
+            TimingGiftManager.Instance.TryAddWhenInsufficient(6, PackType.Item, MinggeManager.Instance.qlItemID, useCnt);
             return;
         }
 
@@ -106,9 +107,14 @@
 
         GameNetSystem.Instance.SendInfo(pack);
         qlEffect.Play();
+        // 濡傛灉姝eソ娑堣�楀畬锛屼笉瑙﹀彂鍥哄畾姒傜巼鐨勫垽瀹�
+        if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(6, PackType.Item, MinggeManager.Instance.qlItemID, useCnt))
+        {
+            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(6, PackType.Item, MinggeManager.Instance.qlItemID, useCnt);
+        }
     }
 
 
-    
+
 }
 
diff --git a/Main/System/Mingge/MinggeWin.cs b/Main/System/Mingge/MinggeWin.cs
index 4666102..5bf1b64 100644
--- a/Main/System/Mingge/MinggeWin.cs
+++ b/Main/System/Mingge/MinggeWin.cs
@@ -64,7 +64,7 @@
         });
 
         autoBtn.AddListener(AutoTY);
-        funPresetBtn.AddListener(()=>
+        funPresetBtn.AddListener(() =>
         {
             FuncPresetManager.Instance.ClickBattlePreset((int)BattlePreSetType.Story);
         });
@@ -72,7 +72,7 @@
 
     protected override void OnPreOpen()
     {
-       
+
         PackManager.Instance.RefreshItemEvent += RefreshItemEvent;
         PackManager.Instance.DeleteItemEvent += DeleteDropItem;
         MinggeManager.Instance.OnMinggeInfoUpdate += OnMinggeInfoUpdate;
@@ -149,7 +149,7 @@
 
         funPresetBtn.SetActive(FuncPresetManager.Instance.IsPreShow());
         ChangeAutoEvent();
-        
+
     }
     void RefreshItemEvent(PackType type, int index, int itemID)
     {
@@ -290,9 +290,16 @@
 
         if (!ItemLogicUtility.CheckItemCount(PackType.Item, MinggeManager.Instance.tyItemID, 1, 2))
         {
+            TimingGiftManager.Instance.TryAddWhenInsufficient(6, PackType.Item, MinggeManager.Instance.tyItemID, 1);
             return;
         }
 
+        // 濡傛灉姝eソ娑堣�楀畬锛屼笉瑙﹀彂鍥哄畾姒傜巼鐨勫垽瀹�
+        if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(6, PackType.Item, MinggeManager.Instance.tyItemID, 1))
+        {
+            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(6, MinggeManager.Instance.tyItemID, 1);
+        }
+
         if (MinggeManager.Instance.isStartAuto)
         {
             MinggeManager.Instance.StartAuto(false);
diff --git a/Main/System/TimingGift.meta b/Main/System/TimingGift.meta
new file mode 100644
index 0000000..c03bf1f
--- /dev/null
+++ b/Main/System/TimingGift.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 12fd399fb4ee8f145bbe14cb76a38d03
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/TimingGift/TimingGiftAwardCell.cs b/Main/System/TimingGift/TimingGiftAwardCell.cs
new file mode 100644
index 0000000..ac56751
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftAwardCell.cs
@@ -0,0 +1,20 @@
+using UnityEngine;
+
+public class TimingGiftAwardCell : MonoBehaviour
+{
+    [SerializeField] ItemCell itemCell;
+    TimingGiftManager manager { get { return TimingGiftManager.Instance; } }
+    public void Display(int index, int[][] gainItemList)
+    {
+        if (gainItemList.IsNullOrEmpty() || index < 0 || index >= gainItemList.Length)
+            return;
+        int itemId = gainItemList[index][0];
+        int count = gainItemList[index][1];
+        itemCell.Init(new ItemCellModel(itemId, false, count));
+        itemCell.button.SetListener(() => { ItemTipUtility.Show(itemId); });
+    }
+}
+
+
+
+
diff --git a/Main/System/TimingGift/TimingGiftAwardCell.cs.meta b/Main/System/TimingGift/TimingGiftAwardCell.cs.meta
new file mode 100644
index 0000000..1555795
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftAwardCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9282d2427d5b8f349a85e3e53c6e92e6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/TimingGift/TimingGiftCell.cs b/Main/System/TimingGift/TimingGiftCell.cs
new file mode 100644
index 0000000..a0b21b8
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftCell.cs
@@ -0,0 +1,137 @@
+using UnityEngine;
+
+public class TimingGiftCell : MonoBehaviour
+{
+    private void Awake()
+    {
+        //濡傛灉鏈夐渶瑕佹寜閽偣鍑婚�昏緫锛屽湪澶栧眰鍒涘缓锛屾澶勪笉澶勭悊鐐瑰嚮閫昏緫
+        LoadPrefab();
+        button.SetListener(() =>
+        {
+            UIManager.Instance.OpenWindow<TimingGiftWin>(type);
+        });
+    }
+
+    ButtonEx m_button;
+    public ButtonEx button
+    {
+        get
+        {
+            if (m_button == null)
+            {
+                m_button = this.GetComponent<ButtonEx>("TimingGiftCell");
+            }
+            return m_button;
+        }
+    }
+
+    TextEx m_Time;
+    private TextEx time
+    {
+        get
+        {
+            if (m_Time == null)
+            {
+                m_Time = this.GetComponent<TextEx>("TimingGiftCell/bgImage/timeImage/timeText");
+            }
+            return m_Time;
+        }
+    }
+
+    public int type;
+    GameObject prefab;
+    TimingGiftManager manager { get { return TimingGiftManager.Instance; } }
+    void OnEnable()
+    {
+        FuncOpen.Instance.OnFuncStateChangeEvent += OnFuncStateChangeEvent;
+        manager.OnShowGiftIdListAddEvent += OnShowGiftIdListAddEvent;
+        manager.OnRemoveExpiredEvent += OnRemoveExpiredEvent;
+        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
+    }
+
+    void OnDisable()
+    {
+        FuncOpen.Instance.OnFuncStateChangeEvent -= OnFuncStateChangeEvent;
+        manager.OnShowGiftIdListAddEvent -= OnShowGiftIdListAddEvent;
+        manager.OnRemoveExpiredEvent -= OnRemoveExpiredEvent;
+        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
+    }
+
+    private void OnFuncStateChangeEvent(int obj)
+    {
+        if (obj == (int)FuncOpenEnum.TimingGift)
+        {
+            Display();
+        }
+    }
+
+    private void OnSecondEvent()
+    {
+        Display();
+    }
+
+    private void OnShowGiftIdListAddEvent()
+    {
+        Display();
+    }
+    private void OnRemoveExpiredEvent()
+    {
+        Display();
+    }
+
+    public void Display()
+    {
+        //閰�0鏃舵槸涓荤晫闈㈠叆鍙�
+        if (type == 0)
+        {
+            var list = manager.GetCurrectTimingGiftIdList();
+            bool isNullOrEmpty = list.IsNullOrEmpty();
+            this.SetActive(!isNullOrEmpty);
+            if (isNullOrEmpty)
+                return;
+            int id = list[0];
+            if (!TimingGiftConfig.TryGetTimingGiftConfig(id, out var config))
+                return;
+            RefreshTime(config.GiftType);
+            return;
+        }
+        RefreshTime(type);
+    }
+
+    private void RefreshTime(int type)
+    {
+        bool isShow = manager.IsShowGiftIdListHasType(type);
+        this.SetActive(isShow);
+        if (isShow)
+        {
+            int times = manager.GetRemainingSecondsByType(type);
+            time.text = times <= 0 ? Language.Get("TimingGift04") : TimeUtility.SecondsToHMS(times);
+            time.colorType = times <= 0 ? TextColType.Red : TextColType.DarkGreen;
+        }
+    }
+
+    protected void LoadPrefab()
+    {
+        if (prefab != null)
+            return;
+        var tmp = transform.Find("TimingGiftCell");
+
+        if (tmp != null)
+        {
+            prefab = tmp.gameObject;
+            return;
+        }
+        prefab = UIUtility.CreateWidget("TimingGiftCell", "TimingGiftCell");
+
+        prefab.transform.SetParentEx(this.transform, Vector3.zero, Quaternion.identity, Vector3.one);
+        prefab.transform.SetAsFirstSibling();
+
+    }
+
+    public void InitUI()
+    {
+        LoadPrefab();   //瀛樺湪琚嵏杞界殑鍙兘锛岄噸鏂板姞杞�
+        Display();
+    }
+
+}
\ No newline at end of file
diff --git a/Main/System/TimingGift/TimingGiftCell.cs.meta b/Main/System/TimingGift/TimingGiftCell.cs.meta
new file mode 100644
index 0000000..c16e65a
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 92affed6278f93648a3e7bff0e3f1eb8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/TimingGift/TimingGiftCtgIdCell.cs b/Main/System/TimingGift/TimingGiftCtgIdCell.cs
new file mode 100644
index 0000000..23ddc61
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftCtgIdCell.cs
@@ -0,0 +1,31 @@
+using UnityEngine;
+
+public class TimingGiftCtgIdCell : MonoBehaviour
+{
+    [SerializeField] TextEx moneyText;
+    [SerializeField] ButtonEx moneyButton;
+    [SerializeField] ImageEx tabIcon;
+    TimingGiftManager manager { get { return TimingGiftManager.Instance; } }
+    public void Display(int index, int[] ctgIds)
+    {
+        if (ctgIds.IsNullOrEmpty() || index < 0 || index >= ctgIds.Length)
+            return;
+        int ctgId = ctgIds[index];
+        if (!RechargeManager.Instance.TryGetOrderInfo(ctgId, out var orderInfoConfig))
+            return;
+        moneyText.text = Language.Get("PayMoneyNum", orderInfoConfig.PayRMBNumOnSale);
+
+        bool isChoose = manager.selectCtgIdIndex == index;
+        tabIcon.SetSprite(isChoose ? "TimingGiftTab2_Select" : "TimingGiftTab2_UnSelect");
+        moneyText.colorType = isChoose ? TextColType.NavyBrown : TextColType.LightWhite;
+        moneyButton.SetListener(() =>
+        {
+            manager.selectCtgId = ctgId;
+            manager.selectCtgIdIndex = index;
+        });
+    }
+}
+
+
+
+
diff --git a/Main/System/TimingGift/TimingGiftCtgIdCell.cs.meta b/Main/System/TimingGift/TimingGiftCtgIdCell.cs.meta
new file mode 100644
index 0000000..a99c7f5
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftCtgIdCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 755ed8982ee993644bfabedf48f2b80a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/TimingGift/TimingGiftManager.cs b/Main/System/TimingGift/TimingGiftManager.cs
new file mode 100644
index 0000000..62169f5
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftManager.cs
@@ -0,0 +1,502 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using LitJson;
+using UnityEngine;
+public class TimingGiftManager : GameSystemManager<TimingGiftManager>
+{
+
+    private int m_selectTabIndex;
+    public int selectTabIndex
+    {
+        get { return m_selectTabIndex; }
+        set
+        {
+            if (m_selectTabIndex == value)
+                return;
+            m_selectTabIndex = value;
+            OnSelectTabIndexChangeEvent?.Invoke();
+        }
+    }
+    public int selectTabGiftId;
+    public event Action OnSelectTabIndexChangeEvent;
+
+    private int m_selectCtgIdIndex;
+    public int selectCtgIdIndex
+    {
+        get { return m_selectCtgIdIndex; }
+        set
+        {
+            if (m_selectCtgIdIndex == value)
+                return;
+            m_selectCtgIdIndex = value;
+            OnSelectCtgIdIndexChangeEvent?.Invoke();
+        }
+    }
+    public int selectCtgId;
+
+    public int[] selectCtgIds;
+    public int[][] selectGainItemList;
+    public event Action OnSelectCtgIdIndexChangeEvent;
+
+    public bool isLogShow = true;
+    public override void Init()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
+        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
+    }
+
+    public override void Release()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk;
+        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
+    }
+
+    private void OnBeforePlayerDataInitializeEventOnRelogin()
+    {
+        showGiftIdList.Clear();
+        lastTriggerTimeDict.Clear();
+    }
+
+    private void OnPlayerLoginOk()
+    {
+        LoadData();
+    }
+
+    private void OnSecondEvent()
+    {
+        RemoveExpired();
+    }
+
+    public static bool TryGetTimingGiftTypeConfig(int giftType, out TimingGiftTypeConfig config)
+    {
+        config = null;
+        if (!TimingGiftTypeConfig.HasKey(giftType))
+            return false;
+        config = TimingGiftTypeConfig.Get(giftType);
+        return true;
+    }
+
+    public bool IsBuy(int ctgId)
+    {
+        if (!CTGConfig.HasKey(ctgId))
+            return false;
+        CTGConfig ctgConfig = CTGConfig.Get(ctgId);
+        bool hasRechargeCount = RechargeManager.Instance.TryGetRechargeCount(ctgId, out RechargeCount _rechargeCount);
+        bool isBuy = hasRechargeCount && _rechargeCount.todayCount >= ctgConfig.DailyBuyCount;
+        return isBuy;
+    }
+
+    public bool IsListHasIndex(int index, List<int> list)
+    {
+        return list != null && index >= 0 && index < list.Count;
+    }
+
+    public int GetListValue(int index, List<int> list)
+    {
+        return IsListHasIndex(index, list) ? list[index] : 0;
+    }
+
+    public bool IsArrHasIndex(int index, int[] arr)
+    {
+        return arr != null && index >= 0 && index < arr.Length;
+    }
+
+    public int GetArrValue(int index, int[] arr)
+    {
+        return IsArrHasIndex(index, arr) ? arr[index] : 0;
+    }
+
+    public void OpenTimingGiftWin(int type)
+    {
+        if (!UIManager.Instance.IsOpened<TimingGiftWin>())
+        {
+            UIManager.Instance.OpenWindow<TimingGiftWin>(type);
+        }
+    }
+
+    List<int> showGiftIdList = new List<int>();
+
+    public event Action OnShowGiftIdListAddEvent;
+
+    public bool IsShowGiftIdListHasType(int type)
+    {
+        if (showGiftIdList.IsNullOrEmpty())
+            return false;
+        for (int i = 0; i < showGiftIdList.Count; i++)
+        {
+            int id = showGiftIdList[i];
+            if (TimingGiftConfig.TryGetTimingGiftConfig(id, out TimingGiftConfig config) && config.GiftType == type)
+                return true;
+        }
+        return false;
+    }
+
+    public List<int> GetCurrectTimingGiftIdList(bool isSort = false)
+    {
+        if (isSort)
+        {
+            // 鎸夊墿浣欐椂闂村皯鐨勬帓搴忛潬鍓�
+            showGiftIdList.Sort((a, b) =>
+            {
+                int remainingSecondsA = GetRemainingSeconds(a);
+                int remainingSecondsB = GetRemainingSeconds(b);
+                return remainingSecondsA.CompareTo(remainingSecondsB);
+            });
+        }
+        return showGiftIdList;
+    }
+
+    public void TryAdd(int type)
+    {
+        if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.TimingGift))
+        {
+
+#if UNITY_EDITOR
+            if (isLogShow)
+            {
+                Debug.Log($"[TimingGift] TryPop:瑙﹀彂Return,鏃舵満绀煎寘鍔熻兘鏈紑鍚�");
+            }
+#endif
+            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}绫诲瀷鐨勭ぜ鍖匢D");
+            }
+#endif
+            return;
+        }
+
+        bool isChange = false;
+        for (int i = 0; i < giftIdList.Count; i++)
+        {
+            int id = giftIdList[i];
+            if (!TimingGiftConfig.TryGetTimingGiftConfig(id, out TimingGiftConfig config))
+                continue;
+            if (showGiftIdList.Contains(id))
+                continue;
+            isChange = true;
+            showGiftIdList.Add(id);
+            lastTriggerTimeDict[id] = TimeUtility.AllSeconds;
+        }
+
+        if (isChange)
+        {
+            SaveData();
+            OpenTimingGiftWin(type);
+            OnShowGiftIdListAddEvent?.Invoke();
+        }
+
+    }
+
+    public event Action OnRemoveExpiredEvent;
+    public void RemoveExpired()
+    {
+        if (showGiftIdList.IsNullOrEmpty())
+            return;
+        bool isChange = false;
+
+        // 浣跨敤鍊掑簭閬嶅巻锛岄伩鍏嶇Щ闄ゅ厓绱犳椂绱㈠紩閿欎綅
+        for (int i = showGiftIdList.Count - 1; i >= 0; i--)
+        {
+            int id = showGiftIdList[i];
+            // 宸茬粡杩囨湡鐨勭Щ闄�
+            if (IsExpired(id))
+            {
+                isChange = true;
+                showGiftIdList.RemoveAt(i);
+            }
+        }
+
+        if (isChange)
+        {
+#if UNITY_EDITOR
+            if (isLogShow)
+            {
+                Debug.Log($"[TimingGift] RemoveExpired:瑙﹀彂绉婚櫎杩囨湡绀煎寘,褰撳墠鏈夋椂鏁堢殑giftID鍒楄〃涓篬{string.Join(", ", showGiftIdList)}]");
+            }
+#endif
+            OnRemoveExpiredEvent?.Invoke();
+        }
+    }
+
+    //瑙﹀彂绫诲瀷 涓婃瑙﹀彂鏃堕棿
+    Dictionary<int, int> lastTriggerTimeDict = new Dictionary<int, int>();
+    public bool TryGetLastTriggerTime(int type, out int lastTriggerTime)
+    {
+        return lastTriggerTimeDict.TryGetValue(type, out lastTriggerTime);
+    }
+
+    public bool IsExpired(int id)
+    {
+        return GetRemainingSeconds(id) <= 0;
+    }
+
+    public int GetRemainingSecondsByType(int type)
+    {
+        if (!TryGetLastTriggerTime(type, out int lastTriggerTime))
+            return 0;
+        if (!TryGetTimingGiftTypeConfig(type, out TimingGiftTypeConfig typeConfig))
+            return 0;
+
+        // 璁$畻鍓╀綑绉掓暟
+        DateTime lastTime = TimeUtility.GetTime((uint)lastTriggerTime);
+        DateTime nowTime = TimeUtility.ServerNow;
+        double elapsedSeconds = (nowTime - lastTime).TotalSeconds;
+        int remainingSeconds = (int)(typeConfig.Duration - elapsedSeconds);
+
+        return remainingSeconds;
+    }
+
+    public int GetRemainingSeconds(int id)
+    {
+        if (!TimingGiftConfig.TryGetTimingGiftConfig(id, out TimingGiftConfig config))
+            return 0;
+        int type = config.GiftType;
+        return GetRemainingSecondsByType(type);
+    }
+
+    // 鍒ゆ柇鎸囧畾type鐨勭ぜ鍖呬粖鏃ユ槸鍚︿笉闄愬脊鍑�
+    // true:浠婃棩涓嶉檺寮瑰嚭 false:浠婃棩宸插脊鍑鸿繃
+    public bool IsTodayUnlimited(int type)
+    {
+        // 娌℃湁瑙﹀彂璁板綍锛岃鏄庤繕娌″脊鍑鸿繃
+        if (!TryGetLastTriggerTime(type, out int lastTriggerTime))
+            return true;
+
+        // 濡傛灉涓婃瑙﹀彂鏃堕棿鍦ㄤ粖澶�0鐐逛箣鍓嶏紝璇存槑浠婂ぉ杩樻病寮瑰嚭杩�
+        int todayStartTick = TimeUtility.GetTodayStartTick();
+        return lastTriggerTime < todayStartTick;
+    }
+
+    private bool IsTriggle(int type)
+    {
+        if (!TryGetTimingGiftTypeConfig(type, out TimingGiftTypeConfig config))
+        {
+#if UNITY_EDITOR
+            if (isLogShow)
+            {
+                Debug.Log($"[TimingGift] IsTriggle:鏈壘鍒皗type}绫诲瀷鐨凾riggleChangeRate閰嶇疆");
+            }
+#endif
+            return false;
+        }
+
+        int rangeNum = UnityEngine.Random.Range(0, 100);
+        bool res = rangeNum < config.TriggleChangeRate;
+#if UNITY_EDITOR
+        if (isLogShow)
+        {
+            Debug.Log($"[TimingGift] IsTriggle:鍒ゅ畾鍥哄畾姒傜巼锛岀粨鏋�={res}, type={type}, rangeNum={rangeNum}, TriggleChangeRate={config.TriggleChangeRate}");
+        }
+#endif
+        return res;
+
+    }
+
+
+
+    #region 绀煎寘寮瑰嚭鏉′欢
+
+    /// <summary>
+    /// 浣跨敤瀹屽箍鍛婃鏁版椂寮瑰嚭
+    /// </summary>
+    public bool TryAddWhenAllAdsUsed(int type, int adId)
+    {
+        if (!ADAwardConfig.HasKey(adId))
+            return false;
+        ADAwardConfig config = ADAwardConfig.Get(adId);
+        int maxCount = config.ADCntMax;
+        int haveCount = AdsManager.Instance.GetADCntByADID(adId) + 1;
+        TryAddWhenAllAdsUsed(type, haveCount, maxCount);
+        return true;
+    }
+
+    /// <summary>
+    /// 浣跨敤瀹屽箍鍛婃鏁版椂寮瑰嚭
+    /// </summary>
+    public bool TryAddWhenAllAdsUsed(int type, int haveCount, int maxCount)
+    {
+        if (maxCount == haveCount)
+        {
+            TryAdd(type);
+            return true;
+        }
+        return false;
+    }
+
+    /// <summary>
+    /// 鏁伴噺涓嶈冻鏃跺繀寮瑰嚭
+    /// </summary>
+    public bool TryAddWhenInsufficient(int type, PackType packType, int itemId, long needCount)
+    {
+        var haveCount = PackManager.Instance.GetItemCountByID(packType, itemId);
+        return TryAddWhenInsufficient(type, haveCount, needCount);
+    }
+
+    /// <summary>
+    /// 鏁伴噺涓嶈冻鏃跺繀寮瑰嚭
+    /// </summary>
+    public bool TryAddWhenInsufficient(int type, long haveCount, long needCount)
+    {
+        // 闇�瑕�0涓椂蹇呭脊
+        if (needCount <= 0)
+        {
+            TryAdd(type);
+            return true;
+        }
+
+        bool isEnough = haveCount >= needCount;
+
+        // 涓嶈冻鏃跺繀寮�
+        if (!isEnough)
+        {
+            TryAdd(type);
+            return true;
+        }
+        return false;
+    }
+
+    /// <summary>
+    /// 鏁伴噺姝eソ娑堣�楀畬鏃跺繀寮瑰嚭
+    /// </summary>
+    public bool TryAddWhenExactConsumption(int type, PackType packType, int itemId, long needCount)
+    {
+        var haveCount = PackManager.Instance.GetItemCountByID(packType, itemId);
+        return TryAddWhenExactConsumption(type, haveCount, needCount);
+    }
+
+    /// <summary>
+    /// 鏁伴噺姝eソ娑堣�楀畬鏃跺繀寮瑰嚭
+    /// </summary>
+    public bool TryAddWhenExactConsumption(int type, long haveCount, long needCount)
+    {
+        // 闇�瑕�0涓椂蹇呭脊
+        if (needCount <= 0)
+        {
+            TryAdd(type);
+            return true;
+        }
+
+        //姝eソ娑堣�楀畬x鏃跺繀寮�
+        if (haveCount - needCount == 0)
+        {
+            TryAdd(type);
+            return true;
+        }
+        return false;
+    }
+    /// <summary>
+    /// 鍏呰冻鏃跺浐瀹氭鐜囧脊鍑�
+    /// </summary>
+    public bool TryAddWithFixedProbabilityWhenSufficient(int type, PackType packType, int itemId, long needCount)
+    {
+        var haveCount = PackManager.Instance.GetItemCountByID(packType, itemId);
+        return TryAddWithFixedProbabilityWhenSufficient(type, haveCount, needCount);
+    }
+
+    /// <summary>
+    /// 鍏呰冻鏃跺浐瀹氭鐜囧脊鍑�
+    /// </summary>
+    public bool TryAddWithFixedProbabilityWhenSufficient(int type, long haveCount, long needCount)
+    {
+        bool isEnough = haveCount >= needCount;
+        if (!isEnough)
+        {
+            return false;
+        }
+
+        bool isTriggle = IsTriggle(type);
+        if (isTriggle)
+        {
+            TryAdd(type);
+            return true;
+        }
+        return false;
+    }
+
+    #endregion
+    #region 鏁版嵁淇濆瓨
+    private string saveKey { get { return StringUtility.Concat("TimingGift_LastTriggerTimeDict_", PlayerDatas.Instance.PlayerId.ToString()); } }
+    private void LoadData()
+    {
+        lastTriggerTimeDict.Clear();
+
+        // 浣跨敤 LocalSave 璇诲彇 JSON 瀛楃涓�
+        string jsonStr = LocalSave.GetString(saveKey);
+        if (string.IsNullOrEmpty(jsonStr) || jsonStr == "{}")
+
+            return;
+        Dictionary<int, int> loadDict = ConfigParse.ParseIntDict(jsonStr);
+        if (loadDict == null)
+            return;
+
+        lastTriggerTimeDict = loadDict;
+        InitCurrectTimingGiftIdList(new List<int>(loadDict.Keys));
+#if UNITY_EDITOR
+        if (isLogShow)
+        {
+            Debug.Log($"[TimingGift] LoadData:褰撳墠鏈夋椂鏁堢殑giftID鍒楄〃涓�: [{string.Join(", ", showGiftIdList)}]\n褰撳墠瑙﹀彂鏃堕棿瀛楀吀鍐呭:\n{string.Join("\n", lastTriggerTimeDict.Select(kv => $"  绫诲瀷{kv.Key}: {TimeUtility.GetTime((uint)kv.Value):yyyy-MM-dd HH:mm:ss}"))}");
+        }
+#endif
+    }
+
+    private void SaveData()
+    {
+        string jsonStr = JsonMapper.ToJson(lastTriggerTimeDict);
+        LocalSave.SetString(saveKey, jsonStr);
+#if UNITY_EDITOR
+        if (isLogShow)
+        {
+            Debug.Log($"[TimingGift] SaveData:褰撳墠鏈夋椂鏁堢殑giftID鍒楄〃涓�: [{string.Join(", ", showGiftIdList)}]\n淇濆瓨鐨勮Е鍙戞椂闂村瓧鍏稿唴瀹�:\n{string.Join("\n", lastTriggerTimeDict.Select(kv => $"  绫诲瀷{kv.Key}: {TimeUtility.GetTime((uint)kv.Value):yyyy-MM-dd HH:mm:ss}"))}");
+        }
+#endif
+    }
+
+    public void InitCurrectTimingGiftIdList(List<int> list)
+    {
+        if (list == null)
+            return;
+        for (int i = 0; i < list.Count; i++)
+        {
+            int type = list[i];
+            if (!TimingGiftConfig.TryGetTypeToGiftIdList(type, out List<int> giftIdList))
+                continue;
+            for (int j = 0; j < giftIdList.Count; j++)
+            {
+                // 琛ㄤ腑娌℃湁鐨勪笉澶勭悊
+                if (!TimingGiftConfig.TryGetTimingGiftConfig(type, out TimingGiftConfig config))
+                    continue;
+                // 宸茬粡杩囨湡鐨勪笉澶勭悊
+                if (IsExpired(type))
+                    continue;
+                // 閲嶅鐨勪笉澶勭悊
+                if (showGiftIdList.Contains(type))
+                    continue;
+                showGiftIdList.Add(type);
+            }
+        }
+    }
+    #endregion
+
+}
diff --git a/Main/System/TimingGift/TimingGiftManager.cs.meta b/Main/System/TimingGift/TimingGiftManager.cs.meta
new file mode 100644
index 0000000..8a3e2ab
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b10601b615e27b1409dbc408badbb1c1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/TimingGift/TimingGiftTabCell.cs b/Main/System/TimingGift/TimingGiftTabCell.cs
new file mode 100644
index 0000000..ddf49ee
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftTabCell.cs
@@ -0,0 +1,29 @@
+using UnityEngine;
+
+public class TimingGiftTabCell : MonoBehaviour
+{
+    [SerializeField] TextEx tabNameText;
+    [SerializeField] ButtonEx tabButton;
+    [SerializeField] ImageEx tabIcon;
+    TimingGiftManager manager { get { return TimingGiftManager.Instance; } }
+    public void Display(int index)
+    {
+        var currectTimingGiftIdList = manager.GetCurrectTimingGiftIdList();
+        if (currectTimingGiftIdList == null || index < 0 || index >= currectTimingGiftIdList.Count)
+            return;
+        int id = currectTimingGiftIdList[index];
+        if (!TimingGiftConfig.HasKey(id))
+            return;
+        TimingGiftConfig config = TimingGiftConfig.Get(id);
+        tabNameText.text = config.GiftName;
+
+        bool isChoose = manager.selectTabIndex == index;
+        tabIcon.SetSprite(isChoose ? "TimingGiftTab1_Select" : "TimingGiftTab1_UnSelect");
+        tabNameText.colorType = isChoose ? TextColType.NavyBrown : TextColType.LightWhite;
+        tabButton.SetListener(() =>
+        {
+            manager.selectTabGiftId = id;
+            manager.selectTabIndex = index;
+        });
+    }
+}
\ No newline at end of file
diff --git a/Main/System/TimingGift/TimingGiftTabCell.cs.meta b/Main/System/TimingGift/TimingGiftTabCell.cs.meta
new file mode 100644
index 0000000..f75dd15
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftTabCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 584a05a7f813e494990d0e3e0e6d3906
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/TimingGift/TimingGiftWin.cs b/Main/System/TimingGift/TimingGiftWin.cs
new file mode 100644
index 0000000..be61e8b
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftWin.cs
@@ -0,0 +1,258 @@
+using System.Collections.Generic;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+
+public class TimingGiftWin : UIBase
+{
+    [SerializeField] ScrollerController tabScroller;
+    [SerializeField] ScrollerController ctgIdScroller;
+    [SerializeField] ScrollerController awardScroller;
+
+    [SerializeField] TextEx timeText;
+    [SerializeField] TextEx rateText;
+    [SerializeField] ImageEx bgImage;
+    [SerializeField] ImageEx IconImage;
+    [SerializeField] TextEx giftNameText;
+    [SerializeField] ButtonEx buyButton;
+    [SerializeField] TextEx buyText;
+    [SerializeField] ButtonEx leftButton;
+    [SerializeField] ButtonEx rightButton;
+    TimingGiftManager manager { get { return TimingGiftManager.Instance; } }
+
+    int type;
+    List<int> currectTimingGiftIdList;
+
+    protected override void InitComponent()
+    {
+        buyButton.SetListener(() =>
+        {
+            RechargeManager.Instance.CTG(manager.selectCtgId);
+        });
+        leftButton.SetListener(() => UpdateTabSelection(-1));
+        rightButton.SetListener(() => UpdateTabSelection(1));
+    }
+
+    protected override void OnPreOpen()
+    {
+        type = functionOrder;
+#if UNITY_EDITOR
+        if (manager.isLogShow)
+        {
+            Debug.Log($"[TimingGift] 鎵撳紑鐣岄潰锛岃{type}绫诲瀷瑙﹀彂");
+        }
+#endif
+        tabScroller.OnRefreshCell += OnRefreshTabCell;
+        ctgIdScroller.OnRefreshCell += OnRefreshCtgIdCell;
+        awardScroller.OnRefreshCell += OnRefreshAwardCell;
+        manager.OnSelectTabIndexChangeEvent += OnSelectTabIndexChangeEvent;
+        manager.OnSelectCtgIdIndexChangeEvent += OnSelectCtgIdIndexChangeEvent;
+        manager.OnRemoveExpiredEvent += OnRemoveExpiredEvent;
+        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
+        RechargeManager.Instance.rechargeCountEvent += OnRechargeCountEvent;
+        CreateAll();
+    }
+
+    protected override void OnPreClose()
+    {
+        tabScroller.OnRefreshCell -= OnRefreshTabCell;
+        ctgIdScroller.OnRefreshCell -= OnRefreshCtgIdCell;
+        awardScroller.OnRefreshCell -= OnRefreshAwardCell;
+        manager.OnSelectTabIndexChangeEvent -= OnSelectTabIndexChangeEvent;
+        manager.OnSelectCtgIdIndexChangeEvent -= OnSelectCtgIdIndexChangeEvent;
+        manager.OnRemoveExpiredEvent -= OnRemoveExpiredEvent;
+        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
+        RechargeManager.Instance.rechargeCountEvent -= OnRechargeCountEvent;
+    }
+
+    private void OnRechargeCountEvent(int obj)
+    {
+        DisplayGift();
+    }
+
+    private void OnSecondEvent()
+    {
+        RefreshTime();
+    }
+
+    private void OnRemoveExpiredEvent()
+    {
+        var list = manager.GetCurrectTimingGiftIdList();
+        if (list.IsNullOrEmpty())
+        {
+            DelayCloseWindow().Forget();
+            return;
+        }
+        CreateAll();
+    }
+
+    private void OnSelectTabIndexChangeEvent()
+    {
+
+        InitializeSelectedCtgConfig(manager.selectTabGiftId);
+        tabScroller.m_Scorller.RefreshActiveCellViews();
+        CreateCtgIdScroller();
+        CreateAwardScroller();
+        DisplayGift();
+    }
+
+    private void OnSelectCtgIdIndexChangeEvent()
+    {
+        ctgIdScroller.m_Scorller.RefreshActiveCellViews();
+        CreateAwardScroller();
+        DisplayGift();
+    }
+
+    private void InitializeSelectedCtgConfig(int giftId)
+    {
+        if (!TimingGiftConfig.TryGetTimingGiftConfig(manager.selectTabGiftId, out TimingGiftConfig config))
+            return;
+        manager.selectCtgIds = config.CTGIDs;
+        int index = 0;
+        manager.selectCtgId = manager.GetArrValue(index, config.CTGIDs);
+
+        if (!CTGConfig.HasKey(manager.selectCtgId))
+            return;
+        CTGConfig ctgConfig = CTGConfig.Get(manager.selectCtgId);
+        manager.selectGainItemList = ctgConfig.GainItemList;
+        manager.selectCtgIdIndex = index;
+    }
+
+    private void UpdateTabSelection(int direction)
+    {
+        int maxIndex = currectTimingGiftIdList.Count - 1;
+        int tempIndex = manager.selectTabIndex + direction;
+        int realIndex = tempIndex < 0 ? maxIndex : (tempIndex > maxIndex ? 0 : tempIndex);
+
+        manager.selectTabGiftId = manager.GetListValue(realIndex, currectTimingGiftIdList);
+        manager.selectTabIndex = realIndex;
+        tabScroller.JumpIndex(manager.selectTabIndex);
+
+        InitializeSelectedCtgConfig(manager.selectTabGiftId);
+    }
+
+    private void RefreshTime()
+    {
+        int times = manager.GetRemainingSeconds(manager.selectTabGiftId);
+        timeText.text = times <= 0 ? Language.Get("TimingGift04") : Language.Get("TimingGift03", TimeUtility.SecondsToHMS(times));
+        timeText.colorType = times <= 0 ? TextColType.Red : TextColType.DarkGreen;
+    }
+
+    private int FindTypeIndex(int type)
+    {
+        for (int i = 0; i < currectTimingGiftIdList.Count; i++)
+        {
+            int id = currectTimingGiftIdList[i];
+            if (!TimingGiftConfig.TryGetTimingGiftConfig(id, out TimingGiftConfig config))
+                continue;
+            if (config.GiftType == type)
+                return i;
+        }
+        return 0;
+    }
+
+    private void CreateAll()
+    {
+        currectTimingGiftIdList = manager.GetCurrectTimingGiftIdList(isSort: true);
+
+        int index = FindTypeIndex(type);
+        manager.selectTabGiftId = manager.GetListValue(index, currectTimingGiftIdList);
+        manager.selectTabIndex = index;
+        InitializeSelectedCtgConfig(manager.selectTabGiftId);
+
+        CreateTabScroller();
+        tabScroller.JumpIndex(manager.selectTabIndex);
+
+        CreateCtgIdScroller();
+        CreateAwardScroller();
+        DisplayGift();
+    }
+
+    private void CreateTabScroller()
+    {
+        if (currectTimingGiftIdList.Count < 2)
+        {
+            tabScroller.SetActive(false);
+            return;
+        }
+        tabScroller.SetActive(true);
+
+        tabScroller.Refresh();
+        for (int i = 0; i < currectTimingGiftIdList.Count; i++)
+        {
+            tabScroller.AddCell(ScrollerDataType.Header, i);
+        }
+        tabScroller.Restart();
+    }
+
+    private void CreateCtgIdScroller()
+    {
+        ctgIdScroller.Refresh();
+        if (!manager.selectCtgIds.IsNullOrEmpty())
+        {
+            for (int i = 0; i < manager.selectCtgIds.Length; i++)
+            {
+                ctgIdScroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        ctgIdScroller.Restart();
+    }
+
+    private void CreateAwardScroller()
+    {
+        awardScroller.Refresh();
+        if (!manager.selectGainItemList.IsNullOrEmpty())
+        {
+            for (int i = 0; i < manager.selectGainItemList.Length; i++)
+            {
+                awardScroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        awardScroller.Restart();
+    }
+
+    private void DisplayGift()
+    {
+        if (!TimingGiftConfig.TryGetTimingGiftConfig(manager.selectTabGiftId, out TimingGiftConfig config))
+            return;
+        if (!CTGConfig.HasKey(manager.selectCtgId))
+            return;
+        CTGConfig ctgConfig = CTGConfig.Get(manager.selectCtgId);
+        if (!RechargeManager.Instance.TryGetOrderInfo(manager.selectCtgId, out var orderInfoConfig))
+            return;
+        bool isShowSwitch = currectTimingGiftIdList.Count > 1;
+        leftButton.SetActive(isShowSwitch);
+        rightButton.SetActive(isShowSwitch);
+
+        bgImage.SetSprite($"TimingGiftBg_{manager.selectTabGiftId}");
+        IconImage.SetSprite($"TimingGiftIcon_{manager.selectTabGiftId}");
+        giftNameText.text = Language.Get("TimingGift01", config.GiftName);
+        rateText.text = Language.Get("TimingGift02", ctgConfig.Percentage);
+
+        bool isBuy = manager.IsBuy(manager.selectCtgId);
+        buyText.text = !isBuy ? Language.Get("PayMoneyNum", orderInfoConfig.PayRMBNumOnSale) : Language.Get("storename11");
+        buyButton.interactable = !isBuy;
+        RefreshTime();
+    }
+
+    private void OnRefreshTabCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<TimingGiftTabCell>();
+        _cell?.Display(cell.index);
+    }
+
+    private void OnRefreshCtgIdCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<TimingGiftCtgIdCell>();
+        _cell?.Display(cell.index, manager.selectCtgIds);
+    }
+
+    private void OnRefreshAwardCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<TimingGiftAwardCell>();
+        _cell?.Display(cell.index, manager.selectGainItemList);
+    }
+}
+
+
+
+
diff --git a/Main/System/TimingGift/TimingGiftWin.cs.meta b/Main/System/TimingGift/TimingGiftWin.cs.meta
new file mode 100644
index 0000000..32a28f0
--- /dev/null
+++ b/Main/System/TimingGift/TimingGiftWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6f6ea5c4c1921414dbc448656d693512
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Utility/EnumHelper.cs b/Main/Utility/EnumHelper.cs
index 94aec5c..77b4e4b 100644
--- a/Main/Utility/EnumHelper.cs
+++ b/Main/Utility/EnumHelper.cs
@@ -848,6 +848,7 @@
     Mingge = 54, //鍛芥牸
     WarlordPavilion = 55, //瀹氬啗闃�
     FuncPreset = 56, //娴佹淳棰勮
+    TimingGift = 57, //鏃舵満绀煎寘
 }
 
 

--
Gitblit v1.8.0