using asap.core; using cfg; using GameCore; using System; using System.Collections.Generic; using System.Linq; using UniRx; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.U2D; using UnityEngine.UI; namespace game { public class EventScratchTicketDataManager { #if UNITY_EDITOR private bool m_IsCheckCondition = true; private bool m_IsSyncData = true; private int m_DefaultToken = 100; #else private bool m_IsCheckCondition = true; private bool m_IsSyncData = true; private int m_DefaultToken = 0; #endif #region 埋点相关 public int TempRecordRewardTaskID { get; set; } = 0; public int TempRecordRewardTaskConsumedToken { get; set; } = 0; public void SyncTempRecordData() { ClearRecordList(); TempRecordRewardTaskID = m_ScratchTicketData.EventScratchRewardTaskID; TempRecordRewardTaskConsumedToken = m_ScratchTicketData.RewardTaskConsumedToken; } public void RecordStatisticalData() { int fromToken = TempRecordRewardTaskConsumedToken; int taskID = TempRecordRewardTaskID; EventScratchReward curScratchReward = GetEventScratchReward(taskID); int count = m_RecordList.Count; for (int i = 0; i < count; i++) { var data = m_RecordList[i]; fromToken += 1; if (fromToken >= curScratchReward.TokenRequired) { data.MilestoneTask = curScratchReward.TaskID; fromToken = 0; taskID = curScratchReward.NextTask; curScratchReward = GetEventScratchReward(taskID); } #if AGG using (var e = GEvent.GameEvent("event_scratchticket")) { e.AddContent("ticket_id", data.TicketID) .AddContent("ticketfinish_id", data.TicketFinishID) .AddContent("scratch_type", data.ScratchType) .AddContent("milestone_task", data.MilestoneTask); } #endif } #if UNITY_EDITOR Debug.LogError("--- RecordStatisticalData m_RecordList: " + Newtonsoft.Json.JsonConvert.SerializeObject(m_RecordList)); #endif } private List m_RecordList = new List(); public void InitRecordList() { ClearRecordList(); } public void ClearRecordList() { m_RecordList?.Clear(); TempRecordRewardTaskID = 0; TempRecordRewardTaskConsumedToken = 0; } public EventScratchTicketRecordData AddRecordData() { EventScratchTicketRecordData data = new EventScratchTicketRecordData(); m_RecordList.Add(data); return data; } public EventScratchTicketRecordData GetRecordData(int index) { return m_RecordList[index]; } #endregion #region 数据相关 private EventScratchTicketData m_ScratchTicketData; public EventScratchTicketData ScratchTicketData { get { return m_ScratchTicketData; } } public EventScratchMain GetFreshScratchTicketData() { // 获取当前开放并符合解锁条件的刮刮乐活动ID FishingEventData fishingEventData = GContext.container.Resolve(); FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition); if (fishingEvent == null) return null; int eventID = fishingEvent.ID; int scratchMainID = fishingEvent.RedirectID; if (scratchMainID < 0) return null; // 获取当前刮刮乐活动数据 EventScratchMain scratchMain = m_TbEventScratchMain.GetOrDefault(scratchMainID); if (scratchMain == null) return null; int token = m_DefaultToken; if (m_ScratchTicketData != null) m_ScratchTicketData.Clear(); m_ScratchTicketData = null; token = GContext.container.Resolve().GetInitWelcomeGift2(eventID); m_ScratchTicketData = new EventScratchTicketData(); m_ScratchTicketData.EventID = eventID; m_ScratchTicketData.ScratchMainID = scratchMainID; m_ScratchTicketData.IsOver = false; m_ScratchTicketData.Token = token; m_ScratchTicketData.ConsumedToken = 0; m_ScratchTicketData.LuckyValue = 0f; m_ScratchTicketData.EventScratchCardID = 0; m_ScratchTicketData.TotalConsumedToken = 0; m_ScratchTicketData.ChainPackProgress = 0; m_ScratchTicketData.StampRewardDict = new Dictionary(); EventScratchReward eventScratchReward = m_TbEventScratchReward.DataList[0]; m_ScratchTicketData.EventScratchRewardTaskID = eventScratchReward.TaskID; m_ScratchTicketData.RewardTaskConsumedToken = 0; m_ScratchTicketData.StampRewardList.Clear(); ClearSaveData(EventScratchTicketConfig.key_first_in); GContext.container.Resolve().SaveTransitionData(m_ScratchTicketData.EventID, m_ScratchTicketData.Token); UpdateLuckyValueAndWeight(); foreach (var item in scratchMain.GuidanceGroupList) { GContext.container.Resolve().ClearGuide(item); } // 同步数据 SyncData(); return scratchMain; } public EventScratchMain GetNextScratchTicketData() { // 获取当前开放并符合解锁条件的刮刮乐活动ID FishingEventData fishingEventData = GContext.container.Resolve(); FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition); if (fishingEvent == null) return null; int eventID = fishingEvent.ID; int scratchMainID = fishingEvent.RedirectID; if (scratchMainID < 0) return null; // 获取当前刮刮乐活动数据 EventScratchMain scratchMain = m_TbEventScratchMain.GetOrDefault(scratchMainID); if (scratchMain == null) return null; m_ScratchTicketData.EventID = eventID; m_ScratchTicketData.ScratchMainID = scratchMainID; m_ScratchTicketData.IsOver = false; m_ScratchTicketData.ConsumedToken = 0; UpdateLuckyValueAndWeight(); // 同步数据 SyncData(); return scratchMain; } public EventScratchMain ContinueGame() { if (m_ScratchTicketData == null) return GetFreshScratchTicketData(); // 获取当前开放并符合解锁条件的刮刮乐活动ID FishingEventData fishingEventData = GContext.container.Resolve(); FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition); if (fishingEvent == null) return null; int eventID = fishingEvent.ID; int scratchMainID = fishingEvent.RedirectID; if (scratchMainID < 0) return null; if (eventID != m_ScratchTicketData.EventID || scratchMainID != m_ScratchTicketData.ScratchMainID) return GetFreshScratchTicketData(); // 获取当前刮刮乐活动数据 EventScratchMain scratchMain = m_TbEventScratchMain.GetOrDefault(scratchMainID); if (scratchMain == null) return null; return scratchMain; } public List m_PointWeightList = new List(); public EventScratchCard UpdateLuckyValueAndWeight() { if (m_TbEventScratchCard == null) return null; m_PointWeightList.Clear(); float luckyValue = m_ScratchTicketData.LuckyValue; int count = m_TbEventScratchCard.DataList.Count; float total = 0; for (int i = 0; i < count; ++i) { EventScratchCard item = m_TbEventScratchCard.DataList[i]; float weight = item.Weight + luckyValue * item.LuckParameter; total += weight; float tmp = total; m_PointWeightList.Add(tmp); } float randomValue = UnityEngine.Random.Range(0f, total); int index = FindInsertionIndex(m_PointWeightList, randomValue); EventScratchCard target = m_TbEventScratchCard.DataList[index]; if (float.MaxValue - target.DeltaLuck <= m_ScratchTicketData.LuckyValue) { m_ScratchTicketData.LuckyValue = 0; } else { m_ScratchTicketData.LuckyValue += target.DeltaLuck; if (m_ScratchTicketData.LuckyValue < 0f) { m_ScratchTicketData.LuckyValue = 0f; } } m_ScratchTicketData.EventScratchCardID = target.ID; return target; } public EventScratchCard GetCurrentEventScratchCard() { if (m_ScratchTicketData == null) return null; return GetEventScratchCard(m_ScratchTicketData.EventScratchCardID); } public int FindInsertionIndex(List sortedList, float target) { if (sortedList == null || sortedList.Count == 0) { Debug.LogError("---- EventScratchTicketDataManager->FindInsertionIndex 数组为空"); return -1; } if (target < sortedList[0]) { return 0; } if (target >= sortedList[sortedList.Count - 1]) { return -1; } // 使用二分查找算法 int left = 0; int right = sortedList.Count - 1; int result = -1; while (left <= right) { int mid = left + (right - left) / 2; if (sortedList[mid] > target) { if (mid > 0 && target > sortedList[mid - 1] && target < sortedList[mid]) { result = mid; break; } right = mid - 1; } else { if (mid < sortedList.Count - 1 && target >= sortedList[mid] && target < sortedList[mid + 1]) { result = mid + 1; break; } left = mid + 1; } } return result; } public EventScratchMain GetCurrentEventScratchMain() { if (m_ScratchTicketData == null) return GetFreshScratchTicketData(); return m_TbEventScratchMain.GetOrDefault(m_ScratchTicketData.ScratchMainID); } private EventScratchTicketChainPackData m_ChainPackData; public EventScratchTicketChainPackData GetChainPackData() { if (m_ChainPackData == null) { EventScratchMain scratchMain = GetCurrentEventScratchMain(); if (scratchMain == null) return null; var chainList = m_Tables.TbEventPackManager[scratchMain.PackId].VIPPackList[0]; var expireTime = GetExpireTime; var chainListIdSet = chainList.ToHashSet(); var packs = m_Tables.TbPack.DataList.Where(p => chainListIdSet.Contains(p.ID)).ToArray(); m_ChainPackData = new EventScratchTicketChainPackData(chainList, expireTime, packs, m_ScratchTicketData.EventID, m_ScratchTicketData.ChainPackProgress); RedPointManager.Instance.SetRedPointState(m_ChainPackData.RedPointKey, m_ChainPackData.DoNeedPackRedPoint); } return m_ChainPackData; } public void InitChainPackData() { ClearChainPackData(); } public void ClearChainPackData() { if (m_ChainPackData != null) { m_ChainPackData = null; } } private EventBreakNormalPackInfo m_NormalPackData; public EventBreakNormalPackInfo GetNormalPackData() { if (m_NormalPackData == null) { EventScratchMain scratchMain = GetCurrentEventScratchMain(); if (scratchMain == null) return null; var expireTime = GetExpireTime; var packList = m_Tables.TbEventPackManager[scratchMain.PackId2].VIPPackList[0]; m_NormalPackData = new EventBreakNormalPackInfo { EventId = m_ScratchTicketData.EventID, PackLeft = m_Tables.TbPack[packList[0]], PackRight = m_Tables.TbPack[packList[1]], ExpireTime = expireTime, }; } return m_NormalPackData; } private void InitNormalPackData() { ClearNormalPackDatas(); } private void ClearNormalPackDatas() { m_NormalPackData = null; } public void InitRewardBagData() { GContext.container.Resolve().Reset(); //List rewardList = new List(); //EventScratchMain curScratchMain = GetCurrentEventScratchMain(); //foreach (var item in curScratchMain.CardList) //{ // var card = GetEventScratchCard(item); // if (card.DropReward > 0) // { // rewardList.Add(card.DropReward); // } //} //EventScratchReward curReward = GetEventScratchReward(m_ScratchTicketData.EventScratchRewardTaskID); //if (curReward.DropReward > 0) //{ // rewardList.Add(curReward.DropReward); //} //GContext.Publish(new RewardStashService.EventStashDropList { DropList = rewardList }); } public void ClearRewardBagData() { } public void SetChainProgress(int progress) { m_ScratchTicketData.ChainPackProgress = progress; SyncData(); } public int GetChainProgress() { return m_ScratchTicketData.ChainPackProgress; } public void AddStampReward(int cardID) { m_ScratchTicketData.StampRewardList.Add(cardID); SyncData(); } public int GetStampRewardNum() { return m_ScratchTicketData.StampRewardList.Count; } public EventScratchCard GetFirstStampReward() { return GetEventScratchCard(m_ScratchTicketData.StampRewardList[0]); } public void RemoveFirstStampReward() { m_ScratchTicketData.StampRewardList.RemoveAt(0); SyncData(); } public void ClearStampReward() { m_ScratchTicketData.StampRewardList.Clear(); SyncData(); } private List m_ScratchStampList = new List(); public List ScratchStampList { get { return m_ScratchStampList; } } private void InitScratchStampList() { m_ScratchStampList.Clear(); } public void ClearScratchStampList() { m_ScratchStampList.Clear(); } public void AddScratchStamp(int cardID) { m_ScratchStampList.Add(cardID); } public EventScratchCard GetFirstScratchStamp() { return GetEventScratchCard(m_ScratchStampList[0]); } #endregion #region 代币相关 public int GetToken() { if (m_ScratchTicketData != null) return m_ScratchTicketData.Token; return 0; } public void AddToken(int count) { if (m_ScratchTicketData != null) { m_ScratchTicketData.Token += count; } else { if (m_ScratchTicketData == null) return; m_ScratchTicketData.Token += count; } if (m_ScratchTicketData.Token > 0) RedPointManager.Instance.SetRedPointState(RedPointName.Home_ScratchTicket, true); else RedPointManager.Instance.SetRedPointState(RedPointName.Home_ScratchTicket, false); FishingScratchTicketAct.Publish(new EventScratchTicketTokenUpdateData()); GContext.container.Resolve().SaveTransitionData(m_ScratchTicketData.EventID, m_ScratchTicketData.Token); SyncData(); } public bool ConsumeTokens(int count) { bool isEnough = IsEnough(count); if (isEnough == false) return false; m_ScratchTicketData.ConsumedToken += count; m_ScratchTicketData.TotalConsumedToken += count; AddToken(-count); return isEnough; } public bool IsEnough(int spend) { if (m_ScratchTicketData == null) return false; return (m_ScratchTicketData.Token - spend) >= 0; } #endregion #region 活动相关 public void ActivateEvent(FishingEvent fishingEvent) { if (fishingEvent == null) return; if (!Init()) return; if (m_ScratchTicketData == null) GetFreshScratchTicketData(); else FixScratchTicketData(); } public void GetDataFromServer(string json) { if (string.IsNullOrEmpty(json)) { GameDebug.LogWarning("EventScratchTicket get data from server has encountered empty data."); return; } try { m_ScratchTicketData = Newtonsoft.Json.JsonConvert.DeserializeObject(json); } catch (Exception e) { GameDebug.LogWarning("EventScratchTicket deserialize data has failed. Exception: " + e.ToString()); return; } if (!Init()) return; } public int FixScratchTicketData() { if (m_ScratchTicketData == null) return 0; // 获取当前开放并符合解锁条件的刮刮乐活动ID FishingEventData fishingEventData = GContext.container.Resolve(); FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition); if (fishingEvent == null) return ClearData(); int eventID = fishingEvent.ID; int scratchMainID = fishingEvent.RedirectID; if (scratchMainID < 0) return ClearData(); if (eventID != m_ScratchTicketData.EventID || scratchMainID != m_ScratchTicketData.ScratchMainID) return ClearData(); return 0; } public DateTime GetExpireTime { get => DateTime.Parse((m_Tables.TbFishingEvent[m_ScratchTicketData.EventID].TimeDefinition as LimitedTime).EndTime); } public TimeSpan RemainingTime { get => GetExpireTime - ZZTimeHelper.UtcNow(); } public int ClearData() { if (m_ScratchTicketData == null) return 0; m_ScratchTicketData.Clear(); m_ScratchTicketData = null; return 0; } public void SyncData() { if (!m_IsSyncData) return; PlayFabMgr.Instance.UpdateUserDataValue(EventScratchTicketConfig.SAVE_DATA_KEY, Newtonsoft.Json.JsonConvert.SerializeObject(m_ScratchTicketData)); } #endregion #region 生命周期 public bool Init() { InitTables(); return true; } public void StartData() { ClearUIData(); InitUIData(); InitChainPackData(); InitNormalPackData(); InitRewardBagData(); InitSpriteDict(); InitScratchStampList(); InitRecordList(); } public void StopData() { ClearUIData(); ClearChainPackData(); ClearNormalPackDatas(); ClearSpriteAtlas(); ClearRewardBagData(); ClearSpriteDict(); ClearScratchStampList(); ClearRecordList(); } #endregion #region UI 设置相关 private UIType m_UIPanel = null, m_InfoPanel = null, m_RewardPanel = null, m_ChainPackPanel = null, m_PackPanel = null; public void ClearUIData() { m_UIPanel = null; m_InfoPanel = null; m_RewardPanel = null; m_ChainPackPanel = null; m_PackPanel = null; } public void InitUIData() { GetUIPanel(); GetInfoPanel(); GetChainPackPanel(); GetPackPanel(); } public UIType GetUIPanel() { if (m_UIPanel == null) { var data = GetCurrentEventScratchMain(); m_UIPanel = new UIType(data.UIPanel); } return m_UIPanel; } public UIType GetRewardPanel() { if (m_RewardPanel == null) { var data = GetCurrentEventScratchMain(); m_RewardPanel = new UIType(data.RewardPanel); } return m_RewardPanel; } public UIType GetInfoPanel() { if (m_InfoPanel == null) { var data = GetCurrentEventScratchMain(); m_InfoPanel = new UIType(data.InfoPanel); } return m_InfoPanel; } public UIType GetChainPackPanel() { if (m_ChainPackPanel == null) { var data = GetCurrentEventScratchMain(); m_ChainPackPanel = new UIType(data.ChainPackPanel); } return m_ChainPackPanel; } public UIType GetPackPanel() { if (m_PackPanel == null) { var data = GetCurrentEventScratchMain(); m_PackPanel = new UIType(data.PackPanel); } return m_PackPanel; } #endregion #region 图集等 private SpriteAtlas m_SpriteAtlas; public SpriteAtlas SpriteAtlas { get { return m_SpriteAtlas; } set { m_SpriteAtlas = value; } } public void ClearSpriteAtlas() { m_SpriteAtlas = null; } private Dictionary m_SpriteDict; private void InitSpriteDict() { if (m_SpriteDict != null) { m_SpriteDict.Clear(); m_SpriteDict = null; } m_SpriteDict = new Dictionary(); } public void AddSprite(string name, Sprite sprite) { m_SpriteDict.Add(name, sprite); } public Sprite GetSprite(string name) { return m_SpriteDict[name]; } private void ClearSpriteDict() { if (m_SpriteDict == null) return; foreach (var pair in m_SpriteDict) { Addressables.Release(pair.Value); } m_SpriteDict.Clear(); m_SpriteDict = null; } #endregion #region Table Data private Tables m_Tables; private TbEventScratchMain m_TbEventScratchMain; private TbEventScratchCard m_TbEventScratchCard; private TbEventScratchReward m_TbEventScratchReward; private TbEventScratchConfig m_TbEventScratchConfig; private TbItem m_TbItem; private void InitTables() { m_Tables = GContext.container.Resolve(); if (m_Tables == null) return; m_TbEventScratchMain = m_Tables.TbEventScratchMain; m_TbEventScratchCard = m_Tables.TbEventScratchCard; m_TbEventScratchReward = m_Tables.TbEventScratchReward; m_TbEventScratchConfig = m_Tables.TbEventScratchConfig; m_TbItem = m_Tables.TbItem; } public Item GetItem(int itemID) { return m_TbItem.GetOrDefault(itemID); } public EventScratchReward GetEventScratchReward(int taskID) { return m_TbEventScratchReward.GetOrDefault(taskID); } public EventScratchReward GetEventScratchFinalReward() { int count = m_TbEventScratchReward.DataList.Count; return m_TbEventScratchReward.DataList[count - 1]; } public EventScratchCard GetEventScratchCard(int cardID) { return m_TbEventScratchCard.GetOrDefault(cardID); } #endregion #region 本地存储数据相关 public string GetSaveKey(string key) { if (m_ScratchTicketData == null) return key; return m_ScratchTicketData.EventID + "-" + m_ScratchTicketData.ScratchMainID + "-" + key; } public bool HasSaveKey(string key) { return PlayerPrefs.HasKey(GetSaveKey(key)); } public void SaveData(string key, string value) { PlayerPrefs.SetString(GetSaveKey(key), value); PlayerPrefs.Save(); } public void ClearSaveData(string key) { PlayerPrefs.DeleteKey(GetSaveKey(key)); } public string GetSaveData(string key) { return PlayerPrefs.GetString(GetSaveKey(key)); } #endregion #region 红点相关 public bool IsRedDotDisplayed() { if (m_ScratchTicketData == null) return false; return m_ScratchTicketData.Token > 0; } #endregion } public enum EventScratchTicketScratchType { TypeInvalid, TypeScratch1, TypeScratch5, TypeTemporary } public class EventScratchTicketData { public int EventID { get; set; } public int ScratchMainID { get; set; } public bool IsOver { get; set; } public int Token { get; set; } public int ConsumedToken { get; set; } public int RewardTaskConsumedToken { get; set; } public int EventScratchRewardTaskID { get; set; } public int EventScratchCardID { get; set; } public float LuckyValue { get; set; } public int TotalConsumedToken { get; set; } public int ChainPackProgress { get; set; } public List StampRewardList = new List(); public Dictionary StampRewardDict { get; set; } public void Clear() { if (StampRewardDict != null) { StampRewardDict.Clear(); } StampRewardList.Clear(); } public void ClearAllLocalData() { Clear(); } } public class EventScratchTicketConfig { public const string SAVE_DATA_KEY = "estdk"; // event scratch ticket data key public const string key_first_in = "scratch_kfcw"; // 本地存储数据key public static Vector2 ConvertUiPosition(RectTransform from, RectTransform to, Camera camera) { Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(camera, from.position); RectTransformUtility.ScreenPointToLocalPointInRectangle( to, screenPoint, camera, out Vector2 localPoint); return localPoint; } public static Vector2 ScreenToGraphicLocalPoint(Graphic graphic, Vector2 screenPoint) { var canvas = graphic.canvas.rootCanvas; var camera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; var rt = graphic.rectTransform; return RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, screenPoint, camera, out var result) ? result : default; } } public class EventScratchTicketTokenUpdateData { } public class EventScratchTicketRewardTaskUpdateData { public EventScratchTicketScratchType ScratchType; public int Token; } public class EventScratchTicketRewardDisplayUpdateData { public int TaskID; public int FromToken; public int TargetToken; public int LevelToken; public float FromRatio; public float TargetRatio; public EventScratchReward Reward; } public class EventScratchTicketChangeScratchTypeData { public EventScratchTicketScratchType ScratchType; } public class EventScratchTicketRevealedAllData { public EventScratchTicketScratchType ScratchType; } public class EventScratchTicketStampRewardUpdateData { } public class EventScratchTicketStartStampRewardCountUpdateData { } public class EventScratchTicketStopStampRewardCountUpdateData { } public class EventScratchTicketOwnedStampRewardData { public int EventScratchCardID; public int Count; } public class EventScratchTicketStampRewardFlyData { public EventScratchTicketScratchType ScratchType; public Vector3 SrcPosition; //public Vector3 DestPosition; public Vector3 LocalScale; public float IconSize; } public class EventScratchTicketRewardEmptyData { public EventScratchTicketScratchType ScratchType; } public class EventScratchTicketUpdateEventScratchCardData { } public class EventScratchTicketPanelPlayEndAnimData { public EventScratchTicketScratchType ScratchType; } public class EventScratchTicketPlayScratchType5 { } public class EventScratchTicketStartMaskData { } public class EventScratchTicketOutData { public EventScratchTicketScratchType ScratchType; } public class EventScratchTicketRecordData { public int TicketID; public int TicketFinishID; public int ScratchType; public int MilestoneTask; public void SetScratchType(EventScratchTicketScratchType type) { if (type == EventScratchTicketScratchType.TypeScratch1) { ScratchType = 1; } else if (type == EventScratchTicketScratchType.TypeScratch5) { ScratchType = 2; } } } }