Files
2026-05-26 16:15:54 +08:00

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
}