519 lines
19 KiB
C#
519 lines
19 KiB
C#
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<EEventCanRewardType, EventCanGemTaskInfo> 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<EventCanTableContext>().GetWelcomeGift();
|
|
// res.CycleId = ;
|
|
// res.RedirectId = ;
|
|
res.CanList = new EventCanCanInfo[GContext.container.Resolve<EventCanTableContext>().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<EventCanTableContext>().InitGemTaskInfo();
|
|
res.ProgressTaskInfo = GContext.container.Resolve<EventCanTableContext>().InitProgressTaskInfo();
|
|
res.RewardPool = GContext.container.Resolve<EventCanTableContext>().InitRewardPool().ToArray();
|
|
res.WeightModifier = new EventCanWeightModifier(GContext.container.Resolve<EventCanTableContext>().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<EventCanTableContext>().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<EventCanTableContext>().GetGemTaskInfo(info.Key, info.Value));
|
|
GContext.container.Resolve<EventCanTableContext>().GetProgressInfoByIdx(pfData.ProgressTaskIdx, out var progressRequired, out var reward);
|
|
res.ProgressTaskInfo = EventCanProgressTaskInfo.Init(pfData.ProgressTaskIdx, pfData.ProgressTaskProgress);
|
|
res.RewardPool = GContext.container.Resolve<EventCanTableContext>().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<EventCanTableContext>().GetWelcomeGift();
|
|
// this.CycleId = ;
|
|
// this.RedirectId = ;
|
|
CanList = new EventCanCanInfo[GContext.container.Resolve<EventCanTableContext>().GetMaxCanCount()];
|
|
for (int i = 0; i < CanList.Length; i++)
|
|
CanList[i] = new EventCanCanInfo(true, UnityEngine.Random.Range(0, CanColorCount));
|
|
GemTasks = GContext.container.Resolve<EventCanTableContext>().InitGemTaskInfo();
|
|
ProgressTaskInfo = GContext.container.Resolve<EventCanTableContext>().InitProgressTaskInfo();
|
|
RewardPool = GContext.container.Resolve<EventCanTableContext>().InitRewardPool().ToArray();
|
|
WeightModifier = new EventCanWeightModifier(GContext.container.Resolve<EventCanTableContext>().GetInitialDrawsSinceLastRewards());
|
|
ChainPackProgress = 0;
|
|
ToPlayerPreferenceData().Save();
|
|
ToPlayfabData().Save();
|
|
}
|
|
else //read from playfab and playerprefs
|
|
{
|
|
EventId = eventId;
|
|
TicketCount = pfData.TicketCount;
|
|
CanList = new EventCanCanInfo[GContext.container.Resolve<EventCanTableContext>().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<EventCanTableContext>().GetGemTaskInfo(info.Key, info.Value));
|
|
GContext.container.Resolve<EventCanTableContext>().GetProgressInfoByIdx(pfData.ProgressTaskIdx, out var progressRequired, out var reward);
|
|
ProgressTaskInfo = EventCanProgressTaskInfo.Init(pfData.ProgressTaskIdx, pfData.ProgressTaskProgress);
|
|
RewardPool = GContext.container.Resolve<EventCanTableContext>().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<FishingEventData>().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
|
|
{
|
|
/// <summary>
|
|
/// RewardId, DrawCount
|
|
/// </summary>
|
|
public Dictionary<int, int> DrawsSinceLastRewards;
|
|
public EventCanWeightModifier(Dictionary<int, int> 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<EventCanTableContext>().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<EventCanTableContext>().GetRewardWeight(rewardId);
|
|
return;
|
|
}
|
|
mustGet = false;
|
|
weight = 0;
|
|
}
|
|
}
|
|
|
|
public class EventCanRewardInfo
|
|
{
|
|
public int RewardId;
|
|
/// <summary>
|
|
/// Used to decide which task to fulfill and whether to calculate dynamic weight
|
|
/// </summary>
|
|
public EEventCanRewardType RewardType;
|
|
public int ItemId;
|
|
public int ItemCount;
|
|
/// <summary>
|
|
/// 0 if this reward has dynamic weight.
|
|
/// </summary>
|
|
public int BaseWeight;
|
|
/// <summary>
|
|
/// Returns the item data of the reward.
|
|
/// </summary>
|
|
/// <returns>Transformed Item Data. Null if the reward is either gem or supply.</returns>
|
|
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<PlayerItemData>().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<EventCanTableContext>().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<EventCanTableContext>().GetNextProgressTaskInfo(this);
|
|
}
|
|
}
|
|
|
|
public static EventCanProgressTaskInfo Init(int TaskIdx, int CurrentProgress)
|
|
{
|
|
EventCanProgressTaskInfo info = new EventCanProgressTaskInfo();
|
|
info.TaskIdx = TaskIdx;
|
|
info.CurrentProgress = CurrentProgress;
|
|
GContext.container.Resolve<EventCanTableContext>().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;
|
|
/// <summary>
|
|
/// GemTaskId, CurrentGemProgress
|
|
/// </summary>
|
|
public Dictionary<int, int> GemTaskInfo;
|
|
/// <summary>
|
|
/// RewardId, DrawCount
|
|
/// </summary>
|
|
public Dictionary<int, int> 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<int, int>();
|
|
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<int, int>();
|
|
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<IUserService>().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<int[]>(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<EventCanTableContext>().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<EventCanModel>();
|
|
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
|
|
}
|