using asap.core; using GameCore; using UnityEngine; using System.Collections.Generic; using System.Linq; using System.Text; using game; using cfg; using System; public class EventCanModel { public int TicketCount, EventId, CycleId, RedirectId, ChainPackProgress; public EventCanCanInfo[] CanList; public Dictionary GemTasks; public EventCanProgressTaskInfo ProgressTaskInfo; public EventCanRewardInfo[] RewardPool; public EventCanWeightModifier WeightModifier; private const int CanColorCount = 4; public EEventCanBlockState BlockState = EEventCanBlockState.Available; public static EventCanModel GetModel(int eventId, EventCanPlayfabData pfData = null, EventCanPlayerPreferenceData ppData = null) { var res = new EventCanModel(); ppData ??= EventCanPlayerPreferenceData.RandomInit(CanColorCount); if (pfData == null || pfData.EventId != eventId) //New Event { res.EventId = eventId; res.TicketCount = GContext.container.Resolve().GetWelcomeGift(); // res.CycleId = ; // res.RedirectId = ; res.CanList = new EventCanCanInfo[GContext.container.Resolve().GetMaxCanCount()]; for (int i = 0; i < res.CanList.Length; i++) res.CanList[i] = new EventCanCanInfo(true, UnityEngine.Random.Range(0, CanColorCount)); res.GemTasks = GContext.container.Resolve().InitGemTaskInfo(); res.ProgressTaskInfo = GContext.container.Resolve().InitProgressTaskInfo(); res.RewardPool = GContext.container.Resolve().InitRewardPool().ToArray(); res.WeightModifier = new EventCanWeightModifier(GContext.container.Resolve().GetInitialDrawsSinceLastRewards()); res.ChainPackProgress = 0; res.ToPlayerPreferenceData().Save(); res.ToPlayfabData().Save(); } else //read from playfab and playerprefs { res.EventId = eventId; res.TicketCount = pfData.TicketCount; res.CanList = new EventCanCanInfo[GContext.container.Resolve().GetMaxCanCount()]; for (int i = 0; i < res.CanList.Length; i++) { var doesExist = (pfData.CanState & (1 << i)) != 0; res.CanList[i] = new EventCanCanInfo(doesExist, ppData.ColorIndices[i]); } res.GemTasks = pfData.GemTaskInfo .ToDictionary(info => (EEventCanRewardType)(info.Key % 1000 / 100), info => GContext.container.Resolve().GetGemTaskInfo(info.Key, info.Value)); GContext.container.Resolve().GetProgressInfoByIdx(pfData.ProgressTaskIdx, out var progressRequired, out var reward); res.ProgressTaskInfo = EventCanProgressTaskInfo.Init(pfData.ProgressTaskIdx, pfData.ProgressTaskProgress); res.RewardPool = GContext.container.Resolve().InitRewardPool().ToArray(); res.WeightModifier = new EventCanWeightModifier(pfData.DrawsSinceLastRewards); res.ChainPackProgress = pfData.ChainPackProgress; } return res; } public void InitializeModel(int eventId, EventCanPlayfabData pfData = null, EventCanPlayerPreferenceData ppData = null) { ppData ??= EventCanPlayerPreferenceData.RandomInit(CanColorCount); if (pfData == null || pfData.EventId != eventId) //New Event { EventId = eventId; TicketCount = GContext.container.Resolve().GetWelcomeGift(); // this.CycleId = ; // this.RedirectId = ; CanList = new EventCanCanInfo[GContext.container.Resolve().GetMaxCanCount()]; for (int i = 0; i < CanList.Length; i++) CanList[i] = new EventCanCanInfo(true, UnityEngine.Random.Range(0, CanColorCount)); GemTasks = GContext.container.Resolve().InitGemTaskInfo(); ProgressTaskInfo = GContext.container.Resolve().InitProgressTaskInfo(); RewardPool = GContext.container.Resolve().InitRewardPool().ToArray(); WeightModifier = new EventCanWeightModifier(GContext.container.Resolve().GetInitialDrawsSinceLastRewards()); ChainPackProgress = 0; ToPlayerPreferenceData().Save(); ToPlayfabData().Save(); } else //read from playfab and playerprefs { EventId = eventId; TicketCount = pfData.TicketCount; CanList = new EventCanCanInfo[GContext.container.Resolve().GetMaxCanCount()]; for (int i = 0; i < CanList.Length; i++) { var doesExist = (pfData.CanState & (1 << i)) != 0; CanList[i] = new EventCanCanInfo(doesExist, ppData.ColorIndices[i]); } GemTasks = pfData.GemTaskInfo .ToDictionary(info => (EEventCanRewardType)(info.Key % 1000 / 100), info => GContext.container.Resolve().GetGemTaskInfo(info.Key, info.Value)); GContext.container.Resolve().GetProgressInfoByIdx(pfData.ProgressTaskIdx, out var progressRequired, out var reward); ProgressTaskInfo = EventCanProgressTaskInfo.Init(pfData.ProgressTaskIdx, pfData.ProgressTaskProgress); RewardPool = GContext.container.Resolve().InitRewardPool().ToArray(); WeightModifier = new EventCanWeightModifier(pfData.DrawsSinceLastRewards); ChainPackProgress = pfData.ChainPackProgress; } } public bool AddTicket(int count) { bool res; TicketCount += count; if (TicketCount < 0) { TicketCount = 0; res = false; } else res = true; ToPlayfabData().Save(); GContext.container.Resolve().SaveTransitionData(EventId, TicketCount); EventCanAct.EventAggregator.Publish(new EventCanTicketUpdate()); return res; } public void Log() { Debug.Log($"[EventCan] Model: TicketCount {TicketCount}"); foreach (var task in GemTasks.Values) { Debug.Log($"[EventCan] GemTask{task.TaskId}: {task.CurrentGemProgress}/{task.TotalGemRequired}"); } Debug.Log($"[EventCan] ProgressTask: {ProgressTaskInfo.TaskIdx} {ProgressTaskInfo.CurrentProgress}/{ProgressTaskInfo.TotalProgressRequired}"); } public EventCanPlayfabData ToPlayfabData() { int cs = 0; for (int i = 0; i < CanList.Length; i++) if (CanList[i].DoesExist) cs += 1 << i; var res = new EventCanPlayfabData { EventId = EventId, CycleId = CycleId, RedirectId = RedirectId, TicketCount = TicketCount, CanState = cs, ChainPackProgress = ChainPackProgress, ProgressTaskIdx = ProgressTaskInfo.TaskIdx, ProgressTaskProgress = ProgressTaskInfo.CurrentProgress, GemTaskInfo = GemTasks.ToDictionary(kv => kv.Value.TaskId, kv => kv.Value.CurrentGemProgress), DrawsSinceLastRewards = WeightModifier.DrawsSinceLastRewards }; return res; } public EventCanPlayerPreferenceData ToPlayerPreferenceData() { var res = new EventCanPlayerPreferenceData { ColorIndices = CanList.Select(c => c.ColorIdx).ToArray() }; return res; } } public class EventCanWeightModifier { /// /// RewardId, DrawCount /// public Dictionary DrawsSinceLastRewards; public EventCanWeightModifier(Dictionary d) => DrawsSinceLastRewards = d; public void UpdateDrawsSinceLastRewards(int rewardId) { foreach (var k in DrawsSinceLastRewards.Keys.ToArray()) if (k == rewardId) DrawsSinceLastRewards[k] = 0; else DrawsSinceLastRewards[k] += 1; } public void GetWeightDelta(int rewardId, out int weight, out bool mustGet) { if (!GContext.container.Resolve().GetWeightChangeThreshold(rewardId, out var t)) { mustGet = false; weight = 0; return; } if (!DrawsSinceLastRewards.TryGetValue(rewardId, out var drawCount)) { // Debug.Log($"[EventCan] No draw count found for reward id {rewardId}, init error."); mustGet = false; weight = 0; return; } // Debug.Log($"[EventCan] Draw count for reward id {rewardId} is {drawCount}, threshold is {t}"); if (drawCount + 1 >= t.Item2) { mustGet = true; weight = 0; return; } if (drawCount + 1 >= t.Item1) { mustGet = false; weight = GContext.container.Resolve().GetRewardWeight(rewardId); return; } mustGet = false; weight = 0; } } public class EventCanRewardInfo { public int RewardId; /// /// Used to decide which task to fulfill and whether to calculate dynamic weight /// public EEventCanRewardType RewardType; public int ItemId; public int ItemCount; /// /// 0 if this reward has dynamic weight. /// public int BaseWeight; /// /// Returns the item data of the reward. /// /// Transformed Item Data. Null if the reward is either gem or supply. public ItemData ToItemData() { if (RewardType == EEventCanRewardType.Supply) { Debug.Log($"[EventCan] RewardType {RewardType} is not Item."); return null; } var res = new ItemData(ItemId, ItemCount); GContext.container.Resolve().ItemTransition(res); return res; } } public class EventCanCanInfo { public bool DoesExist; public int ColorIdx; public EventCanCanInfo(bool doesExist, int colorIdx) { DoesExist = doesExist; ColorIdx = colorIdx; } } public class EventCanGemTaskInfo { public int TaskId; public int CurrentGemProgress; public int TotalGemRequired; public ItemData Reward; public int GemIdx => TaskId % 1000 / 100; public EEventCanRewardType GemType => (EEventCanRewardType)(TaskId % 1000 / 100); public bool IsCompleted => CurrentGemProgress >= TotalGemRequired; public void UpdateGemTasks(EventCanRewardInfo info, out ItemData reward, out int oldTaskId) { reward = null; oldTaskId = 0; if (info.RewardType != EEventCanRewardType.Green && info.RewardType != EEventCanRewardType.Blue && info.RewardType != EEventCanRewardType.Purple && info.RewardType != EEventCanRewardType.Red) { Debug.Log($"[EventCan] RewardType {info.RewardType} is not gem."); return; } CurrentGemProgress += info.ItemCount; if (!IsCompleted) return; reward = Reward; oldTaskId = TaskId; GContext.container.Resolve().GetNextGemTaskInfo(this); } public EventCanGemTaskInfo DeepCopy() { var info = new EventCanGemTaskInfo(); info.TaskId = TaskId; info.CurrentGemProgress = CurrentGemProgress; info.TotalGemRequired = TotalGemRequired; info.Reward = new ItemData(Reward.id, (int)Reward.count); return info; } } public class EventCanProgressTaskInfo { public int TaskIdx; public int CurrentProgress; public int TotalProgressRequired; public ItemData Reward; public bool IsCompleted => CurrentProgress >= TotalProgressRequired; public void UpdateProgressTask(out ItemData reward) { reward = null; CurrentProgress++; if (IsCompleted) { reward = Reward; GContext.container.Resolve().GetNextProgressTaskInfo(this); } } public static EventCanProgressTaskInfo Init(int TaskIdx, int CurrentProgress) { EventCanProgressTaskInfo info = new EventCanProgressTaskInfo(); info.TaskIdx = TaskIdx; info.CurrentProgress = CurrentProgress; GContext.container.Resolve().GetProgressInfoByIdx(TaskIdx, out info.TotalProgressRequired, out info.Reward); return info; } public EventCanProgressTaskInfo DeepCopy() { return new EventCanProgressTaskInfo() { TaskIdx = TaskIdx, CurrentProgress = CurrentProgress, TotalProgressRequired = TotalProgressRequired, Reward = new ItemData(Reward.id, (int)Reward.count) }; } } public class EventCanPlayfabData { public const string PlayFabKey = "EventCan"; private const char Splitter = '|', Comma = ','; private const int SerializedTokenCount = 11; public int TicketCount, ProgressTaskIdx, ProgressTaskProgress, EventId, CycleId, RedirectId, CanState, ChainPackProgress; /// /// GemTaskId, CurrentGemProgress /// public Dictionary GemTaskInfo; /// /// RewardId, DrawCount /// public Dictionary DrawsSinceLastRewards; public string Serialize() { var sb = new StringBuilder(); sb.Append(TicketCount).Append(Splitter); sb.Append(EventId).Append(Splitter); sb.Append(CycleId).Append(Splitter); sb.Append(RedirectId).Append(Splitter); sb.Append(CanState).Append(Splitter); sb.Append(ChainPackProgress).Append(Splitter); sb.Append(ProgressTaskIdx).Append(Comma).Append(ProgressTaskProgress).Append(Splitter); foreach (var kv in GemTaskInfo) sb.Append(kv.Key).Append(Comma).Append(kv.Value).Append(Splitter); foreach (var kv in DrawsSinceLastRewards) sb.Append(kv.Key).Append(Comma).Append(kv.Value).Append(Splitter); // Debug.Log("[EventCan] Serialize: " + sb.ToString()); return sb.ToString(); } public static EventCanPlayfabData Deserialize(string s) { if (s == null || s == "") return null; var res = new EventCanPlayfabData(); try { var tokens = s.Trim(Splitter).Split(Splitter); if (tokens.Length < SerializedTokenCount) throw new Exception($"[EventCan]Wrong amount of parameters. Expect at least {SerializedTokenCount}, but got {tokens.Length} in \"{s}\"."); res.TicketCount = int.Parse(tokens[0]); res.EventId = int.Parse(tokens[1]); res.CycleId = int.Parse(tokens[2]); res.RedirectId = int.Parse(tokens[3]); res.CanState = int.Parse(tokens[4]); res.ChainPackProgress = int.Parse(tokens[5]); res.ProgressTaskIdx = int.Parse(tokens[6].Split(Comma)[0]); res.ProgressTaskProgress = int.Parse(tokens[6].Split(Comma)[1]); res.GemTaskInfo = new Dictionary(); for (int i = 7; i < 11; i++) res.GemTaskInfo.Add(int.Parse(tokens[i].Split(Comma)[0]), int.Parse(tokens[i].Split(Comma)[1])); res.DrawsSinceLastRewards = new Dictionary(); for (int i = 11; i < tokens.Length; i++) res.DrawsSinceLastRewards.Add(int.Parse(tokens[i].Split(Comma)[0]), int.Parse(tokens[i].Split(Comma)[1])); Debug.Log("[EventCan] Deserialize: " + res); } catch (Exception e) { Debug.LogError($"[EventCan] Failed to deserialize event can model: {e.Message}\n{e.StackTrace}"); return null; } return res; } public void Save() { var s = Serialize(); PlayFabMgr.Instance.UpdateUserDataValue(PlayFabKey, s); } public void Log() { Debug.Log($"[EventCan] TicketCount: {TicketCount}\n" + $"EventId: {EventId}\n" + $"CycleId: {CycleId}\n" + $"RedirectId: {RedirectId}\n" + $"CanState: {CanState}\n" + $"ProgressTaskIdx: {ProgressTaskIdx}\n" + $"ProgressTaskProgress: {ProgressTaskProgress}\n" ); foreach (var kv in GemTaskInfo) Debug.Log($"GemTaskInfo: {kv.Key}, {kv.Value}"); foreach (var kv in DrawsSinceLastRewards) Debug.Log($"DrawsSinceLastRewards: {kv.Key}, {kv.Value}"); } } public class EventCanPlayerPreferenceData { public static string PlayerPrefsKey => "EventCanPlayerPreferenceData" + GContext.container.Resolve().UserId; public int[] ColorIndices; public string Serialize() { return Newtonsoft.Json.JsonConvert.SerializeObject(ColorIndices); } public static EventCanPlayerPreferenceData Deserialize(string s) { if (s == null || s == "") return null; var data = new EventCanPlayerPreferenceData(); data.ColorIndices = Newtonsoft.Json.JsonConvert.DeserializeObject(s); return data; } public void Save() { var s = Serialize(); PlayerPrefs.SetString(PlayerPrefsKey, s); } public static EventCanPlayerPreferenceData RandomInit(int canColorCount) { var res = new EventCanPlayerPreferenceData(); res.ColorIndices = new int[GContext.container.Resolve().GetMaxCanCount()]; for (int i = 0; i < res.ColorIndices.Length; i++) { res.ColorIndices[i] = UnityEngine.Random.Range(0, canColorCount); } res.Save(); return res; } } public class EventCanChainPackData : AChainPackData { public EventCanChainPackData() { } public EventCanChainPackData(EventChainPackInfo info, int eventId, ref int chainProgress) : base(eventId, chainProgress) { ChainList = info.ChainList; ExpireTime = info.ExpireTime; Packs = info.Packs; } public void Init(EventChainPackInfo info, int eventId, ref int chainProgress) { EventId = eventId; ChainProgress = chainProgress; ChainList = info.ChainList; ExpireTime = info.ExpireTime; Packs = info.Packs; } public override void UploadData() { var model = GContext.container.Resolve(); model.ChainPackProgress = ChainProgress; model.ToPlayfabData().Save(); } } public class EventCanEntranceBtnData { public string BtnIconUrl; public DateTime ExpiryTime, StartTime; public TimeSpan RemainingTime => ExpiryTime - ZZTimeHelper.UtcNow(); public bool IsActive => ZZTimeHelper.UtcNow() >= StartTime && ZZTimeHelper.UtcNow() < ExpiryTime; public int RedPointTicketThreshold; } public enum EEventCanRewardType { Green = 1, Blue = 2, Purple = 3, Red = 4, Supply = 5, Item = 6 } [Flags] public enum EEventCanBlockState { Available, Gem, Supply, Blocked = Gem | Supply }