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 int limitPopCnt;
|
public int limitShowCnt;
|
public int limitPopCd;
|
|
public event Action OnSelectCtgIdIndexChangeEvent;
|
|
public bool isLogShow = true;
|
public override void Init()
|
{
|
DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
|
DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
|
GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
|
|
var config = FuncConfigConfig.Get("TimingGift");
|
limitPopCnt = int.Parse(config.Numerical1);
|
limitShowCnt = int.Parse(config.Numerical2);
|
limitPopCd = int.Parse(config.Numerical3);
|
}
|
|
public override void Release()
|
{
|
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 Color32 GetColor32(int[] colorArr)
|
{
|
if (colorArr == null || colorArr.Length < 3)
|
return Color.white;
|
return new Color32()
|
{
|
r = (byte)(colorArr.Length > 0 ? colorArr[0] : 0),
|
g = (byte)(colorArr.Length > 1 ? colorArr[1] : 0),
|
b = (byte)(colorArr.Length > 2 ? colorArr[2] : 0),
|
a = (byte)(colorArr.Length > 3 ? colorArr[3] : 255),
|
};
|
}
|
|
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;
|
}
|
|
HashSet<int> typeHashSet;
|
int GetShowCnt()
|
{
|
if (typeHashSet == null)
|
typeHashSet = new HashSet<int>();
|
|
typeHashSet.Clear();
|
if (showGiftIdList.IsNullOrEmpty())
|
return 0;
|
|
for (int i = 0; i < showGiftIdList.Count; i++)
|
{
|
int id = showGiftIdList[i];
|
if (!TimingGiftConfig.TryGetTimingGiftConfig(id, out var config))
|
continue;
|
typeHashSet.Add(config.GiftType);
|
}
|
return typeHashSet.Count;
|
}
|
|
int GetPopCnt()
|
{
|
if (lastTriggerTimeDict.IsNullOrEmpty())
|
return 0;
|
int cnt = 0;
|
foreach (var item in lastTriggerTimeDict)
|
{
|
int type = item.Key;
|
int time = item.Value;
|
var timeData = TimeUtility.GetTime((uint)time);
|
DateTime nowTime = TimeUtility.ServerNow;
|
if (timeData.Day == nowTime.Day && timeData.Month == nowTime.Month && timeData.Year == nowTime.Year)
|
{
|
cnt += 1;
|
}
|
}
|
return cnt;
|
}
|
|
bool IsInLimitCd()
|
{
|
if (lastTriggerTimeDict.IsNullOrEmpty())
|
return false;
|
int lastTime = lastTriggerTimeDict.Values.Max();
|
return TimeUtility.AllSeconds - lastTime <= limitPopCd;
|
}
|
|
public void TryAdd(int type)
|
{
|
if (NewBieCenter.Instance.inGuiding)
|
{
|
#if UNITY_EDITOR
|
if (isLogShow)
|
{
|
Debug.Log($"[TimingGift] TryPop:触发Return,正在引导中");
|
}
|
#endif
|
return;
|
}
|
|
if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.TimingGift))
|
{
|
|
#if UNITY_EDITOR
|
if (isLogShow)
|
{
|
Debug.Log($"[TimingGift] TryPop:触发Return,时机礼包功能未开启");
|
}
|
#endif
|
return;
|
}
|
|
if (!TimingGiftConfig.TryGetTypeToGiftIdList(type, out List<int> giftIdList))
|
{
|
#if UNITY_EDITOR
|
if (isLogShow)
|
{
|
Debug.Log($"[TimingGift] TryPop:触发Return,表格中没有找到{type}类型的礼包ID");
|
}
|
#endif
|
return;
|
}
|
int showCnt = GetShowCnt();
|
|
if (limitShowCnt <= showCnt)
|
{
|
#if UNITY_EDITOR
|
if (isLogShow)
|
{
|
Debug.Log($"[TimingGift] TryPop:触发Return,当前已达到同时展示的礼包类型数量总上限");
|
}
|
#endif
|
return;
|
}
|
int popCnt = GetPopCnt();
|
|
if (limitPopCnt <= popCnt)
|
{
|
#if UNITY_EDITOR
|
if (isLogShow)
|
{
|
Debug.Log($"[TimingGift] TryPop:触发Return,今日已达到礼包类型触发数量总上限");
|
}
|
#endif
|
return;
|
}
|
|
bool isInLimitCd = IsInLimitCd();
|
if (isInLimitCd)
|
{
|
#if UNITY_EDITOR
|
if (isLogShow)
|
{
|
Debug.Log($"[TimingGift] TryPop:触发Return,礼包触发冷却时间中");
|
}
|
#endif
|
return;
|
}
|
|
if (!IsTodayUnlimited(type))
|
{
|
#if UNITY_EDITOR
|
if (isLogShow)
|
{
|
int lastTriggerTime = lastTriggerTimeDict[type];
|
Debug.Log($"[TimingGift] TryPop:触发Return,今天已弹出过{type}类型的礼包,该类型上次弹出时间为{TimeUtility.GetTime((uint)lastTriggerTime):yyyy-MM-dd HH:mm:ss}");
|
}
|
#endif
|
return;
|
}
|
|
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[config.GiftType] = 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 void LoadData()
|
{
|
lastTriggerTimeDict.Clear();
|
|
if (!SettingDataManager.Instance.TryGetSettingData((int)SettingDataKeyNum.TimingGiftType, out string data))
|
return;
|
// 使用 LocalSave 读取 JSON 字符串
|
if (string.IsNullOrEmpty(data) || data == "{}")
|
return;
|
Dictionary<int, int> loadDict = ConfigParse.ParseIntDict(data);
|
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);
|
SettingDataManager.Instance.SendSettingData(SettingDataKeyNum.TimingGiftType, 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
|
|
}
|