lcy
15 小时以前 4b169e9286cc62c2dff0b2cb2bc21e49c2b52596
475 时机礼包-客户端 接入触发时机,山寨界面,记录在本地
19个文件已添加
19个文件已修改
1467 ■■■■■ 已修改文件
Main/Config/ConfigManager.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/TimingGiftConfig.cs 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/TimingGiftConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/TimingGiftTypeConfig.cs 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/TimingGiftTypeConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/TimingGiftConfig.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/TimingGiftConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BeautyMM/BeautyMMShowWin.cs 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BeautyMM/BeautyMMTravelCell.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BoneField/AdsManager.cs 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Equip/BlessLVADWin.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Equip/BlessLVTimeUpWin.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Gubao/GubaoCallWin.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Gubao/GubaoDetailWin.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HappyXB/HeroCallWin.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroGiftWashWin.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroTrainWin.cs 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Horse/HorseRankUPWin.cs 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Horse/HorseWin.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Main/HomeWin.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggeManager.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggePrayWin.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggeWin.cs 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftAwardCell.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftAwardCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftCell.cs 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftCtgIdCell.cs 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftCtgIdCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftManager.cs 502 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftTabCell.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftTabCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftWin.cs 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TimingGift/TimingGiftWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/EnumHelper.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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 字典
Main/Config/Configs/TimingGiftConfig.cs
New file
@@ -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);
        }
    }
}
Main/Config/Configs/TimingGiftConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d0f2d099d904734e8c8cdb9ffa748ad
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/Configs/TimingGiftTypeConfig.cs
New file
@@ -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);
        }
    }
}
Main/Config/Configs/TimingGiftTypeConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79fc1592c88c38746980c51334e7ec72
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/PartialConfigs/TimingGiftConfig.cs
New file
@@ -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;
    }
}
Main/Config/PartialConfigs/TimingGiftConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1dc8c2bc48ca2254aaa2efe08e81f5f8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
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)
        {
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);
            // 如果正好消耗完,不触发固定概率的判定
            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);
            }
        }
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;
            }
        }
        // 如果正好消耗完,不触发固定概率的判定
        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)
        {
            // 空格子
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;
        }
    }
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();
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);
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();
    }
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);
        }
    }
}
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)
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,
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));
                // 如果正好消耗完,不触发固定概率的判定
                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)
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)
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)
    {
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()
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 @@
    }
}
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();
        // 如果正好消耗完,不触发固定概率的判定
        if (!TimingGiftManager.Instance.TryAddWhenExactConsumption(6, PackType.Item, MinggeManager.Instance.qlItemID, useCnt))
        {
            TimingGiftManager.Instance.TryAddWithFixedProbabilityWhenSufficient(6, PackType.Item, MinggeManager.Instance.qlItemID, useCnt);
        }
    }
}
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;
        }
        // 如果正好消耗完,不触发固定概率的判定
        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);
Main/System/TimingGift.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 12fd399fb4ee8f145bbe14cb76a38d03
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimingGift/TimingGiftAwardCell.cs
New file
@@ -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); });
    }
}
Main/System/TimingGift/TimingGiftAwardCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9282d2427d5b8f349a85e3e53c6e92e6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimingGift/TimingGiftCell.cs
New file
@@ -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();
    }
}
Main/System/TimingGift/TimingGiftCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92affed6278f93648a3e7bff0e3f1eb8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimingGift/TimingGiftCtgIdCell.cs
New file
@@ -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;
        });
    }
}
Main/System/TimingGift/TimingGiftCtgIdCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 755ed8982ee993644bfabedf48f2b80a
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimingGift/TimingGiftManager.cs
New file
@@ -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}类型的礼包ID");
            }
#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}类型的TriggleChangeRate配置");
            }
#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>
    /// 数量正好消耗完时必弹出
    /// </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>
    /// 数量正好消耗完时必弹出
    /// </summary>
    public bool TryAddWhenExactConsumption(int type, long haveCount, long needCount)
    {
        // 需要0个时必弹
        if (needCount <= 0)
        {
            TryAdd(type);
            return true;
        }
        //正好消耗完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
}
Main/System/TimingGift/TimingGiftManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b10601b615e27b1409dbc408badbb1c1
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimingGift/TimingGiftTabCell.cs
New file
@@ -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;
        });
    }
}
Main/System/TimingGift/TimingGiftTabCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 584a05a7f813e494990d0e3e0e6d3906
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TimingGift/TimingGiftWin.cs
New file
@@ -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);
    }
}
Main/System/TimingGift/TimingGiftWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6f6ea5c4c1921414dbc448656d693512
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Utility/EnumHelper.cs
@@ -848,6 +848,7 @@
    Mingge = 54, //命格
    WarlordPavilion = 55, //定军阁
    FuncPreset = 56, //流派预设
    TimingGift = 57, //时机礼包
}